Aggiornato per Mathematica versione 11

Primitive grafiche: punti, cerchi, linee, angoli, poligoni ecc.

Gianluca Gorni
Università di Udine, Dipartimento di Matematica e Informatica

Ultima modifica: 18 gennaio 2018

Questo file è concepito principalmente per essere letto interattivamente su schermo. Per questo, e anche per risparmiare spazio, gli output di solito sono omessi. L'utente che legge da schermo li deve rigenerare mandando in esecuzione le celle di input una alla volta.
Qualcuno vorrà stampare per leggere su carta: in tal caso apra i gruppi di celle ed esegua tutte le celle prima di stampare. Le celle si possono eseguire tutte in un sol colpo col menu Evaluation > Evaluate Notebook.

Un problemino geometrico

Consideriamo il problema di costruire un triangolo assegnate le lunghezze dei tre lati. Prendiamo familiarità manuale col problema giocando con il pannello seguente:

DynamicModule[{matriceDiRotazione={{0,1},{-1,0}},a,b,c,p1,p2,cerchi},
Manipulate[
Graphics[{{Point[{{0,0},{a,0}}],Line[{{0,0},{a,0}}],Text[DisplayForm@RowBox[{"a"}],{a/2,0},{0,1}]},{Point[p1=b*Normalize[p1]],Line[{{0,0},p1}],Text[DisplayForm@RowBox[{"b"}],p1/2,matriceDiRotazione.Normalize[p1]]},
{Point[p2={a,0}+c*Normalize[p2-{a,0}]],Line[{{a,0},p2}],
Text[DisplayForm@RowBox[{"c"}],{a,0}+(p2-{a,0})/2,-matriceDiRotazione.Normalize[p2-{a,0}]]},
If[cerchi,{Blue,Circle[{0,0},b],Circle[{a,0},c]},Sequence@@{}]},
PlotRange->{{-1,2},{-1,1}}],
Item["Lunghezze dei lati di un triangolo"],
{{a,1/2},0,1},
{{b,1/2},0,1},
{{c,1/2},0,1},
{{p1,{0,b}},Locator,Appearance->None},
{{p2,{a,c}},Locator,Appearance->None},
{{cerchi,False,"aiuto!"},{False,True},ControlPlacement->Bottom},TrackedSymbols:>All]]

Consideriamo un problema più specifico: disegnare un triangolo isoscele che abbia i tre lati di lunghezze 12, 12 e 17.  Dire poi quant'è l'angolo compreso fra i due lati uguali.

DMSString[ArcTan@@p1*180/Pi]

DynamicModule[{a=12,b=12,c=17,p1,p2,angQ,cerchi,pos,estremo1,estremo2,matriceDiRotazione={{0,1},{-1,0}}},
Manipulate[
Graphics[{{Point[{{0,0},{a,0}}],Line[{{0,0},{a,0}}],Text[12,{a/2,0},{0,1}]},{Point[p1=b*Normalize[p1]],Line[{{0,0},p1}],Text[12,p1/2,matriceDiRotazione.Normalize[p1]]},
{Point[p2={a,0}+c*Normalize[p2-{a,0}]],Line[{{a,0},p2}],
Text[17,{a,0}+(p2-{a,0})/2,-matriceDiRotazione.Normalize[p2-{a,0}]]},
If[angQ,{Circle[{0,0},2,Sort[{0,ArcTan@@p1},Less]],Text[DMSString[ArcTan@@p1*180/Pi],pos=AngleVector[ArcTan@@p1/2];2pos,-Normalize@pos]},Sequence@@{}],
If[cerchi,{Blue,Circle[{0,0},b],Circle[{a,0},c]},Sequence@@{}]},
PlotRange->15{{-1,2},{-.2,1.2}}],
{{p1,{0,b}},Locator,Appearance->None},
{{p2,{a,c}},Locator,Appearance->None},
{{angQ,False,"mostra l'angolo"},{False,True},ControlPlacement->Top},
{{cerchi,False,"aiuto!"},{False,True},ControlPlacement->Bottom}]]

Calcolare e disegnare il triangolo

Nella risoluzione ci aiuteremo con delle figure, così impariamo anche un po' di programmazione grafica.
Dunque, disponiamo uno dei due lati di lunghezza 12 orizzontale, con estremi (0,0) e (12,0):

Graphics[{Point[{0,0}],Red,Point[{12,0}]}]

Variante di sintassi più breve:

Graphics[{Point[{{0,0},{12,0}}]}]

Graphics è una funzione che non attiva nessun calcolo nel Kernel, ma semplicemente dice al FrontEnd di mostrare come output una figura (bidimensionale) invece di un testo. Point è la primitiva grafica che disegna un punto. L'argomento di Point è una lista {ascissa,ordinata}. Familiarizzatevi usando questi pannelli:

Manipulate[Graphics[{Point[{x,y}]},PlotRange->1,Frame->True,PlotLabel->Point[{x,y}]],{{x,0,"ascissa"},-1,1},{{y,0,"ordinata"},-1,1}]

Un cursore bidimensionale al posto di due cursori unidimensionali:

Manipulate[Graphics[{Point[p]},PlotRange->1,Frame->True,PlotLabel->Point[p]],{{p,{0,0},"punto"},{-1,-1},{1,1}}]

Le primitive grafiche come Point sono un po' nascoste nella tavolozza: bisogna andare sotto Basic Commands > 2D > Combining Graphic Objects > Primitives

Si può scegliere la dimensione del punto usando la direttiva grafica PointSize. Dimensioni predefinite sono Tiny, Small, Medium, Large. Guardate l'effetto:

Manipulate[Graphics[{PointSize[dimensione],Point[{x,y}]},PlotRange->1,Frame->True,PlotLabel->{PointSize[dimensione],Point[{x,y}]}],{x,0,1},{y,0,1},{{dimensione,Medium},{Tiny,Small,Medium,Large}}]

Si può anche una dimensione non predefinita: PointSize[d] significa un punto della dimensione ottenuta moltiplicando per d la larghezza della figura. Per esempio, PointSize[0.01] dà un punto largo l'1% della larghezza della figura:

Manipulate[Graphics[{PointSize[dimensione],Point[p]},PlotRange->10,Frame->True,PlotLabel->{PointSize[dimensione],Point[p]}],{{p,{0,0}},Locator,Appearance->None},{{dimensione,.02,"PointSize"},0,1}]

Un'animazione di un punto che si muove su un cerchio:

Animate[Graphics[{PointSize[Large],Point[{Cos[t],Sin[t]}]},PlotRange->2],{t,0,2Pi-Pi/20,Pi/20}]

AngleVector dà il versore che ha il dato angolo rispetto all’asse x positivo, e risparmia di scrivere seno e coseno:

Animate[Graphics[{PointSize[Large],Point[AngleVector[t]]},PlotRange->2],{t,0,2Pi-Pi/20,Pi/20}]

Un'animazione "bruchiforme" di una curva di Lissajous:

Animate[Graphics[Table[{PointSize[Large],GrayLevel[1-a/5],Point[{Cos[t+a/10],Sin[2(t+a/10)]}]},{a,1,10}],PlotRange->1.1],
{t,0,2Pi}]

Generalizzazione:

Manipulate[Graphics[Table[{PointSize[Large],GrayLevel[1-a/4],Point[{Cos[m(t+a/20)],Sin[n(t+a/20)]}]},{a,1,10}],PlotRange->1.1],
{t,0,2Pi,AnimationRunning->True,Animator},
{n,1,5,1,Setter},{m,1,5,1,Setter}]

Aggiungiamo anche il lato, usando la primitiva grafica Line:

Graphics[{PointSize[Large],Point[{{0,0},{12,0}}],
Line[{{0,0},{12,0}}]}]

Poiché questo lato ricorrerà spesso, assegnamolo a una variabile:

base={PointSize[Large],Point[{{0,0},{12,0}}],Line[{{0,0},{12,0}}]}

Graphics[base]

Potremmo anche includere Graphics nella definizione di base, scrivendo base=Graphics[{Point[{0, 0}], Point[{12, 0}], Line[{{0, 0}, {12, 0}}]}]; questo però complicherebbe il séguito.

Ora, il terzo vertice incognito {x,y} si trova a distanza 12 da uno dei due punti (prendiamo l'origine), cioè si trova sul cerchio di centro {0,0} e raggio 12, che si esprime con Circle[{0,0},12] usando la primitiva Circle:

Graphics[{base,Circle[{0,0},12]}]

Anzi, per distinguere meglio il cerchio dal triangolo, disegnamo il cerchio in blu:

Graphics[{base,Blue,Circle[{0,0},12]}]

Per orizzontarci meglio vogliamo vedere le coordinate? Basta aggiungere Axes -> True come opzione a Graphics:

Graphics[{base,Blue,Circle[{0,0},12]},Axes->True]

oppure l'opzione Frame -> True come opzione a Graphics:

Graphics[{base,Blue,Circle[{0,0},12]},Frame->True]

Ora, il terzo vertice incognito {x,y} dista 17 dal secondo vertice {12,0}, cioè sta sul cerchio di centro {12,0} e raggio 17: visualizziamo il cerchio in rosso con Circle[{12,0},17]:

Graphics[{base,Blue,Circle[{0,0},12],Red,Circle[{12,0},17]}]

I due cerchi si incontrano in due punti, uno in alto e uno in basso. Possiamo trovare le coordinate delle intersezioni con Solve: l'equazione del primo cerchio è

Sqrt[(x^2)+(y^2)]==12;

Quella del secondo cerchio è

Sqrt[(((x-12))^2)+(y^2)]==17;

Facciamo il sistema fra le due equazioni e risolviamo (registrando il risultato come sol):

sol=Solve[{Sqrt[x^2+y^2]==12,Sqrt[(x-12)^2+y^2]==17},{x,y}]
N[sol]

Si potrebbe anche usare il comando RegionIntersection:

RegionIntersection[Circle[{0,0},12],Circle[{12,0},17]]

Ecco: abbiamo le coordinate del terzo punto, o, meglio, le due possibilità, che sono simmetriche rispetto all'asse x. Evidenziamo le due soluzioni trovate nella figura. Qui viene a pennello il formato Mathematichese delle soluzioni: infatti per avere i due punti basta scrivere Point[{x,y}]/.sol:

Point[{x,y}]/.sol

Point[{x,y}/.sol]

Figura:

Graphics[{base,{Blue,Circle[{0,0},12],Circle[{12,0},17]},PointSize[Large],Red,Point[{x,y}]/.sol}]

Graphics[{base,{Blue,Circle[{0,0},12],Circle[{12,0},17]},PointSize[Large],Point[{x,y}]/.sol[[2]]}]

Graphics[{base,{Blue,Circle[{0,0},12],Circle[{12,0},17]},PointSize[Large],Point[{x,y}]/.sol[[2]],Line[{{0,0},{x,y}/.sol[[2]]}]}]

Graphics[{base,{Blue,Circle[{0,0},12],Circle[{12,0},17]},PointSize[Large],Point[{x,y}]/.sol[[2]],Line[{{0,0},{x,y}/.sol[[2]]}],Line[{{12,0},{x,y}/.sol[[2]]}]}]

Graphics[{base,PointSize[Large],Point[{x,y}]/.sol[[2]],Line[{{0,0},{x,y}/.sol[[2]]}],Line[{{12,0},{x,y}/.sol[[2]]}]},Frame->True,PlotRangePadding->Scaled[.05]]

Graphics[{base,PointSize[Large],Point[{x,y}]/.sol[[2]],Line[{{0,0},{x,y}/.sol[[2]]}],Line[{{12,0},{x,y}/.sol[[2]]}]},Axes->True]

Si può "raccogliere" la sostituzione, così da scriverla una volta sola:

Graphics[{base,PointSize[Large],{Point[{x,y}],Line[{{0,0},{x,y}}],Line[{{12,0},{x,y}}]}/.sol[[2]]}]

Graphics[{base,PointSize[Large],{Point[{x,y}],Line[{{0,0},{x,y}}],Line[{{12,0},{x,y}}]}/.sol}]

Per ancora più concisione si possono combinare le due Line in una sola:

Graphics[{PointSize[.05],Line[{{0,0},{x,y},{12,0},{0,0}}]/.sol[[2]]}]

Come salvare una figura

Supponiamo di avere una figura:

Graphics[{Circle[],Line[{{0,0},{1,1}}],Point[{0,0}]}]

e di volerla salvare per usarla per esempio in un documento LATEX oppure in una pagina web. Il modo più semplice è di cliccare sulla figura, in modo da selezionarla, e poi di andare al menu File > Save Selection As

Comparirà una finestra di dialogo in cui si chiede dove salvare e in che formato. Se la destinazione è il LATEX il formato consigliato è PDF. Per il web si può provare con PNG, GIF o JPEG.

Un altro metodo per salvare, che è preferibile per esempio quando le figure sono tante e sono prodotte da un programma, è di usare Export. Il seguente comando salva la figura nella stessa cartella di questo notebook, col nome "laMiaPrimaFigura" e in formato PDF:

figura=Graphics[{Circle[],Line[{{0,0},{1,1}}],Point[{0,0}]}];
Export[NotebookDirectory[]<>"laMiaPrimaFigura.pdf",figura]

Controllate che il file della figura sia un grafico di formato vettoriale.

Calcolare l'angolo

Ricordiamoci ora che il problema iniziale era di calcolare l'angolo compreso fra i due lati di lunghezza 12. Nella nostra figura l'angolo è quello in basso a sinistra. Qui bisogna usare la trigonometria. Dato un punto {x,y} del piano, l'angolo compreso fra l'asse x positivo e la semiretta per l'origine e il punto si può ricavare da diverse formule, più o meno adatte a seconda di quale sia il quadrante a cui appartiene {x,y}. Il nostro punto sta nel secondo quadrante (x è negativa e y positiva). Una formula adatta è arccos[ascissa/raggio]:

Manipulate[With[{theta=ArcTan@@p},Graphics[{Point[p],Line[{{0,0},p}],Line[{{-1,0},{1,0}}],{Dashed,Line[{p,{p[[1]],0}}]},{Red,Disk[{0,0},.8Clip[Norm[p],{0,1/10}],Sort[{0,theta},Less]]},Text["x",{p[[1]],0},{0,2}],Text[0,{0,0},{0,2}],Text["(Sqrt[(x^2) + (y^2)])",p/2,{0,-1.5},p],Text["(x, y)",p,-3Normalize[p]],Text["θ",1/10{Cos[theta/2],Sin[theta/2]},-{Cos[theta/2],Sin[theta/2]}]},PlotRange->1,Frame->True,FrameTicks->None]],
{{p,{1,1}/2},Locator}]

Nel nostro caso

sol=Solve[{Sqrt[x^2+y^2]==12,Sqrt[(x-12)^2+y^2]==17},{x,y}]
angolo=ArcCos[(x/(Sqrt[(x^2)+(y^2)]))]/.sol[[2]]
angolo//N

Il valore esatto del nostro angolo è ArcCos[-(1/288)]. Il valore in virgola mobile è

N[angolo]

La funzione ArcCos dà risultati in radianti. Per passare ai gradi basta dividere per π/180, che è il valore di un grado in radianti:

angoloInGradi=N[(angolo/(Pi/180))]

C’è anche la costante Degree, che ha valore π/180:

N[(angolo/Degree)]

Dunque il nostro angolo supera i 90 gradi per meno di 0,2 gradi. Cioè circa 12 primi:

N[((angoloInGradi-90)/(1/60))]

C’è anche la funzione DMSString, che dato un valore in gradi decimali ci dà una stringa con i gradi, primi, secondi:

DMSString[(angolo/Degree)]

C’è anche la funzione DMSList, che dato un valore in gradi decimali ci dà una lista con i gradi, primi, secondi:

DMSList[(angolo/Degree)//N]

Curiosità: questo angolo (in radianti) è un numero algebrico?

appr1=RootApproximant[ArcCos[-(1/288)]]

appr2=RootApproximant[ArcCos[-(1/288)],20]

appr3=RootApproximant[ArcCos[-(1/288)],30]

N[ArcCos[-(1/288)]-appr1,30]

N[ArcCos[-(1/288)]-appr2,30]

N[ArcCos[-(1/288)]-appr3,30]

La primitiva Circle può anche dare un arco, invece di un cerchio completo: basta scrivere Circle[{x,y},raggio,{(Subscript[θ, 0]),(Subscript[θ, 1])}], dove (Subscript[θ, 0]),(Subscript[θ, 1]) sono l'angolo iniziale e finale dell'arco. Con questo possiamo abbellire la figura del triangolo evidenziando l'angolo al vertice:

Show[Graphics[{PointSize[0.016],Point[{0,0}],Point[{12,0}],Point[{x,y}],Line[{{0,0},{12,0},{x,y},{0,0}}],Circle[{0,0},1,{0,angolo}]}/.sol[[2]]]]

Aggiungere un testo dentro a una figura

Ci sono diversi modi per aggiungere un testo dentro a una figura. Il più intuitivo è di usare la tavolozza che si attiva con il menu Graphics ▶ Drawing Tools

I due pulsanti in basso servono per inserire del testo o delle formule dentro il grafico. Provate per esempio a impratichirvi aggiungendo A,B,C come etichette dei vertici nella figura che segue, osservando la differenza fra TraditionalForm Text e Text:

Graphics[{Line[{{0,0},{2,0},{0,1},{0,0}}]}]

Il metodo con la tavolozza ha un difetto: se viene cambiato il grafico sottostante, il testo non permane. Provate ad aggiungere un testo (per esempio "grafico") alla figura seguente:

Plot[Sin[x],{x,-2Pi,2Pi}]

dopo di che cambiate Sin[x] in Sin[2x] nell'input, e rieseguite. Osserverete che la vecchia figura non va persa,  la ritroviamo intatta col testo, mentre la nuova figura con Sin[2x] è senza testo. Raccomandazione: usate la tavolozza soltanto su figure definitive, su cui non farete cambiamenti!!

Un modo programmatico di inserire del testo è di usare la primitiva grafica Text:

Graphics[{Line[{{0,0},{1,0},{0,1},{0,0}}],Text[A,{0,0}]}]

Graphics[{Line[{{0,0},{1,0},{0,1},{0,0}}],Text[A,{0,0}],Text[B,{1,0}],Text[C,{0,1}]}]

Osserverete che le lettere si sovrappongono agli angoli del triangolo. Per spostarle si può per esempio cambiare leggermente la posizione:

Graphics[{Line[{{0,0},{1,0},{0,1},{0,0}}],Text[A,{-.05,-.05}],Text[B,{1.05,0}],Text[C,{0,1.05}]}]

Ma c'è un modo migliore, che tiene conto automaticamente delle dimensioni del testo. A questo scopo si conviene di dare al testo un suo sistema di coordinate scalate, in cui il minimo rettangolo che contiene il testo ("bounding box", in giallo nella figura che segue) ha coordinate che spaziano da (-1,-1) nell'angolo in basso a sinistra fino a (1,1) nell'angolo in alto a destra:

Ebbene, se si dà il comando Text["abc",{x,y},{u,v}], il testo "abc" verrà piazzato in modo che il punto {x,y} del grafico grande avrà coordinate {u,v} nel sistema di coordinate scalate del testo. Per esempio, possiamo fare in modo che la lettera A sia posizionata in modo che il punto {0,0} abbia coordinate {1,1} nel sistema scalato del testo:

Graphics[{Line[{{0,0},{1,0},{0,1},{0,0}}],
Text[A,{0,0},{1,1}]}]

Graphics[{Line[{{0,0},{1,0},{0,1},{0,0}}],
Text[A,{0,0},{1,1}],
Text[B,{1,0},{-1,1}],
Text[C,{0,1},{1,-1}]}]

Insomma, {x,y} e {u,v} indicano lo stesso punto, ma in due sistemi di coordinate diversi.

Nel pannello seguente potete spostare il testo abc usando il mouse, e vedere a che cosa corrisponde quella posizione nella sintassi Text["abc",{x,y},{u,v}]:

DynamicModule[{txt,xmin,xmax,ymin,ymax,Δx,Δy,griglia,p,mostraGriglia},
txt=;
{{xmin,xmax},{ymin,ymax}}=PlotRange/.AbsoluteOptions[txt,PlotRange];
{Δx,Δy}={xmax-xmin,ymax-ymin}/2;
griglia={{Yellow,Opacity[.5],Rectangle[{xmin,ymin},{xmax,ymax}]},Gray,Table[{If[n==0,Red,Sequence@@{}],Line[{{(1+n)Δx,(1-4)Δy},{(1+n)Δx,(1+4)Δy}}],Line[{{(1-4)Δx,(1+n)Δy},{(1+4)Δx,(1+n)Δy}}]},{n,{-2,-1,0,1,2}}]};Manipulate[Graphics[{Translate[{If[mostraGriglia,griglia,Sequence@@{}],First@txt},{p[[1]]- Δx,p[[2]]- Δy}],{Red,Point[{0,0}]}},Frame->True,PlotRange->1,PlotLabel->Text["abc",{0,0},NumberForm[-p/{Δx,Δy},2]]],
{{p,{0,0}},Locator,Appearance->None},
{{mostraGriglia,False,"mostra griglia"},{False,True}},SaveDefinitions->True]]

Si può anche cambiare la dimensione e stile del testo con Style:

Manipulate[Graphics[{Line[{{0,0},{1,0},{0,1},{0,0}}],Text[Style[A,stile],{0,0},{1,1}],Text[Style[B,stile],{1,0},{-1,1}],Text[Style[C,stile],{0,1},{1,-1}]},
BaseStyle->{FontFamily->"Latin Modern Roman"}],
{stile,{Small,Medium,Large,Underlined,Bold,40,Red->HoldForm[Red],{Red,Large}->{HoldForm[Red],Large},(FontFamily->"Times")->(FontFamily->"Times")}}]

Per dare uno stile uniforme al testo (e non solo) si possono mettere le opzioni dentro a BaseStyle come opzione generale di Graphics:

Graphics[{Line[{{0,0},{1,0},{0,1},{0,0}}],
Text[A,{0,0},{1,1}],
Text[B,{1,0},{-1,1}],
Text[C,{0,1},{1,-1}]},
BaseStyle->{FontFamily->"Times"}]

La font “Latin Modern Roman” è particolarmente indicata per figure che siano destinate a un documento LATEX standard. Bisogna però che la font sia installata nel sistema operativo, non soltanto nell’installazione TEX:

FontFamily->"Latin Modern Roman"

Si può cambiare l’orientamento del testo con un ulteriore argomento, che è un vettore che dà la direzione, nelle coordinate in pixel (non nelle unità interne del singolo grafico):

Graphics[{Line[{{0,0},{2,1}}],Text[y==x/2,{1,1/2},{0,-1.5},{2,1}]}]

Quando il grafico ha unità diverse nelle direzioni orizzontale e verticale, la direzione del vettore del testo non ne tiene conto, e per allineare testo con la grafica bisogna compensare:

Manipulate[Graphics[{Line[{{0,0},{2,1}}],Text[y==x/2,{1,1/2},{0,-1.5},p]},AspectRatio->ar,PlotLabel->Text[y==x/2,{1,1/2},{0,-1.5},p]],
{{p,{1,0},"vettore direzione del testo"},{-1,-1},{1,1}},{{ar,1/2,"rapporto d'aspetto"},1/4,4}]

Primitive grafiche in tre dimensioni

Per la grafica in 3 dimensioni il "contenitore" è Graphics3D, invece di Graphics. Molte primitive, come Point e Line, e direttive, come Thick, Red, si estendono banalmente:

Graphics3D[Point[{0,0,0}]]

C'è una sezione della tavolozza per le tre dimensioni:

Graphics3D[{Red,Point[{0,0,0}]}]

Graphics3D[{Red,PointSize[Large],Point[{0,0,0}]}]

Manipulate[Graphics3D[{Red,PointSize[grandezza],Point[{0,0,0}]},PlotLabel->PointSize[grandezza]],{{grandezza,.02},0,1}]

Graphics3D[Line[{{0,0,0},{1,1,1}}]]

Graphics3D[{Thickness[.1],Line[{{0,0,0},{1,1,1}}]}]

Manipulate[Graphics3D[{Thickness[spessore],Line[{{0,0,0},{1,1,1}}]},PlotLabel->Thickness[spessore]],{{spessore,.01},0,1}]

Una piramide a base quadrata:

Graphics3D[{Thick,Line[{{0,0,0},{1,0,0},{1,1,0},{0,1,0},{0,0,0},{1/2,1/2,1},{1,1,0}}],Line[{{1,0,0},{1/2,1/2,1},{0,1,0}}]},Axes->True]

With[{a={0,0,0},b={1,0,0},c={1,1,0},d={0,1,0},e={1/2,1/2,1}},Graphics3D[{Thick,Line[{a,b,c,d,a,e,c}],Line[{b,e,d}]},Axes->True]]

With[{a={0,0,0},b={1,0,0},c={1,1,0},d={0,1,0},e={1/2,1/2,1}},Graphics3D[{Thick,Line[{a,b,c,d,a,e,c}],Line[{b,e,d}],Red,{Text["A",a,{1,1}],Text["B",b,{1,1}],Text["C",c,{1,1}],Text["D",d,{1,1}],Text["E",e,{1,1}]}},Axes->False,Boxed->False]]

Se invece di Line usiamo Tube i lati diventano veri oggetti tridimensionali con effetto prospettico:

With[{a={0,0,0},b={1,0,0},c={1,1,0},d={0,1,0},e={1/2,1/2,1}},Graphics3D[{Tube[{a,b,c,d,a,e,c}],Tube[{b,e,d}]},Axes->True]]

Lo spessore dei Tube si regola dando un valore nelle coordinate interne del grafico, non in percentuale sulla dimensione totale:

With[{a={0,0,0},b={1,0,0},c={1,1,0},d={0,1,0},e={1/2,1/2,1}},Graphics3D[{Tube[{a,b,c,d,a,e,c},.1],Tube[{b,e,d},.1]},Axes->True]]

With[{a={0,0,0},b={1,0,0},c={1,1,0},d={0,1,0},e={1/2,1/2,1}},Manipulate[Graphics3D[{Red,Tube[{a,b,c,d,a,e,c},spessore],Tube[{b,e,d},spessore]},Axes->True],
{{spessore,.02},0,1}]]

Una figura solida è il tetraedro:

Graphics3D[Tetrahedron[]]

Graphics3D[Tetrahedron[{{0,0,0},{1,0,0},{1,1,0},{1/2,1/2,1}}]]

Graphics3D[Tetrahedron[RandomReal[{0,1},{4,3}]]]

Si possono mettere delle tacche lungo i lati del parallelepipedo che racchiude il grafico usando l'opzione Axes->True:

With[{a={0,0,0},b={1,0,0},c={1,1,0},d={0,1,0},e={1/2,1/2,1}},Graphics3D[{Thick,Line[{a,b,c,d,a,e,c}],Line[{b,e,d}]},
Axes->True,AxesLabel->{x,y,z}]]

A dire il vero, Axes->True ha come effetto principale di attivare gli assi, che però di solito non si vedono, perché coincidono con tre lati del parallelepipedo. Possiamo isolare gli assi sopprimendo il parallelepipedo:

With[{a={0,0,0},b={1,0,0},c={1,1,0},d={0,1,0},e={1/2,1/2,1}},Graphics3D[{Thick,Line[{a,b,c,d,a,e,c}],Line[{b,e,d}]},
Axes->True,Boxed->False]]

With[{a={-1.1,-1.1,-.5},b={1,0,0},c={1,1,0},d={0,1,0},e={1/2,1/2,1}},Graphics3D[{Thick,Line[{a,b,c,d,a,e,c}],Line[{b,e,d}]},
Axes->True,Boxed->False,AxesOrigin->{0,0,0},Ticks->False]]

With[{a={-1.1,-1.1,-.5},b={1,0,0},c={1,1,0},d={0,1,0},e={1/2,1/2,1}},Graphics3D[{Thick,Line[{a,b,c,d,a,e,c}],Line[{b,e,d}],Sphere[]},
Axes->True,Boxed->False,AxesOrigin->{0,0,0},Ticks->False]]

With[{a={0,0,0},b={1,0,0},c={1,1,0},d={0,1,0},e={1/2,1/2,1}},Graphics3D[{Thick,Line[{a,b,c,d,a,e,c}],Line[{b,e,d}]},
Boxed->False]]

Noterete che quando si ruota la figura della piramide usando il mouse, al momento in cui il mouse viene rilasciato la figura ha uno scatto e si ridimensiona: questo è perché prende le dimensioni ottimali per la posizione finale. Se per caso vogliamo che le rotazioni avvengano senza scatti, anche a costo di posizioni finali non ottimali e di spreco di spazio, usiamo l'opzione SphericalRegion:

With[{a={0,0,0},b={1,0,0},c={1,1,0},d={0,1,0},e={1/2,1/2,1}},Graphics3D[{Thick,Line[{a,b,c,d,a,e,c}],Line[{b,e,d}]},
Boxed->False,SphericalRegion->True]]

Vogliamo inscrivere una sfera dentro la piramide. Bisogna trovarne il centro.

DynamicModule[{proietta,centro,cima,pr1,pr2,pr3,pr4,z,mostraSezioneQ},
proietta[v_,v0_,v1_,v2_]:=Module[{u1,u2},
{u1,u2}=Orthogonalize[{v1-v0,v2-v0}];
v0+((v-v0).u1)*u1+((v-v0).u2)*u2];
centro[z_]={1/2,1/2,z};
cima={1/2,1/2,1};
pr1[z_]=proietta[centro[z],{0,0,0},{1,0,0},cima];
pr2[z_]=proietta[centro[z],{0,0,0},{0,1,0},cima];
pr3[z_]=proietta[centro[z],{1,1,0},{0,1,0},cima];
pr4[z_]=proietta[centro[z],{1,1,0},{1,0,0},cima];
Manipulate[
Row[{Graphics3D[{{Thick,Line[{{0,0,0},{1,0,0},{1,1,0},{0,1,0},{0,0,0},cima,{1,1,0}}],Line[{{1,0,0},cima,{0,1,0}}]},{Dashing[{.01,.01}],Red,Line[{{1/2,1/2,0},centro[z],pr1[z]}],Line[{centro[z],pr2[z]}],Line[{centro[z],pr3[z]}],Line[{centro[z],pr4[z]}],PointSize[.02],Point[{{1/2,1/2,0},centro[z],pr1[z],pr2[z],pr3[z],pr4[z]}]},Gray,Line[{{1/2,0,0},cima,{1/2,1,0},{1/2,0,0}}],Line[{{0,1/2,0},cima,{1,1/2,0},{0,1/2,0}}],If[mostraSezioneQ,{Opacity[.5],Polygon[{{0,1/2,0},cima,{1,1/2,0},{0,1/2,0}}]},Sequence@@{}]},Boxed->False,ImageSize->200],Spacer[20],Graphics[{{Gray,If[mostraSezioneQ,{Opacity[.5],Polygon[{{0,0},{1,0},{1/2,1},{0,0}}]},Sequence@@{}],Line[{{0,0},{1,0},{1/2,1},{0,0}}]},Red,Dashing[{.01,.01}],Line[{{1/2,0},{1/2,z},Rest@pr1[z]}],Point[{{1/2,0},{1/2,z},Rest@pr1[z]}],Text["z - 2  x = 0",{1/4,1/2},{0,-2},{1/2,1}]},ImageSize->120]}],
{{z,1/2},0,1},{{mostraSezioneQ,False,"Mostra la sezione"},{False,True}},SaveDefinitions->True]]

Per simmetria il centro ha coordinate x e y uguali a 1/2. Resta da trovare la z. L'equazione del piano passante per (0,0,0), (0,1,0), (1/2,1/2,1) è z-2x=0. La distanza (con segno) del punto (x,y,z) dal piano è |z-2x|/(Sqrt[(1^2)+(((-2))^2)]). Quindi per trovare il centro della sfera bisogna risolvere l'equazione

zCentro=z/.Solve[((Abs[z-2*(1/2)])/(Sqrt[1+4]))==z,z][[1]]

RegionDistance[InfinitePlane[{{0,0,0},{0,1,0},{1/2,1/2,1}}],
{x,y,z}]//Simplify

Aggiungiamo la primitiva Sphere alla piramide:

With[{a={0,0,0},b={1,0,0},c={1,1,0},d={0,1,0},e={1/2,1/2,1}},Graphics3D[{Thick,Line[{a,b,c,d,a,e,c}],Line[{b,e,d}],Sphere[{1/2,1/2,zCentro},zCentro]},
Boxed->False]]

E se volessimo le facce della piramide, oltre agli spigoli? Basta usare Polygon. Aggiungiamo una faccia alla volta:

With[{a={0,0,0},b={1,0,0},c={1,1,0},d={0,1,0},e={1/2,1/2,1}},Graphics3D[{Thick,Line[{a,b,c,d,a,e,c}],Line[{b,e,d}],Polygon[{a,b,e}]}]]

With[{a={0,0,0},b={1,0,0},c={1,1,0},d={0,1,0},e={1/2,1/2,1}},Graphics3D[{Thick,Line[{a,b,c,d,a,e,c}],Line[{b,e,d}],Polygon[{a,b,e}],
Polygon[{a,d,e}]}]]

With[{a={0,0,0},b={1,0,0},c={1,1,0},d={0,1,0},e={1/2,1/2,1}},Graphics3D[{Thick,Line[{a,b,c,d,a,e,c}],Line[{b,e,d}],Polygon[{a,b,e}],
Polygon[{a,d,e}],
Polygon[{d,c,e}]}]]

With[{a={0,0,0},b={1,0,0},c={1,1,0},d={0,1,0},e={1/2,1/2,1}},Graphics3D[{Thick,Line[{a,b,c,d,a,e,c}],Line[{b,e,d}],Polygon[{a,b,e}],
Polygon[{a,d,e}],
Polygon[{d,c,e}],
Polygon[{c,b,e}]}]]

With[{a={0,0,0},b={1,0,0},c={1,1,0},d={0,1,0},e={1/2,1/2,1}},Graphics3D[{Thick,Line[{a,b,c,d,a,e,c}],Line[{b,e,d}],
Polygon[{a,b,e}],
Polygon[{a,d,e}],
Polygon[{d,c,e}],
Polygon[{c,b,e}],
Polygon[{a,b,c,d}]}]]

Le facce per default sono opache. Possiamo renderle trasparenti con la direttiva Opacity:

With[{a={0,0,0},b={1,0,0},c={1,1,0},d={0,1,0},e={1/2,1/2,1}},Manipulate[Graphics3D[{Thick,Line[{a,b,c,d,a,e,c}],Line[{b,e,d}],
Opacity[opacità],
Polygon[{a,b,e}],
Polygon[{a,d,e}],
Polygon[{d,c,e}],
Polygon[{c,b,e}],
Polygon[{a,b,c,d}]},
PlotLabel->Opacity[opacità]],
{{opacità,1},0,1}]]

Volendo si possono rendere trasparenti anche gli spigoli. Come si fa?

Aggiungiamo la sfera inscritta:

zCentro=z/.Solve[((Abs[z-2*(1/2)])/(Sqrt[1+4]))==z,z][[1]];With[{a={0,0,0},b={1,0,0},c={1,1,0},d={0,1,0},e={1/2,1/2,1}},Graphics3D[{Thick,Line[{a,b,c,d,a,e,c}],Line[{b,e,d}],
Sphere[{1/2,1/2,zCentro},zCentro],
Opacity[1/2],Polygon[{a,b,e}],
Polygon[{a,d,e}],
Polygon[{d,c,e}],
Polygon[{c,b,e}],
Polygon[{a,b,c,d}]},Boxed->False]]

Si possono anche specificare più poligoni all'interno di un singolo Polygon:

With[{a={0,0,0},b={1,0,0},c={1,1,0},d={0,1,0},e={1/2,1/2,1}},Graphics3D[{Thick,Line[{a,b,c,d,a,e,c}],Line[{b,e,d}],
Polygon[{{a,b,e},{a,d,e},{d,c,e},{c,b,e},{a,b,c,d}}]}]]

C’è anche il comando ConvexHullMesh, che produce l’inviluppo convesso dei punti dati:

piramideRegion=With[{a={0,0,0},b={1,0,0},c={1,1,0},d={0,1,0},e={1/2,1/2,1}},
ConvexHullMesh[{a,b,c,d,e}]]

Volume[piramideRegion]

Però ConvexHullMesh dà un oggetto che non è di tipo Graphics3D, ma una regione di tipo BoundaryMeshRegion, che si comporta diversamente

Con MeshPrimitives si possono estrarre le facce, gli spigoli o i vertici come primitive grafiche:

facce=MeshPrimitives[piramideRegion,2]

spigoli=MeshPrimitives[piramideRegion,1]

vertici=MeshPrimitives[piramideRegion,0]

Graphics3D[{facce,spigoli,vertici}]

Una piramide a più piani:

With[{a={0,0,0},b={1,0,0},c={1,1,0},d={0,1,0},e={1/2,1/2,1}},pir=Polygon[{{a,b,e},{a,d,e},{d,c,e},{c,b,e},{a,b,c,d}}];Graphics3D[{EdgeForm[Thick],{Opacity[.5],pir},
Table[Scale[Polygon[{{a,b,c,d}}],n/4,e],{n,4}]},Boxed->False]]

Come viene senza gli spigoli?

With[{a={0,0,0},b={1,0,0},c={1,1,0},d={0,1,0},e={1/2,1/2,1}},Graphics3D[{Thick,
Polygon[{{a,b,e},{a,d,e},{d,c,e},{c,b,e},{a,b,c,d}}]}]]

With[{a={0,0,0},b={1,0,0},c={1,1,0},d={0,1,0},e={1/2,1/2,1}},Graphics3D[{Thick,EdgeForm[None],
Polygon[{{a,b,e},{a,d,e},{d,c,e},{c,b,e},{a,b,c,d}}]}]]

Altre due primitive grafiche in tre dimensioni sono il Cuboid, il Cylinder: e (dalla versione 7 in poi) il Cone e il Tube:

Graphics3D[{Cuboid[{0,0,0},{1,2,1}],Cylinder[{{-2,1,0},{-2,1,1}},1],Cone[{{-1,-1,0},{-1,-1,1}},1],Tube[{{0,0,-1},{0,0,2}}]}]

Graphics3D[{Cuboid[{0,0,0},{1,2,1}],Cylinder[{{-2,1,0},{-2,1,1}},1],Cone[{{-1,-1,0},{-1,-1,1}},1],Tube[{{0,0,-1},{0,0,2}}]}]

Le primitive grafiche si possono ruotare con Rotate:

Graphics3D[{Rotate[Cuboid[{0,0,0},{1,2,1}],Pi/4,{0,0,1}],Cylinder[{{-2,1,0},{-2,1,1}},1],Cone[{{-1,-1,0},{-1,-1,1}},1],Tube[{{0,0,-1},{0,0,2}}]},Axes->True]

Manipulate[Graphics3D[{Rotate[Cuboid[{0,0,0},{1,2,1}],θ,{0,0,1}],Cylinder[{{-2,1,0},{-2,1,1}},1],Cone[{{-1,-1,0},{-1,-1,1}},1],Tube[{{0,0,-1},{0,0,2}}]},Axes->True],
{θ,0,2Pi}]

Un robot assemblato con primitive:

robot=
With[{r=0.15},
testa=Sphere[{0,0,5 r},r];
torace=Cuboid[{-1.5 r,-0.6 r,0},{1.5 r,0.6 r,4 r}];
addome=Cuboid[{-1.5 r,-0.6 r,0},{1.5 r,0.6 r,-2 r}];
braccioDestro=Tube[{{1.6 r,0,4 r},{3 r,.3,-r}},0.4 r];
braccioSinistro=Cylinder[{{-1.6 r,0,4 r},{-3 r,0,-r}},0.4 r];
cosciaSinistra=Cylinder[{{-0.8 r,0,-2 r},{-0.8 r,0,-4.5 r}},0.6 r];
cosciaDestra=Cylinder[{{0.8 r,0,-2 r},{0.8 r,0,-4.5 r}},0.6 r];
tibiaSinistra=Cylinder[{{-0.8 r,0,-4.5 r},{-0.8 r,0,-8 r}},0.5 r];
tibiaDestra=Cylinder[{{0.8 r,0,-4.5 r},{0.8 r,0,-8 r}},0.5 r];
piedeSinistro=Cuboid[{-0.5 r,-0.5 r,-8 r},{-1.1 r,1.1 r,-8.3 r}];
piedeDestro=Cuboid[{0.5 r,-0.5 r,-8 r},{1.1 r,1.1 r,-8.3 r}];
Graphics3D[{testa,torace,addome,braccioDestro,braccioSinistro,cosciaDestra,cosciaSinistra,tibiaSinistra,tibiaDestra,piedeSinistro,piedeDestro},Boxed->False]]

Se si esporta in pdf un grafico tridimensionale, per default si produce un’immagine bitmap:

Export[NotebookDirectory[]<>"robot.pdf",robot]

Per avere un’immagine vettoriale si può usare l’opzione ImageResolution -> Infinity

Export[NotebookDirectory[]<>"robot.pdf",robot,ImageResolution->Infinity]

Esercizi

Disegnare un triangolo con lati 29, 29 e 41. Calcolare l'angolo compreso fra i due lati uguali.

L'angolo ArcCos[-(1/288)] è forse un angolo notevole, cioè un multiplo razionale di π? Per saggiare il problema calcolare N[((ArcCos[-(1/288)])/π),40] e dire se ha l'aria di essere un numero decimale periodico.

Esprimere ArcSin[1/4] in gradi, primi e secondi.

Scrivere del codice che riproduca il segnale stradale di divieto di transito , quello di senso vietato , quello di semaforo verticale provvisorio .

Il simbolo del sole delle alpi  è interessante geometricamente. Limitandosi al contorno , riuscite a riprodurlo con Mathematica? E a generalizzare a un numero diverso di petali, sceglibile interattivamente?

La costante Degree ha valore numerico π/180. Provare a eseguire 45Degree, Cos[45*Degree], Sin[90Degree], Simplify[Degree/Pi], FullSimplify[Degree/π]. 45 Degree // FunctionExpand.

In una formula, per esempio Sin[45*Degree], selezionare Degree e poi andare sul menù Help > Find Selected Function. Conviene memorizzare l'abbreviazione da tastiera per questo aiuto.

In un'espressione Mathematica complicata può essere difficile da capire come si annidano le varie parentesi. Se si clicca più volte in un punto dell'espressione viene selezionata la parte via via crescente dell'espressione che contiene il punto cliccato. Vedi anche il menù Edit > Extend Selection.

Comporre uno smiley ☺ usando primitive grafiche come Circle, Point, Line. Introducendo un parametro e un Manipulate fare in modo che la curvatura della bocca sia controllabile da un cursore (da ☺ a ☹ e viceversa)

Aggiungere qualche altro dettaglio anatomico, tipo occhi e bocca, al modello umano in 3D.

Esempio avanzato: risolvere un'equazione diofantea di primo grado e visualizzare il risultato

Un programma per risolvere le equazioni diofantee di primo grado in due variabili:

risolviDiofantea[membro1_==membro2_,{x_Symbol,y_Symbol},k_Symbol]:=Module[{eq,coefX,coefY,termineNoto,mcd,solMcd,solOmogenea,solGenerale},
eq=ExpandAll[membro1-membro2];
coefX=Coefficient[eq,x];
coefY=Coefficient[eq,y];
termineNoto=eq/.{x->0,y->0};
{mcd,solMcd}=ExtendedGCD[coefX,coefY];
If[mcd==0,If[termineNoto==0,Return[{{}}],Return[{}]]];
If[Mod[termineNoto,mcd]≠0,Return[{}]];
solOmogenea={coefY,-coefX}/mcd;
solGenerale=Expand[-solMcd*termineNoto/mcd+k*solOmogenea];
Thread[{x,y}->solGenerale]];
risolviDiofantea[True,{x_Symbol,y_Symbol},k_Symbol]:={{}};

Esempio:

risolviDiofantea[3x+2y==5,{x,y},k]

Un programma per visualizzare le soluzioni delle equazioni diofantee di primo grado:

figuraDiofantea[eq_,{x_Symbol,y_Symbol},k_Symbol]:=Module[{sol,soluzionePiena,kmedio,kmin,kmax,x1,y1,x2,y2},
sol=risolviDiofantea[eq,{x,y},k];
If[sol=={},Return[Column[{Row@{"equazione: ",eq},"niente soluzioni!"}]]];
soluzionePiena:=Graphics[{Red,PointSize[.03],Table[Point[{x,y}],{x,-3,3},{y,-3,3}]},{GridLines->{Range[-3,3],Range[-3,3]},Frame->True,AspectRatio->Automatic}];
If[sol=={{}},Return[Show[soluzionePiena]]];
kmedio=k/.Solve[D[x^2+y^2/.sol,k]==0,k][[1]];
{kmin,kmax}={Floor[kmedio-1],Floor[kmedio+2]};
{{x1,y1},{x2,y2}}={x,y}/.sol/.{{k->kmin},{k->kmax}};
Graphics[{Dashed,Line[{x,y}/.sol/.{{k->kmin},{k->kmax}}],
Red,PointSize[Large],Tooltip[Point[{x,y}],{x,y}]/.sol/.Table[{k->r},{r,kmin,kmax}]},GridLines->{Range[Min[x1,x2],Max[x1,x2]],Range[Min[y1,y2],Max[y1,y2]]},Frame->True,FrameTicks->{Range[Min[x1,x2],Max[x1,x2]],Range[Min[y1,y2],Max[y1,y2]],None,None},PlotLabel->Grid[{{"equazione: ",eq},{"soluzioni: ",sol},{"",""}}]]];

Esempio:

figuraDiofantea[3x+2y==5,{x,y},k]

Un pannello interattivo:

Clear[k];
Manipulate[figuraDiofantea[n x+m y==p,{x,y},k],Item[Style["Equazione diofantea lineare",Large],ControlPlacement->Top],
{n,Range[8]},{m,Range[8]},{p,Range[8]},ControlPlacement->Bottom]