/* bspline-cor.c - Edouard Thiel - 03/06/2001 Compilation : cc bspline-cor.c -o bspline-cor `~/helium/helium-cfg --cflags --libs` -lm Exécution : bspline-cor But du TP : - Programmer le tracé d'une courbe B-spline cubique uniforme ouverte ou fermée. À partir d'un ensemble de points de contrôle, calculer les sommets de Bezier équivalents, puis tracer les courbes. - L'interface du programme est entièrement fournie. - Les fonctions à compléter sont toutes dans la partie "Algorithmes". */ #include #include He_node *princ, *canvas, *panel1, *panel2, *mess1, *tog1, *text1; char bla_dessin[] = "Sommets : ajoute, déplace, supprime (tirer dehors)"; #define SMAX 10000 double Px[SMAX], Py[SMAX], Bx[SMAX], By[SMAX]; int Pn = 0, Bn = 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; void init_couleurs () { c_gris = HeAllocRgb (150, 150, 150, he_white); 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); } /* * Dessins de base */ void couleur (int coul) { XSetForeground (he_display, he_gc, coul); } 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 ---------------------------*/ /* * Calcul les sommets de Bezier à partir des points de contrôle des Bsplines. * * Données : Px[i], Py[i], i = 0..Pn-1 : points de contrôle des Bsplines * Résultats : Bx[j], By[j], j = 0..Bn-1 : sommets de Bezier * * Chaque quadruplet P(i),P(i+1),P(i+2),P(i+3) ajoute 4 nouveaux sommets * B(j),B(j+1),B(j+2),B(j+3). i varie de 1 en 1, tandis que j varie de 4 en 4. * * Les formules sont : * B(j) = (P(i)+4*P(i+1)+P(i+2))/6 B(j+1) = (2*P(i+1)+P(i+2))/3 * B(j+2) = (P(i+1)+2*P(i+2))/3 B(j+3) = (P(i+1)+4*P(i+2)+P(i+3))/6 * * Dans un premier temps, ne pas tenir compte de la variable 'ferme' ; * attention, il faut au moins 4 sommets P pour appliquer les formules. Après * compilation vous devez voir les sommets de Bezier (petits cercles verts). * * Dans un deuxième temps, écrire les fonctions de tracé de courbes qui suivent * et tester ; faire varier le pas (par exemple 0.05, 0.1, 0.15). * * Dans un troisième temps, terminer cette fonction en tenant compte de la * variable 'ferme' : si elle est vraie, ajouter les sommets de Bezier * nécessaires pour fermer la courbe. */ void calcul_sommets_bezier (int ferme) { int i, m; Bn = 0; if (ferme) { if (Pn < 3) return; m = Pn; for (i = 0; i < 3; i++) { Px[Pn+i] = Px[i]; Py[Pn+i] = Py[i]; } } else { if (Pn < 4) return; m = Pn-3; } for (i = 0; i < m; i++) { Bx[Bn] = (Px[i]+4*Px[i+1]+Px[i+2])/6; By[Bn] = (Py[i]+4*Py[i+1]+Py[i+2])/6; Bn++; Bx[Bn] = (2*Px[i+1]+Px[i+2])/3; By[Bn] = (2*Py[i+1]+Py[i+2])/3; Bn++; Bx[Bn] = (Px[i+1]+2*Px[i+2])/3; By[Bn] = (Py[i+1]+2*Py[i+2])/3; Bn++; Bx[Bn] = (Px[i+1]+4*Px[i+2]+Px[i+3])/6; By[Bn] = (Py[i+1]+4*Py[i+2]+Py[i+3])/6; Bn++; } } /* * Calcul du polynome cubique de Bezier pour les sommets * B(i), B(i+1), B(i+2), B(i+3) et le paramètre t entre 0 et 1. * * Pour calculer un point x,y, l'appelant doit appeler 2 fois cette * fonction : x = poly_bezier (Bx, i, t); * y = poly_bezier (By, i, t); */ double poly_bezier (double *B, int i, double t) { double s = 1.0 - t; return s*s*(s*B[i] + 3*t*B[i+1]) + t*t*(3*s*B[i+2] + t*B[i+3]); } /* * Tracé d'une courbe de Bezier cubique pour les sommets * B(i), B(i+1), B(i+2), B(i+3). * * Cela consiste à calculer x1(t=0), y1(0), x2(t=pas), y2(pas) * puis appeler dessin_ligne (win, x1, y1, x2, y2); * ensuite sauver x2,y2 dans x1,y1 puis calculer x2(t=2*pas), y2(2*pas) * puis tracer la ligne, etc, jusqu'à t=1. */ void trace_une_bezier (Window win, int i, double pas) { double t, x1, y1, x2, y2; x1 = poly_bezier (Bx, i, 0); y1 = poly_bezier (By, i, 0); for (t = pas; t < 1; t += pas) { x2 = poly_bezier (Bx, i, t); y2 = poly_bezier (By, i, t); dessin_ligne (win, x1, y1, x2, y2); x1 = x2; y1 = y2; } x2 = poly_bezier (Bx, i, 1); y2 = poly_bezier (By, i, 1); dessin_ligne (win, x1, y1, x2, y2); } /* * Tracé des courbes de Beziers formant la Bspline. */ void trace_les_beziers (Window win, double pas) { int i; for (i = 0; i < Bn-3; i+=4) trace_une_bezier (win, i, pas); } /*--------------------------- C A L L B A C K S ------------------------------*/ void princ_resize (He_node *hn, int width, int height) { HeSetWidth (panel1, width); HeSetWidth (panel2, width); HeJustify (panel2, NULL, HE_BOTTOM); HeExpand (canvas, panel2, HE_BOTTOM); HeExpand (canvas, NULL, HE_RIGHT); } void canvas_repaint (He_node *hn, Window win) { int i; calcul_sommets_bezier (HeGetToggleValue (tog1)); HeDrawBg (canvas, he_white); couleur (c_jaune); for (i = 0; i < Pn-1; i++) dessin_ligne (win, Px[i], Py[i], Px[i+1], Py[i+1]); couleur (c_vert); for (i = 0; i < Bn; i++) dessin_rond (win, Bx[i], By[i]); couleur (c_bleu); for (i = 0; i < Pn; i++) dessin_carre (win, Px[i], Py[i]); couleur (c_rouge); trace_les_beziers (win, atof(HeGetTextValue(text1))); } int test_dist (int x0, int y0, int x1, int y1) { return (abs(x1 - x0) <= MIN_DIST && abs(y1 - y0) <= MIN_DIST); } int cherche_clic (int x0, int y0) { int i; for (i = 0; i < Pn; i++) if (test_dist (x0, y0, Px[i], Py[i])) return i; return -1; } void canvas_event (He_node *hn, He_event *hev) { int larg = HeGetWidth (canvas), haut = HeGetHeight (canvas), re = FALSE; static int clic = -1; switch (hev->type) { case ButtonPress : { 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 : { if (hev->sb > 0) { Px[clic] = hev->sx; Py[clic] = hev->sy; re = TRUE; } } break; case ButtonRelease : { /* 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 maj_proc (He_node *hn) { HePostRepaint (canvas); } void vider_proc (He_node *hn) { Pn = Bn = 0; HePostRepaint (canvas); } void quit_proc (He_node *hn) { HeQuit(0); } /*--------------------------------- M A I N ----------------------------------*/ int main (int argc, char **argv) { HeInit (&argc, &argv); init_couleurs (); princ = HeCreateFrame (); HeSetFrameLabel (princ, "B-spline cubique uniforme 2D (cor)"); HeSetFrameResizeProc (princ, princ_resize); panel1 = HeCreatePanel (princ); HeCreateMessageP (panel1, "Pas :", TRUE); text1 = HeCreateText (panel1); HeSetTextVisibleLen (text1, 8); HeSetTextValue (text1, "0.05"); HeSetTextNotifyProc (text1, maj_proc); tog1 = HeCreateToggle (panel1, HE_CHECK); HeSetToggleLabel (tog1, "Fermer"); HeSetToggleNotifyProc (tog1, maj_proc); HeCreateButtonP (panel1, "Vider", vider_proc, NULL); HeCreateButtonP (panel1, "Quit", quit_proc, NULL); HeFit (panel1); canvas = HeCreateCanvas (princ); HeSetCanvasRepaintProc (canvas, canvas_repaint); HeSetCanvasEventProc (canvas, canvas_event); HeSetWidth (canvas, 400); HeSetHeight (canvas, 400); panel2 = HeCreatePanel (princ); mess1 = HeCreateMessageP (panel2, bla_dessin, FALSE); HeFit(panel2); HeJustify (canvas, panel1, HE_TOP); HeJustify (panel2, canvas, HE_TOP); HeFit (princ); return HeMainLoop (princ); }