Openscad

Attention, cette page n'est pas une aide à l'utilisation d'OpenSCAD mais une initiation passant par différentes étapes afin d'apprendre, petit à petit, le fonctionnement du logiciel.

Il y a donc plusieurs choses dont je n'ai, volontairement, pas parlé car elles sont inutiles à un débutant et pourront donc être vues plus loin dans l'initiation, quand le besoin sera présent.

Si vous vous rendez compte qu'il manque des paramètres à une primitive, qu'il manque des primitives ou qu'il aurait été plus facile de faire quelque chose selon une autre méthode, c'est donc normal et, si vous vous en rendez compte, c'est que cette initiation n'est pas faite pour vous :)

Vous retrouverez des exercices tout au long de la page sur ce qui a déjà été vu.

Vous pouvez me formuler toutes vos remarques ou demandes à l'adresse suivante : marc@vanlindt.be


Introduction

OpenSCAD est un logiciel de modélisation 2D et 3D solide, paramétrique, multiplate-forme, libre et open-source utilisant un pseudo-langage pour concevoir les modèles.

Cela signifie qu’il est possible de créer, de manière paramétrique :

  • des plans en 2D
  • des pièces en 3D

Les formats d'exportation possibles sont :

  • 2D :
    • DXF : Drawing eXchange Format : Je déteste ce format. Autant il est un “standard” que l'on retrouvera dans quasi tous les logiciels, autant il est sujet à la plus grande incompatibilité, cela venant du fait que Autodesk a préféré faire évoluer le DXF sans changer de nom, et amenant donc au fait qu'il existe maintenant quatorze versions différentes de ce format…
      A moins de ne préparer votre DXF exactement en fonction de l'usage voulu, il y a toutes les chances que les premiers essais d'utilisation passent par des erreurs…
      Par contre, jusqu'à présent, l'exportation en DXF de OpenSCAD a toujours été très propre et fournis des fichiers conformes aux attentes, ne nécessitant pas, ou peu, de modifications.
       
    • SVG : Scalable Vector Graphics : Format vectoriel ouvert basé sur le langage XML. Son ouverture assure une compatibilité inégalée avec quasi tous les logiciels et systèmes.
      Il permet ainsi aussi bien d'être utilisé pour des créations graphiques telles que celles qui pourraient être réalisées avec Illustrator, qu'utilisé (ce qui est le cas de Google Map et OpenStreetMap) pour la cartographie ou encore de format d'exportation pour une application personnalisée (mon spirographe) ^^
       
  • 3D :
    • AMF : Additive Manufacturing Format : Format ouvert et standard, répondant donc à une norme ISO ( https://www.astm.org/Standards/ISOASTM52915.htm ).
      Il est l'évolution du format STL et permet la gestion des couleurs, textures, réseaux géométriques, etc.
       
    • CSG : Constructive Solid Geometry : Format particulier ne permettant l'export que des primitives dans lequel est constitué “l'arbre” de modélisation. Cela signifie qu'à partir de ce format il est possible, pour un logiciel traitant ce format, de récupérer les possibilités paramétriques de l'objet exporté, mais au prix de pas mal d'efforts quand même ;) Ce format est supporté nativement par FreeCAD.
       
    • OFF : Geomview Object File Format : Geomview est un logiciel orienté mathématique et éducation permettant la création d'objets géométriques complexes.
       
    • STL : STereoLithography ou Standard Triangle Language ou Standard Tessellation Language : Format créé par la société 3DSystem et actuellement le plus utilisé car compatible avec, quasi, tous les logiciels.
      C'est ce format que je conseillerais pour l'exportation de vos pièces : il est léger et devenu universel.

Les logiciels de modélisation 3D sont divisés en deux grandes catégories :

  • Logiciels de modélisation surfacique, pour la création d’images de synthèses (3D Studio Max, Maya, Lightwave, Blender, …)
  • Logiciel de modélisation de solides, en général à orientation industrielle (SolidWorks, Inventor, REvit, Fusion360, OpenSCAD,…)

La principale différence vient du fait que ce qui est important dans un logiciel de modélisation surfacique est “ce qu’on voit” là où dans un logiciel de modélisation de solide est important “ce qui est”.

L’accroissement du nombre d’imprimantes 3D a donc eu à faire face à beaucoup de désillusions quand certains ont cru pouvoir imprimer sans problème du surfacique.

Exemple rencontré lors du traitement d’un fichier exporté depuis 3D Studio Max :

Nous pensons voir un cube “solide”… …en réalité six surfaces placées pour donner l’illusion du cube.

En solide, un cube ne comprendra que 8 points et 12 vecteurs pour 6 faces.

En surfacique, nous risquons de nous retrouver avec un cube formé de 6 objets, formé en tout de 24 points et 24 vecteurs pour 6 faces visibles…

Cela signifie qu’au travers de quelques paramètres, la pièce pourra se modifier sans qu’il ne soit besoin de recréer entièrement la pièce.

Cela signifie aussi que la conception en elle-même diffère de la conception dans un logiciel classique et qu’il faudra donc s’habituer à cette méthode de modélisation différente.

Cela signifie que ce logiciel est disponible sur, quasi, tous les systèmes existants : Windows, MacOS, Linux et est compatible avec des systèmes tel que Raspberry Pi.

Cela signifie que le code-source est disponible et déposé sous licence libre (Licence GPL), assurant :

  • Droit garanti : Les termes de la GPL autorisent toute personne à recevoir une copie d'un travail sous GPL.
  • Le copyleft : La GPL ne donne pas à l'utilisateur des droits de redistribution sans limite. Le droit de redistribuer est garanti seulement si l'utilisateur fournit le code source de la version modifiée. En outre, les copies distribuées, incluant les modifications, doivent être aussi sous les termes de la GPL.
  • La Licence : Le fait que la GPL soit conçue comme une licence et non un contrat assure que celle-ci relève du droit d'auteur dans toutes les juridictions, certaines juridictions faisant passer tout contrat comme relevant du droit des contrats, même si le sujet du contrat est le droit d'auteur.
  • Gratuité de la source : Il n'est pas interdit de faire payer l'accès aux sources. Néanmoins, les droits fondamentaux accordés par la licence libre feront que dès le premier partage, elles pourront être redistribuées gratuitement par la personne les ayant reçu en partage.
  • La liberté ou la mort : S'il n'est pas possible de respecter la licence par, par exemple, décision de justice, le développement cesse.

Contrairement à un logiciel de modélisation classique dans lequel une interface graphique est pensée pour permettre l’appel, via un système d’icônes, à des objets que nous plaçons à l’écran et sur lesquels nous pouvons agir au travers d’autres icônes présentes dans cette interface, il n’y a ici qu’une interface graphique minimaliste, tout devant être “codé” dans un langage de programmation simplifié mais néanmoins pensé pour permettre la modélisation de pièces extrêmement complexes.

L’utilisation d’un pseudo-langage permet donc une très grande facilité pour la conception d’objets paramétriques.

Installation, interface et configuration de base

La première chose à faire est de charger et installer OpenSCAD.

Rendez-vous à cette adresse : http://www.openscad.org/downloads.html et descendez dans la page jusqu’à trouver “Development snapshot”.

Téléchargez cette version car, même si version en cours de développement, elle est exempte de bugs et propose des options qui nous seront utiles et présentes dans ce tutoriel.

La seconde chose à faire est mettre dans vos marques-pages le OpenScad Cheat Sheet : http://www.openscad.org/cheatsheet/

Ne vous attardez pas trop dessus pour le moment, mais n'hésitez pas à y revenir à tout moment pour mieux comprendre ce qui est vu.

Une fois le logiciel installé, vous devriez avoir une interface ressemblant à celle-ci : Elle est divisée en trois fenêtres :

  • Fenêtre de gauche : celle où nous écrirons le code de notre objet.
  • Fenêtre de droite en haut : celle où sera visualisé la pièce en cours de création.
  • Fenêtre de droite en bas : celle où s’indiqueront les informations générales, celles que l’on aura demandé à voir affichées ainsi que les erreurs éventuelles en cours de conception.

Une fois que vous aurez créer ou modifier un objet les deux principales touches à connaître sont :

  • F5 : Prévisualisation rapide de l’objet
  • F6 : Rendu de l’objet, qu’il vous sera alors possible d’exporter.

De nombreuses options utiles sont désactivées par défaut, la première chose à faire sera de les activer. Pour ce faire, rendez-vous dans Edition et choisissez Préférences

Vue 3D


Dans cette fenêtre, vous pourrez choisir les couleurs de l’interface 3D.

Editeur


Dans cette section, vous configurerez l’éditeur en choisissant la police, la taille de caractère, etc… Je vous conseille de ne rien changer ici.

Fonctionnalités


La partie des préférences la plus importante. En principe, tout devrait être décoché. Cochez donc l’ensemble, les plus importants étant svg-import et customizer.

Pourquoi ces paramètres ne sont pas activés par défaut ? Car ils ne sont pas pris en compte lors d'un appel à OpenSCAD en mode console. (cf. plus loin)

Avancé


Cette section des préférences sera importante lorsque vous réaliserez des pièces complexes ou fortement détaillées.

Selon la puissance de votre ordinateur et de votre carte graphique, n’hésitez pas à modifier ces valeurs.

Néanmoins, ce tutoriel ne nécessitera pas que l’on change actuellement ces valeurs, donc, si vous n’avez pas la moindre idée de ce que peuvent-être ces paramètres, n’y touchez pas :)

Les principes de base de modélisation

Modélisation non-surfacique

Que cela soit en 2D ou en 3D, OpenSCAD ne permet pas la modélisation surfacique (par opposition à volumique, ou “solide” comme expliqué plus haut).

Ce que cela signifie également est qu’il n’existe pas de courbes dans OpenSCAD : toutes les courbes sont formées de droites pour la 2D et de triangles pour la 3D et calculées en fonction de la taille de l’objet auquel nous faisons appel.

Pourquoi ?

Car la ligne, forcément, c'est une ligne, donc elle est droite, le triangle car c'est la seule forme forcément plane.

Exemple de l'importance de la triangulation :

Rectangles (quad)Triangles (tri)

où nous voyons que si l'on ne passait pas par des triangles, certaines surfaces ne sont pas planes.

Cela a un grand intérêt pour plusieurs raisons :

  • Ne pas travailler en surfacique assure ne pas avoir à trianguler ces surfaces par la suite, cause de nombre d’erreurs si triangulation demandée sur pièces complexes.
  • La taille du fichier est diminuée puisqu’il n’y a pas, comme le ferait SolidWorks, une triangulation ne tenant compte que d’un facteur “qualité” mais bien d’un facteur “qualité selon taille”.

Exemple :

Sphère de 1mm de diamètreSphère de 1cm de diamètreSphère de 10cm de diamètre
Cercle de 1mm de diamètreCercle de 1cm de diamètreCercle de 10cm de diamètre

Cela est fait pour une raison très simple : à cette taille, plus de détails serait inutile et alourdirait considérablement la taille du fichier final. Il est néanmoins possible de forcer le nombre de faces voulues.

Les différentes primitives vues peuvent donner l'impression que ce ne sont pas des triangles qui sont utilisés, néanmoins si on analyse les vecteurs créés, ce sont bien des triangles qui apparaîtront :

Le langage

Avant de commencer à réaliser une pièce, voici quelques petites choses importantes à savoir car nous partions directement de certaines bases présentées ici par la suite.

Tous les objets 2D ou 3D de base seront appelés par la suite “primitives” car ce sont les objets de base sur lesquels nous travaillerons et que nous transformerons.

La ponctuation

;
Le point virgule doit être placé à la fin de chaque primitive ou à la fin de chaque déclaration de variable afin de montrer que nous en avons fini.
()
Les parenthèses sont placées directement après le nom de tout chose à laquelle nous pouvons faire appel, à l'exception des variables, et sert à y indiquer les paramètres de ce à quoi nous faisons appel.
[]
Les crochets sont utilisés au sein de parenthèses afin qu'un seul paramètre reçoive plusieurs valeurs.
,
La virgule s'utilise au sein des parenthèses et sert à séparer les éléments, paramètres, etc.
{}
Les accolades sont utilisées pour indiquer que nous travaillons sur tout le groupe d'objets contenu dans ces accolades.
//
Permet de créer un commentaire au sein du code.
/* 
*/
Permet de créer un commentaire sur plusieurs lignes, tout ce qui est placé entre ces signes étant considéré comme tel.

Exemple :

// Ma Croix version 1
taille=10;
translate([taille,taille,0])
{
    square([taille,taille/5],center=true);
    square([taille/5,taille],center=true);
/*
Nous obtenons
ainsi une
belle croix!    
*/
}

L’unité de mesure

OpenSCAD travaillera en “unités”, c’est au moment où vous importerez votre création dans un autre logiciel qu’il vous sera demandé en quelle unité vous désirez travailler.

Ainsi, si vous avez créé votre pièce en décidant que “1” valait 1 cm, vous indiquerez dans le logiciel d’impression ou de découpe que l’unité choisie est le centimètre.

Par défaut, nous partirons du fait que nous travaillons en millimètres.

Cela s'applique également à l'importation d'image, un pixel valant une unité.

Les variables

Ce qui rend OpenSCAD “paramétrique” est, entre autres, de pouvoir faire appel à des variables.

Les variables ne prennent pas de parenthèses car elles ont une valeur définie et ne peuvent être transformées.

Contrairement à la majorité des langages, selon le type de données indiquées, OpenSCAD saura comment traiter ces variables.

Exemple :

Longueur   = 10;
Largeur    = 20;
Profondeur = 30;
MonTexte   = "Bravo! votre pièce est prête!";
cube([Longueur,Largeur,Profondeur]);
echo(MonTexte);

Cela nous donnera un cube ayant ces dimensions et pour lequel il suffira de modifier ces variables pour voir notre pièce se transformer.

“echo()” permet d’envoyer un message à la console qui sera alors celui contenu dans la variable “MonTexte” :

Nous pouvons voir que la création d’une variable termine également par un point-virgule afin d’indiquer que nous en avons “terminé” avec cette déclaration.

Les variables ont également une grande importance puisque c’est elles qui permettent d’utiliser le menu “Customizer” que l’on a coché dans les préférences :

Enfin, c'est également via les variables qu'un objet sera “customizable” sur le site Thingiverse :

Attention! Si votre but est de créer une pièce pouvant être modifiée via le customizer ou via celui de Thingiverse, il est possible de faire en sorte que le customizer offre des possibilités différentes en modifiant la manière dont la variable est déclarée, cela en utilisant un commentaire directement placé à la fin de la déclaration de la variable :

Exemple :

Variable1 = "Texte";
Variable2 = "Oui";//[Oui,Non,Sans doute,Peut-être]
Variable3 = 0;//[-15,-10,-5,0,5,10,15]
Variable4 = 0;//[-15:15]
Variable5 = 0;
Variable6 = 0.00000000000000000001;
Variable7 = true;

La différence entre les variables 5 et 6 est que déclarées de cette manière, la variable 5 ne pourra être modifiée que par pas de 1 là où la variable 6 pourra l'être par pas de 0.0000001. En indiquant autant de 0, nous restons donc à une valeur de 0 pour cette variable tout en permettant l'utilisation de nombre à virgule.

Les opérations mathématiques

Il est possible à tout moment de faire appel à des formules mathématiques, en fait, idéalement, il faudrait que vous ne soyez pas tout à fait nul en math :-x

Exemple :

Taille = 10; 
Modificateur_taille = sqrt(0.5); 
square([Taille,Taille]); 
translate ([Taille*1.5,0,0]) 
rotate([0,0,360/8]) 
scale([Modificateur_taille,Modificateur_taille,Modificateur_taille]) 
square([Taille,Taille]);

Nous pouvons voir ici que nous avons fait appel à “sqrt(0.5)” afin de définir que le redimensionnement devant être réalisé est de la racine carrée de un demi (0.707107…).

Nous avons également effectué une translation de “Taille multiplié par 1 et demi” et une rotation 360 degrés divisés en 8.

Nous pouvons voir également un premier objet paramétrique puisqu’il suffira de modifier la variable “Taille” pour que tout l’objet soit transformé :

Taille = 1; 
Modificateur_taille = sqrt(0.5); 
square([Taille,Taille]); 
translate ([Taille*1.5,0,0]) 
rotate([0,0,45]) 
scale([Modificateur_taille,Modificateur_taille,Modificateur_taille]) 
square([Taille,Taille]);

Voici une liste des principales que nous utiliserons par la suite ou qui peuvent être utiles “en général” :

sin(60)
Valeur : 0.866025
cos(60)
Valeur : 0.5
ceil(4.5)
Valeur : 4
floor(4.5)
Valeur : 5
round(4.4)
Valeur : 4
round(4.6)
Valeur : 5
sign(2)
Valeur : 1
sign(-2)
Valeur : -1
len([2,4,6,8])
Valeur : 4
sqrt(16)
Valeur : 4
pow(2,3)
Valeur : 8

Nous pouvons faire appel à des objets 2D ou 3D.

Les objets 2D peuvent être transformés en objet 3D au moyen d’extrusions tout comme un objet 3D peut être transformé en objet 2D au moyen de projections et coupes, néanmoins il est impossible de mélanger 2D et 3D : l’ensemble de la création finale doit être, soit entièrement 2D pour permettre l’exportation en DXF/SVG, soit entièrement 3D pour permettre l’exportation en STL.

Voici la liste des différentes primitives auxquelles vous pouvez faire appel ainsi que les paramètres disponibles pour chacun. Attention, elles ne sont pas toutes présentes, certaines plus complexes allant être vues plus tard lorsque l’utilisation répondra à un besoin.

Primitives 2D

circle()

Primitive permettant la réalisation de cercles et formes géométriques grâce au nombre de faces forcées.

  • r : indique le rayon du cercle
  • d : indique le diamètre du cercle
  • $fn : force le nombre de faces

Exemples :

circle(r=10);
circle(d=10);
circle(r=10,$fn=8);

square()

Primitive permettant de réaliser des carrés et rectangles.

  • [x,y] : taille du rectangle en X et Y. S'il n'est indiqué qu'un nombre, hors crochets, la forme sera un carré.
  • center = true/false : si sur “true”, le rectangle verra son centre situé aux coordonnées 0,0. Par défaut : false.

Attention, center est le seul paramètre dont la réponse est un texte ne s’écrivant pas entre guillemets.

Exemples :

square(10);
square([10,10],center=true);
square([10,10],center=false);

text()

Primitive permettant l’affichage d’un texte. Dès que vous y ferez appel, OpenSCAD générera la liste des polices disponibles afin qu’il soit possible d’y faire appel. Le message suivant s’affichera alors pendant quelques secondes :

Cette primitive dispose d’un très grand nombre de paramètres :

  • Le texte : à mettre en premier, entre guillemets. Il n’y a aucun problème avec les caractères spéciaux.
  • size : permet de définir la taille du texte. Par défaut : 10.
  • font : permet de définir la police de caractère à choisir.
  • halign = left/center/right : permet de définir l’alignement horizontal. Par défaut : left.
  • valign = top/center/baseline/bottom : permet de définit l’alignement vertical. Par défaut : baseline.
  • spacing : permet de définir l’espacement entre les lettres. Par défaut : 1
  • direction = rtl/ltr/ttb/bbt: permet de définir la direction du texte. Par défaut : ltr
    • rtl = right to left
    • ltr = left to right
    • ttb = top to bottom
    • btt = bottom to top.
  • $fn : permet de déterminer la qualité générale du texte.

Afin de connaître les polices disponibles, rendez-vous dans “Aide”-”Liste des polices”.

La fenêtre suivante apparaîtra :
Dans laquelle, après avoir sélectionné la police voulue, vous cliquerez sur “Coller dans le presse-papier” pour pouvoir ensuite la re-coller dans votre code.

Exemples :

text("Test");
text("OpenSCAD");
text("█▓▒░ ₪ ░▒▓█");
text( 
  "Test",
  size=10, 
  halign="center", 
  valign="top", 
  spacing=0.8, 
  direction="ttb" 
);
text( 
  "Test",
  size=20, 
  halign="center", 
  valign="center", 
  spacing=2, 
  direction="ltr"
);
text( 
  "Test", 
  font="Halloween:style=Regular", 
  size=20, 
  halign="center", 
  valign="center", 
  spacing=0.8, 
  direction="ltr"
);

Comme nous pouvons le voir dans ces exemples, pour plus de facilité, les paramètres peuvent être mis chacun sur une ligne. Nous pouvons également voir que, contrairement à “center”, tous les paramètres nécessitant une réponse sous forme de texte voient cette réponse formulée entre guillemets.

Primitives 3D

sphere()

Primitive permettant la réalisation de sphères.

  • r : indique le rayon du cercle
  • d : indique le diamètre du cercle
  • $fn : force le nombre de faces

Exemples :

sphere(r=10);
sphere(d=10);
sphere(r=10,$fn=8);

cube()

Primitive permettant de réaliser des cubes.

  • [x,y,z] : taille du cube en X, Y et Z.
  • center = true/false : si sur “true”, le cube verra son centre situé aux coordonnées 0,0. Par défaut : false.

Exemples :

cube([10,20,40]);
cube([10,10],center=true);
cube([10,20,40],center=true);
cube([10,40,20],center=true);

cylinder()

Le cylindre peut être utilisé de deux manières différentes selon que le cylindre soit voulu conique ou non.

Simple cylindre :

  • h : indique la hauteur du cylindre
  • r : indique le rayon du cercle
  • d : indique le diamètre du cercle
  • $fn : force le nombre de faces
  • center = true/false : si sur “true”, le cylindre sera aligné au centre de sa hauteur.

Forme conique :

  • h : indique la hauteur du cylindre
  • r1 : indique le rayon du cercle à la base du cylindre
  • r2 : indique le rayon du cercle au sommet du cylindre
  • d1 : indique le diamètre du cercle à la base du cylindre
  • d2 : indique le diamètre du cercle au sommet du cylindre
  • $fn : force le nombre de faces
  • center = true/false : si sur “true”, le cylindre sera aligné au centre de sa hauteur.

Exemples :

cylinder(r1=10,r2=5,h=20);
cylinder(d1=10,d2=5,h=20);
cylinder(d=10,h=20);

Essayez de réaliser ces différentes pièces, les corrigés se trouvent en fin de page.

Exercice 1Exercice 2Exercice 3Exercice 4
Exercice 5Exercice 6Exercice 7Exercice 8

Modélisation

Il est possible d’importer trois types de fichiers : vectoriels, images ou solides.

Vectoriel

  • DXF : Il est impératif que le DXF importé ne comprenne aucune courbe. Si c’était le cas, le DXF ne s’afficherait pas (ou malà et le message d’erreur suivant s’afficherait dans la console :

  • SVG : Un SVG peut comprendre des courbes. Néanmoins, contrairement à une modélisation classique, le nombre de points formant la courbure d’un SVG sera fixe : que vous importiez le SVG d’un rond de 1mm ou 10cm, ils seront formés d’un grand nombre de traits pour simuler la courbure.

Si vous en avez la possibilité, je vous conseille fortement de travailler avec des SVG plutôt que du DXF.
Nous faisons appel un fichier vectoriel de la manière suivante :

import("monfichier.xxx");

Attention! Il existe une différence importante entre les deux formats de fichier quant au résultat obtenu après importation.
Par exemple, nous créons un carré dans InkScape de 100mm² avec le mm utilisé comme unité de mesure par défaut :

Nous sauvegardons ensuite ce résultat aux formats SVG et DXF et nous les importons dans OpenSCAD

SVG DXF

Nous voyons que notre SVG a bien, comme indiqué dans InkScape, une taille de 100 sur 100 et est parfaitement centré. Le DXF semble ne pas être du tout à la bonne taille.

Cela s'explique par le fait que le DXF travaille en “points” et qu'il faut donc, avant de commencer à travailler dans votre logiciel vectoriel, si c'est du DXF que vous voulez utiliser, s'assurer de travailler dans cette unité de mesure :

permettant ainsi d'obtenir le bon résultat après importation du DXF :

Il est donc très fortement déconseillé de travailler à la fois avec du DXF et du SVG et il vaut donc mieux choisir son format dès le début et faire en sorte que tous les fichiers auxquels nous ayons à faire appel soient dans le même, avec les unités de mesure bien définies.

Images

Le format utilisable dans OpenSCAD est le PNG et doit idéalement être une image en noir et blanc car elle sert à créer un relief.

Exemple :

Image en noir et blancRésultat de l’importation dans OpenSCAD

Nous faisons appel une image de la manière suivante :

surface(file = "monfichier.png");

Solides

A partir du moment où le fichier STL est conforme, il devrait s’afficher très rapidement.

Attention cependant que, parfois, l’objet a été créé sans tenir compte des dimensions. Il n’est donc pas rare de ne rien voir s’afficher tout simplement car l’objet est beaucoup trop grand ou situé à des coordonnées lointaines.

Pour savoir si un objet est conforme le mieux est de l’ouvrir dans le logiciel MeshLab afin de vérifier la pièce :

Nous pouvons voir dans ce STL que de nombreuses erreurs (Non Manifold…) sont présentes.

Ce fichier sera quand même utilisable dans OpenSCAD mais il sera impossible de réaliser une opération sur celui-ci et le fichier final sorti d’OpenSCAD comprendra, pour le STL importé, les mêmes erreurs à l’exportation.

OpenSCAD ne corrige rien.

Nous pouvons d’ailleur voir sur le résultat imprimé que là où il y a ces problèmes, il semble y avoir des erreurs d’impression (Nous pouvons voir également qu’il a stoppé l’impression avant la fin…) :

Nous faisons appel à un fichier solide de la manière suivante :

import("monfichier.stl");

Les trois transformations basiques :

  • la translation : translate()
  • la rotation : rotate()
  • l’échelle : scale()

s’emploient de la même façon : nous indiquons entre parenthèses et entre crochets les valeurs pour les axes X,Y et Z. La rotation s’exprime en degrés.

Attention à l’ordre dans lequel vous mettez transformateurs : effectuer une translation puis une rotation n’est pas la même chose qu’effectuer une rotation puis la translation…

Vous pouvez également multiplier les appels aux transformateurs.

Exemples :

square(); 
translate([2,2,0]) 
circle(d=1,$fn=32);
square(center=true); 
translate([2,2,0]) 
rotate([0,0,45]) 
square(center=true);
square(center=true); 
rotate([0,0,45]) 
translate([2,2,0]) 
square(center=true);
circle(d=1,$fn=10); 
rotate([0,0,120]) 
translate([5,0,0]) 
circle(d=1,$fn=10); 
rotate([0,0,120]) 
translate([5,0,0]) 
rotate([0,0,120]) 
translate([5,0,0]) 
circle(d=1,$fn=10);
cube(center=true); 
scale([0.5,0.5,0.5]) 
translate([1.5,0,0]) 
cube(center=true);
cube(center=true); 
translate([0.75,0,0]) 
scale([0.5,0.5,0.5]) 
cube(center=true);
Taille=10; 
cube(Taille,center=true); 
scale([0.5,0.5,0.5]) 
translate([Taille*1.5,0,0]) 
cube(Taille,center=true);
Taille=10; 
cube([Taille,Taille,Taille],center=true); 
scale([1/2,0.5,2*0.25]) 
translate([Taille*1.5,0,0]) 
cube([Taille,Taille,Taille],center=true);
cylinder(d=100,h=50); 
translate([30,0,0]) 
cylinder(d=10,h=80); 
rotate([0,0,360/5]) 
translate([30,0,0]) 
cylinder(d=10,h=80); 
rotate([0,0,360/5*2]) 
translate([30,0,0]) 
cylinder(d=10,h=80); 
rotate([0,0,360/5*3]) 
translate([30,0,0]) 
cylinder(d=10,h=80); 
rotate([0,0,360/5*4]) 
translate([30,0,0]) 
cylinder(d=10,h=80);

A cela se rajoute un transformateur qui s'utilise différemment, resize(), permettant de définir une taille exacte à un objet. Exemple :

scale([1,1,2])
sphere(d=10,$fn=24);
resize([30,7.5,5])
scale([1,1,2])
sphere(d=10,$fn=24);

resize() nous permettra donc de faire en sorte qu'un objet ait exactement les dimensions en X, Y et Z que nous voulons.

Il peut néanmoins être utilisé d'une autre manière afin de forcer un objet à avoir une taille voulue dans un des axes :

resize([0,0,10],auto=true)
scale([1,1,2])
sphere(d=10,$fn=24);

Nous laissons à 0 les valeurs devant rester variables, nous indiquons la taille voulue pour l'axe qui nous intéresse et nous indiquons auto=true comme paramètre afin que les valeurs laissées à 0 soient calculées automatiquement. Nous aurons donc l'objet d'origine avec, exactement, une taille de 10 sur l'axe Z.

Exercices : les transformateurs basiques

Réaliseé ces formes en utilisant des variables. S’il y a deux images pour un seul exercice c’est qu’il est possible d’obtenir ces deux formes avec le même code mais en modifiant simplement les variables.

Exercice 1Exercice 2
Exercice 3

Les modules sont les objets personnalisés auxquels on fait appel comme nous ferions appel à une primitive et auquel nous pouvons appliquer toutes les transformations s’appliquant aux primitives..

Par exemple, vous créez cet objet en forme de croix :

cube([10,2,2],center=true);
cube([2,2,10],center=true);

Nous pouvons donc rendre cet objet paramétrable via des variables de la manière suivante :

Hauteur=10; 
Largeur=10; 
Epaisseur=2;

cube( 
  [Largeur,Epaisseur,Epaisseur], 
  center=true 
);

cube( 
  [Epaisseur,Epaisseur,Hauteur], 
  center=true 
);

Nous allons maintenant adapter ceci pour en créer un module. Voici deux manières de coder et faire appel à un module :

croix(h=10,l=10,e=2); 
module croix() 
{ 
  cube([l,e,e],center=true); 
  cube([e,e,h],center=true); 
}
croix(10,10,2); 
module croix(h,l,e) 
{ 
  cube([l,e,e],center=true); 
  cube([e,e,h],center=true); 
}

Je vous conseille de commencer à “hiérarchiser” votre code en plaçant systématiquement les éléments situés au sein d’un autre à une tabulation du début de la ligne et ainsi de suite selon le niveau.

Dans cet exemple nous pouvons voir que la croix d’origine a été placée entre :

module croix(h,l,e) 
{ 
}

Tout ce qui sera placé entre ces accolades fera partie de l’objet “croix()”.

Nous avons également indiqué les paramètres auxquels cet objet pourrait faire appel : “h” pour la hauteur, “l” pour la largeur et “e” pour l’épaisseur.

Cela permet maintenant de faire appel à notre objet “croix()” afin d’obtenir la croix correspondant aux paramètres voulus :

croix(h=20,l=10,e=1); 
translate([15,0,10]) 
croix(l=20,h=10,e=2); 
module croix() 
{ 
  cube([l,e,e],center=true); 
  cube([e,e,h],center=true); 
}
Exercice 1
pillule(d=10,h=20,q=32);
pillule(d=10,h=10,q=64);
Exercice 2
fenetre(l=100,h=150,d=10);
fenetre(l=200,h=70,d=30);

Il s’agit d’un ensemble d’opérations se réalisant entre deux ou plusieurs objets afin de transformer leur géométrie.

J’entend souvent dire : “Ah! C’est un peu comme le pathfinder dans Illustrator?!”

Non! Ce n’est pas comme le pathinder dans Illustrator , c’est Adobe qui a décidé de ne rien faire comme tout le monde et appelé “pathfinder” sa version des outils booléens que l’on retrouve dans beaucoup d’autres logiciels sous le nom “opérateurs booléens”…

Ils ne sont pas nombreux et s’utilisent de la même manière, l’opération se réalisant entre le premier et le second (et suivants…) objet.

Union Difference Intersection
Taille=100; 
union() 
{ 
  cube( 
    Taille, 
    center=true 
  ); 
  sphere( 
    d=Taille*sqrt(2), 
    $fn=64 
  ); 
}
Taille=100; 
difference() 
{ 
  cube( 
    Taille, 
    center=true 
  ); 
  sphere( 
    d=Taille*sqrt(2), 
    $fn=64 
  ); 
}
Taille=100; 
intersection() 
{ 
  cube( 
    Taille, 
    center=true 
  ); 
  sphere( 
    d=Taille*sqrt(2), 
    $fn=64 
  ); 
}

Bien que l’on ne voit pas grand chose dans le cas de l’union, l’objet présenté ne fait réellement plus qu’un seul objet solide et non deux se traversant.

Il est donc très important de réaliser les unions quand cela est nécessaire car c’est un garant de l’obtention d’un fichier final conforme, propre et léger.

Les opérations boolééennes s’effectuent aussi bien sur les primitives 2D que 3D :

taille=100; 
difference() 
{ 
  translate([taille/4,taille/4,0]) 
  square(taille,center=true); 
  translate([-taille/4,-taille/4,0]) 
  square(taille,center=true); 
}
taille=100; 
intersection() 
{ 
  translate([taille/4,taille/4,0]) 
  square(taille,center=true); 
  translate([-taille/4,-taille/4,0]) 
  square(taille,center=true); 
}
taille=100; 
difference() 
{ 
  translate([taille/4,taille/4,0]) 
  cube(taille,center=true); 
  translate([-taille/4,-taille/4,0]) 
  cube(taille,center=true); 
}
taille=100; 
intersection() 
{ 
  translate([taille/4,taille/4,0]) 
  cube(taille,center=true); 
  translate([-taille/4,-taille/4,0]) 
  cube(taille,center=true); 
}

Il est, bien entendu, parfaitement possible de réaliser des opérations booléennes entre modules.
Nous allons d’ailleurs, maintenant que les opérateurs booléens sont vus, modifier le module “croix()” que nous avons créé plus haut :

difference() 
{ 
  croix(h=6,l=6,e=2); 
  croix(h=7,l=7,e=1); 
} 
module croix() 
{ 
  union() 
  { 
    cube([l,e,e],center=true); 
    cube([e,e,h],center=true); 
  } 
}
difference() 
{ 
  croix(h=6,l=6,e=2); 
  scale([1,2,1]) 
  croix(h=7,l=7,e=1); 
} 
module croix() 
{ 
  union() 
  { 
    cube([l,e,e],center=true); 
    cube([e,e,h],center=true); 
  } 
}

Enfin, il est possible d’imbriquer les opérateurs booléens, rendant la hiérarchisation du code encore plus importante pour assurer une meilleure visibilité :

taille=100; 
difference() 
{ 
  intersection() 
  { 
    cube(taille,center=true); 
    sphere(d=taille*sqrt(2)); 
  } 
  translate([0,0,taille/2]) 
  sphere(d=taille/8); 
  translate([taille/2,0,0]) 
  sphere(d=taille/8); 
  translate([taille/2,taille/6,taille/6]) 
  sphere(d=taille/8); 
  translate([taille/2,-taille/6,-taille/6]) 
  sphere(d=taille/8); 
  translate([-taille/6,-taille/2,taille/6]) 
  sphere(d=taille/8); 
  translate([taille/6,-taille/2,-taille/6]) 
  sphere(d=taille/8); 
}
taille = 100; 
difference() 
{ 
  difference() 
  { 
    cube(taille,center=true); 
    sphere(d=taille*sqrt(2)*95/100,$fn=128); 
  } 
  rotate([56,0,45]) 
  cylinder(d=taille/10, $fn=16, h=taille*sqrt(2),center=true);
} 
union() 
{ 
  difference() 
  { 
    rotate([56,0,45]) 
    cylinder(d=taille/11,$fn=16,h=taille*sqrt(2)-1,center=true); 
    cube(taille*77/100,center=true); 
  } 
  difference() 
  { 
    cube(taille*771/1000,center=true); 
    sphere(d=taille*sqrt(2)*95/100*771/1000,$fn=128); 
  } 
}

Il est parfois difficile de visualiser ce qu’il se passe lorsque nous utilisons les opérateurs booléens, nous pouvons devant tout module ou primitive rajouter un dièse (#) afin de voir l’objet en surbrillance.

taille=100; 
difference() 
{ 
  sphere(d=taille); 
  cylinder(d=taille/5,h=taille*2,center=true); 
  rotate([90,0,0]) 
  cylinder(d=taille/5,h=taille*2,center=true); 
  rotate([0,90,0]) 
  cylinder(d=taille/5, h=taille*2,center=true); 
}
taille=100; 
difference() 
{ 
  sphere(d=taille); 
  #cylinder(d=taille/5,h=taille*2,center=true); 
  rotate([90,0,0]) 
  cylinder(d=taille/5,h=taille*2,center=true); 
  rotate([0,90,0]) 
  cylinder(d=taille/5, h=taille*2,center=true); 
}
taille=100; 
difference() 
{ 
  sphere(d=taille); 
  #cylinder(d=taille/5,h=taille*2,center=true); 
  rotate([90,0,0]) 
  #cylinder(d=taille/5,h=taille*2,center=true); 
  rotate([0,90,0]) 
  #cylinder(d=taille/5, h=taille*2,center=true); 
}

Il est également possible de rendre un objet transparent en mettant un signe “pourcent” (%) :

taille = 100; 
%difference() 
{ 
  difference() 
  { 
    cube(taille,center=true); 
    sphere(d=taille*sqrt(2)*95/100,$fn=128); 
  } 
  rotate([56,0,45]) 
  cylinder(d=taille/10,$fn=16,h=taille*sqrt(2),center=true); 
} 
union() 
{ 
  difference() 
  { 
    rotate([56,0,45]) 
    cylinder(d=taille/11,$fn=16,h=taille*sqrt(2)-1,center=true); 
    cube(taille*77/100,center=true); 
  } 
  difference() 
  { 
    cube(taille*771/1000,center=true); 
    #sphere(d=taille*sqrt(2)*95/100*771/1000,$fn=128); 
  } 
}
Exercice 1Exercice 2
Exercice 3Exercice 4
Attention! A réaliser sans cylindre!

En plus des trois transformateurs de base, il en existe de nombreux autres.
Nous allons maintenant en voir quelques nouveaux qui vont augmenter grandement les possibilités :)

offset()

permet de “grossir” ou retrécrir une forme 2D.

Utile, par exemple, pour simuler le résultat avec une CNC :

Forme voulueForme avec mèche de 4.5mm
Taille_meche = 4.5; 
difference() 
{ 
  square(50,center=true); 
  square(40,center=true); 
}
Taille_meche = 4.5; 
offset(-Taille_meche/2) 
offset(Taille_meche/2) 
difference() 
{ 
  square(50,center=true); 
  square(40,center=true); 
}

ou pour créer des formes à découper avec une découpeuse laser :

Nom = "LGHS"; 
Bordure = 30; 
Lissage = 20; 
difference() 
{ 
  offset(-Lissage) 
  offset(Bordure) 
  union() 
  { 
    translate([-50,0]) 
    circle(d=60); 
    text(Nom,font="Arial:style=Black",size=100); 
  } 
  translate([-50,0]) 
  circle(d=60); 
  text(Nom,font="Arial:style=Black",size=100); 
}

hull()

permet de créer l'enveloppe convexe d'un ou plusieurs objets 2D ou 3D.

Exemple :

taille = 50; 
hull() 
{ 
  square(taille,center=true); 
  translate([taille*3,0,0]) 
  circle(d=taille); 
}

Attention : hull() ne permet pas de transformer deux objets 2D en un objet 3D. Cela s'applique donc entre deux objets 2D pour obtenir un nouvel objet 2D ou entre objets 3D pour obtenir un nouvel objet 3D.

Exemple :

taille = 50; 
hull() 
{ 
  cube([taille,taille,0.01],center=true); 
  translate([0,0,taille*2]) 
  cylinder(d=taille*sqrt(2),h=0.01); 
}

Nous avons donc ici créé un cube et un cylindre ayant chacun une épaisseur de 0.01 et non un carré et un cercle.

Exercices : transformateurs complexes

Exercice 1
Réalisez un module permettant de réaliser un verre de cette manière, où h indique la hauteur, d le diamètre, e l'épaisseur, fond l'épaisseur du fond, et f1 et f2 le nombre de bords en bas et en haut :
verre(h=100,d=40,e=2,fond=10,f1=4,f2=8);
verre(h=40,d=40,e=5,fond=4,f1=64,f2=5);
Exercice 2

linear_extrude()

permet de transformer un objet 2D ou un DXF/SVG importé en objet 3D.

De nombreux paramètres permettent de définir la manière dont l'objet sera “extrudé” :

  • height : Hauteur
  • center=true/false : Tout comme pour le cylindre, mettra le centre de l'objet aux coordonnées [0,0,0]
  • convexity : permet de corriger la visualisation de la forme lorsque réalisé sur objet complexe
  • twist : Rotation effectuée entre la base et le sommet de la forme créée (s'exprime en degrés)
  • slices : Nombre de segments en lequel la forme générée sera découpées pour permettre un aspect plus “lisse”.
  • scale : Echelle entre la base et le sommet de l'objet créé.

Exemple :

linear_extrude(height=50)
square([10,10],center=true);
linear_extrude(height=50,center=true,twist=90)
square([10,10],center=true);
linear_extrude(height=50,center=true,twist=90)
square([10,10],center=true);
linear_extrude(height=50,center=true,twist=360,slices=32,scale=1/10)
square([10,10],center=true);

Nous pouvons voir sur tous ces exemples que l'extrusion se fait de manière linéaire vers le haut.
Cela vient du fait que l'objet de départ est parfaitement centré.
Si vous décentrez cet objet, l'extrusion se réalisera autour des coordonnées [0,0] de la forme 2D de départ.

Exemple :

linear_extrude(height=100,center=true,twist=1440,slices=128)
translate([10,0])
square([10,10],center=true);
linear_extrude(height=100,center=true,twist=1440,slices=128,scale=0)
translate([10,0])
square([10,10],center=true);

Comme dit plus haut, il est possible de réaliser cette opération sur des objets importés. Par exemple, nous partons de cette photo de la tour Eiffel pour en faire un SVG (très approximatif ^^) :

intersection() 
{ 
  linear_extrude(height=100,center=true) 
  import("eiffel.svg"); 
  rotate([0,90,0]) 
  linear_extrude(height=100,center=true) 
  import("eiffel.svg"); 
}

Résultat dans Blender afin de mieux visualiser l'objet :

Exercices : linear_extrude

Exercice 1
Créer un module générant une corne de licorne où d est le diamètre de chaque partie de la corne, e l'espacement entre chacune, h la hauteur, r le nombre de rotation (exprimé en tours et non en degrés) et q la qualité
licorne(d=10,e=5,r=1,h=100,q=64);
licorne(d=10,e=8,r=3,h=100,q=128);
Exercice 2
Créer un module général une colonne où d est le diamètre et h la hauteur. Vous pouvez utiliser un cylindre pour la partie cylindrique
colonne(d=40,h=200);
colonne(d=60,h=180);

rotate_extrude()

a beaucoup moins de paramètres que linear_extrude :

  • angle : indique sur combien de degrés se réalise l'extrusion
  • $fn : indique ici le nombre de subdivisions

Exemples :

torus(de=50,di=40,q=128,f=64,a=120); 
torus(de=35,di=25,q=16,f=3,a=240); 
torus(de=20,di=10,q=4,f=64,a=360); 

module torus() 
{ 
  Diff =de-di; 
  rotate_extrude($fn=q,angle=a) 
  translate([de-Diff/2,0]) 
  circle(d=Diff,$fn=f); 
}

Attention, si vous utilisez une forme importée, celle-ci doit impérativement se trouver collée au bord de la surface de travail.

PAS BONBONRésultat

Attention! Utiliser un angle défini demande une assez haute qualité dans l'extrusion car le nombre de faces se fera en fonction de la forme entière.

Exemple :

rotate_extrude(angle=270,$fn=8)
translate([10,0,0]) circle(d=3);
rotate_extrude(angle=270,$fn=5)
translate([10,0,0]) circle(d=3);
où nous voyons 6 faces avec un $fn de 8où nous voyons 3 faces avec un $fn de 5

Pour savoir le nombre de faces :
360°/8 = 45° → 270/45 = 6 d'où 6 faces.
360°/5 = 72° → 270/72 = 3,75 d'où 3 faces.

Exercices : rotate_extrude()

Exercice 1
l est la longueur, d1 et d2 le diametre en bas et haut de branche, a la force de l'arrondi et q la qualité
branche(l=100,d1=20,d2=30,a=80,q=128);
branche(l=100,d1=40,d2=40,a=15,q=256);

En plus des différentes primitives vues plus haut il en est deux beaucoup moins utilisées mais qui peuvent parfois être utiles : les polygones et les polyèdres.

A l'origine, OpenSCAD ne permettait pas l'importation vectorielle et 3D. Pour pallier à ce problème, ces deux primitives ont été créées et des convertisseurs se sont créés pour transformer les SVG et STL en polygones ou polyèdre.

Nous allons passer les polyèdres dont la création est vraiment trop complexe et devenue obsolète depuis la possibilité d'importer du STL.

Néanmoins, il est parfois utile de pouvoir créer rapidement une forme 2D simple ne correspondant pas aux primitives présentes sans passer par un logiciel vectoriel.

Par exemple, voici une pièce réalisée pour un ami dans le cadre d'un Repair Café :

Cette pièce est à la fois un bon exemple d'utilisation des polygons et de méthode de conception.

La première chose faite a été de créer le contour exterieur de la pièce :

polygon(points=[[0,4],[0,0],[75,0],[75,4],[75-28,40],[28,40]]);

Nous créons ensuite une nouvelle forme pour l'intérieur (j'aurais pu faire plus simple en faisant appel à square() …) :

polygon(points=[[0,0],[75,0],[75,10],[0,10]]);

Le fait de “huller” tout ça nous permettra de déjà mieux voir la pièce en :

hull()
{
    for(i=[-1:2:1])
    {
        translate([0,0,5*i])linear_extrude(0.01)
        polygon(points=[[0,4],[0,0],[75,0],[75,4],[75-28,40],[28,40]]);
    }
    linear_extrude(0.01)    
    polygon(points=[[0,0],[75,0],[75,10],[0,10]]);
}

Nous créons ensuite un nouveau polygone :

polygon(points=[[5,0],[70,0],[70-25,25],[30,25]]);

Et nous créons la différence :

difference()
{
  hull()
  {
    for(i=[-1:2:1])
    {
      translate([0,0,5*i])linear_extrude(0.01)
      polygon(points=[[0,4],[0,0],[75,0],[75,4],[75-28,40],[28,40]]);
    }
    linear_extrude(0.01)    
    polygon(points=[[0,0],[75,0],[75,10],[0,10]]);
  }
  translate([0,0,-10])
  linear_extrude(20)
  polygon(points=[[5,0],[70,0],[70-25,25],[30,25]]);
}

Il n'y a plus qu'à rajouter les derniers éléments à soustraire pour obtenir la pièce finale :

difference()
{
  hull()
  {
    for(i=[-1:2:1])
    {
      translate([0,0,5*i])linear_extrude(0.01)
      polygon(points=[[0,4],[0,0],[75,0],[75,4],[75-28,40],[28,40]]);
    }
    linear_extrude(0.01)    
    polygon(points=[[0,0],[75,0],[75,10],[0,10]]);
  }
  translate([0,0,-10])
  linear_extrude(20)
  polygon(points=[[5,0],[70,0],[70-25,25],[30,25]]);

  translate([75/2,0,0])
  rotate([-90,0,0])
  cylinder(d=4,$fn=64,h=45);

  translate([75/2,40,-10])
  cylinder(d=18,h=20,$fn=64);
}

Les boucles

Nous avons pu voir que, plusieurs fois, nous avons créé des objets où beaucoup de lignes étaient les mêmes à un changement de valeur près.

Exemple, le “gâteau” vu plus haut :

cylinder(d=100,h=50); 
translate([30,0,0]) 
cylinder(d=10,h=80); 
rotate([0,0,360/5]) 
translate([30,0,0]) 
cylinder(d=10,h=80); 
rotate([0,0,360/5*2]) 
translate([30,0,0]) 
cylinder(d=10,h=80); 
rotate([0,0,360/5*3]) 
translate([30,0,0]) 
cylinder(d=10,h=80); 
rotate([0,0,360/5*4]) 
translate([30,0,0]) 
cylinder(d=10,h=80);

Nous avons ici fait autant d'appels à un cylindre qu'il n'y a de bougies. La seule chose différente entre ces appels étant l'angle de rotation.
Nous allons donc créer une boucle de la manière suivante :

gateau(b=12); 

module gateau() 
{ 
  cylinder(d=100,h=50); 
  for(i=[0:b]) 
  { 
    rotate([0,0,360/b*i]) 
    translate([35,0,0]) 
    cylinder(d=8,h=70); 
  } 
}

Nous avons donc, ici, fait appel à :

for(i=[x:y])
{
}

et modifié la formule de rotation en :

rotate([0,0,360/b*i])

afin que la rotation s'effectue de 360° divisé par le nombre de bougies voulues et multiplié par i.
i prendra toutes les valeurs comprises entre x et y dans [x:y] et c'est donc autant d'objets qui seront créés.

Pourquoi i ? Car c'est comme ça. C'est via cette lettre que l'on représente la variable de boucle dans la majorité des langages. J'ai fait une recherche sur l'origine et n'ai rien trouvé. Si quelqu'un a l'information, elle est donc bienvenue :) Ce qui me parait le plus logique est que, i étant la 15ème lettre de l'alphabet, dans les tous premiers langages, il n'était pas possible de prendre plus de 15 variables car celles-ci étaient sans doute codées en binaire sur 4 chiffres (1111 = 15 en binaire).

Exemple :

roue_dentee(d=16); 
translate([19.5,8.2,0]) 
roue_dentee(d=24); 
translate([24.5,-8,0]) 
roue_dentee(d=8); 

module roue_dentee() 
{ 
  difference()
  { 
    union() 
    { 
      offset(-d/20) 
      offset(d/20) 
      union() 
      { 
        difference() 
        {  
          circle(d=d,$fn=d); 
          circle(d=d-2,$fn=d); 
        } 
        square([d-2,2],center=true); 
        square([2,d-2],center=true); 
      } 
      for(i=[0:d-1]) 
      { 
        rotate([0,0,360/d*i]) 
        translate([d/2,0,0]) 
        rotate([0,0,36]) 
        circle($fn=5); 
      } 
    } 
    circle($fn=6); 
  } 
}

Cette roue dentée étant un objet plus complexe, voici les différentes phases de modélisation pour y arriver :

La première chose à faire est de réaliser une “dent” de la roue. Pour cela, on fait au plus simple, un cylindre à cinq faces d'un diamètre de 1 :

circle(d=1,$fn=5);

Comme la taille de notre roue dépend du nombre de dents, le diamètre sera calculé en fonction de ce nombre. Nous créons une variable qui nous servira à placer les dents convenablement :

d=12;
for(i=[1:d])
{
    rotate([0,0,360/d*i])
    translate([d/4,0,0])
    circle(d=1,$fn=5);
}

Nous pouvons voir ici que ça donnerait mieux si la dent était inversée, nous le faisons donc et en profitons pour voir ce que donnerait la roue dentée pleine :

d=12;
#circle(d=d/2,$fn=d);
for(i=[1:d])
{
    rotate([0,0,360/d*i])
    translate([d/4,0,0])
    rotate([0,0,180])
    circle(d=1,$fn=5);
}

Nous continuons à réaliser le centre de notre roue dentée en supprimant le centre et rajoutant des barres. Nous ferons également appel à offset pour lisser :

d=16;
offset(-d/20)
offset(d/20)
union()
{
  difference()
  {
    circle(d=d/2,$fn=d);
    circle(d=d/2-2,$fn=d);
  }
  square([1,d/2-1],center=true);
  square([d/2-1,1],center=true);
}
/*
for(i=[1:d])
{
  rotate([0,0,360/d*i])
  translate([d/4,0,0])
  rotate([0,0,180])
  circle(d=1,$fn=5);
}
*/

Nous pouvons maintenant “unioner” tous nos éléments :

d=16;
union()
{
  offset(-d/20)
  offset(d/20)
  union()
  {
    difference()
    {
      circle(d=d/2,$fn=d);
      circle(d=d/2-2,$fn=d);
    }
    square([1,d/2-1],center=true);
    square([d/2-1,1],center=true);
  }
  for(i=[1:d])
  {
    rotate([0,0,360/d*i])
    translate([d/4,0,0])
    rotate([0,0,180])
    circle(d=1,$fn=5);
  }
}

Il ne reste plus alors qu'à finaliser l'objet en créant un trou (ici hexagonal) en son centre :

d=16;
difference()
{
    union()
    {
      offset(-d/20)
      offset(d/20)
      union()
      {
        difference()
        {
          circle(d=d/2,$fn=d);
          circle(d=d/2-2,$fn=d);
        }
        square([1,d/2-1],center=true);
        square([d/2-1,1],center=true);
      }
      for(i=[1:d])
      {
        rotate([0,0,360/d*i])
        translate([d/4,0,0])
        rotate([0,0,180])
        circle(d=1,$fn=5);
      }
    }
    circle(d=1,$fn=6);
}

Il est également possible d'imbriquer les boucles.

Exemple :

for(i=[0:180]) 
{ 
  for(j=[0:180]) 
  { 
    translate([i,j]) 
    cube([1,1,(cos(i*4)*sin(j*4)+1)*20]); 
  } 
}

Que nous pouvons “lisser” via un appel à hull() :

for(i=[0:89])
{
  for(j=[0:89])
  {
    hull()
    {
      translate([i,j])
      cube([1,1,(cos(i*8)*sin(j*8)+1)*20]);
      translate([i+1,j])
      cube([1,1,(cos((i+1)*8)*sin(j*8)+1)*20]);
      translate([i,j+1])
      cube([1,1,(cos((i)*8)*sin((j+1)*8)+1)*20]);
      translate([i+1,j+1])
      cube([1,1,(cos((i+1)*8)*sin((j+1)*8)+1)*20]);
    }
  }
}

Exemple de pièce réalisée avec ces techniques : un ruban de Moebius :
La première chose que nous allons faire est un objet définissant le nombre de faces :

Nombre_faces = 3;
Diametre_interieur = 40;
Diametre_exterieur = 50;

MaBase();

module MaBase()
{
  hull()
  {
    for(i=[0:Nombre_faces-1])
    {
      rotate([0,0,360/Nombre_faces*i])
      translate([(Diametre_exterieur-Diametre_interieur)/2,0,0])
      sphere(d=1);
    }
  }
}

Nous créons maintenant une boucle en fonction d'une variable Qualite afin de placer cette forme en cercle :

Qualite = 16;
Nombre_faces = 3;
Diametre_interieur = 40;
Diametre_exterieur = 60;
Diff = Diametre_exterieur-Diametre_interieur;

for(i=[0:Qualite-1])
{
    rotate([360/Qualite*i,0,0])
    translate([0,Diametre_interieur+Diff/2,0])
    MaBase();
}

module MaBase()
{
  hull()
  {
    for(i=[0:Nombre_faces-1])
    {
      rotate([0,0,360/Nombre_faces*i])
      translate([Diff/2,0,0])
      sphere(d=1);
    }
  }
}

Nous pouvons maintenant déjà “huller” notre forme en utilisant le hull() au sein de la boucle :

Qualite = 16;
Nombre_faces = 3;
Diametre_interieur = 40;
Diametre_exterieur = 60;
Diff = Diametre_exterieur-Diametre_interieur;

for(i=[0:Qualite-1])
{
  hull()
  {
    rotate([360/Qualite*i,0,0])
    translate([0,Diametre_interieur+Diff/2,0])
    MaBase();

    rotate([360/Qualite*(i+1),0,0])
    translate([0,Diametre_interieur+Diff/2,0])
    MaBase();
  }
}

module MaBase()
{
  hull()
  {
    for(i=[0:Nombre_faces-1])
    {
      rotate([0,0,360/Nombre_faces*i])
      translate([Diff/2,0,0])
      sphere(d=1);
    }
  }
}

Et pour obtenir notre effet Moebius, rajouter une rotation à MaBase() :

Qualite = 64;
Nombre_faces = 3;
Diametre_interieur = 40;
Diametre_exterieur = 60;
Nombre_tours = 1;
Diff = Diametre_exterieur-Diametre_interieur;

for(i=[0:Qualite-1])
{
  hull()
  {
    rotate([360/Qualite*i,0,0])
    translate([0,Diametre_interieur+Diff/2,0])
    rotate([0,0,(360*Nombre_tours)/Qualite*i])
    MaBase();

    rotate([360/Qualite*(i+1),0,0])
    translate([0,Diametre_interieur+Diff/2,0])
    rotate([0,0,(360*Nombre_tours)/Qualite*(i+1)])
    MaBase();
  }
}

module MaBase()
{
  hull()
  {
    for(i=[0:Nombre_faces-1])
    {
      rotate([0,0,360/Nombre_faces*i])
      translate([Diff/2,0,0])
      sphere(d=1);
    }
  }
}

Type de boucle s'utilisant pour créer l'intersection du résultat d'une boucle. Par exemple, si nous prenons un ensemble de cube :

rotate([17*1,189*i,4546*1])
cube([1,1,2],center=true);
rotate([17*2,189*i,4546*2])
cube([1,1,2],center=true);
rotate([17*3,189*i,4546*3])
cube([1,1,2],center=true);
rotate([17*4,189*i,4546*4])
cube([1,1,2],center=true);

et que nous en réalisons l'intersection :

intersection()
{
    rotate([17*1,189*i,4546*1])
    cube([1,1,2],center=true);
    rotate([17*2,189*i,4546*2])
    cube([1,1,2],center=true);
    rotate([17*3,189*i,4546*3])
    cube([1,1,2],center=true);
    rotate([17*4,189*i,4546*4])
    cube([1,1,2],center=true);
}

c'est bien l'intersection de nos différents cubes que nous voyons apparaître comme objet.

Néanmoins, si nous utilisons ici une simple boucle, le résultat ne tiendra pas compte de l'intersection :

intersection()
{
    for(i=[1:4])
    {
        rotate([17*i,189*i,4546*i])
        cube([1,1,3],center=true);
    }
}

Nous utilisons alors dans ce cas intersection_for :

intersection_for(i=[1:4])
{
    rotate([17*i,189*i,4546*i])
    cube([1,1,3],center=true);
}

Les variables - partie 2

Nous avons pu voir dans la boucle que i était une variable dont la valeur était [x:y]x est la valeur de début et y la valeur finale.

Il s'agit d'un nouveau type de variable que nous allons voir : les tables, c'est à dire une suite de valeurs.

Exemple :

Valeurs=[-5:2:5]; 
for(i=Valeurs) 
{ 
  translate([i*10,0,0]) 
  circle(d=10); 
}

où l'on peut voir que des cercles sont créés de -5 à 5 avec un pas de 2.

Valeurs=[-5,-4,-3,0,1,2,3,5]; 
for(i=Valeurs) 
{ 
  translate([i*10,0,0]) 
  circle(d=10); 
}

où seront prises toutes les valeurs, une par une, reprises dans aa. Lorsque les valeurs sont à prendre une par une, nous utilisons des virgules pour les séparer et non deux points.

Attention! Il n'est pas possible de mélanger les deux formes.

Valeurs=[0:10,12,15,18];

ne fonctionnera pas et il faut passer par :

Valeurs=[0,1,2,3,4,5,6,7,8,9,10,12,15,18];

Il est également possible de “retirer” le nombre de valeurs présentes dans ce type de variable grâce à len() et d'aller rechercher une valeur précise au sein de la variable.

Exemple :

Valeurs=[45,62,10,24,32,48,10]; 

for(i=[0:len(Valeurs)-1]) 
{ 
  translate([0,-10*i,0]) 
  text (str((i)," : ",Valeurs[i])); 
}

Nous pouvons donc voir que le nombre en première position a, en réalité, la position 0, raison pour laquelle les boucles commencent en général à 0 et finissent à -1 du nombre total de valeurs présentes.

Les tables donnent donc un tableau à une dimension. Il est parfaitement possible d'imbriquer les tables et les boucles.

Exemple :

Tableau=[
[1,2,3,4,5],
[2,3,4,5,1],
[3,4,5,1,2],
[4,5,1,2,3],
[5,1,2,3,4]
];

for(i=[0:4])
{
  for(j=[0:4])
  {
      translate([i,j])
      cube([1,1,Tableau[i][j]]);
  }
}

Nous avons donc ici deux boucles permettant de parcourir le tableau en allant de [0][0] à [4][4] et reprenant à chaque fois la valeur présente à cet endroit pour en faire la hauteur du cube.

Attention a ne pas imbriquer trop de boucles. Si nous reprenons notre surface lissée vue plus haut :

for(i=[1:90])
{
  for(j=[1:90])
  {
    hull()
    {
      translate([i,j])
      cube([1,1,(cos(i*8)*sin(j*8)+1)*20]);
      translate([i+1,j])
      cube([1,1,(cos((i+1)*8)*sin(j*8)+1)*20]);
      translate([i,j+1])
      cube([1,1,(cos((i)*8)*sin((j+1)*8)+1)*20]);
      translate([i+1,j+1])
      cube([1,1,(cos((i+1)*8)*sin((j+1)*8)+1)*20]);
    }
  }
}

nous pourrions être tenté, voyant qu'il y a quatre appels quasi similaires à un cube de modifier le code :

Valeurs=[[0,0],[1,0],[0,1],[1,1]];
for(i=[1:90])
{
  for(j=[1:90])
  {   
    hull()
    {
      for(k=[0:3])
      {
        translate([i+Valeurs[k][0],j+Valeurs[k][1]])
        cube([1,1,(cos((i+Valeurs[k][0])*8)*sin((j+Valeurs[k][1])*8)+1)*20]);
      }
    }
  }
}

C'est une bonne idée, mais si vous testez, vous remarquerez que la génération de la pièce est beaucoup (beaucoup!) plus longue.
Faites donc attention à choisir le juste milieu entre optimisation de l'écriture du code et rapidité de création.

Il est possible de faire en sorte qu'une variable voit sa valeur assignée en fonction de différentes conditions.

Nous allons pour cet exemple reprendre notre module “torus()” vu plus haut :

torus(de=50,di=40,q=128,f=64,a=120); 
torus(de=35,di=25,q=16,f=3,a=240); 
torus(de=20,di=10,q=4,f=64,a=360); 

module torus() 
{ 
  Diff =de-di; 
  rotate_extrude($fn=q,angle=a) 
  translate([de-Diff/2,0]) 
  circle(d=Diff,$fn=f); 
}

et le modifier comme suit :

torus(de=22,di=5,q=128,f=64,a=120);
module torus() 
{ 
  di = (di<=0) ? 0 : di;
  de = (de<=di) ? di+1 : de;
  Diff =de-di; 
  rotate_extrude($fn=q,angle=a) 
  translate([de-Diff/2,0]) 
  circle(d=Diff,$fn=f); 
}

Nous voyons donc au sein du module torus() la ligne :

  di = (di<=0) ? 0 : di;

di deviendra donc 0 si la valeur de di était inférieure ou égale à 0 (entraînant donc une erreur) et restera inchangée si ce n'était pas le cas.

La ligne :

  de = (de<=di) ? di+1 : de;

vérifie si de est bien supérieur à di et, si c'est le cas, fait garder la valeur souhaitée, sinon lui donne une valeur de di + 1

Cela vous permet donc également de donner des valeurs par défaut à vos modules.

Nous allons aller un peu plus loin en tentant de “corriger” le problème vu plus haut concernant le nombre de faces à un rotate_extrude() quand un angle est utilisé avec une qualité définie :

torus(de=22,di=15,q=5,f=64,a=270,qa=false);
module torus() 
{ 
  di = (di<=0) ? 0 : di;
  de = (de<=di) ? di+1 : de;
  q = (qa==true) ? ceil(360/a*q) :q;
  Diff =de-di; 
  rotate_extrude($fn=q,angle=a) 
  translate([de-Diff/2,0]) 
  circle(d=Diff,$fn=f);
}
torus(de=22,di=15,q=5,f=64,a=270,qa=true);
module torus() 
{ 
  di = (di<=0) ? 0 : di;
  de = (de<=di) ? di+1 : de;
  q = (qa==true) ? ceil(360/a*q) :q;
  Diff =de-di; 
  rotate_extrude($fn=q,angle=a) 
  translate([de-Diff/2,0]) 
  circle(d=Diff,$fn=f);
}

Nous avons rajouté un paramètre à torus() : qa indiquant si la “qualité automatique” est activée ou non.
Si c'est le cas, la ligne :

q = (qa==true) ? ceil(360/a*q) :q;

changera la qualité et le nombre de faces afin de s'assurer qu'il corresponde à ce qui est indiqué en q.

Reprenons notre module croix() vu plus haut et modifions le :

croix(); 

module croix() 
{ 
  l = l !=undef ? l : 3;
  h = h !=undef ? h : 3;
  e = e !=undef ? e : 1;
  cube([l,e,e],center=true); 
  cube([e,e,h],center=true); 
}

Cela permet ainsi d'avoir réellement une croix, simpliste, qui apparaît là où sans ces variables conditionnelles, n'aurait été affiché que la version basique de la primitive à laquelle on fait appel : le cube.

Vous aurez remarqué sans doute des ponctuations étranges : et !=.

Dés que l'on fait appel à une condition, sous quelque forme que ce soit (car il y en a d'autres que nous verrons plus loin), il faudra utiliser une ponctuation particulière dont voici la signification :

  • == : “… est égal à …”
  • < : “… est plus petit que …”
  • > : “… est plus grand que …”
  • : “… est plus petit ou égal à …”
  • >= : “… est plus grand ou égal à …
  • != : ”… est différent de …“

Exemples :

Résultat console
a = 4;
c = a==5 ? "oui" : "non";
text(c);
ECHO: "non"
a = 4;
c = a<5 ? "oui" : "non";
echo(c);
ECHO: "oui"
a = 4;
c = a>5 ? "oui" : "non";
echo(c);
ECHO: "non"
a = 4;
c = a<=5 ? "oui" : "non";
echo(c);
ECHO: "oui"
a = 4;
c = a>=5 ? "oui" : "non";
echo(c);
ECHO: "non"
a = 4;
c = a!=5 ? "oui" : "non";
echo(c);
ECHO: "oui"

Il faut également ajouter à cela un “gros morceau” avec lequel beaucoup de gens semblent avoir du mal, que nous “survolerons” donc ici et reverrons plus tard à nouveau : les conditions booléenes.

Nous avons vu jusqu'ici comment donner telle ou telle valeur en fonction d'un critère, nous allons maintenant voir comment faire s'il faut tenir compte de plusieurs critères.

Exemples :

ET
a = true;
b = false;
c = (a == true && b== true) ? "oui":"non";
echo(c);
ECHO: "non"
OU
a = true;
b = false;
c = (a == true || b== true) ? "oui":"non";
echo(c);
ECHO: "oui"

Il n'existe pas de ou exclusif dans OpenSCAD, donc :

a = true;
b = true;
c = (a == true || b== true) ? "oui":"non";
echo(c);
ECHO: "oui"

donnera “oui” car le OU sera pris dans le sens “un des deux” et non “un seul des deux”.

Il faudra donc imbriquer les conditions booléennes pour obtenir un “ou” exclusif :

a = true;
b = true;
c = ((a == true && b== false) || (a == false && b== true)) ? "oui":"non";
echo(c);
ECHO: "non"

Les fonctions

Nous avons vu que les modules étaient des objets personnalisés, les fonctions sont des opérations mathématiques personnalisées.

Nous allons reprendre un de nos objets où l'on a utilisé plusieurs fois la même série d'opérations mathématiques :

for(i=[0:89])
{
  for(j=[0:89])
  {
    hull()
    {
      translate([i,j])
      cube([1,1,(cos(i*8)*sin(j*8)+1)*20]);
      translate([i+1,j])
      cube([1,1,(cos((i+1)*8)*sin(j*8)+1)*20]);
      translate([i,j+1])
      cube([1,1,(cos((i)*8)*sin((j+1)*8)+1)*20]);
      translate([i+1,j+1])
      cube([1,1,(cos((i+1)*8)*sin((j+1)*8)+1)*20]);
    }
  }
}

Nous allons donc créer une fonction de la manière suivante :

function MonCalcul(a,b) = (cos(a*8)*sin(b*8)+1)*20;

et nous l'utiliserons alors de la manière suivante :

for(i=[0:89])
{
  for(j=[0:89])
  {
    hull()
    {
      translate([i,j])
      cube([1,1,MonCalcul(i,j)]);
      translate([i+1,j])
      cube([1,1,MonCalcul(i+1,j)]);
      translate([i,j+1])
      cube([1,1,MonCalcul(i,j+1)]);
      translate([i+1,j+1])
      cube([1,1,MonCalcul(i+1,j+1)]);
    }
  }
}

function MonCalcul(a,b) = (cos(a*8)*sin(b*8)+1)*20;

Il est également possible de créer des fonctions récursives :

Résultat dans console
for(i=[0:5])
{
  echo(fibo(0,1,1,i));
}

function fibo(a,b,c,d) = c<=d ? fibo(b,a+b,c+1,d) : a+b;
ECHO: 1
ECHO: 2
ECHO: 3
ECHO: 5
ECHO: 8
ECHO: 13
Résultat dans console
a = fibo(0,1,1,15);
echo(a);

function fibo(a,b,c,d) = c<=d ? fibo(b,a+b,c+1,d) : a+b;
ECHO: 1597

Notre fonction fibo() a donc comme deux premiers paramètres les deux nombres sur lesquels la suite doit se créer et pour troisième et quatrième paramètre à quelle itération il doit commencer et à quelle itération il doit terminer.

Donc : si c est inférieur ou égal à d il rappelle fibo en augmentant c de 1 et donnant à a et b les nouvelles valeurs de la suite de Fibonacci.
Si c est égal à d et que nous sommes donc arrivés là où nous voulions, il affiche le résultat.

Enfin :

a = fibo(0,1,1,48)/fibo(0,1,1,47);
b = fibo(0,1,1,47)/fibo(0,1,1,48);
echo(a);
echo(b);
function fibo(a,b,c,d) = c<=d ? fibo(b,a+b,c+1,d) : a+b;
ECHO: 1.61803
ECHO: 0.618034

Parfait :)

Comme nous pouvons le voir dans l'exemple suivant, nous pouvons aller très loin avec les fonctions :

Rayon           = 3;
Resolution      = 60*3; 
MaxIteration    = 196; 

Deplacement_x = 422.12*3;
Deplacement_y = 4.57*3;

Zoom = 0.0010000001;

Julia_X = 0;//[-10000:10000]
Julia_Y = 0;//[-10000:10000]

JX              = Julia_X/10000;
JY              = Julia_Y/10000;

Julia           = false;
scale([1,1,0.2])
translate([-Resolution/2,-Resolution/2,0])
MandelTest();

module MandelTest()
{
  union()
  {
  for (x=[0:Resolution])
  {
    for (y=[0:Resolution])
    {
      translate([x, y, 0])
      if (Julia)
      {
        color([1/Resolution*x,1/Resolution*y,1/MaxIteration*escape(Rayon, replace(x-(Deplacement_x/Zoom/4)), replace(y-(Deplacement_y/Zoom/4)), JX, JY, 0, MaxIteration)])
        cube([1, 1, escape(Rayon, replace(x-(Deplacement_x/Zoom/4)), replace(y-(Deplacement_y/Zoom/4)), JX, JY, 0, MaxIteration)]);
      }
      else 
      {
        color([1/Resolution*x,1/Resolution*y,1/MaxIteration*escape(Rayon, replace(x-(Deplacement_x/Zoom/4)), replace(y-(Deplacement_y/Zoom/4)), replace(x-(Deplacement_x/Zoom/4)), replace(y-(Deplacement_y/Zoom/4)), 0, MaxIteration)])
        cube([1, 1, escape(Rayon, replace(x-(Deplacement_x/Zoom/4)), replace(y-(Deplacement_y/Zoom/4)), replace(x-(Deplacement_x/Zoom/4)), replace(y-(Deplacement_y/Zoom/4)), 0, MaxIteration)]);
      }
    }
  }
}

function hypo(a, b)                                     = sqrt((a*a)+(b*b));
function real(a,b,c)                                    = (((a*a)-(b*b))+c);
function imag(a,b,c)                                    = ((2*a*b)+c);
function escape(rad, pr,pi,JX,JY, step=0, maxstep)    = ( hypo(pr, pi) < rad && step < maxstep ) ? escape(rad, real(pr,pi,JX), imag(pr, pi, JY), JX, JY, step+1, maxstep) : step;
function replace(x)                                     = (Zoom*x/Resolution)-2+(4-Zoom)/2;

Les conditions

Nous avons vu plus haut les variables conditionnelles, nous allons maintenant voir une autre manière de faire appel à des conditions lors de la modélisation d'un objet.
La manière d'y faire appel est très proche de l'énoncée d'une condition en langue parlée : SI condition ALORS faire ceci SINON faire cela (ou ne rien faire)…

Utilisations possibles :
Si la condition est remplie alors il exécute le code, sinon il ne se passe rien :

if (condition)
{
  code à exécuter 
}

Si la condition est remplie alors il exécute le code 1, sinon il exécute le code 2 :

if (condition)
{
  code à exécuter 1
}
else
{
  code à exécuter 2
}

Exemple :

Taille=100;

Forme_voulue = "cylindre";//["cube","cylindre","autre"]

difference()
{
  cube(Taille,center=true);  
  if(Forme_voulue=="cube")
  {
    cube([Taille*2,Taille/3,Taille/3],center=true);
    rotate([0,0,90])
    cube([Taille*2,Taille/3,Taille/3],center=true);
    rotate([0,90,0])
    cube([Taille*2,Taille/3,Taille/3],center=true);
  }
  if(Forme_voulue=="cylindre")
  {
    cylinder(center=true,d=Taille/3,h=Taille*3);
    rotate([90,0,0])
    cylinder(center=true,d=Taille/3,h=Taille*3);
    rotate([0,90,0])
    cylinder(center=true,d=Taille/3,h=Taille*3);
  }
  if(Forme_voulue != "cube" && Forme_voulue != "cylindre")
  {
    sphere(Taille*sqrt(0.5));   
  }
}

J'utilise principalement ce type de conditions pour ajouter ou non des éléments à une pièce.

Hauteur = 150;
Diametre = 80;
Barreau = 16;
Decalage = 3;
Taille_Barreau =4;

Cylindre_dessus=;

union()
{
  for(i=[0:Barreau])
  {
    rotate([0,0,360/Barreau*i])
    for(j=[-1,1])
    {
      hull()
      { 
        rotate([0,0,360/Barreau*Decalage*j])
        translate([Diametre/2,0,Taille_Barreau/2])
        sphere(d=Taille_Barreau,$fn=8);
        translate([Diametre/2,0,Hauteur-Taille_Barreau/2])
        sphere(d=Taille_Barreau,$fn=8);
      }
    }
  }
  cylinder(d=Diametre+Taille_Barreau*1.5,h=Taille_Barreau,center=true);
  
  if (Cylindre_dessus==true)
  {
    translate([0,0,Hauteur-Taille_Barreau])
    difference()
    {
      cylinder(d=Diametre+Taille_Barreau*1.5,h=Taille_Barreau,center=true);
      cylinder(d=Diametre-Taille_Barreau*1.5,h=Taille_Barreau*1.5,center=true);
    }
  }
}

La variable Cylindre_dessus modifiant la forme selon que cette variable soit sur true ou false :

Cylindre_dessus=falseCylindre_dessus=true

La condition permet enfin de créer des modules récursifs.

Par exemple :

taille=100;

recursif(evolution=1,fin=5);

module recursif()
{
  square(taille,center=true);
  if(evolution<=fin)
  {
    translate([-taille/2,taille,0])
    rotate([0,0,45])
    scale([sqrt(0.5),sqrt(0.5),sqrt(0.5)])
    recursif(evolution=evolution+1,fin=fin);        
  }
}

tout en permettant l'appel à plusieurs fois le même module :

taille=100;

recursif(evolution=1,fin=5);

module recursif()
{
  square(taille,center=true);
  if(evolution<=fin)
  {
    translate([-taille/2,taille,0])
    rotate([0,0,45])
    scale([sqrt(0.5),sqrt(0.5),sqrt(0.5)])
    recursif(evolution=evolution+1,fin=fin);

    translate([taille/2,taille,0])
    rotate([0,0,-45])
    scale([sqrt(0.5),sqrt(0.5),sqrt(0.5)])
    recursif(evolution=evolution+1,fin=fin);
  }
}
recursif(debut=1,fin=4);

module recursif()
{
       
branche(l=100,d1=20,d2=20*sqrt(0.5),a=210,q=128);
if(debut<=fin)
{
    translate([0,0,100])
    rotate([45,-45,0])
    scale([sqrt(0.5),sqrt(0.5),sqrt(0.5)])
    recursif(debut=debut+1,fin=fin);

    translate([0,0,100])
    rotate([-45,45,0])
    scale([sqrt(0.5),sqrt(0.5),sqrt(0.5)])
    recursif(debut=debut+1,fin=fin);
}
}

module branche()
{
  rotate_extrude()
  difference()
  {
    offset(-a,$fn=q)
    offset(a,$fn=q)
    union()
    {
      translate([0,0])
      circle(d=d1,$fn=q);
      translate([0,l])
      circle(d=d2,$fn=q);
    }
  translate([0,-2*l,0])
    square([d1*2+d2*2,l*4]);
  }
}

Voici un autre exemple où l'on retrouve l'utilisation de la condition. Vous remarquerez sans doute que nous avons utilisé une variable prenant la valeur “oui” ou “non” à la place de true ou false, cela vient du fait que Thingiverse a du mal avec les variables de type true/false et ne les affiche pas dans sa version du customizer.

Visualisation = "2D";//[2D,3D]
Previsualisation = "oui";//[oui,non]
Longueur_de_bras = 25;
Nombre_de_bras = 3;
Arrondi_1 = 15;
Arrondi_2 = 10;

if(Visualisation=="2D")
{
  handspinner(l=Longueur_de_bras,b=Nombre_de_bras,a1=Arrondi_1,a2=Arrondi_2);
}
else
{
  translate([0,0,-2.5])
  linear_extrude(5)
  handspinner(l=Longueur_de_bras,b=Nombre_de_bras,a1=Arrondi_1,a2=Arrondi_2);
  if(Previsualisation=="oui")
  {
    roulement();
    for(i=[1:Nombre_de_bras])
    {
      rotate([0,0,360/Nombre_de_bras*i])
      translate([Longueur_de_bras,0,0])
      roulement();        
    }
  }  
}

module handspinner()
{
  a1 = a1<=a2 ? a2+1 : a1;  
  translate([0,0,-2.5])
  difference()
  {
    offset(-a2,$fn=64)
    offset(a1,$fn=64)
    union()
    {
      circle(d=20,center=true);
      for(i=[1:b])
      {
        rotate([0,0,360/b*i])
        translate([l,0,0])
        circle(d=20,center=true);        
      }
      for(i=[1:b])
      {
        rotate([0,0,360/b*i])
        square([l,0.1]);
      }  
    }
    circle(d=20,center=true);
    for(i=[1:b])
    {
      rotate([0,0,360/b*i])
      translate([l,0,0])
      circle(d=20,center=true);        
    }
  }
}

module roulement()
{
  color([0.1,0.1,0.1,1])
  {
    difference()
    {
      cylinder(d=20,h=5,center=true);    
      cylinder(d=18,h=20,center=true);    
    }
    difference()
    {
      cylinder(d=11,h=5,center=true);    
      cylinder(d=9,h=20,center=true);    
    }
  }
  color([0.8,.8,0.8,1])
  for(i=[0:8])
  {
    rotate([0,0,360/8*i])
    translate([7.25,0,0])
    sphere(d=5.5);
  }
}

Les projections

Les projections permettent de retirer une coupe 2D d'un objet 3D.
Pour les exemples suivants, nous utiliserons le buste de Yoda fait par chylld que vous trouverez ici.

Nous allons commencer par l'importer dans OpenSCAD et, par exemple, lui donner la taille à laquelle nous voudrons le voir imprimé :

Hauteur_voulue = 150;

MonYoda()

module MonYoda()
{
  resize([0,0,Hauteur_voulue],auto=true)
  import("yoda.stl");
}

projection() n'a qu'un seul paramètre :

  • cut = true/false : indiquant si l'on fait une projection de tout l'objet ou d'une tranche. Par défaut : false.
    La tranche est la surface X/Y à hauteur 0 représentée en rouge sur la capture suivante :

Ainsi, si nous décalons notre Yoda vers le bas, voici les résultats obtenus :

cut=false cut=true

Attention! Ce Yoda est formé de nombreux triangles et, si vous essayez, vous remarquerez que le temps de calcul des projections est assez long. Je vous conseille donc fortement de diminuer le maillage dans MeshLab selon les besoins que vous avez :

Cela permet par exemple, d'en retirer toutes les coupes selon une épaisseur voulue (et nous avons vider l'objet au passage) :

Epaisseur_materiau = 5;
Hauteur_voulue = 150;
Hollow = 4;

for(i=[0:Hauteur_voulue/Epaisseur_materiau])
{
    translate([150*i,0,i*Epaisseur_materiau])
//    linear_extrude(Epaisseur_materiau)
    difference()
    {
        projection(cut=true)
        translate([0,0,-i*Epaisseur_materiau-0.1])
        MonYoda();
        
        offset(-Hollow)
        projection(cut=true)
        translate([0,0,-i*Epaisseur_materiau-0.1])
        MonYoda();
    }
}
module MonYoda()
{
    resize([0,0,Hauteur_voulue],auto=true)
    import("yodalowmesh.stl");
}

ou permet de déjà avoir une représentation de ce que ces couches pourraient donner :

Les animations

Si vous avez parcouru les menus, vous aurez sans doute remarqué le Animer présent dans Vue faisant apparaître ces nouvelles possibilités sous la fenêtre de visualisation :

L'animation se créera en multipliant une des valeurs utilisées dans votre code par $t

Exemple :

Corrigés

Exercice 1Exercice 2Exercice 3Exercice 4
square([5,20]);
circle(d=10,$fn=8);
square([30,1],center=true);
square([1,30],center=true);
cube([10,20,30]);
cube([10,10,30]);
cube([30,10,10]);
Exercice 5Exercice 6Exercice 7Exercice 8
cube([10,10,30],center=true); 
cube([30,10,10],center=true);
sphere(r=10,$fn=8); 
cylinder(d=10,h=40,$fn=8,center=true);
cylinder(d1=5,d2=20,h=30,$fn=16); 
sphere(d=5,$fn=16);
cylinder(d1=5,d2=40,h=60,$fn=64); 
cylinder(d1=40,d2=5,h=60,$fn=64);
Exercice 1Exercice 2Exercice3
Taille=10; 
square(Taille,center=true); 
translate([Taille/2,0,0]) 
circle(r=Taille/2); 
translate([-Taille/2,0,0]) 
circle(r=Taille/2); 
translate([0,Taille/2,0]) 
circle(r=Taille/2); 
translate([0,-Taille/2,0]) 
circle(r=Taille/2);
Taille=10; 
translate([-Taille/2,0,0]) 
square([1,Taille],center=true); 
translate([Taille/2,0,0]) 
square([1,Taille],center=true); 
translate([0,Taille/2,0]) 
square([Taille,1],center=true); 
translate([0,-Taille/2,0]) 
square([Taille,1],center=true);
Taille1=90; 
Taille2=40; 
DiamCercle=30; 
square([Taille1,Taille2],center=true); 
translate([Taille1/2,Taille2/2,0]) 
circle(d=DiamCercle); 
translate([-Taille1/2,-Taille2/2,0]) 
circle(d=DiamCercle); 
translate([Taille1/2,-Taille2/2,0]) 
circle(d=DiamCercle); 
translate([-Taille1/2,Taille2/2,0]) 
circle(d=DiamCercle);
Exercice 1Exercice 2
module pillule() 
{ 
  cylinder(d=d,h=h,$fn=q,center=true); 
  translate([0,0,h/2]) 
  sphere(d=d,$fn=q); 
  translate([0,0,-h/2]) 
  sphere(d=d,$fn=q); 
}
module fenetre() 
{ 
  translate([-l/2,0,0]) 
  cylinder(d=d,h=h,center=true); 
  translate([l/2,0,0]) 
  cylinder(d=d,h=h,center=true); 
  translate([0,0,-h/2]) 
  rotate([0,90,0]) 
  cylinder(d=d,h=l,center=true); 
  translate([0,0,h/2]) 
  rotate([0,90,0]) 
  cylinder(d=d,h=l,center=true); 
  translate([l/2,0,h/2]) 
  sphere(d=d); 
  translate([-l/2,0,h/2]) 
  sphere(d=d); 
  translate([l/2,0,-h/2]) 
  sphere(d=d); 
  translate([-l/2,0,-h/2]) 
  sphere(d=d); 
}
Exercice 1Exercice 2Exercice 3
Exterieur = 100; 
Interieur = 90; 
difference() 
{ 
  circle(d=Exterieur); 
  circle(d=Interieur); 
}
Hauteur = 100; 
Largeur = 60; 
Exterieur = 10; 
Interieur = 5; 
difference() 
{ 
croix(h=Hauteur,l=Largeur,e=Exterieur); 
croix(h=Hauteur+1,l=Largeur+1,e=Interieur); 
} 
module croix() 
{ 
  union() 
  { 
    square([l,e],center=true); 
    square([e,h],center=true); 
  } 
}
taille = 100; 
difference() 
{ 
  sphere(d=taille); 
  sphere(d=taille-5); 
  cylinder(d=taille/2,h=taille*2,center=true); 
}
Exercice 4
intersection() 
{ 
  cube([20,10,10],center=true); 
  rotate([0,0,60]) 
  cube([20,10,10],center=true); 
  rotate([0,0,120]) 
  cube([20,10,10],center=true); 
}
Exercice1Exercice 2
module verre() 
{ 
  difference() 
  { 
    hull() 
    { 
      cylinder(d=d,h=0.01,$fn=f1); 
      translate([0,0,h]) 
      cylinder(d=d*sqrt(2),h=0.01,$fn=f2); 
    } 
    difference() 
    { 
      hull() 
      { 
        cylinder(d=d-e,h=0.01,$fn=f1); 
        translate([0,0,h+0.01]) 
        cylinder(d=d*sqrt(2)-e,h=0.01,$fn=f2); 
      } 
      cube([d*2,d*2,fond*2],center=true); 
    } 
  } 
}
rotate([0,0,-45]) 
clef(); 
rotate([0,0,45]) 
clef(); 
module clef() 
{ 
  translate([0,-50,0]) 
  difference() 
  { 
    offset(-5) 
    offset(5) 
    union() 
    { 
      hull() 
      { 
        circle(d=20); 
        translate([0,100,0]) 
        circle(d=15); 
      } 
      translate([0,100,0]) 
      circle(d=50); 
    } 
    hull() 
    { 
      translate([0,150,0]) 
      rotate([0,0,30]) 
      circle(d=25,$fn=6); 
      translate([0,110,0]) 
      rotate([0,0,30]) 
      circle(d=25,$fn=6); 
    } 
    circle(d=10); 
  } 
}
Exercice 1Exercice 2
module licorne() 
{ 
  linear_extrude(height=h,center=true,twist=r*360,scale=0,slices=q) 
  union() 
  { 
    translate([e,0]) 
    circle(d,$fn=q); 
    translate([-e,0]) 
    circle(d,$fn=q); 
  } 
}
module colonne() 
{ 
  pied_colonne(d=d); 
  translate([0,0,h]) 
  rotate([180,0,0]) 
  pied_colonne(d=d); 
  cylinder(d=d,h=h); 
} 
module pied_colonne() 
{ 
  rotate([090,0,0]) 
  intersection() 
  { 
    linear_extrude(center=true,height=d*2,convexity=128) 
    base_colonne(d=d); 
    rotate([0,90,0]) 
    linear_extrude(center=true,height=d*2,convexity=128) 
    base_colonne(d=d); 
  } 
} 
module base_colonne() 
{ 
  translate([0,d/4]) 
  square([d,d/2],center=true); 
  translate([0,d/8]) 
  square([d*1.25,d/4],center=true); 
  translate([-d/2,d/2-d/8]) 
  circle(d=d/4); 
  translate([d/2,d/2-d/8]) 
  circle(d=d/4); 
}
Exercice 1
module branche()
{
  rotate_extrude()
  difference()
  {
    offset(-a,$fn=q)
    offset(a,$fn=q)
    union()
    {
      translate([0,d1/2])
      circle(d=d1,$fn=q);
      translate([0,l-d2/2])
      circle(d=d2,$fn=q);
    }
  square([d1*2+d2*2,l*2]);
  }
}