\section{La méthode Dscene3d}

\subsection{Le principe, les limites}

Le défaut majeur des méthodes \cmd{g:Dpoly()}, \cmd{g:Dfacet()} et \cmd{g:Dmixfacet()} est de ne pas gérer les intersections éventuelles entre facettes de différents solides, sans compter que parfois, même pour un polyèdre convexe simple, l'algorithme du peintre ne donne pas toujours le bon résultat (car le tri de facettes se fait uniquement sur leur centre de gravité). D'autre part, ces méthodes permettent de dessiner uniquement des facettes.

Le principe de la méthode \cmd{g:Dscene3d()} est de classer les objets 3D à dessiner (facettes, lignes polygonales, points, labels,...) dans un arbre (qui représente la scène). À chaque nœud de l'arbre il y a un objet 3D, appelons-le $A$, et deux descendants, l'un des descendants va contenir les objets 3D qui sont devant l'objet $A$ (c'est à dire plus près de l'observateur que $A$), et l'autre descendant va contenir les objets 3D qui sont derrière l'objet $A$ (c'est à dire plus loin de l'observateur que $A$).

En particulier, pour classer une facette $B$ par rapport à une facette $A$ qui est déjà dans l'arbre, on procède ainsi : on découpe la facette $B$ avec le plan contenant la facette $A$, ce qui donne en général deux "demi" facettes, une qui sera devant $A$ (celle dans le demi-espace "contenant" l'observateur) , et l'autre qui sera donc derrière $A$.

Cette méthode est efficace mais comporte des limites car elle peut entraîner une explosion du nombre de facettes dans l'arbre augmentant ainsi sa taille de manière exponentielle, ce qui peut rendre rédhibitoire l'utilisation de cette méthode lorsqu'il y a beaucoup de facettes (temps de calcul long\footnote{Lua est un langage interprété donc l'exécution est en général plus longue qu'avec un langage compilé.}, taille trop importante du fichier \emph{*.tkz}, temps de dessin par TikZ trop long). Par contre, elle est très efficace lorsqu'il y a peu de facettes, et donc peu d'intersections de facettes (objets convexes avec peu de facettes). De plus, il est possible de dessiner sous la scène 3D et au-dessus, c'est à dire avant l'utilisation de la méthode \cmd{g:Dscene3d()}, et après son utilisation.

Cette méthode doit donc être réservée à des scènes très simples. Pour des scènes 3D complexes le format vectoriel n'est pas adapté, mieux vaux se tourner alors vers des d'autres outils comme POV-Ray ou Blender ou WebGL ...

\subsection{Construction d'une scène 3D}

La méthode \cmd{g:Dscene3d(\ldots)} permet cette construction. Elle prend en argument les objets 3D qui vont constituer cette scène les uns après les autres. Ces objets 3D sont eux-mêmes fabriqués à partir de méthodes dédiées qui vont être détaillées plus loin. Dans la version actuelle, ces objets 3D peuvent être :
\begin{itemize}
    \item des polyèdres, 
    \item des listes de facettes (avec points 3D),
    \item des lignes polygonales 3D,
    \item des points 3D,
    \item des labels,
    \item des axes,
    \item des plans, des droites,
    \item des angles,
    \item des cercles, des arcs de cercle.
\end{itemize}

\begin{demo}[plans]{Premier exemple avec Dscene3d : intersection de deux plans}
\begin{luadraw}{name=intersection_plans}
local ld = luadraw
local pt3d = ld.pt3d
local Origin, vecI, vecJ, vecK, M = pt3d.Origin, pt3d.vecI, pt3d.vecJ, pt3d.vecK, pt3d.M

local g = ld.graph3d:new{viewdir={"central",-10,60}, window={-5,6.5,-6.5,6},bg="gray", size={10,10}}
local P1 = ld.planeEq(1,1,1,-2) -- plan d'équation x+y+z-2=0
local P2 = {Origin, vecK-vecJ} -- plan passant par O et normal à (0,-1,1)
local D = ld.interPP(P1,P2) -- droite d'intersection entre P1 et P2 (D = {A,u})
local posD = D[1]+1.85*D[2] -- pour placer le label
ld.Hiddenlines = true; ld.Hiddenlinestyle = "dotted" -- affichage des lignes cachées en pointillées
g:Dscene3d(
    g:addPlane(P1, {color="Crimson",edge=true,edgecolor="Pink",edgewidth=8}), -- ajout du plan P1
    g:addPlane(P2, {color="ForestGreen",edge=true,edgecolor="Pink",edgewidth=8}), -- ajout du plan P2
    g:addLine(D, {color="Navy",edgewidth=12}), -- ajout de la droite D
    g:addAxes(Origin, {arrows=1, color="Gold",width=8}), -- ajout des axes fléchés
    g:addLabel( -- ajout de labels, ceux-ci auraient pu être ajoutés par dessus la scène
        "$D=P_1\\cap P_2$",posD,{color="Navy"},
        "$P_2$", M(3,0,0)+3.5*M(0,1,1)+0.2*vecI,{color="white",dir={vecI,vecJ+vecK}},
        "$P_1$",M(2,0,0)+1.85*M(-1,-1,2)-1.5*M(-1,1,0), {dir={M(-1,1,0),M(-1,-1,2)}} )
)
g:Show()
\end{luadraw}
\end{demo}

\subsection{Méthodes pour ajouter un objet dans la scène 3D}

Ces méthodes sont à utiliser comme argument de la méthode \cmd{g:Dscene3d(\ldots)} comme dans l'exemple ci-dessus.

\subsubsection{Ajouter des facettes : g:addFacet et g:addPoly}

La méthode \cmd{g:addFacet(list, options)} où \argu{list} est une facette ou bien une liste de facettes (avec points 3D), permet d'ajouter ces facettes à la scène. 

La méthode \cmd{g:addPoly(P, options)} permet d'ajouter le polyèdre \argu{P} à la scène. 

Dans les deux cas, l'argument \argu{options} est une table dont les champs définissent ces options (avec leur valeur par défaut) sont :

    \begin{itemize}
        \item \opt{color="white"} : définit la couleur de remplissage des facettes, cette couleur sera nuancée en fonction de l'inclinaison de celles-ci. Par défaut, le bord des facettes n'est pas dessiné (seulement le remplissage).
        
        \item \opt{usepalette=nil}, cette option permet de préciser une palette de couleurs pour peindre les facettes ainsi qu'un mode de calcul, la syntaxe est : \opt{usepalette=\{palette,mode\}}, où \argu{palette} désigne une table de couleurs qui sont elles-mêmes des tables de la forme $\{r,g,b\}$ où $r$, $g$ et $b$ sont des nombres entre $0$ et $1$, et \argu{mode} qui est une chaîne qui peut être soit \val{"x"}, soit \val{"y"}, ou soit \val{"z"}. Dans le premier cas par exemple, les facettes au centre de gravité d'abscisse minimale ont la première couleur de la palette, les facettes au centre de gravité d'abscisse maximale ont la dernière couleur de la palette, pour les autres, la couleur est calculée en fonction de l'abscisse du centre de gravité par interpolation linéaire.
                
        \item \opt{opacity=1} : nombre entre $0$ et $1$ pour définir l'opacité des facettes ($1$ signifie pas de transparence).
        
        \item \opt{backcull=false} : booléen qui indique si les facettes non visibles doivent être exclues de la scène. Par défaut elles sont présentes.
        
        \item \opt{clip=false} : booléen qui indique si les facettes doivent être clippées par la fenêtre 3D.
        \item \opt{contrast=1} : valeur numérique permettant d'accentuer ou diminuer de contraste de couleur entre les facettes. Avec la valeur $0$ toutes les facettes ont la même couleur.
        
        \item \opt{twoside=true} : booléen qui indique si on distingue la face interne de la face externe des facettes. La couleur de la face interne est un peu plus claire que celle de l'externe.
        
        \item \opt{reverse=false} : avec la valeur \true l'orientation des facettes est inversée.
        
        \item \opt{edge=false} : booléen qui indique si les arêtes doivent être ajoutées à la scène.
        
        \item \opt{edgecolor=<couleur courante>} : indique la couleur des arêtes lorsqu'elles sont dessinées, c'est la couleur courante par défaut.
        
        \item \opt{edgewidth=<épaisseur courante>}: indique l'épaisseur de trait (en dixième de point) des arêtes.
        \item \opt{hidden=ld.Hiddenlines} : booléen qui indique si les arêtes cachées doivent être représentées. \varglob{ld.Hiddenlines} est une variable globale qui vaut \false par défaut.
        
        \item \opt{hiddenstyle=ld.Hiddenlinestyle} : chaîne définissant le style de ligne des arêtes cachées. \varglob{ld.Hiddenlinestyle} est une variable globale qui vaut \val{"dotted"} par défaut.
        
        \item \opt{hiddenscale=ld.Hiddenlinescale} : nombre représentant un pourcentage, l'épaisseur des lignes cachées est égale à celle des lignes visibles multipliée par ce nombre. \varglob{ld.Hiddenlinescale} est une variable globale qui vaut \val{2/3} par défaut.
        
        \item \opt{matrix=ld.ID3d} : matrice 3D de transformation des facettes, par défaut celle-ci est la matrice 3D de l'identité, c'est à dire la table \code{\{M(0,0,0),vecI,vecJ,vecK\}}.
    \end{itemize}

\subsubsection{Ajouter un plan : g:addPlane et g:addPlaneEq}

La méthode \cmd{g:addPlane(P, options)} permet d'ajouter le plan \argu{P} à la scène 3D, ce plan est défini sous la forme d'une table $\{A,u\}$ où $A$ est un point du plan (point 3D) et $u$ un vecteur normal au plan (point 3D non nul). Cette fonction détermine l'intersection entre ce plan et le parallélépipède donné par l'option \opt{window3d} (définie à la création du graphe), ce qui donne une facette, c'est celle-ci qui est ajoutée à la scène. Cette méthode utilise \cmd{g:addFacet()}.

La méthode \cmd{g:addPlaneEq(coef, options)} où \argu{coef} est une table constituée de quatre réels $\{a,b,c,d\}$, permet d'ajouter à la scène le plan d'équation $ax+by+cz+d=0$ (cette méthode utilise la précédente).

Dans les deux cas, l'argument facultatif \argu{options} est une table dont les champs définissent les options, celles-ci sont celles de la méthode \cmd{g:addFacet()}, plus les options :

\begin{itemize}
    \item \opt{rectangle=\nil} : avec la valeur \nil le plan est intersecté avec la fenêtre 3D et c'est la facette qui en résulte qui sera dessinée, elle n'est pas forcément rectangulaire, avec \opt{rectangle=\{V,L1,L2\}}, la facette dessinée sera rectangulaire avec des côtés de longueurs \argu{L1} et \argu{L2}, et le vecteur \argu{V} (qui doit appartenir au plan) imposera la direction d'un côté du rectangle. Ce vecteur \argu{V} est facultatif, s'il est omis la fonction le choisira elle-même. L'argument \argu{L2} est facultatif, s'il est omis, il a implicitement la même valeur que \argu{L1}.
    
    \item \opt{scale=1} : ce nombre est un rapport d'homothétie, il est pris en compte seulement lorsque \opt{rectangle=\nil}, dans ce cas, on applique à la facette l'homothétie de centre le centre de gravité de la facette et de rapport \opt{scale}. Cela permet de jouer sur la taille du plan dans sa représentation.
\end{itemize}

        

\subsubsection{Ajouter une ligne polygonale : g:addPolyline}

La méthode \cmd{g:addPolyline(L, options)} où \argu{L} est une liste de points 3D, ou une liste de listes de points 3D, permet d'ajouter \argu{L} à la scène. L'argument \argu{options} est une table dont les champs définissent les options, celles-ci (avec leur valeur par défaut) sont :
    \begin{itemize}
        \item \opt{style=<style courant>} : pour définir le style de ligne, c'est le style courant par défaut.
        
        \item \opt{color=<couleur  courante>} : couleur de la ligne, c'est la couleur courante par défaut.
        
        \item \opt{close=false} : indique si la ligne \argu{L} (ou chaque composante de \argu{L}) doit être refermée.
        
        \item \opt{clip=false} : indique si la ligne \argu{L} (ou chaque composante de \argu{L}) doit être clippée par la fenêtre 3D.
        
        \item \opt{width=<épaisseur courante>} : épaisseur de la ligne en dixième de point, c'est l'épaisseur courante par défaut.
        
        \item \opt{opacity=1} : opacité du tracé de ligne ($1$ signifie pas de transparence).
        
        \item \opt{hidden=ld.Hiddenlines} : booléen qui indique si les parties cachées de la ligne doivent être représentées. \varglob{ld.Hiddenlines} est une variable globale qui vaut \false par défaut.
        
        \item \opt{hiddenstyle=ld.Hiddenlinestyle} : chaîne définissant le style de ligne des parties cachées. \varglob{ld.Hiddenlinestyle} est une variable globale qui vaut \val{"dotted"} par défaut.
        
        \item \opt{hiddenscale=ld.Hiddenlinescale} : nombre représentant un pourcentage, l'épaisseur des lignes cachées est égale à celle des lignes visibles multipliée par ce nombre. \varglob{ld.Hiddenlinescale} est une variable globale qui vaut \val{2/3} par défaut.
        
        \item \opt{arrows=0} : cette option peut valoir $0$ (aucune flèche ajoutée à la ligne), $1$ (une flèche ajoutée en fin de ligne), ou $2$ (une flèche en début et en fin de ligne). Les flèches sont des petits cônes.
        
        \item \opt{arrowscale=\{1,1\}}, permet de jouer sur la taille des flèches (largeur pour le premier nombre, hauteur pour le second). Lorsque \opt{arrowscale} est un nombre, la deuxième valeur est considérée égale à la première.
        
        \item \opt{matrix=ld.ID3d} : matrice 3D de transformation (de la ligne), par défaut celle-ci est la matrice 3D de l'identité, c'est à dire la table \code{\{M(0,0,0),vecI,vecJ,vecK\}}.
        
        \item \opt{double=\nil} : cette option expérimentale permet d'ajouter une bordure de chaque côté de la ligne, la syntaxe est \opt{double=\{border color, border width\}}. Avec la valeur par défaut (\nil) il n'y a pas de bordure.
    \end{itemize}
    
\subsubsection{Ajouter des axes : g:addAxes}

La méthode \cmd{g:addAxes(O, options)} permet d'ajouter les axes : (\argu{O},\emph{vecI}), (\argu{O},\emph{vecJ}) et (\argu{O},\emph{vecK}) à la scène 3D, où l'argument \argu{O} est un point 3D. Les options sont celles de la méthode \cmd{g:addPolyline()}, plus l'option \opt{legend=true} qui permet d'ajouter automatiquement une légende à l'extrémité de chaque axe, ces légendes sont gérées par l'option \opt{labels=}\verb|{"$x$", "$y$","$z$"}|. Les axes ne sont pas gradués.
    
\subsubsection{Ajouter une droite : g:addLine}

La méthode \cmd{g:addLine(d, options)} permet d'ajouter la droite \argu{d} à la scène, cette droite est une table de la forme $\{A,u\}$ où $A$ est un point de la droite (point 3D) et $u$ un vecteur directeur (point 3D non nul).  L'argument facultatif \argu{options} est une table dont les champs définissent les options, celles-ci sont celles de la méthode \cmd{g:addPolyline()}, plus l'option \opt{scale=1} : ce nombre est un rapport d'homothétie, on applique l'homothétie de centre le milieu du segment représentant la droite, et de rapport \opt{scale}. Cela permet de jouer sur la taille du segment dans sa représentation, ce segment est la droite clippée par le polyèdre donné par l'option \opt{window3d} (définie à la création du graphe), ce qui donne une segment (éventuellement vide).

\subsubsection{Ajouter un angle "droit" : g:addAngle}

La méthode \cmd{g:addAngle(B, A, C \fac{, r, options})} permet d'ajouter l'angle $\widehat{BAC}$ sous forme d'un parallélogramme de côté \argu{r} ($0.25$ par défaut), seuls deux côtés sont représentés. les arguments \argu{B}, \argu{A} et \argu{C} sont des points 3D. Les options sont celles de la méthode \cmd{g:addPolyline()}.

\subsubsection{Ajouter un arc de cercle : g:addArc}

La méthode \cmd{g:addArc(B, A, C, r, sens \fac{, normal, options})} permet d'ajouter l'arc de cercle de centre \argu{A} (point 3D), de rayon \argu{r}, allant de \argu{B} vers \argu{C} (points 3D) dans le sens direct si \argu{sens} vaut $1$ (indirect sinon). L'arc est tracé dans le plan passant par \argu{A} et orthogonal au vecteur\argu{normal} (point 3D non nul), c'est ce même vecteur qui oriente le plan. Si le vecteur \argu{normal} n'est pas précisé, alors par défaut celui-ci sera le produit vectoriel $\vec{AB}\wedge\vec{AC}$. Les options sont celles de la méthode \cmd{g:addPolyline()}.

\subsubsection{Ajouter un cercle : g:addCircle}

La méthode \cmd{g:addCircle(A, r, normal, options)} permet d'ajouter le cercle de centre \argu{A} (point 3D) et de rayon \argu{A} dans le plan passant par \argu{A} et orthogonal au vecteur\argu{normal} (point 3D non nul). Les options sont celles de la méthode \cmd{g:addPolyline()}.

\begin{demo}{Cylindre plein plongé dans de l'eau}
\begin{luadraw}{name=cylindres_imbriques}
local ld = luadraw
local pt3d = ld.pt3d
local Origin, vecI, vecJ, vecK, M = pt3d.Origin, pt3d.vecI, pt3d.vecJ, pt3d.vecK, pt3d.M

local g = ld.graph3d:new{window={-5,5,-7,5}, viewdir={"central",30,65,20},
    size={10,10},margin={0,0,0,0}}
ld.Hiddenlines = false
local R, r, A, B = 3, 1.5
local C1 = ld.cylinder(M(0,0,-5),5*vecK,R) -- pour modéliser l'eau
local C2 = ld.cylinder(Origin,2*vecK,R,35,true) -- partie du contenant au dessus de l'eau (cylindre ouvert)
local C3 = ld.cylinder(M(0,0,-3),7*vecK,r) -- petit cylindre plongé dans l'eau
-- sous la scène 3D
g:Lineoptions(nil,"gray",12)
g:Dcylinder(M(0,0,-5),7*vecK,R,{hiddenstyle="noline"}) -- contour du contenant (grand cylindre)
-- scène 3D
g:Dscene3d(
    g:addPoly(C1,{contrast=0.125,color="cyan",opacity=0.5}), -- eau
    g:addPoly(C2,{contrast=0.125,color="WhiteSmoke", opacity=0.5}), -- partie du contenant au-dessus de l'eau
    g:addPoly(C3,{contrast=0.25,color="Salmon",backcull=true}), -- petit cylindre dans l'eau
    g:addCircle(M(0,0,2),R,vecK,{color="gray"}), -- bord supérieur du contenant
    g:addCircle(M(0,0,-5),R,vecK,{color="gray"}), -- bord inférieur du contenant
    g:addCircle(Origin,R-0.025,vecK, {width=2,color="cyan"}) -- bord supérieur eau
)
-- par dessus la scène 3D
g:Lineoptions(nil,"black",8); A = 4*vecK; B = A+r*g:ScreenX()
g:Dpolyline3d( {A,B}, "<->"); g:Dlabel3d("$3\\,$cm",(A+B)/2,{pos="N",dist=0.25})
A = Origin+(r+1)*g:ScreenX(); A = ld.rotate3d(A,-10,{Origin,vecK})
B = A-3*vecK
g:Dpolyline3d( {A,B}, "<->"); g:Dlabel3d("h",(A+B)/2,{pos="E"})
g:Lineoptions("dashed")
g:Dpolyline3d({{A,A-g:ScreenX()},{B,B-g:ScreenX()}})
A = Origin-(R+1)*g:ScreenX(); A = ld.rotate3d(A,10,{Origin,vecK})
B = A-vecK
g:Dpolyline3d({{A,A+g:ScreenX()},{B,B+g:ScreenX()}})
g:Linestyle("solid")
g:Dpolyline3d( {A,B}, "<->"); g:Dlabel3d("$2$\\,cm",(A+B)/2,{pos="W"})
g:Show()
\end{luadraw}
\end{demo}

\paragraph{Remarques} : 
\begin{itemize}
    \item La méthode \cmd{g:ScreenX()} renvoie le vecteur de l'espace (point 3D) correspondant au vecteur d'affixe $1$ dans le plan de l'écran, et la méthode \cmd{g:ScreenY()} renvoie le vecteur de l'espace (point 3D) correspondant au vecteur d'affixe $\imath$ dans le plan de l'écran.
    \item Pour le petit cylindre (C3) on utilise l'option \opt{backcull=true} pour diminuer le nombre de facettes, par contre, on ne le fait pas pour les deux autres cylindres (C1 et C2) car ils sont transparents.
\end{itemize}

\subsubsection{Ajouter des points : g:addDots}

La méthode \cmd{g:addDots(dots, options)} permet d'ajouter des points 3D à la scène. L'argument \argu{dots} est soit un point 3D, soit une liste de points 3D. L'argument facultatif \argu{options} est une table dont les champs définissent les options, celles-ci sont (avec la valeur par défaut) :
\begin{itemize}
        \item \opt{style="ball"} : chaîne définissant le style de points, ce sont tous les styles de points 2D, plus le style \val{"ball"} (sphère) qui est le style par défaut. 
    \item \opt{color="black"} : chaîne définissant la couleur des points.
    \item \opt{scale=1} : nombre permettant de jouer sur la taille des points.
    \item \opt{matrix=ld.ID3d} : matrice 3D de transformation, par défaut celle-ci est la matrice 3D de l'identité, c'est à dire la table \code{\{M(0,0,0),vecI,vecJ,vecK\}}.
\end{itemize}

\subsubsection{Ajouter des labels : g:addLabel}

La méthode \cmd{g:addLabel(text1, anchor1, options1, text2, anchor2, options2, \ldots)} permet d'ajouter les labels \argu{text1}, \argu{text2}, etc. Les arguments (obligatoires) \argu{anchor1}, \argu{anchor2}, etc, sont des points 3D représentant les points d'ancrage des labels. Les arguments (obligatoires) \argu{options1}, \argu{options2}, etc, sont des tables dont les champs définissent les options, celles-ci sont (avec la valeur par défaut) :
\begin{itemize}
    \item \opt{color=<couleur courante des labels>} : chaîne définissant la couleur du label.
    \item \opt{pos=<style courant des labels>} : chaîne définissant la position du label par rapport au point d'ancrage (comme en 2D : \val{"N"}, \val{"NW"}, \val{"W"}, \ldots),  initialisée au style en cours des labels.
    \item \opt{dist=0} : exprime la distance entre le label et son point d'ancrage (dans le plan de l'écran).
    \item \opt{size=<taille courante>} : chaîne définissant la taille du label.
    \item \opt{dir=\{\}} : table définissant le sens de l'écriture dans l'espace (sens usuel par défaut).
    En général, \opt{dir=\{dirX,dirY,dep\}}, et les 3 valeurs \argu{dirX}, \argu{dirY} et \argu{dep} sont trois points 3D représentant 3 vecteurs, les deux premiers (obligatoires) indiquent le sens de l'écriture, le troisième (facultatif) indique un déplacement (translation) du label par rapport au point d'ancrage.
    \item \opt{showdot=false} : booléen qui indique si un point (2D) doit être dessiné au point d'ancrage.
    \item \opt{matrix=ld.ID3d} : matrice 3D de transformation, par défaut celle-ci est la matrice 3D de l'identité, c'est à dire la table \code{\{M(0,0,0),vecI,vecJ,vecK\}}.
\end{itemize}

\textbf{Remarque} : comme en 2D, les options d'un label s'appliquent aux labels suivants tant qu'elles ne sont pas modifiées.

\begin{demo}{Construction d'un icosaèdre}
\begin{luadraw}{name=icosaedre}
local ld = luadraw
local M = ld.pt3d.M

local g = ld.graph3d:new{window={-2.25,2.25,-2,2}, viewdir={40,60},bg="gray",size={10,10},margin={0,0,0,0}}
ld.Hiddenlines = false
local phi = (1+math.sqrt(5))/2 -- nombre d'or
local A1, B1, C1, D1 = M(phi,-1,0), M(phi,1,0), M(-phi,1,0), M(-phi,-1,0) -- dans le plan z=0
local A2, B2, C2, D2 = M(0,phi,1), M(0,phi,-1), M(0,-phi,-1), M(0,-phi,1) -- dans le plan x=0
local A3, B3, C3, D3 = M(1,0,phi), M(-1,0,phi), M(-1,0,-phi), M(1,0,-phi) -- dans le plan y=0
local ico = {
{A1,B1,A3}, {B1,A1,D3}, {D1,C1,C3}, {C1,D1,B3},
{B2,A2,B1}, {A2,B2,C1}, {D2,C2,A1}, {C2,D2,D1},
{B3,A3,A2}, {A3,B3,D2}, {D3,C3,B2}, {C3,D3,C2},
{A1,A3,D2}, {B1,A2,A3}, {A2,C1,B3}, {D1,D2,B3},
{B2,B1,D3}, {A1,C2,D3}, {B2,C3,C1}, {C2,D1,C3} }
g:Dscene3d(
    g:addFacet({A2,B2,C2,D2},{color="Navy",twoside=false,opacity=0.8}),
    g:addFacet({A1,B1,C1,D1},{color="Crimson",twoside=false,opacity=0.8}),
    g:addFacet({A3,B3,C3,D3},{color="Chocolate",twoside=false,opacity=0.8}),
    g:addPolyline(ld.facetedges(ico), {color="Gold",width=12}), -- dessin des arêtes uniquement
    g:addDots({A1,B1,C1,D1,A2,B2,C2,D2,A3,B3,C3,D3}, {color="black",scale=1.2}),
    g:addLabel("A1",A1,{style="W",dist=0.1}, "B1",B1,{style="S"}, "C2",C2,{}, "C3",C3,{},
        "A3",A3,{style="N"}, "D1",D1,{}, "A2",A2,{}, "D2",D2,{}, "B3",B3,{style="E"},
        "C1",C1,{}, "B2",B2,{}, "D3",D3,{style="W"} )
)
g:Show()
\end{luadraw}
\end{demo}

\subsubsection{Ajouter des cloisons séparatrices : g:addWall}

Les cloisons séparatrices sont des objets 3D qui sont insérés en tout premier dans l'arbre représentant la scène. Ces objets ne sont pas dessinés (donc invisibles), leur rôle est de partitionner l'espace car une facette qui est d'un côté d'une cloison séparatrice ne peut pas être découpée par le plan d'une facette qui est de l'autre côté de la cloison. Cela permet dans certains cas de diminuer significativement le nombre de découpage de facettes (ou lignes polygonales) lors de la construction de la scène. Une cloison séparatrice peut être un plan entier (donc une table de deux points 3D la forme $\{A,n\}$, c'est à dire un point et un vecteur normal), ou bien seulement une facette.

La syntaxe est : \cmd{g:addWall(C, options)} où \argu{C} est soit un plan, soit une liste de plans, soit une facette, soit une liste de facettes. L'argument \argu{options} est une table définissant une seule option qui est :
\begin{itemize}
    \item \opt{matrix=ld.ID3d} : matrice 3D de transformation, par défaut celle-ci est la matrice 3D de l'identité, c'est à dire la table code{\{M(0,0,0),vecI,vecJ,vecK\}}.
\end{itemize}

Dans l'exemple suivant les deux cloisons séparatrices ont été dessinées afin de les visualiser, mais normalement elles sont invisibles :

\begin{demo}{Exemple avec addWall (les deux facettes transparentes roses sont normalement invisibles)}
\begin{luadraw}{name=addWall}
local ld = luadraw
local pt3d = ld.pt3d
local Origin, vecI, vecJ, vecK, M = pt3d.Origin, pt3d.vecI, pt3d.vecJ, pt3d.vecK, pt3d.M

local g = ld.graph3d:new{size={10,10},window={-8,8,-4,8}, margin={0,0,0,0}}
local C = ld.cylinder(M(0,0,-1),5*vecK,2)
g:Dscene3d(
    g:addWall( {{Origin,vecI}, {Origin,vecJ}}),
    g:addPlane({Origin,vecI}, {color="Pink",opacity=0.3,scale=1.125,edge=true}), -- premier mur
    g:addPlane({Origin,vecJ}, {color="Pink",opacity=0.3,scale=1.125,edge=true}), -- second mur
    g:addPoly( ld.shift3d(C,M(-3,-3,1)), {color="Cyan"} ),
    g:addPoly( ld.shift3d(C,M(-3,3,0.5)), {color="ForestGreen"} ),
    g:addPoly( ld.shift3d(C,M(3,-3,-0.5)), {color="Crimson"} )
)
g:Show()
\end{luadraw}
\end{demo}

\paragraph{Remarques sur cet exemple} : 
\begin{itemize}
    \item avec les deux cloisons séparatrices, il n'y a aucune facette découpée, et la scène en contient exactement $111$ ($37$ par cylindre).
    \item sans les cloisons séparatrices, il y a $117$ découpages (inutiles) de facettes, ce qui porte leur nombre à $228$ dans la scène.
        \item avec les deux cloisons séparatrices, et l'option \opt{backcull=true} pour chaque cylindre, il n'y a aucune facette découpée, et la scène en contient $57$ seulement.
\end{itemize}

Voici un autre exemple bien plus probant où l'utilisation de cloisons séparatrices est indispensable pour avoir un dessin de taille raisonnable. Il s'agit de l'obtention d'une lemniscate comme intersection d'un tore avec un certain plan. Le tore étant non convexe le nombre de découpage inutile de facettes peut être très important.

\begin{demo}{Tore et lemniscate}
\begin{luadraw}{name=torus}
local ld = luadraw
local pt3d = ld.pt3d
local Origin, vecI, vecJ, vecK, M = pt3d.Origin, pt3d.vecI, pt3d.vecJ, pt3d.vecK, pt3d.M

local g = ld.graph3d:new{size={10,10}, margin={0,0,0,0}}
local cos, sin, pi = math.cos, math.sin, math.pi
local R, r = 2.5, 1
local x0 = R-r
local f = function(t) return M(0,R+r*cos(t),r*sin(t)) end
local plan = {M(x0,0,0),-vecI} -- plan dont la section avec le tore donne la lemniscate
local C, wall = ld.rotcurve(f,-pi,pi,{Origin,vecK},360,0,{grid={25,37},addwall=2})
local C1 = ld.cutfacet(C,plan) -- partie du tore dans le demi espace contenant -vecI
g:Dscene3d(
    g:addWall(plan), g:addWall(wall), -- ajout de cloisons séparatrices
    g:addFacet( C1, {color="Crimson", backcull=false}),
    g:addPlane(plan, {color="Pink",opacity=0.4,edge=true}), -- plan de coupe
    g:addAxes( Origin, {arrows=1})
)
-- équation cartésienne du tore : (x^2+y^2+z^2+R^2-r^2)^2-4*R^2*(x^2+y^2) = 0
-- la lemniscate a donc pour équation (x0^2+y^2+z^2+R^2-r^2)^2-4*R^2*(x0^2+y^2)=0 (courbe implicite)
local h = function(y,z) return (x0^2+y^2+z^2+R^2-r^2)^2-4*R^2*(x0^2+y^2) end
local I = ld.implicit(h,-4,4,-3,3,{50,50}) -- ligne polygonale 2D (liste de listes de complexes)
local lemniscate = ld.map(function(z) return M(x0,z.re,z.im) end, I[1]) -- conversion en coordonnées spatiales
g:Dpolyline3d(lemniscate,"Navy,line width=1.2pt")
g:Show()
\end{luadraw}
\end{demo}

\paragraph{Remarques sur cet exemple} : 
\begin{itemize}
    \item Avec les cloisons séparatrices on a $30$ facettes qui sont coupées et un fichier \emph{*.tkz} de $140$ Ko environ.
    \item Sans les cloisons séparatrices on a $2068$ découpages de facettes (!) et un fichier \emph{*.tkz} de $550$ Ko environ.
    \item On aurait pu utiliser la section de coupe qui est renvoyée par la fonction \cmd{ld.cutfacet()}, mais le résultat n'est pas très satisfaisant (cela vient du fait que le tore est non convexe).
    \item Si on n'avait pas voulu les axes traversant le tore et le plan de coupe, on aurait pu faire le dessin avec la méthode \cmd{g:Dfacet()}, en remplaçant l'instruction \cmd{g:Dscene3d()} par :
    
\begin{Luacode}
g:Dfacet(C1, {mode=ld.mShadedOnly,color="Crimson"} )
g:Dfacet( g:Plane2facet(plan,0.75), {color="Pink",opacity=0.4}) 
\end{Luacode}
On obtient exactement la même chose mais sans les axes (et sans découpage de facettes bien sûr).
\end{itemize}

\paragraph{Pour conclure cette partie} : on utilise la méthode \cmd{g:Dscene3d()} lorsqu'il n'est pas possible de faire autrement, par exemple lorsqu'il y a des intersections (peu nombreuses) qui ne peuvent pas être traitées "à la main". Mais ce n'est pas le cas de toutes les intersections ! Dans l'exemple suivant, on représente une section de sphère par un plan mais sans passer par la méthode \cmd{g:Dscene3d()} car celle-ci obligerait à dessiner une sphère à facettes ce qui n'est pas très joli. L'astuce ici, consiste à dessiner la sphère avec la méthode \cmd{g:Dsphere()}, puis dessiner par dessus le plan sous forme d'une facette préalablement trouée, le trou correspondant au contour (chemin 3D) de la partie de la sphère située au-dessus du plan :

\begin{demo}{Section de sphère sans Dscene3d()}
\begin{luadraw}{name=section_sphere}
local ld = luadraw
local pt3d = ld.pt3d
local Origin, vecI, vecJ, vecK, M = pt3d.Origin, pt3d.vecI, pt3d.vecJ, pt3d.vecK, pt3d.M

local g = ld.graph3d:new{ window3d={-4,4,-4,4,-4,4}, window={-5.5,5.5,-4,5}, viewdir={30,75}, size={10,10}}
local O, R = Origin, 2.5 -- center et rayon
local S, P = ld.sphere(O,R), {M(0,0,1.5),vecK+vecJ/2} -- la sphère et le plan de coupe
local w, n = pt3d.normalize(P[2]), g.Normal -- vecteurs unitaires normaux à P pour w et à l'écran pour n
local I, r = ld.interPS(P,{O,R}) -- centre et rayon du petit cercle (intersection entre le plan et la sphère)
local C = g:Intersection3d(S,P) -- C est une liste d'arêtes
local N = I-O
local J = I+r*pt3d.normalize(vecJ-vecK/2) -- un point sur le petit cercle
local a = R/pt3d.abs(N)
local A, B = O+a*N, O-a*N -- points d'intersection de l'axe (O,I) avec la sphère
local c1, alpha = ld.Orange, 0.4
local coul = {c1[1]*alpha, c1[2]*alpha,c1[3]*alpha} -- pour simuler la transparence
g:Dhline( g:Proj3d({B,-N})) -- demi-droite (le point B est non visible)
g:Dsphere(O,R,{mode=ld.mBorder,color="orange"})
g:Dline3d(A,B,"dotted") -- droite (A,B) en pointillés
g:Dedges(C, {hidden=true,hiddenstyle="dashed"}) -- dessin de l'intersection
g:Dpolyline3d({I,J,O},"dashed")
g:Dangle3d(O,I,J) -- angle droit
g:Dcrossdots3d({{B,N},{I,N},{O,N}},ld.rgb(coul),0.75) -- points dans la sphère
g:Dlabel3d("$O$", O, {pos="NW"})
local L = C.visible[1] -- partie visible de l'intersection (arc de cercle)
A1 = L[1]; A2 = L[#L] -- extrémités de L
local F = g:Plane2facet(P) -- plan converti en facette
-- plan troué sous forme de chemin 3D, le trou est le contour de la partie de la sphère au-dessus du plan
ld.insert(F,{"l","cl",A1,"m",I,A2,r,-1,w,"ca",Origin,A1,R,-1,n,"ca"})
g:Dpath3d( F,"fill=Beige,fill opacity=0.6") -- dessin du plan troué
g:Dhline( g:Proj3d({A,N})) -- demi-droite, partie supérieure de l'axe (AB)
g:Dcrossdots3d({A,N},"black",0.75); g:Dballdots3d(J,"black",0.75)
g:Dlabel3d("$A$", A, {pos="NW"}, "$I$", I, {}, "$B$", B, {pos="E"}, "$J$", J, {pos="S"})
g:Show()
\end{luadraw}
\end{demo}
