/* bezier-cor.c - Edouard Thiel - 31/01/2001 Compilation : cc bezier-cor.c -o bezier-cor `~/helium/helium-cfg --cflags --libs` -lm Usage : bezier-cor */ #include #include He_node *princ, *canvas, *panel1, *panel2, *mess1, *tog1, *tog2, *tog3, *tog4, *text1, *text2, *text3; int timeout1; /* * Les sommets du polygone de contrôle Px[] et Py[] et leur nombre Pn * sont en global ; le degré d'une courbe de Bezier de Pn sommets est Pn-1. */ #define PMAX 10000 double Px[PMAX], Py[PMAX]; int Pn = 0; #define MIN_DIST 3 /*--------------------------- A F F I C H A G E ------------------------------*/ int c_gris, c_rouge, c_vert, c_bleu, c_jaune, c_cyan, c_magenta; /* * Initialise les couleurs */ void init_couleurs () { c_gris = HeAllocRgb (150, 150, 150, he_black); c_rouge = HeAllocRgb (255, 0, 0, he_black); c_vert = HeAllocRgb ( 0, 255, 0, he_black); c_bleu = HeAllocRgb ( 0, 0, 255, he_black); c_jaune = HeAllocRgb (255, 255, 0, he_black); c_cyan = HeAllocRgb ( 0, 255, 255, he_black); c_magenta = HeAllocRgb (255, 0, 255, he_black); } /* * Change la couleur courante */ void couleur (int coul) { XSetForeground (he_display, he_gc, coul); } /* * Dessins de base */ void dessin_ligne (Window win, int x1, int y1, int x2, int y2) { XDrawLine (he_display, win, he_gc, x1, y1, x2, y2); } void dessin_carre (Window win, int x, int y) { int l = MIN_DIST; XDrawRectangle (he_display, win, he_gc, x-l, y-l, l*2, l*2); } void dessin_rond (Window win, int x, int y) { int l = MIN_DIST; XDrawArc (he_display, win, he_gc, x-l, y-l, l*2, l*2, 0, 360*64); } /*----------------------- A L G O R I T H M E S ------------------------------*/ /* * Calcule les coordonnées du point de paramètre t (t entre 0 et 1) * avec l'algorithme de De Casteljau ; met le résultat dans *x et *y. */ void calcule_point_castel (double t, double *x, double *y) { double Qx[PMAX], Qy[PMAX], s = 1-t; int i, j, k; /* Recopie */ for (i = 0; i < Pn; i++) { Qx[i] = Px[i]; Qy[i] = Py[i]; } /* Algorithme de De Casteljau */ for (j = 1; j < Pn; j++) { k = Pn-1 - j; for (i = 0; i <= k; i++) { Qx[i] = (s*Qx[i] + t*Qx[i+1]); Qy[i] = (s*Qy[i] + t*Qy[i+1]); } } *x = Qx[0]; *y = Qy[0]; } /* * Dessin géométrique de l'algorithme de De Casteljau : * trace les segments pour le paramètre t (t entre 0 et 1). */ void trace_geom_castel (Window win, double t) { double Qx[PMAX], Qy[PMAX], s = 1-t; int i, j, k; /* Recopie */ for (i = 0; i < Pn; i++) { Qx[i] = Px[i]; Qy[i] = Py[i]; } /* Algorithme de De Casteljau */ for (j = 1; j < Pn; j++) { k = Pn-1 - j; for (i = 0; i <= k; i++) { Qx[i] = (s*Qx[i] + t*Qx[i+1]); Qy[i] = (s*Qy[i] + t*Qy[i+1]); } for (i = 0; i < k; i++) dessin_ligne (win, Qx[i], Qy[i], Qx[i+1], Qy[i+1]); } } /* * Dessin de la courbe de Bezier par échantillonnage avec un pas : * calcule les points de paramètre t de 0 à 1 en incrémentant de pas, * et relie ces points 2 à 2 par un segment. */ void trace_courbe_echant (Window win, double pas) { double t, x1, y1, x2, y2; if (Pn < 2) return; x1 = Px[0]; y1 = Py[0]; for (t = pas; t < 1; t += pas) { calcule_point_castel (t, &x2, &y2); dessin_ligne (win, x1, y1, x2, y2); x1 = x2; y1 = y2; } dessin_ligne (win, x1, y1, Px[Pn-1], Py[Pn-1]); } /* * Calcule le carré de la distance entre un point C et la droite AB */ double d2d_droite (double ax, double ay, double bx, double by, double cx, double cy) { double x1 = ax-bx, y1 = ay-by, x2 = ax-cx, y2 = ay-cy, aire = x1*y2 - y1*x2, nab2 = x1*x1 + y1*y1; return (fabs(nab2) < 1E-5) ? 1E5 : aire * aire / nab2; } /* * Dessin de la courbe par subdivision récursive avec un seuil : * * - on recopie Sx[] et Sy[] dans Qx[] et Qy[] ; * - on calcule le point milieu (t = 0.5) par De Casteljau ; en même temps * on mémorise les côtes de la pyramide dans Ax[],Ay[] et Bx[],By[] ; * - on calcule le carré d2 de la distance entre le point milieu et la droite * qui passe par les 2 extrémités ; * - si d2 > seuil on rappelle trace_courbe_subdiv sur Ax[],Ay[] et Bx[],By[] * sinon on dessine le segment joignant les 2 extrémités. */ void trace_courbe_subdiv (Window win, double *Sx, double *Sy, double seuil) { double Ax[PMAX], Ay[PMAX], Bx[PMAX], By[PMAX], Qx[PMAX], Qy[PMAX], d2; int i, j, k; /* Recopie */ for (i = 0; i < Pn; i++) { Qx[i] = Sx[i]; Qy[i] = Sy[i]; } Ax[0] = Qx[0]; Ay[0] = Qy[0]; Bx[0] = Qx[Pn-1]; By[0] = Qy[Pn-1]; /* Calcul point milieu par De Casteljau */ for (j = 1; j < Pn; j++) { k = Pn-1 - j; for (i = 0; i <= k; i++) { Qx[i] = (Qx[i] + Qx[i+1])/2; Qy[i] = (Qy[i] + Qy[i+1])/2; } Ax[j] = Qx[0]; Ay[j] = Qy[0]; Bx[j] = Qx[k]; By[j] = Qy[k]; } d2 = d2d_droite(Sx[0], Sy[0], Sx[Pn-1], Sy[Pn-1], Qx[0], Qy[0]); if (d2 > seuil) { trace_courbe_subdiv (win, Ax, Ay, seuil); trace_courbe_subdiv (win, Bx, By, seuil); } else { dessin_ligne (win, Sx[0], Sy[0], Sx[Pn-1], Sy[Pn-1]); } } /* * Augmente le degré de la courbe de Bezier de Pn-1 à Pn, en augmentant le * nombre de sommets et en recalculant les coordonnées de tous les sommets. */ void augmente_degre () { int i; for (i = Pn; i > 0; i--) { Px[i] = ( i*Px[i-1] + (Pn-i)*Px[i] )/Pn; Py[i] = ( i*Py[i-1] + (Pn-i)*Py[i] )/Pn; } Pn++; } /*---------------------------- I N T E R F A C E ----------------------------*/ void quit_proc (He_node *hn) { HeQuit(0); } void vider_proc (He_node *hn) { Pn = 0; HePostRepaint (canvas); } void degre_proc (He_node *hn) { augmente_degre (); HePostRepaint (canvas); } int time1_proc (int h, void *data) { double t = atof (HeGetTextValue (text1)) + 0.01; char bla[80]; if (t > 1) t = 0; sprintf (bla, "%.2f", t); HeSetTextValue (text1, bla); HePostRepaint (canvas); return TRUE; } void anim_proc (He_node *hn) { if (HeGetToggleValue(hn)) timeout1 = HeAddTimeout (40, time1_proc, NULL); else HeRemoveTimeout (timeout1); } void reaffi_proc (He_node *hn) { HePostRepaint (canvas); } void princ_resize_proc (He_node *hn, int width, int height) { /* ajuste largeurs panels */ HeSetWidth (panel1, width); HeSetWidth (panel2, width); /* met le panel2 en bas */ HeJustify (panel2, NULL, HE_BOTTOM); /* le canvas prend toute la place disponible */ HeExpand (canvas, panel2, HE_BOTTOM); HeExpand (canvas, NULL, HE_RIGHT); } void canvas_resize_proc (He_node *hn, int width, int height) { static int old_width = 1, old_height = 1; int i; /* Mise à l'échelle du polygone */ for (i = 0; i < Pn; i++) { Px[i] = Px[i] * width / old_width; Py[i] = Py[i] * height / old_height; } old_width = width; old_height = height; } /* * Renvoie TRUE si les points (x0,y0) et (x1,y1) sont proches (par exemple * si (x1,y1) est dans le carré centré en (x0,y0) de rayon MIN_DIST). */ int est_proche (int x0, int y0, int x1, int y1) { return (abs(x1 - x0) <= MIN_DIST && abs(y1 - y0) <= MIN_DIST); } /* * Renvoie le numéro du premier sommet qui est proche de (x0,y0) * Si aucun sommet n'est trouvé, renvoie -1. */ int cherche_clic (int x0, int y0) { int i; for (i = 0; i < Pn; i++) if (est_proche (x0, y0, Px[i], Py[i])) return i; return -1; } void canvas_event_proc (He_node *hn, He_event *hev) { int larg = HeGetWidth (canvas), haut = HeGetHeight (canvas), re = FALSE; static int clic = -1; switch (hev->type) { case ButtonPress : { /* Bouton souris pressé */ clic = cherche_clic (hev->sx, hev->sy); if (clic < 0) { clic = Pn; Px[clic] = hev->sx; Py[clic] = hev->sy; Pn ++; re = TRUE; } } break; case MotionNotify : { /* Souris déplacée */ if (hev->sb > 0) { /* avec un bouton enfoncé */ Px[clic] = hev->sx; Py[clic] = hev->sy; re = TRUE; } } break; case ButtonRelease : { /* Bouton souris relaché */ /* Sommet glissé à l'extérieur */ if ( hev->sx < 0 || hev->sy < 0 || hev->sx >= larg || hev->sy >= haut ) { int i; /* Suppression du sommet en décalant à gauche */ Pn--; for (i = clic; i < Pn; i++) { Px[i] = Px[i+1]; Py[i] = Py[i+1]; } re = TRUE; } } break; } if (re) HePostRepaint (canvas); } void canvas_repaint_proc (He_node *hn, Window win) { int i; HeDrawBg (canvas, he_white); /* Dessin du polygone de contrôle */ couleur (c_jaune); for (i = 0; i < Pn-1; i++) dessin_ligne (win, Px[i], Py[i], Px[i+1], Py[i+1]); /* Dessin des points de contrôle */ couleur (c_bleu); for (i = 0; i < Pn; i++) dessin_carre (win, Px[i], Py[i]); /* Dessin géométrique de De Casteljau */ couleur (c_vert); if (HeGetToggleValue (tog2) && Pn > 1) { double t = atof(HeGetTextValue(text1)); trace_geom_castel (win, t); } /* Dessin du point de paramètre t */ couleur (he_black); if (HeGetToggleValue (tog1) && Pn > 1) { double t = atof(HeGetTextValue(text1)), x, y; calcule_point_castel (t, &x, &y); dessin_rond (win, x, y); } /* Dessin de la courbe par échantillonnage avec un pas */ couleur (c_rouge); if (HeGetToggleValue (tog3) && Pn > 1) { double pas = atof(HeGetTextValue(text2)); if (pas > 0) trace_courbe_echant (win, pas); } /* Dessin de la courbe par subdivision récursive avec un seuil */ couleur (c_magenta); if (HeGetToggleValue (tog4) && Pn > 1) { double seuil = atof(HeGetTextValue(text3)); if (seuil > 0) trace_courbe_subdiv (win, Px, Py, seuil); } } /*--------------------------------- M A I N ----------------------------------*/ int main (int argc, char **argv) { HeInit (&argc, &argv); init_couleurs (); princ = HeCreateFrame (); HeSetFrameLabel (princ, "Courbes de Bezier (cor)"); HeSetFrameResizeProc (princ, princ_resize_proc); panel1 = HeCreatePanel (princ); tog1 = HeCreateToggleCheckP (panel1, "Point", reaffi_proc, FALSE); tog2 = HeCreateToggleCheckP (panel1, "Géom", reaffi_proc, FALSE); HeCreateMessageP (panel1, "t", TRUE); text1 = HeCreateText (panel1); HeSetTextVisibleLen (text1, 8); HeSetTextValue (text1, "0.5"); HeSetTextNotifyProc (text1, reaffi_proc); HeCreateToggleLedP (panel1, "Anim", anim_proc, FALSE); HeCreateButtonP (panel1, "Degré+1", degre_proc, NULL); HeCreateButtonP (panel1, "Vider", vider_proc, NULL); HeCreateButtonP (panel1, "Quit", quit_proc, NULL); HeSetPanelLayout (panel1, HE_LINE_FEED); tog3 = HeCreateToggleCheckP (panel1, "Échantillon", reaffi_proc, FALSE); HeCreateMessageP (panel1, "Pas", TRUE); text2 = HeCreateText (panel1); HeSetTextVisibleLen (text2, 8); HeSetTextValue (text2, "0.02"); HeSetTextNotifyProc (text2, reaffi_proc); tog4 = HeCreateToggleCheckP (panel1, "Subdivise", reaffi_proc, FALSE); HeCreateMessageP (panel1, "Seuil", TRUE); text3 = HeCreateText (panel1); HeSetTextVisibleLen (text3, 8); HeSetTextValue (text3, "1.0"); HeSetTextNotifyProc (text3, reaffi_proc); HeFit (panel1); canvas = HeCreateCanvas (princ); HeSetCanvasResizeProc (canvas, canvas_resize_proc); HeSetCanvasRepaintProc (canvas, canvas_repaint_proc); HeSetCanvasEventProc (canvas, canvas_event_proc); HeSetWidth (canvas, 400); HeSetHeight (canvas, 400); HeJustify (canvas, panel1, HE_TOP); panel2 = HeCreatePanel (princ); HeJustify (panel2, canvas, HE_TOP); mess1 = HeCreateMessageP (panel2, "Sommets : ajoute, " "déplace (tirer souris), supprime (tirer dehors)", FALSE); HeFit (panel2); HeFit (princ); return HeMainLoop (princ); }