{ domaine de variation } {min} Dilation (U,O,-1)[hidden]; {max} Dilation (U,O,2)[hidden]; {range} Segment (max,min)[hidden]; { point courant } {X} Point on object (range, 0)[hidden]; {x} Ratio/Points (O,U,X,0,0,' ')[hidden]; {y} Calculate (0,0,' ','1@atan8*A*@sin_')(x)[hidden]; { report Y } {Y} Dilation/MarkedRatio (V,O,y)[hidden]; {M} VectorTranslation (Y,O,X)[hidden]; {loc} Locus (M,X,range, 200)[red]; |
Traçons ainsi la courbe y² = x(x-1)(x-2)
Le domaine de définition est [0, 1] ∪ [2, +∞[
que l'on simplifie en [0, 3], le Calculate rendra simplement "undefined" dans ]1, 2[
Comme on obtient la seule valeur y ≥ 0, une symétrie y → -y donne la deuxième moitié de la courbe.
{ point courant } {X} Point on object (range, 0.2)[label('X')]; {x} Ratio/Points (O,U,X,50,15,'x = '); {y} Calculate (50,30,'y = ','A3-A*2+A*@sqrt')(x); { report Y } {Y} Dilation/MarkedRatio (V,O,y)[yellow,label('Y')]; {M} VectorTranslation (Y,O,X)[label('M'),yellow]; {loc} Locus (M,X,range, 100)[red,layer(2)]; {M!} Reflection (M,Ou)[label('M'''),yellow]; {loc2} Locus (M!,X,range, 100)[red,layer(2)]; |
Un petit problème d'intervalles apparaît ici : il faut un point du Locus exactement en 0, 1 et 2.
Sinon un "trou" apparaît sur la courbe
On est ainsi amené à ajuster au mieux le nombre de points du Locus.
Ce phénomène est mis en évidence dans l'applet en traçant un Locus avec 200 points au lieu de 100
Il faut ici 3n+1 points, soit 3n intervalles, mais la division par 3 ne tombe pas juste !
Il est ainsi impossible d'avoir les points exactement en x = 1 et 2.
Selon la distance des points voisins de 1 et 2, le trou est plus ou moins visible.
Bien entendu 200 n'est pas du tout 3n+1, le trou est invisible avec 202 ou 199 points !
Nota : le calcul de y² est fait par la formule y² = ((x - 3)×x + 2)×x (calcul de polynôme par la méthode de Horner)
Donnons l'exemple d'une courbe fermée : une épicycloïde, d'équation
x = (R/3) (4 cos(2πt) - cos(8πt)) t∈[0, 1]
y = (R/3) (4 sin(2πt) - sin(8πt))
{OU} Segment (U,O)[hidden]; {t!} Point on object (OU, 0)[hidden]; {t1} Ratio/Points (O,U,t!,0,0,' ')[hidden]; { entre 0 et 2pi } {t} Calculate (0,0,' ','1@atan8*A*')(t1)[hidden]; { Calcul coordonnées } {x} Calculate (85,15,'x = ','A@cos_4*A4*@cos_-3/')(t)[hidden]; {y} Calculate (165,15,'y = ','A@sin_4*A4*@sin_-3/')(t)[hidden]; {X} Dilation/MarkedRatio (U,O,x)[hidden]; {Y} Dilation/MarkedRatio (V,O,y)[hidden]; {P} VectorTranslation (X,O,Y)[hidden]; {loc} Locus (P,t!,OU, 200); |
Si comme ici, le repère utilisé est orthonormé, on peut utiliser les systèmes de coordonnées de JavaSketchpad :
... {xOy} Origin&Unit (O,U)[hidden]; {P} PlotXY (y, xOy, x)[hidden]; {loc} Locus (P,t!,OU, 200); |
Une courbe infinie sera toujours limitée, et si on veut éviter des points aberrants, il faut s'arrêter avant les points donnant des valeurs infinies pour x ou y, si possible...
{ segment unité ]0, 1[ } {O!} Dilation (U,O,0.001)[hidden]; {U!} Dilation (O,U,0.001)[hidden]; {OU} Segment (U!,O!)[hidden]; {t!} Point on object (OU, 0.6)[hidden]; {t1} Ratio/Points (O,U,t!,0,0,' ')[hidden]; { dans ]0, 2pi[ } {t} Calculate (0,0,' ','1@atan8*A*')(t1)[hidden]; { paramètres } {a} Parameter (0.2,0,0,' ')[hidden]; {b} Parameter (1,0,0,' ')[hidden]; { Calcul coordonnées polaires } {r} Calculate (0,0,' ','BA2/@tan_/C+')(t,a,b)[hidden]; {X} Dilation/MarkedRatio (U,O,r)[hidden]; {P} Rotation/MeasuredAngle (X,O,t)[hidden]; {loc} Locus (P,t!,OU, 200); |
A titre d'exemple arbitraire, la courbe y4 - x2y2 + x4 - 1 = 0
Cette courbe est de degré 2 en Y = y² : Y2 - x2Y + x4 - 1 = 0
Le discriminant est Δ = x4 - 4(x4 - 1) = 4 - 3x4
et donc la courbe est nécessairement
avec |x| ≤ 4√4/3.
On va la tracer sous forme de quatre Locus :
y = ±√(x² ± √Δ)/2 ,
avec les ± indépendants.
En fait il faut de plus que l'expression sous le radical soit ≥0, on ne va pas trop se poser de question et
laisser faire le Calculate (qui retournera "undefined" si <0)
{ mesure bidon pour le Calculate (4/3)^0.25 } {un} Distance (O,U,0,0,' ')[hidden]; {max} Calculate (0,0,' ','4 3/0.25^')(un); {Xmax} Dilation/MarkedRatio (U,O,max)[hidden]; {Xmin} Dilation (Xmax,O, -1)[hidden]; {XX} Segment (Xmax,Xmin)[hidden]; { point courant } {X} Point on object (XX, 0)[hidden]; {x} Ratio/Points (O,U,X,0,0,' ')[hidden]; {y1} Calculate (0,0,' ','AA* 4AAAA***3*-@sqrt + 2/ @sqrt')(x)[hidden]; {Y1} Dilation/MarkedRatio (V,O,y1)[hidden]; {P1} VectorTranslation (Y1,O,X)[hidden]; {loc1} Locus (P1,X,XX, 200); { autre branche } {y2} Calculate (225,30,'y2 = ','AA* 4AAAA***3*-@sqrt - 2/ @sqrt')(x)[hidden]; {Y2} Dilation/MarkedRatio (V,O,y2)[hidden]; {P2} VectorTranslation (Y2,O,X)[hidden]; {loc2} Locus (P2,X,XX, 200); { symétrie y / -y } {P3} Reflection (P1,OU)[hidden]; {loc3} Locus (P3,X,XX, 200); {P4} Reflection (P2,OU)[hidden]; {loc4} Locus (P4,X,XX, 200); |
Cette méthode "sauvage" fait apparaître un problème général :
la courbe est largement trouée au voisinage des points à tangente verticale.
Une solution est d'augmenter très fortement le nombre de points du Locus :
il faut ainsi plus de 2000 points pour combler le trou au voisinage de (1,0) !
Un peu d'astuce ici montre que la courbe est symétrique en x,y et qu'il suffit donc de tracer
la branche supérieure
avec -1≤x≤1 pour la tracer toute entière par symétrie,
sans aucun trou,
avec moins d'une centaine de points par Locus.
(la tracer pour 0≤x≤1 suffirait, mais il faudrait répéter 8 morceaux au lieu de 4)
{U!} Dilation (U,O, -1)[hidden]; {UU!} Segment (U,U!)[hidden]; {X!} Point on object (UU!, 0)[hidden]; {x!} Ratio/Points (O,U,X!,5,30,'x = ')[hidden]; {y!} Calculate (105,30,'y = ','AA* 4AAAA***3*-@sqrt + 2/ @sqrt')(x!)[hidden]; {Y1!} Dilation/MarkedRatio (V,O,y!)[hidden]; {P1!} VectorTranslation (Y1!,O,X!)[hidden]; {loc1!} Locus (P1!,X!,UU!, 50); { reflexion d'axe horizontal } {P2!} Reflection (P1!,OU)[hidden]; {loc2!} Locus (P2!,X!,UU!, 50); { echange x,y } {X3!} Dilation/MarkedRatio (U,O,y!)[hidden]; {Y3!} Dilation/MarkedRatio (V,O,x!)[hidden]; {P3!} VectorTranslation (Y3!,O,X3!)[hidden]; {loc3!} Locus (P3!,X!,UU!, 50); { reflexion d'axe vertical } {P4!} Reflection (P3!,OV)[hidden]; {loc4!} Locus (P4!,X!,UU!, 50); |
A titre d'exemple, nous allons peindre l'intérieur d'une ellipse.
Reprenons le tracé de l'ellipse par affinité d'un cercle.
Si on peint à l'aide du segment P'H', les segments successifs ne sont pas régulièrement répartis si
le point de contrôle décrit le cercle principal.
Le remplissage est plus propre si le point de contrôle décrit le grand axe (ou le petit d'ailleurs).
Le contour de l'ellipse doit ici être tracé en deux parties, par symétrie autour de l'axe.
{ tracé ellipse } {AA} Segment (A,A!)[hidden]; {H!} Point on object (AA,0)[hidden]; {hp!} Perpendicular (AA,H!)[hidden]; {M!} Intersect1 (hp!,C)[hidden]; {P!} Dilation/3PtRatio (M!,H!,H,M,P)[hidden]; {P2!} Reflection (P!,AA)[hidden]; { Ellipse } {ell} Locus (P!,H!,AA,200)[layer(2)]; {ell2} Locus (P2!,H!,AA,200)[layer(2)]; {remplissage } {PP!} Segment (P!,P2!)[hidden]; {area} Locus (PP!,H!,AA,200)[layer(1)]; |
Pour ne pas cacher le contour, mettre le Locus de remplissage en dessous (layer).
Le nombre de points du Locus de remplissage doit être > nombre de pixels le long de AA'
(sinon trous, voire hachures).
En tout cas, si l'axe n'est pas horizontal, apparait un effet de moire.
Il vaut mieux remplir dans tous les cas par des segments verticaux ou horizontaux.
Ici cela nécessite de construire un balayage par une droite différente de l'axe de l'ellipse.
Une verticale est transformée en OY par l'affinité inverse P→M, on balaye alors par une droite parallèle
à cette direction,
coupant le cercle en M1 et M2 qui sont transformés par l'affinité M→P pour obtenir un
segment vertical P1P2 balayant l'ellipse.
Le point de controle H* balaye le segment définit par les tangentes au cercle parallèles à OY.
{x} Translation (O, 100, 0)[hidden]; {y} Translation (O, 0, 50)[hidden]; {ox} Line (x,O)[hidden]; { transformée d'une verticale } {ky} Perpendicular (aa,y)[hidden]; {K} Intersect (ky,aa)[hidden]; {Y} Dilation/3PtRatio (y,K,H,P,M)[hidden]; {yy} Line (Y,O)[hidden]; { tangentes au cercle } {xx} Perpendicular (yy,O)[hidden]; {T1} Intersect1 (xx,C)[hidden]; {ty1} Perpendicular (xx,T1)[hidden]; { domaine } {min} Intersect (ty1,ox)[hidden]; {max} Dilation (min,O, -1)[hidden]; {XX} Segment (max,min)[hidden]; { point courant } {H*} Point on object (XX, 0.15)[hidden]; {Hy} Perpendicular (xx,H*)[hidden]; {Y1} Intersect1 (Hy,C)[hidden]; {hy1} Perpendicular (aa,Y1)[hidden]; {H1} Intersect (hy1,aa)[yellow,hidden]; {P1} Dilation/3PtRatio (Y1,H1,H,M,P)[hidden]; {Y2} Intersect2 (Hy,C)[hidden]; {hy2} Perpendicular (aa,Y2)[hidden]; {H2} Intersect (hy2,aa)[hidden]; {P2} Dilation/3PtRatio (Y2,H2,H,M,P)[hidden]; {PP} Segment (P1,P2)[hidden]; {area} Locus (PP,H*,XX, 250)[layer(1)]; {ell1} Locus (P1,H*,XX, 250)[layer(2)]; {ell2} Locus (P2,H*,XX, 250)[layer(2)]; |
Evidemment, un polygone approché évite ces inconvénients, mais il faut transformer explicitement une bonne vingtaine de points du cercle.