1 
2 /*
3  * MODULE NAME: ex_cut_round.c
4  *
5  * FUNCTION:
6  * This module contains code that draws extrusions with cut or round
7  * join styles. The cut join style is a beveled edge.
8  * The code also inserts colors and normals if appropriate.
9  *
10  * HISTORY:
11  * written by Linas Vepstas August/September 1991
12  * split into multiple compile units, Linas, October 1991
13  * added normal vectors Linas, October 1991
14  * Fixed filleting problem, Linas February 1993
15  * Modified to handle round joins as well (based on common code),
16  *                           Linas, March 1993
17  * work around OpenGL's lack of support for concave polys, June 1994
18  *
19  * Copyright (C) 1991,1993,1994,2003 Linas Vepstas <linas@linas.org>
20  */
21 
22 
23 #include <stdlib.h>
24 #include <math.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>	/* for the memcpy() subroutine */
28 
29 #include "gle.h"
30 #include "port.h"
31 #include "vvector.h"
32 #include "tube_gc.h"
33 #include "extrude.h"
34 #include "intersect.h"
35 #include "segment.h"
36 
37 typedef void (*gleCapCallback) (int iloop,
38                                 double cap[][3],
39                                 float face_color[3],
40                                 gleDouble cut_vector[3],
41                                 gleDouble bisect_vector[3],
42                                 double norms[][3],
43                                 int frontwards);
44 
45 #define INVALID_BUG_NEEDS_FIXING 0
46 
47 #ifdef NONCONCAVE_CAPS
48 
49 /* ============================================================ */
50 /*
51  * This subroutine draws a flat cap, to close off the cut ends
52  * of the cut-style join.  Because OpenGL doe not natively handle
53  * concave polygons, this will cause some artifacts to appear on the
54  * screen.
55  */
56 
57 static void
draw_cut_style_cap_callback(int iloop,double cap[][3],float face_color[3],gleDouble cut_vector[3],gleDouble bisect_vector[3],double norms[][3],int frontwards)58 draw_cut_style_cap_callback (int iloop,
59                                   double cap[][3],
60                                   float face_color[3],
61                                   gleDouble cut_vector[3],
62                                   gleDouble bisect_vector[3],
63                                   double norms[][3],
64                                   int frontwards)
65 {
66    int i;
67 
68    if (face_color != NULL) C3F (face_color);
69 
70    if (frontwards) {
71 
72       /* if lighting is on, specify the endcap normal */
73       if (cut_vector != NULL) {
74          /* if normal pointing in wrong direction, flip it. */
75          if (cut_vector[2] < 0.0) {
76             VEC_SCALE (cut_vector, -1.0, cut_vector);
77          }
78          N3F_D (cut_vector);
79       }
80       BGNPOLYGON();
81       for (i=0; i<iloop; i++) {
82          V3F_D (cap[i], i, FRONT_CAP);
83       }
84       ENDPOLYGON();
85    } else {
86 
87       /* if lighting is on, specify the endcap normal */
88       if (cut_vector != NULL) {
89          /* if normal pointing in wrong direction, flip it. */
90          if (cut_vector[2] > 0.0)
91            { VEC_SCALE (cut_vector, -1.0, cut_vector); }
92          N3F_D (cut_vector);
93       }
94       /* the sense of the loop is reversed for backfacing culling */
95       BGNPOLYGON();
96       for (i=iloop-1; i>-1; i--) {
97          V3F_D (cap[i], i, BACK_CAP);
98       }
99       ENDPOLYGON();
100    }
101 
102 }
103 
104 #else /* NONCONCAVE_CAPS */
105 
106 /* ============================================================ */
107 /*
108  * This subroutine draws a flat cap, to close off the cut ends
109  * of the cut-style join.  Properly handles concave endcaps.
110  */
111 
112 static void
draw_cut_style_cap_callback(int iloop,double cap[][3],float face_color[3],gleDouble cut_vector[3],gleDouble bisect_vector[3],double norms[][3],int frontwards)113 draw_cut_style_cap_callback (int iloop,
114                                   double cap[][3],
115                                   float face_color[3],
116                                   gleDouble cut_vector[3],
117                                   gleDouble bisect_vector[3],
118                                   double norms[][3],
119                                   int frontwards)
120 {
121 #ifdef GL_32
122    int i;
123 #endif /* GL_32 */
124 
125 #ifdef DELICATE_TESSELATOR
126    int i;
127    int is_colinear;
128    double *previous_vertex = 0x0;
129    double *first_vertex = 0x0;
130 #endif /* DELICATE_TESSELATOR */
131 
132 #ifdef OPENGL_10
133    GLUtriangulatorObj *tobj;
134    tobj = gluNewTess ();
135    gluTessCallback (tobj, GLU_BEGIN, glBegin);
136    gluTessCallback (tobj, GLU_VERTEX, glVertex3dv);
137    gluTessCallback (tobj, GLU_END, glEnd);
138 #endif /* OPENGL_10 */
139 
140    if (face_color != NULL) C3F (face_color);
141 
142    if (frontwards) {
143 
144       /* if lighting is on, specify the endcap normal */
145       if (cut_vector != NULL) {
146          /* if normal pointing in wrong direction, flip it. */
147          if (cut_vector[2] < 0.0) {
148             VEC_SCALE (cut_vector, -1.0, cut_vector);
149          }
150          N3F_D (cut_vector);
151       }
152 #ifdef GL_32
153       BGNPOLYGON();
154       for (i=0; i<iloop; i++) {
155          V3F_D (cap[i], i, FRONT_CAP);
156       }
157       ENDPOLYGON();
158 #endif /* GL_32 */
159 #ifdef OPENGL_10
160 
161 /* If you have a tesselator that is happy with anything,
162  * including degenerate points, colinear segments, etc.
163  * then define this. Otherwise, pick one of the others.
164  *
165  * I beleive that the stock SGI tesselator is "lenient",
166  * despite explicit disclaimers in the documentation.
167  * (circa 1995).
168  *
169  * The Mesa tesselator is not at all forgiving of
170  * degenerate points.
171  * (circa 1997-1998)
172  */
173 
174 #ifdef LENIENT_TESSELATOR
175       gluBeginPolygon (tobj);
176       for (i=0; i<iloop; i++) {
177          gluTessVertex (tobj, cap[i], cap[i]);
178       }
179       gluEndPolygon (tobj);
180 #endif /* LENIENT_TESSELATOR */
181 
182 #ifdef DELICATE_TESSELATOR
183       gluBeginPolygon (tobj);
184 
185       first_vertex = 0x0;
186       previous_vertex = cap[iloop-1];
187       for (i=0; i<iloop-1; i++) {
188          COLINEAR (is_colinear, previous_vertex, cap[i], cap[i+1]);
189          if (!is_colinear) {
190             gluTessVertex (tobj, cap[i], cap[i]);
191             previous_vertex = cap[i];
192             if (!first_vertex) first_vertex = previous_vertex;
193          }
194       }
195 
196       if (!first_vertex) first_vertex = cap[0];
197       COLINEAR (is_colinear, previous_vertex, cap[iloop-1], first_vertex);
198       if (!is_colinear) gluTessVertex (tobj, cap[iloop-1], cap[iloop-1]);
199 
200       gluEndPolygon (tobj);
201 #endif /* DELICATE_TESSELATOR */
202 
203 #endif /* OPENGL_10 */
204    } else {
205 
206       /* if lighting is on, specify the endcap normal */
207       if (cut_vector != NULL) {
208          /* if normal pointing in wrong direction, flip it. */
209          if (cut_vector[2] > 0.0) {
210             VEC_SCALE (cut_vector, -1.0, cut_vector);
211          }
212          N3F_D (cut_vector);
213       }
214       /* the sense of the loop is reversed for backfacing culling */
215 #ifdef GL_32
216       BGNPOLYGON();
217       for (i=iloop-1; i>-1; i--) {
218          V3F_D (cap[i], i, BACK_CAP);
219       }
220       ENDPOLYGON();
221 #endif /* GL_32 */
222 #ifdef OPENGL_10
223 
224 #ifdef LENIENT_TESSELATOR
225       gluBeginPolygon (tobj);
226       for (i=iloop-1; i>-1; i--) {
227          gluTessVertex (tobj, cap[i], cap[i]);
228       }
229       gluEndPolygon (tobj);
230 #endif /* LENIENT_TESSELATOR */
231 
232 #ifdef DELICATE_TESSELATOR
233       gluBeginPolygon (tobj);
234 
235       first_vertex = 0x0;
236       previous_vertex = cap[0];
237       for (i=iloop-1; i>0; i--) {
238          COLINEAR (is_colinear, previous_vertex, cap[i], cap[i-1]);
239          if (!is_colinear) {
240             gluTessVertex (tobj, cap[i], cap[i]);
241             previous_vertex = cap[i];
242             if (!first_vertex) first_vertex = previous_vertex;
243          }
244       }
245 
246       if (!first_vertex) first_vertex = cap[iloop-1];
247       COLINEAR (is_colinear, previous_vertex, cap[0], first_vertex);
248       if (!is_colinear) gluTessVertex (tobj, cap[0], cap[0]);
249 
250       gluEndPolygon (tobj);
251 #endif /* DELICATE_TESSELATOR */
252 
253 #endif /* OPENGL_10 */
254    }
255 
256 #ifdef OPENGL_10
257    gluDeleteTess (tobj);
258 #endif /* OPENGL_10 */
259 
260 }
261 #endif /* NONCONCAVE_ENDCAPS */
262 
263 /* ============================================================ */
264 /*
265  * This subroutine matchs the cap callback template, but is a no-op
266  */
267 
268 static void
null_cap_callback(int iloop,double cap[][3],float face_color[3],gleDouble cut_vector[3],gleDouble bisect_vector[3],double norms[][3],int frontwards)269 null_cap_callback (int iloop,
270                         double cap[][3],
271                         float face_color[3],
272                         gleDouble cut_vector[3],
273                         gleDouble bisect_vector[3],
274                         double norms[][3],
275                         int frontwards)
276 {}
277 
278 /* ============================================================ */
279 /*
280  * This little routine draws the little idd-biddy fillet triangle with
281  * the right  color, normal, etc.
282  *
283  * HACK ALERT -- there are two aspects to this routine/interface that
284  * are "unfinished".
285  * 1) the third point of the triangle should get a color thats
286  *    interpolated beween the front and back color.  The interpolant
287  *    is not currently being computed.  The error introduced by not
288  *    doing this should be tiny and/or non-exitant in almost all
289  *    expected uses of this code.
290  *
291  * 2) additional normal vectors should be supplied, and these should
292  *    be interpolated to fit.  Currently, this is not being done.  As
293  *    above, the expected error of not doing this should be tiny and/or
294  *    non-existant in almost all expected uses of this code.
295  */
296 static void
draw_fillet_triangle_plain(gleDouble va[3],gleDouble vb[3],gleDouble vc[3],int face,float front_color[3],float back_color[3])297 draw_fillet_triangle_plain
298                           (gleDouble va[3],
299                            gleDouble vb[3],
300                            gleDouble vc[3],
301                            int face,
302                            float front_color[3],
303                            float back_color[3])
304 {
305 
306    if (front_color != NULL) C3F (front_color);
307    BGNTMESH (-5, 0.0);
308    if (face) {
309       V3F (va, INVALID_BUG_NEEDS_FIXING, FILLET);
310       V3F (vb, INVALID_BUG_NEEDS_FIXING, FILLET);
311    } else {
312       V3F (vb, INVALID_BUG_NEEDS_FIXING, FILLET);
313       V3F (va, INVALID_BUG_NEEDS_FIXING, FILLET);
314    }
315    V3F (vc, INVALID_BUG_NEEDS_FIXING, FILLET);
316    ENDTMESH ();
317 
318 }
319 
320 /* ============================================================ */
321 /*
322  * This little routine draws the little idd-biddy fillet triangle with
323  * the right  color, normal, etc.
324  *
325  * HACK ALERT -- there are two aspects to this routine/interface that
326  * are "unfinished".
327  * 1) the third point of the triangle should get a color thats
328  *    interpolated beween the front and back color.  The interpolant
329  *    is not currently being computed.  The error introduced by not
330  *    doing this should be tiny and/or non-exitant in almost all
331  *    expected uses of this code.
332  *
333  * 2) additional normal vectors should be supplied, and these should
334  *    be interpolated to fit.  Currently, this is not being done.  As
335  *    above, the expected error of not doing this should be tiny and/or
336  *    non-existant in almost all expected uses of this code.
337  */
draw_fillet_triangle_n_norms(gleDouble va[3],gleDouble vb[3],gleDouble vc[3],int face,float front_color[3],float back_color[3],double na[3],double nb[3])338 static void draw_fillet_triangle_n_norms
339                           (gleDouble va[3],
340                            gleDouble vb[3],
341                            gleDouble vc[3],
342                            int face,
343                            float front_color[3],
344                            float back_color[3],
345                            double na[3],
346                            double nb[3])
347 {
348 
349    if (front_color != NULL) C3F (front_color);
350    BGNTMESH (-5, 0.0);
351    if (__TUBE_DRAW_FACET_NORMALS) {
352       N3F_D (na);
353       if (face) {
354          V3F (va, INVALID_BUG_NEEDS_FIXING, FILLET);
355          V3F (vb, INVALID_BUG_NEEDS_FIXING, FILLET);
356       } else {
357          V3F (vb, INVALID_BUG_NEEDS_FIXING, FILLET);
358          V3F (va, INVALID_BUG_NEEDS_FIXING, FILLET);
359       }
360       V3F (vc, INVALID_BUG_NEEDS_FIXING, FILLET);
361    } else {
362       if (face) {
363          N3F_D (na);
364          V3F (va, INVALID_BUG_NEEDS_FIXING, FILLET);
365          N3F_D (nb);
366          V3F (vb, INVALID_BUG_NEEDS_FIXING, FILLET);
367       } else {
368          N3F_D (nb);
369          V3F (vb, INVALID_BUG_NEEDS_FIXING, FILLET);
370          N3F_D (na);
371          V3F (va, INVALID_BUG_NEEDS_FIXING, FILLET);
372          N3F_D (nb);
373       }
374       V3F (vc, INVALID_BUG_NEEDS_FIXING, FILLET);
375    }
376    ENDTMESH ();
377 
378 }
379 
380 /* ============================================================ */
381 
draw_fillets_and_join_plain(int ncp,gleDouble trimmed_loop[][3],gleDouble untrimmed_loop[][3],int is_trimmed[],gleDouble bis_origin[3],gleDouble bis_vector[3],float front_color[3],float back_color[3],gleDouble cut_vector[3],int face,gleCapCallback cap_callback)382 static void draw_fillets_and_join_plain
383                     (int ncp,
384                     gleDouble trimmed_loop[][3],
385                     gleDouble untrimmed_loop[][3],
386                     int is_trimmed[],
387                     gleDouble bis_origin[3],
388                     gleDouble bis_vector[3],
389                     float front_color[3],
390                     float back_color[3],
391                     gleDouble cut_vector[3],
392                     int face,
393                     gleCapCallback cap_callback)
394 {
395    int istop;
396    int icnt, icnt_prev, iloop;
397    double *cap_loop;
398    gleDouble sect[3];
399    gleDouble tmp_vec[3];
400    int save_style = 0;
401    int was_trimmed = FALSE;
402 
403    cap_loop = (double *) malloc ((ncp+3)*3*sizeof (double));
404 
405    /*
406     * If the first point is trimmed, keep going until one
407     * is found that is not trimmed, and start join there.
408     */
409 
410    save_style = gleGetJoinStyle ();
411    icnt = 0;
412    iloop = 0;
413    if (!is_trimmed[0]) {
414 
415       /* if the first point on the contour isn't trimmed, go ahead and
416        * drop an edge down to the bisecting plane, (thus starting the
417        * join).  (Only need to do this for cut join, its bad if done for
418        * round join). (Also, do this only for open contours; leads to
419        * bugs when done for closed contours).
420        */
421       if ((__TUBE_CUT_JOIN) && (!(save_style & TUBE_CONTOUR_CLOSED))) {
422          VEC_SUM (tmp_vec, trimmed_loop[0], bis_vector);
423          INNERSECT (sect,
424                     bis_origin,
425                     bis_vector,
426                     trimmed_loop[0],
427                     tmp_vec);
428          VEC_COPY ( (&cap_loop[3*iloop]), sect);
429          iloop ++;
430       }
431       VEC_COPY ( (&cap_loop[3*iloop]), (trimmed_loop[0]));
432       iloop++;
433       icnt_prev = icnt;
434       icnt ++;
435    } else {
436 
437       /* else, loop until an untrimmed point is found */
438       was_trimmed = TRUE;
439       while (is_trimmed[icnt]) {
440          icnt_prev = icnt;
441          icnt ++;
442          if (icnt >= ncp) {
443             free (cap_loop);
444             return;    /* oops - everything was trimmed */
445          }
446       }
447    }
448 
449    /* Start walking around the end cap.  Every time the end loop is
450     * trimmed, we know we'll need to draw a fillet triangle.  In
451     * addition, after every pair of visibility changes, we draw a cap. */
452    if (__TUBE_CLOSE_CONTOUR) {
453       istop = ncp;
454    } else {
455       istop = ncp-1;
456    }
457 
458    /* save the join style, and disable a closed contour.
459     * Need to do this so partial contours don't close up. */
460    gleSetJoinStyle (save_style & ~TUBE_CONTOUR_CLOSED);
461 
462    for (; icnt_prev < istop; icnt_prev ++, icnt ++, icnt %= ncp) {
463 
464       /* There are four interesting cases for drawing caps and fillets:
465        *    1) this & previous point were trimmed.  Don't do anything,
466        *       advance counter.
467        *    2) this point trimmed, previous not -- draw fillet, and
468        *       draw cap.
469        *    3) this point not trimmed, previous one was -- compute
470        *       intersection point, draw fillet with it, and save
471        *       point for cap contour.
472        *    4) this & previous point not trimmed -- save for endcap.
473        */
474 
475       /* Case 1 -- noop, just advance pointers */
476       if (is_trimmed[icnt_prev] && is_trimmed[icnt]) {
477       }
478 
479       /* Case 2 --  Hah! first point! compute intersect & draw fillet! */
480       if (is_trimmed[icnt_prev] && !is_trimmed[icnt]) {
481 
482          /* important note: the array "untrimmed" contains valid
483           * untrimmed data ONLY when is_trim is TRUE.  Otherwise,
484           * only "trim" containes valid data */
485 
486          /* compute intersection */
487          INNERSECT (sect,
488                     bis_origin,
489                     bis_vector,
490                     untrimmed_loop[icnt_prev],
491                     trimmed_loop[icnt]);
492 
493          /* Draw Fillet */
494          draw_fillet_triangle_plain (trimmed_loop[icnt_prev],
495                                trimmed_loop[icnt],
496                                sect,
497                                face,
498                                front_color,
499                                back_color);
500 
501          VEC_COPY ( (&cap_loop[3*iloop]), sect);
502          iloop ++;
503          VEC_COPY ( (&cap_loop[3*iloop]), (trimmed_loop[icnt]));
504          iloop++;
505       }
506 
507       /* Case 3 -- add to collection of points */
508       if (!is_trimmed[icnt_prev] && !is_trimmed[icnt]) {
509          VEC_COPY ( (&cap_loop[3*iloop]), (trimmed_loop[icnt]));
510          iloop++;
511       }
512 
513       /* Case 4 -- Hah! last point!  draw fillet & draw cap!  */
514       if (!is_trimmed[icnt_prev] && is_trimmed[icnt]) {
515          was_trimmed = TRUE;
516 
517          /* important note: the array "untrimmed" contains valid
518           * untrimmed data ONLY when is_trim is TRUE.  Otherwise,
519           * only "trim" containes valid data */
520 
521          /* compute intersection */
522          INNERSECT (sect,
523                     bis_origin,
524                     bis_vector,
525                     trimmed_loop[icnt_prev],
526                     untrimmed_loop[icnt]);
527 
528          /* Draw Fillet */
529          draw_fillet_triangle_plain (trimmed_loop[icnt_prev],
530                                trimmed_loop[icnt],
531                                sect,
532                                face,
533                                front_color,
534                                back_color);
535 
536          VEC_COPY ( (&cap_loop[3*iloop]), sect);
537          iloop ++;
538 
539          /* draw cap */
540          if (iloop >= 3) (*cap_callback) (iloop,
541                                           (gleDouble (*)[3]) cap_loop,
542                                           front_color,
543                                           cut_vector,
544                                           bis_vector,
545                                           NULL,
546                                           face);
547 
548          /* reset cap counter */
549          iloop = 0;
550       }
551    }
552 
553    /* now, finish up in the same way that we started.  If the last
554     * point of the contour is visible, drop an edge to the bisecting
555     * plane, thus finishing the join, and then, draw the join! */
556 
557    icnt --;  /* decrement to make up for loop exit condititons */
558    icnt += ncp;
559    icnt %= ncp;
560    if ((!is_trimmed[icnt]) && (iloop >= 2))  {
561 
562       VEC_SUM (tmp_vec, trimmed_loop[icnt], bis_vector);
563       INNERSECT (sect,
564                  bis_origin,
565                  bis_vector,
566                  trimmed_loop[icnt],
567                  tmp_vec);
568       VEC_COPY ( (&cap_loop[3*iloop]), sect);
569       iloop ++;
570 
571       /* if nothing was ever trimmed, then we want to draw the
572        * cap the way the user asked for it -- closed or not closed.
573        * Therefore, reset the closure flag to its original state.
574        */
575       if (!was_trimmed) {
576          gleSetJoinStyle (save_style);
577       }
578 
579       /* draw cap */
580       (*cap_callback) (iloop,
581                        (gleDouble (*)[3]) cap_loop,
582                        front_color,
583                        cut_vector,
584                        bis_vector,
585                        NULL,
586                        face);
587    }
588 
589    /* rest to the saved style */
590    gleSetJoinStyle (save_style);
591    free (cap_loop);
592 }
593 
594 /* ============================================================ */
595 
596 static void
draw_fillets_and_join_n_norms(int ncp,gleDouble trimmed_loop[][3],gleDouble untrimmed_loop[][3],int is_trimmed[],gleDouble bis_origin[3],gleDouble bis_vector[3],double normals[][3],float front_color[3],float back_color[3],gleDouble cut_vector[3],int face,gleCapCallback cap_callback)597 draw_fillets_and_join_n_norms
598                     (int ncp,
599                     gleDouble trimmed_loop[][3],
600                     gleDouble untrimmed_loop[][3],
601                     int is_trimmed[],
602                     gleDouble bis_origin[3],
603                     gleDouble bis_vector[3],
604                     double normals[][3],
605                     float front_color[3],
606                     float back_color[3],
607                     gleDouble cut_vector[3],
608                     int face,
609                     gleCapCallback cap_callback)
610 {
611    int istop;
612    int icnt, icnt_prev, iloop;
613    double *cap_loop, *norm_loop;
614    gleDouble sect[3];
615    gleDouble tmp_vec[3];
616    int save_style = 0;
617    int was_trimmed = FALSE;
618 
619    save_style = gleGetJoinStyle ();
620    cap_loop = (double *) malloc ((ncp+3)*3*2*sizeof (double));
621    norm_loop = cap_loop + (ncp+3)*3;
622 
623    /*
624     * If the first point is trimmed, keep going until one
625     * is found that is not trimmed, and start join there.
626     */
627 
628    icnt = 0;
629    iloop = 0;
630    if (!is_trimmed[0]) {
631 
632       /* if the first point on the contour isn't trimmed, go ahead and
633        * drop an edge down to the bisecting plane, (thus starting the
634        * join).  (Only need to do this for cut join, its bad if done for
635        * round join).  (Also, leads to bugs when done for closed
636        * contours ... do this only if contour is open).
637        */
638       if ((__TUBE_CUT_JOIN) && (!(save_style & TUBE_CONTOUR_CLOSED))) {
639          VEC_SUM (tmp_vec, trimmed_loop[0], bis_vector);
640          INNERSECT (sect,
641                     bis_origin,
642                     bis_vector,
643                     trimmed_loop[0],
644                     tmp_vec);
645          VEC_COPY ( (&cap_loop[3*iloop]), sect);
646          VEC_COPY ( (&norm_loop[3*iloop]), normals[0]);
647          iloop ++;
648       }
649       VEC_COPY ( (&cap_loop[3*iloop]), (trimmed_loop[0]));
650       VEC_COPY ( (&norm_loop[3*iloop]), normals[0]);
651       iloop++;
652       icnt_prev = icnt;
653       icnt ++;
654    } else {
655 
656       /* else, loop until an untrimmed point is found */
657       was_trimmed = TRUE;
658       while (is_trimmed[icnt]) {
659          icnt_prev = icnt;
660          icnt ++;
661          if (icnt >= ncp) {
662             free (cap_loop);
663             return;    /* oops - everything was trimmed */
664          }
665       }
666    }
667 
668    /* Start walking around the end cap.  Every time the end loop is
669     * trimmed, we know we'll need to draw a fillet triangle.  In
670     * addition, after every pair of visibility changes, we draw a cap. */
671    if (__TUBE_CLOSE_CONTOUR) {
672       istop = ncp;
673    } else {
674       istop = ncp-1;
675    }
676 
677    /* save the join style, and disable a closed contour.
678     * Need to do this so partial contours don't close up. */
679    save_style = gleGetJoinStyle ();
680    gleSetJoinStyle (save_style & ~TUBE_CONTOUR_CLOSED);
681 
682    for (; icnt_prev < istop; icnt_prev ++, icnt ++, icnt %= ncp) {
683 
684       /* There are four interesting cases for drawing caps and fillets:
685        *    1) this & previous point were trimmed.  Don't do anything,
686        *       advance counter.
687        *    2) this point trimmed, previous not -- draw fillet, and
688        *       draw cap.
689        *    3) this point not trimmed, previous one was -- compute
690        *       intersection point, draw fillet with it, and save
691        *       point for cap contour.
692        *    4) this & previous point not trimmed -- save for endcap.
693        */
694 
695       /* Case 1 -- noop, just advance pointers */
696       if (is_trimmed[icnt_prev] && is_trimmed[icnt]) {
697       }
698 
699       /* Case 2 --  Hah! first point! compute intersect & draw fillet! */
700       if (is_trimmed[icnt_prev] && !is_trimmed[icnt]) {
701 
702          /* important note: the array "untrimmed" contains valid
703           * untrimmed data ONLY when is_trim is TRUE.  Otherwise,
704           * only "trim" containes valid data */
705 
706          /* compute intersection */
707          INNERSECT (sect,
708                     bis_origin,
709                     bis_vector,
710                     untrimmed_loop[icnt_prev],
711                     trimmed_loop[icnt]);
712 
713          /* Draw Fillet */
714          draw_fillet_triangle_n_norms (trimmed_loop[icnt_prev],
715                                trimmed_loop[icnt],
716                                sect,
717                                face,
718                                front_color,
719                                back_color,
720                                normals[icnt_prev],
721                                normals[icnt]);
722          VEC_COPY ( (&cap_loop[3*iloop]), sect);
723          VEC_COPY ( (&norm_loop[3*iloop]), normals[icnt_prev]);
724          iloop ++;
725          VEC_COPY ( (&cap_loop[3*iloop]), (trimmed_loop[icnt]));
726          VEC_COPY ( (&norm_loop[3*iloop]), normals[icnt]);
727          iloop++;
728       }
729 
730       /* Case 3 -- add to collection of points */
731       if (!is_trimmed[icnt_prev] && !is_trimmed[icnt]) {
732          VEC_COPY ( (&cap_loop[3*iloop]), (trimmed_loop[icnt]));
733          VEC_COPY ( (&norm_loop[3*iloop]), normals[icnt]);
734          iloop++;
735       }
736 
737       /* Case 4 -- Hah! last point!  draw fillet & draw cap!  */
738       if (!is_trimmed[icnt_prev] && is_trimmed[icnt]) {
739          was_trimmed = TRUE;
740 
741          /* important note: the array "untrimmed" contains valid
742           * untrimmed data ONLY when is_trim is TRUE.  Otherwise,
743           * only "trim" containes valid data */
744 
745          /* compute intersection */
746          INNERSECT (sect,
747                     bis_origin,
748                     bis_vector,
749                     trimmed_loop[icnt_prev],
750                     untrimmed_loop[icnt]);
751 
752          /* Draw Fillet */
753          draw_fillet_triangle_n_norms (trimmed_loop[icnt_prev],
754                                trimmed_loop[icnt],
755                                sect,
756                                face,
757                                front_color,
758                                back_color,
759                                normals[icnt_prev],
760                                normals[icnt]);
761 
762          VEC_COPY ( (&cap_loop[3*iloop]), sect);
763 
764          /* OK, maybe phong normals are wrong, but at least facet
765           * normals will come out OK. */
766          if (__TUBE_DRAW_FACET_NORMALS) {
767             VEC_COPY ( (&norm_loop[3*iloop]), normals[icnt_prev]);
768          } else {
769             VEC_COPY ( (&norm_loop[3*iloop]), normals[icnt]);
770          }
771          iloop ++;
772 
773          /* draw cap */
774          if (iloop >= 3) (*cap_callback) (iloop,
775                                           (gleVector *) cap_loop,
776                                           front_color,
777                                           cut_vector,
778                                           bis_vector,
779                                           (gleVector *) norm_loop,
780                                           face);
781 
782          /* reset cap counter */
783          iloop = 0;
784       }
785    }
786 
787    /* now, finish up in the same way that we started. */
788 
789    icnt --;  /* decrement to make up for loop exit condititons */
790    icnt += ncp;
791    icnt %= ncp;
792    if ((!is_trimmed[icnt]) && (iloop >= 2))  {
793 
794       /* If the last point of the contour is visible, drop an edge
795        * to the bisecting plane, thus finishing the join.
796        * Note that doing this leads to bugs if done for closed
797        * contours ... do this only if contour is open.
798        */
799       if ((__TUBE_CUT_JOIN) && (!(save_style & TUBE_CONTOUR_CLOSED))) {
800          VEC_SUM (tmp_vec, trimmed_loop[icnt], bis_vector);
801          INNERSECT (sect,
802                     bis_origin,
803                     bis_vector,
804                     trimmed_loop[icnt],
805                     tmp_vec);
806          VEC_COPY ( (&cap_loop[3*iloop]), sect);
807          VEC_COPY ( (&norm_loop[3*iloop]), normals[icnt]);
808          iloop ++;
809       }
810 
811       /* if nothing was ever trimmed, then we want to draw the
812        * cap the way the user asked for it -- closed or not closed.
813        * Therefore, reset the closure flag to its original state.
814        */
815       if (!was_trimmed) {
816          gleSetJoinStyle (save_style);
817       }
818       /* draw cap */
819       (*cap_callback) (iloop,
820                        (gleVector *) cap_loop,
821                        front_color,
822                        cut_vector,
823                        bis_vector,
824                        (gleVector *) norm_loop,
825                        face);
826    }
827 
828    /* rest to the saved style */
829    gleSetJoinStyle (save_style);
830    free (cap_loop);
831 }
832 
833 /* ============================================================ */
834 /* This routine draws "cut" style extrusions.
835  */
836 
837 void
extrusion_round_or_cut_join(int ncp,gleDouble contour[][2],gleDouble cont_normal[][2],gleDouble up[3],int npoints,gleDouble point_array[][3],gleColor color_array[],gleDouble xform_array[][2][3])838 extrusion_round_or_cut_join (int ncp,	/* number of contour points */
839                            gleDouble contour[][2],	/* 2D contour */
840                            gleDouble cont_normal[][2],/* 2D normal vecs */
841                            gleDouble up[3],	/* up vector for contour */
842                            int npoints,		/* numpoints in poly-line */
843                            gleDouble point_array[][3],	/* polyline */
844                            gleColor color_array[],	/* color of polyline */
845                            gleDouble xform_array[][2][3])   /* 2D contour xforms */
846 {
847    int i, j;
848    int inext, inextnext;
849    gleDouble m[4][4];
850    gleDouble tube_len, seg_len;
851    gleDouble diff[3];
852    gleDouble bi_0[3], bi_1[3];		/* bisecting plane */
853    gleDouble bisector_0[3], bisector_1[3];		/* bisecting plane */
854    gleDouble cut_0[3], cut_1[3];	/* cutting planes */
855    gleDouble lcut_0[3], lcut_1[3];	/* cutting planes */
856    int valid_cut_0, valid_cut_1;	/* flag -- cut vector is valid */
857    gleDouble end_point_0[3], end_point_1[3];
858    gleDouble torsion_point_0[3], torsion_point_1[3];
859    gleDouble isect_point[3];
860    gleDouble origin[3], neg_z[3];
861    gleDouble yup[3];		/* alternate up vector */
862    gleDouble *front_cap, *back_cap;	/* arrays containing the end caps */
863    gleDouble *front_loop, *back_loop; /* arrays containing the tube ends */
864    double *front_norm, *back_norm; /* arrays containing normal vecs */
865    double *norm_loop=0x0, *tmp; /* normal vectors, cast into 3d from 2d */
866    int *front_is_trimmed, *back_is_trimmed;   /* T or F */
867    float *front_color, *back_color;  /* pointers to segment colors */
868    gleCapCallback cap_callback = 0x0 ;  /* function callback to draw cap */
869    gleCapCallback tmp_cap_callback = 0x0;  /* function callback to draw cap */
870    int join_style_is_cut;      /* TRUE if join style is cut */
871    double dot;                  /* partial dot product */
872    char *mem_anchor;
873    int first_time = TRUE;
874    gleDouble *cut_vec;
875 
876    /* create a local, block scope copy of of the join style.
877     * this will alleviate wasted cycles and register write-backs */
878    /* choose the right callback, depending on the choosen join style */
879    if (__TUBE_CUT_JOIN) {
880       join_style_is_cut = TRUE;
881       cap_callback =  draw_cut_style_cap_callback;
882    } else {
883       join_style_is_cut = FALSE;
884       cap_callback =  draw_round_style_cap_callback;
885    }
886 
887    /* By definition, the contour passed in has its up vector pointing in
888     * the y direction */
889    if (up == NULL) {
890       yup[0] = 0.0;
891       yup[1] = 1.0;
892       yup[2] = 0.0;
893    } else {
894       VEC_COPY (yup, up);
895    }
896 
897    /* ========== "up" vector sanity check ========== */
898    (void) up_sanity_check (yup, npoints, point_array);
899 
900    /* the origin is at the origin */
901    origin [0] = 0.0;
902    origin [1] = 0.0;
903    origin [2] = 0.0;
904 
905    /* and neg_z is at neg z */
906    neg_z[0] = 0.0;
907    neg_z[1] = 0.0;
908    neg_z[2] = 1.0;
909 
910    /* malloc the data areas that we'll need to store the end-caps */
911    mem_anchor = malloc (4 * 3*ncp*sizeof(gleDouble)
912                       + 2 * 3*ncp*sizeof(double)
913                       + 2 * 1*ncp*sizeof(int));
914    front_norm = (double *) mem_anchor;
915    back_norm = front_norm + 3*ncp;
916    front_loop = (gleDouble *) (back_norm + 3*ncp);
917    back_loop = front_loop + 3*ncp;
918    front_cap = back_loop + 3*ncp;
919    back_cap  = front_cap + 3*ncp;
920    front_is_trimmed = (int *) (back_cap + 3*ncp);
921    back_is_trimmed = front_is_trimmed + ncp;
922 
923    /* ======================================= */
924 
925    /* |-|-|-|-|-|-|-|-| SET UP FOR FIRST SEGMENT |-|-|-|-|-|-|-| */
926 
927    /* ignore all segments of zero length */
928    i = 1;
929    inext = i;
930    FIND_NON_DEGENERATE_POINT (inext, npoints, seg_len, diff, point_array);
931    tube_len = seg_len;	/* store for later use */
932 
933    /* may as well get the normals set up now */
934    if (cont_normal != NULL) {
935       if (xform_array == NULL) {
936          norm_loop = front_norm;
937          back_norm = norm_loop;
938          for (j=0; j<ncp; j++) {
939             norm_loop[3*j] = cont_normal[j][0];
940             norm_loop[3*j+1] = cont_normal[j][1];
941             norm_loop[3*j+2] = 0.0;
942          }
943       } else {
944          for (j=0; j<ncp; j++) {
945             NORM_XFORM_2X2 ( (&front_norm[3*j]),
946                               xform_array[inext-1],
947                               cont_normal [j]);
948             front_norm[3*j+2] = 0.0;
949             back_norm[3*j+2] = 0.0;
950          }
951       }
952    } else {
953       front_norm = back_norm = norm_loop = NULL;
954    }
955 
956    /* get the bisecting plane */
957    bisecting_plane (bi_0, point_array[i-1],
958                           point_array[i],
959                           point_array[inext]);
960 
961    /* compute cutting plane */
962    CUTTING_PLANE (valid_cut_0, cut_0, point_array[i-1],
963                          point_array[i],
964                          point_array[inext]);
965 
966    /* reflect the up vector in the bisecting plane */
967    VEC_REFLECT (yup, yup, bi_0);
968 
969    /* |-|-|-|-|-|-|-|-| START LOOP OVER SEGMENTS |-|-|-|-|-|-|-| */
970 
971    /* draw tubing, not doing the first segment */
972    while (inext<npoints-1) {
973 
974       inextnext = inext;
975       /* ignore all segments of zero length */
976       FIND_NON_DEGENERATE_POINT (inextnext, npoints,
977                                  seg_len, diff, point_array);
978 
979       /* get the far bisecting plane */
980       bisecting_plane (bi_1, point_array[i],
981                              point_array[inext],
982                              point_array[inextnext]);
983 
984 
985       /* compute cutting plane */
986       CUTTING_PLANE (valid_cut_1, cut_1, point_array[i],
987                             point_array[inext],
988                             point_array[inextnext]);
989 
990       /* rotate so that z-axis points down v2-v1 axis,
991        * and so that origen is at v1 */
992       uviewpoint (m, point_array[i], point_array[inext], yup);
993       PUSHMATRIX ();
994       MULTMATRIX (m);
995 
996       /* rotate the cutting planes into the local coordinate system */
997       MAT_DOT_VEC_3X3 (lcut_0, m, cut_0);
998       MAT_DOT_VEC_3X3 (lcut_1, m, cut_1);
999 
1000       /* rotate the bisecting planes into the local coordinate system */
1001       MAT_DOT_VEC_3X3 (bisector_0, m, bi_0);
1002       MAT_DOT_VEC_3X3 (bisector_1, m, bi_1);
1003 
1004 
1005       neg_z[2] = -tube_len;
1006 
1007       /* draw the tube */
1008       /* --------- START OF TMESH GENERATION -------------- */
1009       for (j=0; j<ncp; j++) {
1010 
1011          /* set up the endpoints for segment clipping */
1012          if (xform_array == NULL) {
1013             VEC_COPY_2 (end_point_0, contour[j]);
1014             VEC_COPY_2 (end_point_1, contour[j]);
1015             VEC_COPY_2 (torsion_point_0, contour[j]);
1016             VEC_COPY_2 (torsion_point_1, contour[j]);
1017          } else {
1018             /* transform the contour points with the local xform */
1019             MAT_DOT_VEC_2X3 (end_point_0,
1020                              xform_array[inext-1], contour[j]);
1021             MAT_DOT_VEC_2X3 (torsion_point_0,
1022                              xform_array[inext], contour[j]);
1023             MAT_DOT_VEC_2X3 (end_point_1,
1024                              xform_array[inext], contour[j]);
1025             MAT_DOT_VEC_2X3 (torsion_point_1,
1026                              xform_array[inext-1], contour[j]);
1027 
1028             /* if there are normals and there are affine xforms,
1029              * then compute local coordinate system normals.
1030              * Set up the back normals. (The front normals we inherit
1031              * from previous pass through the loop).  */
1032             if (cont_normal != NULL) {
1033                /* do up the normal vectors with the inverse transpose */
1034                NORM_XFORM_2X2 ( (&back_norm[3*j]),
1035                                 xform_array[inext],
1036                                 cont_normal [j]);
1037             }
1038          }
1039          end_point_0 [2] = 0.0;
1040          torsion_point_0 [2] = 0.0;
1041          end_point_1 [2] = - tube_len;
1042          torsion_point_1 [2] = - tube_len;
1043 
1044          /* The two end-points define a line.  Intersect this line
1045           * against the clipping plane defined by the PREVIOUS
1046           * tube segment.  */
1047 
1048          /* if this and the last tube are co-linear, don't cut the angle
1049           * if you do, a divide by zero will result.  This and last tube
1050           * are co-linear when the cut vector is of zero length */
1051          if (valid_cut_0 && join_style_is_cut) {
1052              INNERSECT (isect_point,  /* isect point (returned) */
1053                        origin,		/* point on intersecting plane */
1054                        lcut_0,		/* normal vector to plane */
1055                        end_point_0,	/* point on line */
1056                        end_point_1);	/* another point on the line */
1057 
1058             /* determine whether the raw end of the extrusion would have
1059              * been cut, by checking to see if the raw and is on the
1060              * far end of the half-plane defined by the cut vector.
1061              * If the raw end is not "cut", then it is "trimmed".
1062              */
1063             if (lcut_0[2] < 0.0) { VEC_SCALE (lcut_0, -1.0, lcut_0); }
1064             dot = lcut_0[0] * end_point_0[0];
1065             dot += lcut_0[1] * end_point_0[1];
1066 
1067             VEC_COPY ((&front_loop[3*j]), isect_point);
1068          } else {
1069             /* actual value of dot not interseting; need
1070              * only be positive so that if test below failes */
1071             dot = 1.0;
1072             VEC_COPY ((&front_loop[3*j]), end_point_0);
1073          }
1074 
1075          INNERSECT (isect_point, 	/* intersection point (returned) */
1076                     origin,		/* point on intersecting plane */
1077                     bisector_0,		/* normal vector to plane */
1078                     end_point_0,	/* point on line */
1079                     torsion_point_1);	/* another point on the line */
1080 
1081          /* trim out interior of intersecting tube */
1082          /* ... but save the untrimmed version for drawing the endcaps */
1083          /* ... note that cap contains valid data ONLY when is_trimmed
1084           * is TRUE. */
1085          if ((dot <= 0.0) || (isect_point[2] < front_loop[3*j+2])) {
1086 /*
1087          if ((dot <= 0.0) || (front_loop[3*j+2] > 0.0)) {
1088 */
1089             VEC_COPY ((&front_cap[3*j]), (&front_loop [3*j]));
1090             VEC_COPY ((&front_loop[3*j]), isect_point);
1091             front_is_trimmed[j] = TRUE;
1092          } else {
1093             front_is_trimmed[j] = FALSE;
1094          }
1095 
1096          /* if intersection is behind the end of the segment,
1097           * truncate to the end of the segment
1098           * Note that coding front_loop [3*j+2] = -tube_len;
1099           * doesn't work when twists are involved, */
1100          if (front_loop[3*j+2] < -tube_len) {
1101             VEC_COPY( (&front_loop[3*j]), end_point_1);
1102          }
1103 
1104          /* --------------------------------------------------- */
1105          /* The two end-points define a line.  We did one endpoint
1106           * above. Now do the other.Intersect this line
1107           * against the clipping plane defined by the NEXT
1108           * tube segment.  */
1109 
1110          /* if this and the last tube are co-linear, don't cut the angle
1111           * if you do, a divide by zero will result.  This and last tube
1112           * are co-linear when the cut vector is of zero length */
1113          if (valid_cut_1 && join_style_is_cut) {
1114             INNERSECT (isect_point,  /* isect point (returned) */
1115                        neg_z,		/* point on intersecting plane */
1116                        lcut_1,		/* normal vector to plane */
1117                        end_point_1,	/* point on line */
1118                        end_point_0);	/* another point on the line */
1119 
1120             if (lcut_1[2] > 0.0) { VEC_SCALE (lcut_1, -1.0, lcut_1); }
1121             dot = lcut_1[0] * end_point_1[0];
1122             dot += lcut_1[1] * end_point_1[1];
1123 
1124 
1125             VEC_COPY ((&back_loop[3*j]), isect_point);
1126          } else {
1127             /* actual value of dot not interseting; need
1128              * only be positive so that if test below failes */
1129             dot = 1.0;
1130             VEC_COPY ((&back_loop[3*j]), end_point_1);
1131          }
1132 
1133          INNERSECT (isect_point, 	/* intersection point (returned) */
1134                     neg_z,		/* point on intersecting plane */
1135                     bisector_1,		/* normal vector to plane */
1136                     torsion_point_0,	/* point on line */
1137                     end_point_1);	/* another point on the line */
1138 
1139          /* cut out interior of intersecting tube */
1140          /* ... but save the uncut version for drawing the endcaps */
1141          /* ... note that cap contains valid data ONLY when is
1142           *_trimmed is TRUE. */
1143 /*
1144         if ((dot <= 0.0) || (back_loop[3*j+2] < -tube_len)) {
1145 */
1146         if ((dot <= 0.0) || (isect_point[2] > back_loop[3*j+2])) {
1147             VEC_COPY ((&back_cap[3*j]), (&back_loop [3*j]));
1148             VEC_COPY ((&back_loop[3*j]), isect_point);
1149             back_is_trimmed[j] = TRUE;
1150          } else {
1151             back_is_trimmed[j] = FALSE;
1152          }
1153 
1154          /* if intersection is behind the end of the segment,
1155           * truncate to the end of the segment
1156           * Note that coding back_loop [3*j+2] = 0.0;
1157           * doesn't work when twists are involved, */
1158          if (back_loop[3*j+2] > 0.0) {
1159             VEC_COPY( (&back_loop[3*j]), end_point_0);
1160          }
1161       }
1162 
1163       /* --------- END OF TMESH GENERATION -------------- */
1164 
1165       /* |||||||||||||||||| START SEGMENT DRAW |||||||||||||||||||| */
1166       /* There are six different cases we can have for presence and/or
1167        * absecnce of colors and normals, and for interpretation of
1168        * normals. The blechy set of nested if statements below
1169        * branch to each of the six cases */
1170       if (xform_array == NULL) {
1171          if (color_array == NULL) {
1172             if (cont_normal == NULL) {
1173                draw_segment_plain (ncp, (gleVector *) front_loop, (gleVector *) back_loop, inext, seg_len);
1174             } else
1175             if (__TUBE_DRAW_FACET_NORMALS) {
1176                draw_segment_facet_n (ncp, (gleVector *) front_loop, (gleVector *) back_loop, (gleVector *) norm_loop,
1177                                      inext, seg_len);
1178             } else {
1179                draw_segment_edge_n (ncp, (gleVector *) front_loop, (gleVector *) back_loop, (gleVector *) norm_loop,
1180                                     inext, seg_len);
1181             }
1182          } else {
1183             if (cont_normal == NULL) {
1184                draw_segment_color (ncp, (gleVector *) front_loop, (gleVector *) back_loop,
1185                                    color_array[inext-1],
1186                                    color_array[inext], inext, seg_len);
1187             } else
1188             if (__TUBE_DRAW_FACET_NORMALS) {
1189                draw_segment_c_and_facet_n (ncp,
1190                                    (gleVector *) front_loop, (gleVector *) back_loop, (gleVector *) norm_loop,
1191                                    color_array[inext-1],
1192                                    color_array[inext], inext, seg_len);
1193             } else {
1194                draw_segment_c_and_edge_n (ncp,
1195                                    (gleVector *) front_loop, (gleVector *) back_loop, (gleVector *) norm_loop,
1196                                    color_array[inext-1],
1197                                    color_array[inext], inext, seg_len);
1198              }
1199           }
1200       } else {
1201          if (color_array == NULL) {
1202             if (cont_normal == NULL) {
1203                draw_segment_plain (ncp, (gleVector *) front_loop, (gleVector *) back_loop, inext, seg_len);
1204             } else
1205             if (__TUBE_DRAW_FACET_NORMALS) {
1206                draw_binorm_segment_facet_n (ncp, (gleVector *) front_loop, (gleVector *) back_loop,
1207                                                  (gleVector *) front_norm, (gleVector *) back_norm,
1208                                                  inext, seg_len);
1209             } else {
1210                draw_binorm_segment_edge_n (ncp, (gleVector *) front_loop, (gleVector *) back_loop,
1211                                                 (gleVector *) front_norm, (gleVector *) back_norm,
1212                                                 inext, seg_len);
1213             }
1214          } else {
1215             if (cont_normal == NULL) {
1216                draw_segment_color (ncp, (gleVector *) front_loop, (gleVector *) back_loop,
1217                                    color_array[inext-1],
1218                                    color_array[inext], inext, seg_len);
1219             } else
1220             if (__TUBE_DRAW_FACET_NORMALS) {
1221                draw_binorm_segment_c_and_facet_n (ncp,
1222                                    (gleVector *) front_loop, (gleVector *) back_loop,
1223                                    (gleVector *) front_norm, (gleVector *) back_norm,
1224                                    color_array[inext-1],
1225                                    color_array[inext], inext, seg_len);
1226             } else {
1227                draw_binorm_segment_c_and_edge_n (ncp,
1228                                    (gleVector *) front_loop, (gleVector *) back_loop,
1229                                    (gleVector *) front_norm, (gleVector *) back_norm,
1230                                    color_array[inext-1],
1231                                    color_array[inext], inext, seg_len);
1232              }
1233           }
1234       }
1235       /* |||||||||||||||||| END SEGMENT DRAW |||||||||||||||||||| */
1236 
1237       /* v^v^v^v^v^v^v^v^v  BEGIN END CAPS v^v^v^v^v^v^v^v^v^v^v^v */
1238 
1239       /* if end caps are required, draw them. But don't draw any
1240        * but the very first and last caps */
1241       if (first_time) {
1242          first_time = FALSE;
1243          tmp_cap_callback = cap_callback;
1244          cap_callback = null_cap_callback;
1245          if (__TUBE_DRAW_CAP) {
1246             if (color_array != NULL) C3F (color_array[inext-1]);
1247             draw_angle_style_front_cap (ncp, bisector_0,
1248                                        (gleVector *) front_loop);
1249          }
1250       }
1251       /* v^v^v^v^v^v^v^v^v  END END CAPS v^v^v^v^v^v^v^v^v^v^v^v */
1252 
1253       /* $$$$$$$$$$$$$$$$ BEGIN -1, FILLET & JOIN DRAW $$$$$$$$$$$$$$$$$ */
1254       /*
1255        * Now, draw the fillet triangles, and the join-caps.
1256        */
1257       if (color_array != NULL) {
1258          front_color = color_array[inext-1];
1259          back_color = color_array[inext];
1260       } else {
1261          front_color = NULL;
1262          back_color = NULL;
1263       }
1264 
1265       if (cont_normal == NULL) {
1266          /* the flag valid-cut is true if the cut vector has a valid
1267           * value (i.e. if a degenerate case has not occured).
1268           */
1269          if (valid_cut_0) {
1270             cut_vec = lcut_0;
1271          } else {
1272             cut_vec = NULL;
1273          }
1274          draw_fillets_and_join_plain (ncp,
1275                                   (gleVector *) front_loop,
1276                                   (gleVector *) front_cap,
1277                                   front_is_trimmed,
1278                                   origin,
1279                                   bisector_0,
1280                                   front_color,
1281                                   back_color,
1282                                   cut_vec,
1283                                   TRUE,
1284                                   cap_callback);
1285 
1286          /* v^v^v^v^v^v^v^v^v  BEGIN END CAPS v^v^v^v^v^v^v^v^v^v^v^v */
1287          if (inext == npoints-2) {
1288             if (__TUBE_DRAW_CAP) {
1289                if (color_array != NULL) C3F (color_array[inext]);
1290                draw_angle_style_back_cap (ncp, bisector_1,
1291                                            (gleVector *) back_loop);
1292                cap_callback = null_cap_callback;
1293             }
1294          } else {
1295             /* restore ability to draw cap */
1296             cap_callback = tmp_cap_callback;
1297          }
1298          /* v^v^v^v^v^v^v^v^v  END END CAPS v^v^v^v^v^v^v^v^v^v^v^v */
1299 
1300          /* the flag valid-cut is true if the cut vector has a valid
1301           * value (i.e. if a degenerate case has not occured).
1302           */
1303          if (valid_cut_1) {
1304             cut_vec = lcut_1;
1305          } else {
1306             cut_vec = NULL;
1307          }
1308          draw_fillets_and_join_plain (ncp,
1309                                   (gleVector *) back_loop,
1310                                   (gleVector *) back_cap,
1311                                   back_is_trimmed,
1312                                   neg_z,
1313                                   bisector_1,
1314                                   back_color,
1315                                   front_color,
1316                                   cut_vec,
1317                                   FALSE,
1318                                   cap_callback);
1319       } else {
1320 
1321          /* the flag valid-cut is true if the cut vector has a valid
1322           * value (i.e. if a degenerate case has not occured).
1323           */
1324          if (valid_cut_0) {
1325             cut_vec = lcut_0;
1326          } else {
1327             cut_vec = NULL;
1328          }
1329          draw_fillets_and_join_n_norms (ncp,
1330                                   (gleVector *) front_loop,
1331                                   (gleVector *) front_cap,
1332                                   front_is_trimmed,
1333                                   origin,
1334                                   bisector_0,
1335                                   (gleVector *) front_norm,
1336                                   front_color,
1337                                   back_color,
1338                                   cut_vec,
1339                                   TRUE,
1340                                   cap_callback);
1341 
1342          /* v^v^v^v^v^v^v^v^v  BEGIN END CAPS v^v^v^v^v^v^v^v^v^v^v^v */
1343          if (inext == npoints-2) {
1344             if (__TUBE_DRAW_CAP) {
1345                if (color_array != NULL) C3F (color_array[inext]);
1346                draw_angle_style_back_cap (ncp, bisector_1,
1347                                          (gleDouble (*)[3]) back_loop);
1348                cap_callback = null_cap_callback;
1349             }
1350          } else {
1351             /* restore ability to draw cap */
1352             cap_callback = tmp_cap_callback;
1353          }
1354          /* v^v^v^v^v^v^v^v^v  END END CAPS v^v^v^v^v^v^v^v^v^v^v^v */
1355 
1356          /* the flag valid-cut is true if the cut vector has a valid
1357           * value (i.e. if a degenerate case has not occured).
1358           */
1359          if (valid_cut_1) {
1360             cut_vec = lcut_1;
1361          } else {
1362             cut_vec = NULL;
1363          }
1364          draw_fillets_and_join_n_norms (ncp,
1365                                   (gleVector *) back_loop,
1366                                   (gleVector *) back_cap,
1367                                   back_is_trimmed,
1368                                   neg_z,
1369                                   bisector_1,
1370                                   (gleVector *) back_norm,
1371                                   back_color,
1372                                   front_color,
1373                                   cut_vec,
1374                                   FALSE,
1375                                   cap_callback);
1376       }
1377 
1378       /* $$$$$$$$$$$$$$$$ END FILLET & JOIN DRAW $$$$$$$$$$$$$$$$$ */
1379 
1380       /* pop this matrix, do the next set */
1381       POPMATRIX ();
1382 
1383       /* slosh stuff over to next vertex */
1384       tmp = front_norm;
1385       front_norm = back_norm;
1386       back_norm = tmp;
1387 
1388       tube_len = seg_len;
1389       i = inext;
1390       inext = inextnext;
1391       VEC_COPY (bi_0, bi_1);
1392       VEC_COPY (cut_0, cut_1);
1393       valid_cut_0 = valid_cut_1;
1394 
1395       /* reflect the up vector in the bisecting plane */
1396       VEC_REFLECT (yup, yup, bi_0);
1397    }
1398    /* |-|-|-|-|-|-|-|-| END LOOP OVER SEGMENTS |-|-|-|-|-|-|-| */
1399 
1400    free (mem_anchor);
1401 
1402 }
1403 
1404 /* =================== END OF FILE =============================== */
1405