2 static char *RCSid() { return RCSid("$Id: hidden3d.c,v 1.56.2.3 2008/06/02 19:40:10 sfeam Exp $"); }
5 /* GNUPLOT - hidden3d.c */
8 * Copyright 1986 - 1993, 1998, 1999, 2004 Thomas Williams, Colin Kelley
10 * Permission to use, copy, and distribute this software and its
11 * documentation for any purpose with or without fee is hereby granted,
12 * provided that the above copyright notice appear in all copies and
13 * that both that copyright notice and this permission notice appear
14 * in supporting documentation.
16 * Permission to modify the software is granted, but not the right to
17 * distribute the complete modified source code. Modifications are to
18 * be distributed as patches to the released version. Permission to
19 * distribute binaries produced by compiling modified sources is granted,
21 * 1. distribute the corresponding source modifications from the
22 * released version in the form of a patch file along with the binaries,
23 * 2. add special version identification to distinguish your version
24 * in addition to the base release version number,
25 * 3. provide your name and address as the primary contact for the
26 * support of your modified version, and
27 * 4. retain our contact information in regard to use of the base
29 * Permission to distribute the released version of the source code along
30 * with corresponding source modifications in the form of a patch file is
31 * granted with same provisions 2 through 4 for binary distributions.
33 * This software is provided "as is" without express or implied warranty
34 * to the extent permitted by applicable law.
39 * 1999 Hans-Bernhard Broeker (Broeker@physik.rwth-aachen.de)
41 * Major rewrite, affecting just about everything
59 /*************************/
60 /* Configuration section */
61 /*************************/
63 /* If this module is compiled with HIDDEN3D_GRIDBOX = 1 defined, it
64 * will store the information about {x|y}{min|max} in an other
65 * (additional!) form: a bit mask, with each bit representing one
66 * horizontal or vertical strip of the screen. The bits for strips a
67 * polygon spans are set to one. This allows to test for xy overlap
68 * of an edge with a polygon simply by comparing bit patterns. */
69 #ifndef HIDDEN3D_GRIDBOX
70 #define HIDDEN3D_GRIDBOX 0
73 /* HBB 19991204: new code started to finally implement a spatially
74 * ordered data structure to store the polygons in. This is meant to
75 * speed up the HLR process. Before, the hot spot of hidden3d was the
76 * loop in in_front, where by far most of the polygons are rejected by
77 * the first test, already. The idea is to _not_ to loop over all
78 * those polygons far away from the edge under consideration, in the
79 * first place. Instead, store the polygons in an xy grid of lists,
80 * so we can select a sample of these lists to test a given edge
82 #ifndef HIDDEN3D_QUADTREE
83 #define HIDDEN3D_QUADTREE 0
85 #if HIDDEN3D_QUADTREE && HIDDEN3D_GRIDBOX
86 # warning HIDDEN3D_QUADTREE & HIDDEN3D_GRIDBOX do not work together, sensibly!
89 /* If you don't want the color-distinction between the
90 * 'top' and 'bottom' sides of the surface, like I do, then just compile
91 * with -DBACKSIDE_LINETYPE_OFFSET = 0. */
92 #ifndef BACKSIDE_LINETYPE_OFFSET
93 # define BACKSIDE_LINETYPE_OFFSET 1
96 /* This #define lets you choose if the diagonals that
97 * divide each original quadrangle in two triangles will be drawn
98 * visible or not: do draw them, define it to be 7L, otherwise let be
100 #ifndef TRIANGLE_LINESDRAWN_PATTERN
101 # define TRIANGLE_LINESDRAWN_PATTERN 3L
104 /* Handle out-of-range or undefined points. Compares the maximum
105 * marking (0=inrange, 1=outrange, 2=undefined) of the coordinates of
106 * a vertex to the value #defined here. If not less, the vertex is
107 * rejected, and all edges that hit it, as well. NB: if this is set to
108 * anything above 1, gnuplot may crash with a floating point exception
109 * in hidden3d. You get what you asked for ... */
110 #ifndef HANDLE_UNDEFINED_POINTS
111 # define HANDLE_UNDEFINED_POINTS 1
113 /* Symbolic value for 'do not handle Undefined Points specially' */
114 #define UNHANDLED (UNDEFINED+1)
116 /* If both subtriangles of a quad were cancelled, try if using the
117 * other diagonal is better. This only makes a difference if exactly
118 * one vertex of the quad is unusable, and that one is on the 'usual'
119 * tried diagonal. In such a case, the border of the hole in the
120 * surface will be less rough than with the previous method, as the
121 * border follows the undefined region as close as it can. */
122 #ifndef SHOW_ALTERNATIVE_DIAGONAL
123 # define SHOW_ALTERNATIVE_DIAGONAL 1
126 /* If the two triangles in a quad are both drawn, and they show
127 * different sides to the user (the quad is 'bent over'), then it's
128 * preferrable to force the diagonal being visible to avoid other
129 * parts of the scene being obscured by a line the user can't
130 * see. This avoids unnecessary user surprises. */
131 #ifndef HANDLE_BENTOVER_QUADRANGLES
132 # define HANDLE_BENTOVER_QUADRANGLES 1
135 /* The actual configuration is stored in these variables, modifiable
136 * at runtime through 'set hidden3d' options */
137 static int hiddenBacksideLinetypeOffset = BACKSIDE_LINETYPE_OFFSET;
138 static long hiddenTriangleLinesdrawnPattern = TRIANGLE_LINESDRAWN_PATTERN;
139 static int hiddenHandleUndefinedPoints = HANDLE_UNDEFINED_POINTS;
140 static int hiddenShowAlternativeDiagonal = SHOW_ALTERNATIVE_DIAGONAL;
141 static int hiddenHandleBentoverQuadrangles = HANDLE_BENTOVER_QUADRANGLES;
144 /**************************************************************/
145 /**************************************************************
146 * The 'real' code begins, here. *
148 * first: types and global variables *
149 **************************************************************/
150 /**************************************************************/
152 /* precision of calculations in normalized space. Coordinates closer to
153 * each other than an absolute difference of EPSILON are considered
154 * equal, by some of the routines in this module. */
157 /* The code used to die messily if the scale parameters got over-large.
158 * Prevent this from happening due to mousing by locking out the mouse
160 TBOOLEAN disable_mouse_z = FALSE;
162 /* Some inexact operations: == , > , >=, sign() */
163 #define EQ(X,Y) (fabs( (X) - (Y) ) < EPSILON) /* X == Y */
164 #define GR(X,Y) ((X) > (Y) + EPSILON) /* X > Y */
165 #define GE(X,Y) ((X) >= (Y) - EPSILON) /* X >= Y */
166 #define SIGN(X) ( ((X)<-EPSILON) ? -1: ((X)>EPSILON) )
168 /* A plane equation, stored as a four-element vector. The equation
169 * itself is: x*p[0]+y*p[1]+z*p[2]+1*p[3]=0 */
170 typedef coordval t_plane[4];
172 /* One edge of the mesh. The edges are (currently) organized into a
173 * linked list as a method of traversing them back-to-front. */
174 typedef struct edge {
175 long v1, v2; /* the vertices at either end */
176 int style; /* linetype index */
177 struct lp_style_type *lp; /* line/point style attributes */
178 long next; /* index of next edge in z-sorted list */
180 typedef edge GPHUGE *p_edge;
183 /* One polygon (actually: always a triangle) of the surface
186 typedef struct polygon {
187 long vertex[POLY_NVERT]; /* The vertices (indices on vlist) */
188 /* min/max in all three directions */
189 coordval xmin, xmax, ymin, ymax, zmin, zmax;
190 t_plane plane; /* the plane coefficients */
191 TBOOLEAN frontfacing; /* is polygon facing front- or backwards? */
192 #if ! HIDDEN3D_QUADTREE
193 long next; /* index of next polygon in z-sorted list */
196 unsigned long xbits; /* x coverage mask of bounding box */
197 unsigned long ybits; /* y coverage mask of bounding box */
200 typedef polygon GPHUGE *p_polygon;
203 # define UINT_BITS (CHAR_BIT * sizeof(unsigned int))
204 # define COORD_TO_BITMASK(x,shift) \
205 (~0U << (unsigned int) ((((x) / surface_scale) + 1.0) / 2.0 * UINT_BITS + (shift)))
206 # define CALC_BITRANGE(range_min, range_max) \
207 ((~COORD_TO_BITMASK((range_max), 1)) & COORD_TO_BITMASK(range_min, 0))
210 /* Enumeration of possible types of line, for use with the
211 * store_edge() function. Influences the position in the grid the
212 * second vertex will be put to, relative to the one that is passed
213 * in, as another argument to that function. edir_none is for
214 * single-pixel 'blind' edges, which exist only to facilitate output
215 * of 'points' style splots.
217 * Directions are interpreted in a pseudo-geographical coordinate
218 * system of the data grid: within the isoline, we count from left to
219 * right (west to east), and the isolines themselves are counted from
220 * top to bottom, described as north and south. */
221 typedef enum edge_direction {
222 edir_west, edir_north,
224 edir_impulse, edir_point,
228 /* direction into which the polygon is facing (the corner with the
229 * right angle, inside the mesh, that is). The reference identifiying
230 * the whole cell is always the lower right, i.e. southeast one. */
231 typedef enum polygon_direction {
232 pdir_NE, pdir_SE, pdir_SW, pdir_NW
235 /* Three dynamical arrays that describe what we have to plot: */
236 static dynarray vertices, edges, polygons;
238 /* convenience #defines to make the generic vector useable as typed arrays */
239 #define vlist ((p_vertex) vertices.v)
240 #define plist ((p_polygon) polygons.v)
241 #define elist ((p_edge) edges.v)
243 static long pfirst; /* first polygon in zsorted chain*/
244 static long efirst; /* first edges in zsorted chain */
246 #if HIDDEN3D_QUADTREE
247 /* HBB 20000716: spatially oriented hierarchical data structure to
248 * store polygons in. For now, it's a simple xy grid of z-sorted
249 * lists. A single polygon can appear in several lists, if it spans
251 typedef struct qtreelist {
252 long p; /* the polygon */
253 long next; /* next element in this chain */
255 typedef qtreelist GPHUGE *p_qtreelist;
257 /* the number of cells in x and y direction: */
258 # ifndef QUADTREE_GRANULARITY
259 # define QUADTREE_GRANULARITY 10
261 /* indices of the heads of all the cells' chains: */
262 static long quadtree[QUADTREE_GRANULARITY][QUADTREE_GRANULARITY];
264 /* and a routine to calculate the cells' position in that array: */
266 coord_to_treecell(coordval x)
269 index = ((((x) / surface_scale) + 1.0) / 2.0) * QUADTREE_GRANULARITY;
270 if (index >= QUADTREE_GRANULARITY)
271 index = QUADTREE_GRANULARITY - 1;
278 /* the dynarray to actually store all that stuff in: */
279 static dynarray qtree;
280 #define qlist ((p_qtreelist) qtree.v)
281 #endif /* HIDDEN3D_QUADTREE*/
283 /* Prototypes for internal functions of this module. */
284 static long int store_vertex __PROTO((struct coordinate GPHUGE *point,
285 lp_style_type *lp_style, TBOOLEAN color_from_column));
286 static long int make_edge __PROTO((long int vnum1, long int vnum2,
287 struct lp_style_type *lp,
288 int style, int next));
289 static long int store_edge __PROTO((long int vnum1, edge_direction direction,
290 long int crvlen, struct lp_style_type *lp,
292 static GP_INLINE double eval_plane_equation __PROTO((t_plane p, p_vertex v));
293 static long int store_polygon __PROTO((long int vnum1,
294 polygon_direction direction,
296 static void color_edges __PROTO((long int new_edge, long int old_edge,
297 long int new_poly, long int old_poly,
298 int style_above, int style_below));
299 static void build_networks __PROTO((struct surface_points * plots,
301 int compare_edges_by_zmin __PROTO((SORTFUNC_ARGS p1, SORTFUNC_ARGS p2));
302 int compare_polys_by_zmax __PROTO((SORTFUNC_ARGS p1, SORTFUNC_ARGS p2));
303 static void sort_edges_by_z __PROTO((void));
304 static void sort_polys_by_z __PROTO((void));
305 static TBOOLEAN get_plane __PROTO((p_polygon p, t_plane plane));
306 static long split_line_at_ratio __PROTO((long int vnum1, long int vnum2, double w));
307 static GP_INLINE double area2D __PROTO((p_vertex v1, p_vertex v2,
309 static void draw_vertex __PROTO((p_vertex v));
310 static GP_INLINE void draw_edge __PROTO((p_edge e, p_vertex v1, p_vertex v2));
311 static GP_INLINE void handle_edge_fragment __PROTO((long int edgenum,
314 long int firstpoly));
315 static int in_front __PROTO((long int edgenum,
316 long int vnum1, long int vnum2,
317 long int *firstpoly));
320 /* Set the options for hidden3d. To be called from set.c, when the
321 * user has begun a command with 'set hidden3d', to parse the rest of
324 set_hidden3doptions()
329 while (!END_OF_COMMAND) {
330 switch (lookup_table(&set_hidden3d_tbl[0], c_token)) {
332 /* reset all parameters to defaults */
333 reset_hidden3doptions();
337 "No further options allowed after 'defaults'");
342 hiddenBacksideLinetypeOffset = (int) real(const_express(&t));
346 hiddenBacksideLinetypeOffset = 0;
348 case S_HI_TRIANGLEPATTERN:
350 hiddenTriangleLinesdrawnPattern = (int) real(const_express(&t));
355 tmp = (int) real(const_express(&t));
356 if (tmp <= 0 || tmp > UNHANDLED)
358 hiddenHandleUndefinedPoints = tmp;
361 case S_HI_NOUNDEFINED:
362 hiddenHandleUndefinedPoints = UNHANDLED;
364 case S_HI_ALTDIAGONAL:
365 hiddenShowAlternativeDiagonal = 1;
367 case S_HI_NOALTDIAGONAL:
368 hiddenShowAlternativeDiagonal = 0;
371 hiddenHandleBentoverQuadrangles = 1;
373 case S_HI_NOBENTOVER:
374 hiddenHandleBentoverQuadrangles = 0;
377 int_error(c_token, "No such option to hidden3d (or wrong order)");
386 show_hidden3doptions()
389 \t Back side of surfaces has linestyle offset of %d\n\
390 \t Bit-Mask of Lines to draw in each triangle is %ld\n\
392 hiddenBacksideLinetypeOffset, hiddenTriangleLinesdrawnPattern,
393 hiddenHandleUndefinedPoints);
395 switch (hiddenHandleUndefinedPoints) {
397 fputs("Outranged and undefined datapoints are omitted from the surface.\n",
401 fputs("Only undefined datapoints are omitted from the surface.\n",
405 fputs("Will not check for undefined datapoints (may cause crashes).\n",
409 fputs("Value stored for undefined datapoint handling is illegal!!!\n",
415 \t Will %suse other diagonal if it gives a less jaggy outline\n\
416 \t Will %sdraw diagonal visibly if quadrangle is 'bent over'\n",
417 hiddenShowAlternativeDiagonal ? "" : "not ",
418 hiddenHandleBentoverQuadrangles ? "" : "not ");
421 /* Implements proper 'save'ing of the new hidden3d options... */
423 save_hidden3doptions(FILE *fp)
426 fputs("unset hidden3d\n", fp);
429 fprintf(fp, "set hidden3d offset %d trianglepattern %ld undefined %d %saltdiagonal %sbentover\n",
430 hiddenBacksideLinetypeOffset,
431 hiddenTriangleLinesdrawnPattern,
432 hiddenHandleUndefinedPoints,
433 hiddenShowAlternativeDiagonal ? "" : "no",
434 hiddenHandleBentoverQuadrangles ? "" : "no");
437 /* Initialize the necessary steps for hidden line removal and
438 initialize global variables. */
440 init_hidden_line_removal()
442 /* Check for some necessary conditions to be set elsewhere: */
443 /* HandleUndefinedPoints mechanism depends on these: */
444 assert(OUTRANGE == 1);
445 assert(UNDEFINED == 2);
447 /* Re-mapping of this value makes the test easier in the critical
449 if (hiddenHandleUndefinedPoints < OUTRANGE)
450 hiddenHandleUndefinedPoints = UNHANDLED;
452 init_dynarray(&vertices, sizeof(vertex), 100, 100);
453 init_dynarray(&edges, sizeof(edge), 100, 100);
454 init_dynarray(&polygons, sizeof(polygon), 100, 100);
455 #if HIDDEN3D_QUADTREE
456 init_dynarray(&qtree, sizeof(qtreelist), 100, 100);
461 /* Reset the hidden line data to a fresh start. */
463 reset_hidden_line_removal()
468 #if HIDDEN3D_QUADTREE
474 /* Terminates the hidden line removal process. */
475 /* Free any memory allocated by init_hidden_line_removal above. */
477 term_hidden_line_removal()
479 free_dynarray(&polygons);
480 free_dynarray(&edges);
481 free_dynarray(&vertices);
482 #if HIDDEN3D_QUADTREE
483 free_dynarray(&qtree);
489 /* Do we see the top or bottom of the polygon, or is it 'on edge'? */
490 #define GET_SIDE(vlst,csign) \
493 vlist[vlst[0]].x * (vlist[vlst[1]].y - vlist[vlst[2]].y) + \
494 vlist[vlst[1]].x * (vlist[vlst[2]].y - vlist[vlst[0]].y) + \
495 vlist[vlst[2]].x * (vlist[vlst[0]].y - vlist[vlst[1]].y); \
496 csign = SIGN (ctmp); \
502 struct coordinate GPHUGE * point,
503 lp_style_type *lp_style,
504 TBOOLEAN color_from_column)
506 p_vertex thisvert = nextfrom_dynarray(&vertices);
508 thisvert->lp_style = lp_style;
509 if ((int) point->type >= hiddenHandleUndefinedPoints) {
510 FLAG_VERTEX_AS_UNDEFINED(*thisvert);
513 map3d_xyz(point->x, point->y, point->z, thisvert);
514 if (color_from_column) {
515 thisvert->real_z = point->CRD_COLOR;
516 thisvert->lp_style->pm3d_color.lt = LT_COLORFROMCOLUMN;
518 thisvert->real_z = point->z;
520 #ifdef HIDDEN3D_VAR_PTSIZE
521 /* Store pointer back to original point */
522 /* Needed to support variable pointsize */
523 thisvert->original = point;
526 return (thisvert - vlist);
529 /* A part of store_edge that does the actual storing. Used by
530 * in_front(), as well, so I separated it out. */
533 long vnum1, long vnum2,
534 struct lp_style_type *lp,
538 p_edge thisedge = nextfrom_dynarray(&edges);
539 p_vertex v1 = vlist + vnum1;
540 p_vertex v2 = vlist + vnum2;
542 /* ensure z ordering inside each edge */
543 if (v1->z >= v2->z) {
544 thisedge->v1 = vnum1;
545 thisedge->v2 = vnum2;
547 thisedge->v1 = vnum2;
548 thisedge->v2 = vnum1;
551 thisedge->style = style;
553 thisedge->next = next;
555 return thisedge - elist;
558 /* store the edge from vnum1 to vnum2 into the edge list. Ensure that
559 * the vertex with higher z is stored in v1, to ease sorting by zmax */
563 edge_direction direction,
565 struct lp_style_type *lp,
568 p_vertex v1 = vlist + vnum1;
569 p_vertex v2 = NULL; /* just in case: initialize... */
571 unsigned int drawbits = (0x1 << direction);
585 v2 = v1 - crvlen - 1;
590 drawbits >>= 1; /* altDiag is handled like normal NW one */
594 drawbits = 0; /* don't care about the triangle pattern */
598 drawbits = 0; /* nothing to draw, but disable check */
604 if (VERTEX_IS_UNDEFINED(*v1)
605 || VERTEX_IS_UNDEFINED(*v2)) {
609 if (drawbits && /* no bits set: 'blind' edge --> no test! */
610 ! (hiddenTriangleLinesdrawnPattern & drawbits)
614 return make_edge(vnum1, vnum2, lp, style, -1);
618 /* Calculate the normal equation coefficients of the plane of polygon
619 * 'p'. Uses is the 'signed projected area' method. Its benefit is
620 * that it doesn't rely on only three of the vertices of 'p', as the
621 * naive cross product method does. */
623 get_plane(p_polygon poly, t_plane plane)
628 TBOOLEAN frontfacing=TRUE;
630 /* calculate the signed areas of the polygon projected onto the
631 * planes x=0, y=0 and z=0, respectively. The three areas form
632 * the components of the plane's normal vector: */
633 v1 = vlist + poly->vertex[POLY_NVERT - 1];
634 v2 = vlist + poly->vertex[0];
635 plane[0] = (v1->y - v2->y) * (v1->z + v2->z);
636 plane[1] = (v1->z - v2->z) * (v1->x + v2->x);
637 plane[2] = (v1->x - v2->x) * (v1->y + v2->y);
638 for (i = 1; i < POLY_NVERT; i++) {
640 v2 = vlist + poly->vertex[i];
641 plane[0] += (v1->y - v2->y) * (v1->z + v2->z);
642 plane[1] += (v1->z - v2->z) * (v1->x + v2->x);
643 plane[2] += (v1->x - v2->x) * (v1->y + v2->y);
646 /* Normalize the resulting normal vector */
647 s = sqrt(plane[0] * plane[0] + plane[1] * plane[1] + plane[2] * plane[2]);
650 /* The normal vanishes, i.e. the polygon is degenerate. We build
651 * another vector that is orthogonal to the line of the polygon */
652 v1 = vlist + poly->vertex[0];
653 for (i = 1; i < POLY_NVERT; i++) {
654 v2 = vlist + poly->vertex[i];
655 if (!V_EQUAL(v1, v2))
659 /* build (x,y,z) that should be linear-independant from <v1, v2> */
668 /* Re-do the signed area computations */
669 plane[0] = v1->y * (v2->z - z) + v2->y * (z - v1->z) + y * (v1->z - v2->z);
670 plane[1] = v1->z * (v2->x - x) + v2->z * (x - v1->x) + z * (v1->x - v2->x);
671 plane[2] = v1->x * (v2->y - y) + v2->x * (y - v1->y) + x * (v1->y - v2->y);
672 s = sqrt(plane[0] * plane[0] + plane[1] * plane[1] + plane[2] * plane[2]);
675 /* ensure that normalized c is > 0 */
676 if (plane[2] < 0.0) {
685 /* Now we have the normalized normal vector, insert one of the
686 * vertices into the equation to get 'd'. For an even better result,
687 * an average over all the vertices might be used */
688 plane[3] = -plane[0] * v1->x - plane[1] * v1->y - plane[2] * v1->z;
694 /* Evaluate the plane equation represented a four-vector for the given
695 * vector. For points in the plane, this should result in values ==0.
696 * < 0 is 'away' from the polygon, > 0 is infront of it */
697 static GP_INLINE double
698 eval_plane_equation(t_plane p, p_vertex v)
700 return (p[0]*v->x + p[1]*v->y + p[2]*v->z + p[3]);
704 /* Build the data structure for this polygon. The return value is the
705 * index of the newly generated polygon. This is memorized for access
706 * to polygons in the previous isoline, from the next-following
709 store_polygon(long vnum1, polygon_direction direction, long crvlen)
711 long int v[POLY_NVERT];
718 v[2] = vnum1 - crvlen;
722 /* triangle points southwest, here */
725 v[2] = v[1] - crvlen;
728 /* alt-diagonal, case 1: southeast triangle: */
730 v[2] = vnum1 - crvlen;
734 v[2] = vnum1 - crvlen;
736 v[1] = v[0] - crvlen;
744 if (VERTEX_IS_UNDEFINED(*v1)
745 || VERTEX_IS_UNDEFINED(*v2)
746 || VERTEX_IS_UNDEFINED(*v3)
750 /* Check if polygon is degenerate */
751 if (V_EQUAL(v1,v2) || V_EQUAL(v2,v3) || V_EQUAL(v3,v1))
754 /* All else OK, fill in the polygon: */
756 p = nextfrom_dynarray(&polygons);
758 memcpy (p->vertex, v, sizeof(v));
759 #if ! HIDDEN3D_QUADTREE
763 /* Some helper macros for repeated code blocks: */
765 /* Gets Minimum 'var' value of polygon 'poly' into variable
766 * 'min. C is one of x, y, or z: */
767 #define GET_MIN(poly, var, min) \
770 long *v = poly->vertex; \
772 min = vlist[*v++].var; \
773 for (i = 1; i< POLY_NVERT; i++, v++) \
774 if (vlist[*v].var < min) \
775 min = vlist[*v].var; \
776 if (min < -surface_scale) disable_mouse_z = TRUE; \
779 /* Gets Maximum 'var' value of polygon 'poly', as with GET_MIN */
780 #define GET_MAX(poly, var, max) \
783 long *v = poly->vertex; \
785 max = vlist[*v++].var; \
786 for (i = 1; i< POLY_NVERT; i++, v++) \
787 if (vlist[*v].var > max) \
788 max = vlist[*v].var; \
789 if (max > surface_scale) disable_mouse_z = TRUE; \
792 GET_MIN(p, x, p->xmin);
793 GET_MIN(p, y, p->ymin);
794 GET_MIN(p, z, p->zmin);
795 GET_MAX(p, x, p->xmax);
796 GET_MAX(p, y, p->ymax);
797 GET_MAX(p, z, p->zmax);
802 p->xbits = CALC_BITRANGE(p->xmin, p->xmax);
803 p->ybits = CALC_BITRANGE(p->ymin, p->ymax);
806 p->frontfacing = get_plane(p, p->plane);
811 /* color edges, based on the orientation of polygon(s). One of the two
812 * edges passed in is a new one, meaning there is no other polygon
813 * sharing it, yet. The other, 'old' edge is common to the new polygon
814 * and another one, which was created earlier on. If these two polygon
815 * differ in their orientation (one front-, the other backsided to the
816 * viewer), this routine has to resolve that conflict. Edge colours
817 * are changed only if the edge wasn't invisible, before */
820 long int new_edge, /* index of 'new', conflictless edge */
821 long int old_edge, /* index of 'old' edge, may conflict */
822 long int new_poly, /* index of current polygon */
823 long int old_poly, /* index of poly sharing old_edge */
824 int above, /* style number for front of polygons */
825 int below) /* style number for backside of polys */
830 /* new polygon was built successfully */
832 /* old polygon doesn't exist. Use new_polygon for both: */
836 (plist[new_poly].frontfacing ? 1 : 0)
837 + 2 * (plist[old_poly].frontfacing ? 1 : 0);
838 switch (casenumber) {
840 /* both backfacing */
841 if (elist[new_edge].style >= -2)
842 elist[new_edge].style = below;
843 if (elist[old_edge].style >= -2)
844 elist[old_edge].style = below;
847 if (elist[new_edge].style >= -2)
848 elist[new_edge].style = below;
851 /* new front-, old one backfacing, or */
852 /* new back-, old one frontfacing */
853 if (((new_edge == old_edge)
854 && hiddenHandleBentoverQuadrangles) /* a diagonal edge! */
855 || (elist[old_edge].style >= -2)) {
856 /* conflict has occured: two polygons meet here, with opposige
857 * sides being shown. What's to do?
858 * 1) find a vertex of one polygon outside this common
860 * 2) check wether it's in front of or behind the
861 * other polygon's plane
862 * 3) if in front, color the edge accoring to the
863 * vertex' polygon, otherwise, color like the other
865 long int vnum1 = elist[old_edge].v1;
866 long int vnum2 = elist[old_edge].v2;
867 p_polygon p = plist + new_poly;
869 double point_to_plane;
871 if (p->vertex[0] == vnum1) {
872 if (p->vertex[1] == vnum2) {
873 pvert = p->vertex[2];
874 } else if (p->vertex[2] == vnum2) {
875 pvert = p->vertex[1];
877 } else if (p->vertex[1] == vnum1) {
878 if (p->vertex[0] == vnum2) {
879 pvert = p->vertex[2];
880 } else if (p->vertex[2] == vnum2) {
881 pvert = p->vertex[0];
883 } else if (p->vertex[2] == vnum1) {
884 if (p->vertex[0] == vnum2) {
885 pvert = p->vertex[1];
886 } else if (p->vertex[1] == vnum2) {
887 pvert = p->vertex[0];
893 eval_plane_equation(plist[old_poly].plane, vlist + pvert);
895 if (point_to_plane > 0) {
896 /* point in new_poly is in front of old_poly plane */
897 elist[old_edge].style = p->frontfacing ? above : below;
899 elist[old_edge].style =
900 plist[old_poly].frontfacing ? above : below;
905 /* both frontfacing: nothing to do */
909 /* Ooops? build_networks() must have guessed incorrectly that
910 * this polygon should exist. */
916 /* This somewhat monstrous routine fills the vlist, elist and plist
917 * dynamic arrays with values from all those plots. It strives to
918 * respect all the topological linkage between vertices, edges and
919 * polygons. E.g., it has to find the correct color for each edge,
920 * based on the orientation of the two polygons sharing it, WRT both
921 * the observer and each other. */
922 /* NEW FEATURE HBB 20000715: allow non-grid datasets too, by storing
923 * only vertices and 'direct' edges, but no polygons or 'cross' edges
926 build_networks(struct surface_points *plots, int pcount)
929 struct surface_points *this_plot;
930 int surface; /* count the surfaces (i.e. sub-plots) */
931 long int crv, ncrvs; /* count isolines */
932 long int nverts; /* count vertices */
933 long int max_crvlen; /* maximal length of isoline in any plot */
934 long int nv, ne, np; /* local poly/edge/vertex counts */
935 long int *north_polygons; /* stores polygons of isoline above */
936 long int *these_polygons; /* same, being built for use by next turn */
937 long int *north_edges; /* stores edges of polyline above */
938 long int *these_edges; /* same, being built for use by next turn */
939 struct iso_curve *icrvs;
940 int above = -3, below; /* line type for edges of front/back side*/
941 struct lp_style_type *lp; /* pointer to line and point properties */
943 /* Count out the initial sizes needed for the polygon and vertex
948 for (this_plot = plots, surface = 0;
950 this_plot = this_plot->next_sp, surface++) {
953 /* Quietly skip empty plots */
954 if (this_plot->plot_type == NODATA)
957 crvlen = this_plot->iso_crvs->p_count;
959 /* Allow individual plots to opt out of hidden3d calculations */
960 if (this_plot->opt_out_of_hidden3d)
963 /* register maximal isocurve length. Only necessary for
964 * grid-topology plots that will create polygons, so I can do
965 * it here, already. */
966 if (crvlen > max_crvlen)
969 /* count 'curves' (i.e. isolines) and vertices in this plot */
971 if(this_plot->plot_type == FUNC3D) {
973 for(icrvs = this_plot->iso_crvs;
974 icrvs; icrvs = icrvs->next) {
977 nverts += ncrvs * crvlen;
978 } else if(this_plot->plot_type == DATA3D) {
979 ncrvs = this_plot->num_iso_read;
980 if (this_plot->has_grid_topology)
981 nverts += ncrvs * crvlen;
982 else if (this_plot->plot_style == VECTOR)
983 nverts += this_plot->iso_crvs->p_count;
985 /* have to check each isoline separately: */
986 for (icrvs = this_plot->iso_crvs; icrvs; icrvs = icrvs->next)
987 nverts += icrvs->p_count;
990 graph_error("Plot type is neither function nor data");
994 /* To avoid possibly suprising error messages, several 2d-only
995 * plot styles are mapped to others, that are genuinely
996 * available in 3d. */
997 switch (this_plot->plot_style) {
1004 ne += nverts - ncrvs;
1005 if (this_plot->has_grid_topology) {
1006 ne += 2 * nverts - ncrvs - 2 * crvlen + 1;
1007 np += 2 * (ncrvs - 1) * (crvlen - 1);
1019 /* treat all remaining ones like 'points' */
1021 ne += nverts; /* a 'phantom edge' per isolated point */
1026 /* Check for no data at all */
1027 if (max_crvlen <= 0)
1030 /* allocate all the lists to the size we need: */
1031 resize_dynarray(&vertices, nv);
1032 resize_dynarray(&edges, ne);
1033 resize_dynarray(&polygons, np);
1035 /* allocate the storage for polygons and edges of the isoline just
1036 * above the current one, to allow easy access to them from the
1037 * current isoline */
1038 north_polygons = gp_alloc(2 * max_crvlen * sizeof (long int),
1039 "hidden north_polys");
1040 these_polygons = gp_alloc(2 * max_crvlen * sizeof (long int),
1041 "hidden these_polys");
1042 north_edges = gp_alloc(3 * max_crvlen * sizeof (long int),
1043 "hidden north_edges");
1044 these_edges = gp_alloc(3 * max_crvlen * sizeof (long int),
1045 "hidden these_edges");
1047 /* initialize the lists, all in one large loop. This is different
1048 * from the previous approach, which went over the vertices,
1049 * first, and only then, in new loop, built polygons */
1050 for (this_plot = plots, surface = 0;
1052 this_plot = this_plot->next_sp, surface++) {
1053 lp_style_type *lp_style = NULL;
1054 TBOOLEAN color_from_column = this_plot->pm3d_color_from_column;
1057 /* Quietly skip empty plots */
1058 if (this_plot->plot_type == NODATA)
1061 crvlen = this_plot->iso_crvs->p_count;
1063 /* Allow individual plots to opt out of hidden3d calculations */
1064 if (this_plot->opt_out_of_hidden3d)
1067 lp = &(this_plot->lp_properties);
1068 above = this_plot->lp_properties.l_type;
1069 below = this_plot->lp_properties.l_type + hiddenBacksideLinetypeOffset;
1071 /* calculate the point symbol type: */
1072 /* Assumest hat upstream functions have made sure this is
1073 * initialized sensibly --- thou hast been warned */
1074 lp_style = &(this_plot->lp_properties);
1076 /* HBB 20000715: new initialization code block for non-grid
1077 * structured datasets. Sufficiently different from the rest
1078 * to warrant separate code, I think. */
1079 if (! this_plot->has_grid_topology) {
1080 for (crv = 0, icrvs = this_plot->iso_crvs;
1082 crv++, icrvs = icrvs->next) {
1083 struct coordinate GPHUGE *points = icrvs->points;
1084 long int previousvertex = -1;
1086 #ifdef EAM_DATASTRINGS
1087 /* To handle labels we must look inside a separate list */
1088 /* rather than just walking through the points arrays. */
1089 if (this_plot->plot_style == LABELPOINTS) {
1090 struct text_label *label;
1091 long int thisvertex;
1092 struct coordinate labelpoint = {0};
1094 lp->pointflag = 1; /* Labels can use the code for hidden points */
1095 for (label = this_plot->labels->next; label != NULL; label = label->next) {
1096 labelpoint.x = label->place.x;
1097 labelpoint.y = label->place.y;
1098 labelpoint.z = label->place.z;
1099 thisvertex = store_vertex(&labelpoint,
1100 &(this_plot->lp_properties), color_from_column);
1103 (vlist+thisvertex)->label = label;
1104 store_edge(thisvertex, edir_point, crvlen, lp, above);
1109 for (i = 0; i < icrvs->p_count; i++) {
1110 long int thisvertex, basevertex;
1112 thisvertex = store_vertex(points + i, lp_style,
1115 if (this_plot->plot_style == VECTOR) {
1116 store_vertex(icrvs->next->points+i, 0, 0);
1119 if (thisvertex < 0) {
1120 previousvertex = thisvertex;
1124 switch (this_plot->plot_style) {
1130 if (previousvertex >= 0)
1131 store_edge(thisvertex, edir_west, 0, lp, above);
1134 store_edge(thisvertex, edir_vector, 0, lp, above);
1139 /* set second vertex to the low end of zrange */
1141 coordval remember_z = points[i].z;
1143 points[i].z = axis_array[FIRST_Z_AXIS].min;
1144 basevertex = store_vertex(points + i, lp_style,
1146 points[i].z = remember_z;
1149 store_edge(basevertex, edir_impulse, 0, lp, above);
1153 default: /* treat all the others like 'points' */
1154 store_edge(thisvertex, edir_point, crvlen, lp, above);
1156 } /* switch(plot_style) */
1158 previousvertex = thisvertex;
1162 continue; /* done with this plot! */
1165 /* initialize stored indices of north-of-this-isoline polygons and
1167 for (i=0; i < this_plot->iso_crvs->p_count; i++) {
1168 north_polygons[2 * i]
1169 = north_polygons[2 * i + 1]
1170 = north_edges[3 * i]
1171 = north_edges[3 * i + 1]
1172 = north_edges[3 * i + 2]
1176 for (crv = 0, icrvs = this_plot->iso_crvs;
1178 crv++, icrvs = icrvs->next) {
1179 struct coordinate GPHUGE *points = icrvs->points;
1181 for (i = 0; i < icrvs->p_count; i++) {
1182 long int thisvertex, basevertex;
1183 long int e1, e2, e3;
1186 thisvertex = store_vertex(points + i, lp_style,
1189 /* Preset the pointers to the polygons and edges
1190 * belonging to this isoline */
1191 these_polygons[2 * i] = these_polygons[2 * i + 1]
1192 = these_edges[3 * i] = these_edges[3 * i + 1]
1193 = these_edges[3 * i + 2]
1196 switch (this_plot->plot_style) {
1203 /* not first point, so we might want to set up
1204 * the edge(s) to the left of this vertex */
1205 if (thisvertex < 0) {
1207 && (hiddenShowAlternativeDiagonal)
1209 /* this vertex is invalid, but the
1210 * other three might still form a
1211 * valid triangle, facing northwest to
1212 * do that, we'll need the 'wrong'
1213 * diagonal, which goes from SW to NE:
1215 these_edges[i*3+2] = e3
1216 = store_edge(vertices.end - 1, edir_NE, crvlen,
1219 /* don't store this polygon for
1220 * later: it doesn't share edges
1221 * with any others to the south or
1222 * east, so there's need to */
1224 = store_polygon(vertices.end - 1, pdir_NW, crvlen);
1225 /* The other two edges of this
1226 * polygon need to be checked
1227 * against the neighboring
1228 * polygons' orientations, before
1230 color_edges(e3, these_edges[3*(i-1) +1],
1231 pnum, these_polygons[2*(i-1) + 1],
1233 color_edges(e3, north_edges[3*i],
1234 pnum, north_polygons[2*i], above, below);
1237 break; /* nothing else to do for invalid vertex */
1240 /* Coming here means that the current vertex
1241 * is valid: check the other three of this
1242 * cell, by trying to set up the edges from
1243 * this one to there */
1244 these_edges[i*3] = e1
1245 = store_edge(thisvertex, edir_west, crvlen, lp, above);
1247 if (crv > 0) { /* vertices to the north exist */
1248 these_edges[i*3 + 1] = e2
1249 = store_edge(thisvertex, edir_north, crvlen, lp, above);
1250 these_edges[i*3 + 2] = e3
1251 = store_edge(thisvertex, edir_NW, crvlen, lp, above);
1253 /* diagonal edge of this cell is OK,
1254 * so try to build both the polygons:
1257 /* one pair of edges is valid: put
1258 * first polygon, which points
1259 * towards the southwest */
1260 these_polygons[2*i] = pnum
1261 = store_polygon(thisvertex, pdir_SW, crvlen);
1262 color_edges(e1, these_edges[3*(i-1)+1],
1263 pnum, these_polygons[2*(i-1)+ 1], above, below );
1266 /* other pair of two is fine, put
1267 * the northeast polygon: */
1268 these_polygons[2*i + 1] = pnum
1269 = store_polygon(thisvertex, pdir_NE, crvlen);
1270 color_edges(e2, north_edges[3*i],
1271 pnum, north_polygons[2*i], above, below);
1273 /* In case these two new polygons
1274 * differ in orientation, find good
1275 * coloring of the diagonal */
1276 color_edges(e3, e3, these_polygons[2*i],
1277 these_polygons[2*i+1], above, below);
1279 else if ((e1 > -2) && (e2 > -2)
1280 && hiddenShowAlternativeDiagonal) {
1281 /* looks like all but the north-west
1282 * vertex are usable, so we set up the
1283 * southeast-pointing triangle, using
1284 * the 'wrong' diagonal: */
1285 these_edges[3*i + 2] = e3
1286 = store_edge(thisvertex, edir_NE, crvlen, lp, above);
1288 /* fill this polygon into *both*
1289 * polygon places for this
1290 * quadrangle, as this triangle
1291 * coincides with both edges that
1292 * will be used by later polygons
1294 these_polygons[2*i] = these_polygons[2*i+1] = pnum
1295 = store_polygon(thisvertex, pdir_SE, crvlen);
1296 /* This case is somewhat special:
1297 * all edges are new, so there is
1298 * no other polygon orientation to
1300 if (!plist[pnum].frontfacing)
1301 elist[e1].style = elist[e2].style = elist[e3].style
1306 } else if ((crv > 0)
1307 && (thisvertex >= 0)) {
1308 /* We're at the west border of the grid, but
1309 * not on the north one: put vertical end-wall
1311 these_edges[3*i + 1] =
1312 store_edge(thisvertex, edir_north, crvlen, lp, above);
1322 /* set second vertex to the low end of zrange */
1324 coordval remember_z = points[i].z;
1326 points[i].z = axis_array[FIRST_Z_AXIS].min;
1327 basevertex = store_vertex(points + i, lp_style,
1329 points[i].z = remember_z;
1332 store_edge(basevertex, edir_impulse, 0, lp, above);
1336 default: /* treat all the others like 'points' */
1337 if (thisvertex < 0) /* Ignore invalid vertex */
1339 store_edge(thisvertex, edir_point, crvlen, lp, above);
1344 /* Swap the 'north' lists of polygons and edges with
1345 * 'these' ones, which have been filled in the pass
1346 * through this isocurve */
1348 long int *temp = north_polygons;
1349 north_polygons = these_polygons;
1350 these_polygons = temp;
1353 north_edges = these_edges;
1359 free (these_polygons);
1360 free (north_polygons);
1365 /* Sort the elist in order of growing zmax. Uses qsort on an array of
1366 * plist indices, and then fills in the 'next' fields in struct
1367 * polygon to store the resulting order inside the plist */
1368 /* HBB 20010720: removed 'static' to avoid HP-sUX gcc bug */
1370 compare_edges_by_zmin(SORTFUNC_ARGS p1, SORTFUNC_ARGS p2)
1372 return SIGN(vlist[elist[*(const long *) p1].v2].z
1373 - vlist[elist[*(const long *) p2].v2].z);
1385 sortarray = gp_alloc((unsigned long) sizeof(long) * edges.end,
1386 "hidden sort edges");
1387 /* initialize sortarray with an identity mapping */
1388 for (i = 0; i < edges.end; i++)
1391 qsort(sortarray, (size_t) edges.end, sizeof(long), compare_edges_by_zmin);
1393 /* traverse plist in the order given by sortarray, and set the
1394 * 'next' pointers */
1395 this = elist + sortarray[0];
1396 for (i = 1; i < edges.end; i++) {
1397 this->next = sortarray[i];
1398 this = elist + sortarray[i];
1402 /* 'efirst' is the index of the leading element of plist */
1403 efirst = sortarray[0];
1408 /* HBB 20010720: removed 'static' to avoid HP-sUX gcc bug */
1410 compare_polys_by_zmax(SORTFUNC_ARGS p1, SORTFUNC_ARGS p2)
1412 return (SIGN(plist[*(const long *) p1].zmax
1413 - plist[*(const long *) p2].zmax));
1425 sortarray = gp_alloc((unsigned long) sizeof(long) * polygons.end,
1426 "hidden sortarray");
1428 /* initialize sortarray with an identity mapping */
1429 for (i = 0; i < polygons.end; i++)
1433 qsort(sortarray, (size_t) polygons.end, sizeof(long),
1434 compare_polys_by_zmax);
1436 /* traverse plist in the order given by sortarray, and set the
1437 * 'next' pointers */
1438 #if HIDDEN3D_QUADTREE
1439 /* HBB 20000716: Loop backwards, to ease construction of
1440 * linked lists from the head: */
1443 int grid_x_low, grid_x_high, grid_y_low, grid_y_high;
1445 for (grid_x = 0; grid_x < QUADTREE_GRANULARITY; grid_x++)
1446 for (grid_y = 0; grid_y < QUADTREE_GRANULARITY; grid_y++)
1447 quadtree[grid_x][grid_y] = -1;
1449 for (i=polygons.end - 1; i >= 0; i--) {
1450 this = plist + sortarray[i];
1452 grid_x_low = coord_to_treecell(this->xmin);
1453 grid_x_high = coord_to_treecell(this->xmax);
1454 grid_y_low = coord_to_treecell(this->ymin);
1455 grid_y_high = coord_to_treecell(this->ymax);
1457 for (grid_x = grid_x_low; grid_x <= grid_x_high; grid_x++) {
1458 for (grid_y = grid_y_low; grid_y <= grid_y_high; grid_y++) {
1459 p_qtreelist newhead = nextfrom_dynarray(&qtree);
1461 newhead->next = quadtree[grid_x][grid_y];
1462 newhead->p = sortarray[i];
1464 quadtree[grid_x][grid_y] = newhead - qlist;
1470 #else /* HIDDEN3D_QUADTREE */
1471 this = plist + sortarray[0];
1472 for (i = 1; i < polygons.end; i++) {
1473 this->next = sortarray[i];
1474 this = plist + sortarray[i];
1477 /* 'pfirst' is the index of the leading element of plist */
1478 #endif /* HIDDEN3D_QUADTREE */
1479 pfirst = sortarray[0];
1485 #define LASTBITS (USHRT_BITS -1) /* ????? */
1487 /************************************************/
1488 /******* Drawing the polygons ********/
1489 /************************************************/
1491 /* draw a single vertex as a point symbol, if requested by the chosen
1492 * plot style (linespoints, points, or dots...) */
1494 draw_vertex(p_vertex v)
1499 if (v->lp_style && v->lp_style->p_type >= 0 && !clip_point(x,y)) {
1500 int colortype = v->lp_style->pm3d_color.type;
1502 #ifdef EAM_DATASTRINGS
1504 write_label(x,y, v->label);
1510 /* EAM DEBUG - Check for extra point properties */
1511 if (colortype == TC_LT)
1512 /* Should have been set already! */
1514 else if (colortype == TC_RGB && v->lp_style->pm3d_color.lt == LT_COLORFROMCOLUMN)
1515 set_rgbcolor(v->real_z);
1516 else if (colortype == TC_RGB)
1517 set_rgbcolor(v->lp_style->pm3d_color.lt);
1518 else if (colortype == TC_Z)
1519 set_color( cb2gray(z2cb(v->real_z)) );
1521 #ifdef HIDDEN3D_VAR_PTSIZE
1522 if (v->lp_style->p_size == PTSZ_VARIABLE)
1523 (term->pointsize)(pointsize * v->original->CRD_PTSIZE);
1526 (term->point)(x,y, v->lp_style->p_type);
1528 /* vertex has been drawn --> flag it as done */
1534 /* The function that actually does the drawing of the visible portions
1536 /* HBB 20001108: changed to take the pointers to the end vertices as
1537 * additional arguments. */
1539 draw_edge(p_edge e, p_vertex v1, p_vertex v2)
1541 assert (e >= elist && e < elist + edges.end);
1543 draw3d_line_unconditional(v1, v2, e->lp, e->style);
1544 if (e->lp->pointflag) {
1551 /*************************************************************/
1552 /*************************************************************/
1553 /******* The depth sort algorithm (in_front) and its ******/
1554 /******* whole lot of helper functions ******/
1555 /*************************************************************/
1556 /*************************************************************/
1558 /* Split a given line segment into two at an inner point. The inner
1559 * point is specified as a fraction of the line-length (0 is V1, 1 is
1561 /* HBB 20001108: changed to now take two vertex pointers as its
1562 * arguments, rather than an edge pointer. */
1563 /* HBB 20001204: changed interface again. Now use vertex indices,
1564 * rather than pointers, to avoid problems with dangling pointers
1565 * after nextfrom_dynarray() call. */
1567 split_line_at_ratio(
1568 long vnum1, long vnum2, /* vertex indices of line to split */
1569 double w) /* where to split it */
1578 /* Create a new vertex */
1579 v = nextfrom_dynarray(&vertices);
1581 v->x = (vlist[vnum2].x - vlist[vnum1].x) * w + vlist[vnum1].x;
1582 v->y = (vlist[vnum2].y - vlist[vnum1].y) * w + vlist[vnum1].y;
1583 v->z = (vlist[vnum2].z - vlist[vnum1].z) * w + vlist[vnum1].z;
1584 v->real_z = (vlist[vnum2].real_z - vlist[vnum1].real_z) * w
1585 + vlist[vnum1].real_z;
1587 /* no point symbol for vertices generated by splitting an edge */
1590 /* additional checks to prevent adding unnecessary vertices */
1591 if (V_EQUAL(v, vlist + vnum1)) {
1592 droplast_dynarray(&vertices);
1595 if (V_EQUAL(v, vlist + vnum2)) {
1596 droplast_dynarray(&vertices);
1604 /* Compute the 'signed area' of 3 points in their 2d projection
1605 * to the x-y plane. Essentially the z component of the crossproduct.
1606 * Should come out positive if v1, v2, v3 are ordered counter-clockwise */
1608 static GP_INLINE double
1609 area2D(p_vertex v1, p_vertex v2, p_vertex v3)
1612 dx12 = v2->x - v1->x, /* x/y components of (v2-v1) and (v3-v1) */
1613 dx13 = v3->x - v1->x,
1614 dy12 = v2->y - v1->y,
1615 dy13 = v3->y - v1->y;
1616 return (dx12 * dy13 - dy12 * dx13);
1619 /* Utility routine: takes an edge and makes a new one, which is a fragment
1620 * of the old one. The fragment inherits the line style and stuff of the
1621 * given edge; only the two new vertices are different. The new edge
1622 * is then passed to in_front, for recursive handling */
1623 /* HBB 20001108: Changed from edge pointer to edge index. Don't
1624 * allocate a fresh anymore, as this is no longer needed after the
1625 * change to in_front(). What remains of this function may no longer
1626 * be worth having. I.e. it can be replaced by a direct recursion call
1627 * of in_front(), sometime soon. */
1628 static GP_INLINE void
1629 handle_edge_fragment(long edgenum, long vnum1, long vnum2, long firstpoly)
1631 #if !HIDDEN3D_QUADTREE
1632 /* Avoid checking against the same polygon again. */
1633 firstpoly = plist[firstpoly].next;
1635 in_front(edgenum, vnum1, vnum2, &firstpoly);
1638 /*********************************************************************/
1639 /* The actual heart of all this: determines if edge at index 'edgenum'
1640 * of the elist is in_front of all the polygons, or not. If necessary,
1641 * it will recursively call itself to isolate more than one visible
1642 * fragment of the input edge. Wherever possible, recursion is
1643 * avoided, by in-place modification of the edge.
1645 * The visible fragments are then drawn by a call to 'draw_edge' from
1646 * inside this routine. */
1647 /*********************************************************************/
1648 /* HBB 20001108: changed to now take the vertex numbers as additional
1649 * arguments. The idea is to not overwrite the endpoint stored with
1650 * the edge, so Test 2 will catch on even after the subject edge has
1651 * been split up before one of its two polygons is tested against
1652 * it. FIXME: allocates new vertices when splitting, but never frees
1653 * them, currently. */
1657 long edgenum, /* number of the edge in elist */
1658 long vnum1, long vnum2, /* numbers of its endpoints */
1659 long *firstpoly) /* first plist index to consider */
1661 p_polygon p; /* pointer to current testing polygon */
1662 long int polynum; /* ... and its index in the plist */
1663 p_vertex v1, v2; /* pointers to vertices of input edge */
1665 coordval xmin, xmax; /* all of these are for the edge */
1666 coordval ymin, ymax;
1667 coordval zmin, zmax;
1668 #if HIDDEN3D_GRIDBOX
1669 unsigned int xextent; /* extent bitmask in x direction */
1670 unsigned int yextent; /* same, in y direction */
1672 # define SET_XEXTENT \
1673 xextent = CALC_BITRANGE(xmin, xmax);
1674 # define SET_YEXTENT \
1675 yextent = CALC_BITRANGE(ymin, ymax);
1677 # define SET_XEXTENT /* nothing */
1678 # define SET_YEXTENT /* nothing */
1680 #if HIDDEN3D_QUADTREE
1682 int grid_x_low, grid_x_high;
1683 int grid_y_low, grid_y_high;
1687 /* zmin of the edge, as it started out. This is needed separately to
1688 * allow modifying '*firstpoly', without moving it too far to the
1690 coordval first_zmin;
1692 /* macro for eliminating tail-recursion inside in_front: when the
1693 * current edge is modified, recompute all function-wide status
1694 * variables. Note that it guarantees that v1 is always closer to
1695 * the viewer than v2 (in z direction) */
1696 /* HBB 20001108: slightly changed so it can be called with vnum1
1697 * and vnum2 as its arguments, too */
1698 #define setup_edge(vert1, vert2) \
1700 if (vlist[vert1].z > vlist[vert2].z) { \
1701 v1 = vlist + (vert1); \
1702 v2 = vlist + (vert2); \
1704 v1 = vlist + (vert2); \
1705 v2 = vlist + (vert1); \
1707 vnum1 = v1 - vlist; \
1708 vnum2 = v2 - vlist; \
1709 zmax = v1->z; zmin = v2->z; \
1711 if (v1->x > v2->x) { \
1712 xmin = v2->x; xmax = v1->x; \
1714 xmin = v1->x; xmax = v2->x; \
1718 if (v1->y > v2->y) { \
1719 ymin = v2->y; ymax = v1->y; \
1721 ymin = v1->y; ymax = v2->y; \
1724 } while (0) /* end macro setup_edge */
1726 /* use the macro for initial setup, too: */
1727 setup_edge(vnum1, vnum2);
1731 #if HIDDEN3D_QUADTREE
1732 grid_x_low = coord_to_treecell(xmin);
1733 grid_x_high = coord_to_treecell(xmax);
1734 grid_y_low = coord_to_treecell(ymin);
1735 grid_y_high = coord_to_treecell(ymax);
1737 for (grid_x = grid_x_low; grid_x <= grid_x_high; grid_x ++)
1738 for (grid_y = grid_y_low; grid_y <= grid_y_high; grid_y ++)
1739 for (listhead = quadtree[grid_x][grid_y];
1741 listhead = qlist[listhead].next)
1742 #else /* HIDDEN3D_QUADTREE */
1743 /* loop over all the polygons in the sorted list, starting at the
1744 * currently first (i.e. furthest, from the viewer) polgon. */
1745 for (polynum = *firstpoly; polynum >=0; polynum = p->next)
1746 #endif /* HIDDEN3D_QUADTREE */
1748 /* shortcut variables for the three vertices of 'p':*/
1749 p_vertex w1, w2, w3;
1750 /* signed areas of each of e's vertices wrt. the edges of
1751 * p. I store them explicitly, because they are used more
1752 * than once, eventually */
1753 double e_side[2][POLY_NVERT];
1754 /* signed areas of each of p's vertices wrt. to edge e. Stored
1755 * for re-use in intersection calculations, as well */
1756 double p_side[POLY_NVERT];
1757 /* values got from throwing the edge vertices into the plane
1758 * equation of the polygon. Used for testing if the vertices are
1759 * in front of or behind the plane, and also to determine the
1760 * point where the edge goes through the polygon, by
1762 double v1_rel_pplane, v2_rel_pplane;
1763 /* Orientation of polygon wrt. to the eye: front or back side
1765 coordval polyfacing;
1767 /* stores classification of cases as 4 2-bit patterns, mainly */
1768 unsigned int classification[POLY_NVERT + 1];
1770 #if HIDDEN3D_QUADTREE
1771 polynum = qlist[listhead].p;
1773 p = plist + polynum;
1775 /* OK, off we go with the real work. This algo is mainly
1776 * following the one of 'HLines.java', as described in the
1777 * book 'Computer Graphics for Java Programmers', by Dutch
1778 * professor Leen Ammeraal, published by J.Wiley & Sons,
1779 * ISBN 0 471 98142 7. It happens to the only(!) actual
1780 * code or algorithm example I got out of a question to
1781 * the Newsgroup comp.graphics.algorithms, asking for a
1782 * hidden *line* removal algorithm. */
1784 /* Test 1 (2D): minimax tests. Do x/y ranges of polygon
1785 * and edge have any overlap? */
1787 #if HIDDEN3D_GRIDBOX
1788 /* First, check by comparing the extent bit patterns: */
1789 || (!(xextent & p->xbits))
1790 || (!(yextent & p->ybits))
1799 /* Tests 2 and 3 switched... */
1801 /* Test 3 (3D): Is edge completely in front of polygon? */
1802 if (p->zmax < zmin) {
1803 /* Polygon completely behind this edge. Move start of
1804 * relevant plist to this point, to speed up next
1805 * run. This makes use of the fact that elist is also
1806 * kept in upwardly sorted order of zmin, i.e. the
1807 * condition found here will also hold for all coming
1808 * edges in the list */
1809 if (p->zmax < first_zmin)
1810 *firstpoly = polynum;
1811 continue; /* this polygon is done with */
1814 /* Test 2 (0D): does edge belong to this very polygon? */
1815 /* 20001108: to make this rejector more effective, do keep
1816 * the original edge vertices unchanged */
1819 || (p->vertex[0] == elist[edgenum].v1)
1820 || (p->vertex[1] == elist[edgenum].v1)
1821 || (p->vertex[2] == elist[edgenum].v1)
1824 || (p->vertex[0] == elist[edgenum].v2)
1825 || (p->vertex[1] == elist[edgenum].v2)
1826 || (p->vertex[2] == elist[edgenum].v2)
1831 w1 = vlist + p->vertex[0];
1832 w2 = vlist + p->vertex[1];
1833 w3 = vlist + p->vertex[2];
1836 /* Test 4 (2D): Is edge fully on the 'wrong side' of one of the
1839 * Done by comparing signed areas (cross-product z components of
1840 * both edge endpoints) to that of the polygon itself (as given by
1841 * the plane normal z component). If both v1 and v2 are found on
1842 * the 'outside' side of a polygon edge (interpreting it as
1843 * infinite line), the polygon can't obscure this edge. All the
1844 * signed areas are remembered, for later tests. */
1845 polyfacing = p->plane[2];
1846 whichside = SIGN(polyfacing);
1848 /* We're looking at the border of this polygon. It
1849 * looks like an infitisemally thin line, i.e. it'll
1850 * be practically invisible*/
1851 /* FIXME: should such a polygon have been stored in the plist,
1852 * in the first place??? */
1855 /* p->plane[2] is now forced >=0, so it doesn't tell anything
1856 * about which side the polygon is facing */
1857 whichside = (p->frontfacing) ? -1 : 1;
1860 /* Just make sure I got this the right way round: */
1862 assert (GE(area2D(w1, w2, w3),0));
1864 assert (GE(0,area2D(w1, w2, w3)));
1867 /* classify both endpoints of the test edge, by their
1868 * position relative to some plane that defines the volume
1869 * shadowed by a triangle. Used both by macro 'checkside',
1870 * and for individual checking, below --> put this into a
1872 #define classify(par1, par2, classnum) \
1874 if (GR(par1, 0)) { \
1875 /* v1 strictly outside --> v2 in-plane is enough, \
1877 classification[classnum] \
1878 = (1 << 0) | ((GE(par2, 0)) << 1); \
1879 } else if (GR(par2, 0)) { \
1880 /* v2 strictly outside --> v1 in-plane is enough, \
1882 classification[classnum] \
1883 = ((GE(par1, 0)) << 0) | (1 << 1); \
1885 /* as neither one of them is strictly outside, \
1886 * there's no need for any edge-splitting, at all. \
1888 classification[classnum] = 0; \
1892 /* code block used thrice, so put into a macro. The
1893 * 'area2D' calls return <0, ==0 or >0 if the three
1894 * vertices passed to it are in clockwise order, colinear,
1895 * or in conter-clockwise order, respectively. So the sign
1896 * of this can be used to determine if a point is inside
1897 * or outside the shadow volume. */
1898 #define checkside(a,b,s) \
1899 e_side[0][s] = whichside * area2D((a), (b), v1); \
1900 e_side[1][s] = whichside * area2D((a), (b), v2); \
1901 classify(e_side[0][s], e_side[1][s], s); \
1902 if (classification[s] == 3) \
1905 checkside(w1, w2, 0);
1906 checkside(w2, w3, 1);
1907 checkside(w3, w1, 2);
1908 #undef checkside /* get rid of that macro, again */
1911 /* Test 5 (2D): Does the whole polygon lie on one and the same
1912 * side of the tested edge? Again, area2D is used to determine on
1913 * which side of a given edge a vertex lies. */
1914 /* HBB 20000621: this test only makes sense if this is a
1915 * 'real' edge, not just a single point */
1916 /* HBB 20051206: but p_side[] may still have to be computed, to be used
1918 p_side[0] = area2D(v1, v2, w1);
1919 p_side[1] = area2D(v1, v2, w2);
1920 p_side[2] = area2D(v1, v2, w3);
1922 /* HBB 20001104: made this more restrictive: only
1923 * reject p if areas are greater than 0, i.e. don't
1924 * allow EQ(..,0). Otherwise, edges coincident with a
1925 * polygon edge in 2D will not be hidden by it. */
1926 /* FIXME HBB 20001108: some more code will be needed
1927 * here or further down below. It will currently fail
1928 * if two edges fall exactly on top of each other
1929 * (like the crossover line in the Klein-bottle demo):
1930 * both edges will be hidden by the other's polygon,
1934 || (GR(p_side[0], 0)
1938 || (GR(0 , p_side[0])
1939 && GR(0 , p_side[1])
1940 && GR(0 , p_side[2])
1945 /* HBB 20020406: try to repair this */
1947 || (GE(p_side[0], 0)
1951 || (GE(0 , p_side[0])
1952 && GE(0 , p_side[1])
1953 && GE(0 , p_side[2])
1960 /* Test 6 (3D): does the whole edge lie on the viewer's
1961 * side of the polygon's plane? */
1962 v1_rel_pplane = eval_plane_equation(p->plane, v1);
1963 v2_rel_pplane = eval_plane_equation(p->plane, v2);
1965 /* Condense into classification bit pattern for v1 and v2
1966 * wrt. this plane, and store into entry 'POLY_NVERT' of
1967 * the classification array, following the three edges of
1969 classify(v1_rel_pplane, v2_rel_pplane, POLY_NVERT);
1971 if (classification[POLY_NVERT] == 3)
1972 continue; /* edge completely in front of polygon */
1974 /* Test 7 (2D): is edge completely inside the triangle?
1975 * The stored results from Test 4 make this rather easy to
1976 * check: if a point is oriented equally wrt. all three
1977 * edges, and also to the triangle itself, the point is
1978 * inside the 2D triangle: */
1980 /* search for any one bits (--> they mean 'outside') */
1981 if (! (classification[0]
1983 | classification[2])) {
1985 /* Edge is completely inside the polygon's 2D
1986 * projection. Unlike Ammeraal, I have to check for
1987 * edges penetrating polygons, as well. */
1989 /* Check if edge really doesn't intersect the triangle
1990 * (this happens in roughly one third of the
1991 * cases). If both vertices have been classified as
1992 * 'behind', the result is true even in the case of
1993 * possibly intersecting polygons */
1994 /* HBB 20020408: softened this check to allow edges
1995 * not to be hidden by polygons they're fully inside.
1996 * I.e. only declare the edge invisible if at least
1997 * one of its endpoints is truly behind, not inside
1998 * the polygon's plane. ---> FIXME 20001108 above */
1999 if (!classification[POLY_NVERT]
2000 && (GR(0, v1_rel_pplane) || GR(0, v2_rel_pplane)))
2005 /* Test 8 (3D): If no edge intersects any polygon, the edge must
2006 * lie infront of the poly if the 'inside' vertex in front of it.
2007 * This test doesn't work if lines might intersect polygons, so I
2008 * took it out of my version of Ammeraal's algorithm. */
2011 /* Test 9 (3D): The final 'catch-all' handler. We now know
2012 * that the edges passes through the walls of the
2013 * semifinite polygonal cylinder of invisible space
2014 * stamped out by the polygon, somewhere, so we have to
2015 * compute the intersection points with the 2D projection
2016 * of the polygon and check whether they are infront of or
2017 * behind the polygon, to isolate the visible fragments of
2018 * the line. Method is to calc. the [0..1] parameter that
2019 * describes the way from V1 to V2, for all intersections
2020 * of the edge with any of the planes that define the
2021 * 'shadow volume' of this polygon, and deciding what to
2022 * do, with all this.
2024 * The remaining work can be classified by the values of
2025 * the variables calculated in previous tests:
2026 * v{1/2}_rel_pplane, e_side[2][POLY_NVERT],
2027 * p_side[POLY_NVERT]. Setting aside cases where one of
2028 * them is zero, this leaves a total of 2^11 = 2048
2029 * separate cases, in principal. Several of those have
2030 * been sorted out, already, by the previous tests. That
2031 * leaves us with 3*27 cases, essentially. The '3' cases
2032 * differ by which of the three vertices of the polygon is
2033 * alone on its side of the test edge. That already fixes
2034 * which two edges might intersect the test edge. The 27
2035 * cases differ by which of the edge points lies on which
2036 * of the three planes in space (two sides, and the
2037 * polygon plane) that remain interesting for the tests.
2038 * That would normally make 2^2 = 4 cases per plane. But
2039 * the 'both outside' cases for all three planes have
2040 * already been taken care of, earlier, leaving 3 of those
2041 * cases to be handled. This is the same for all three
2042 * relevant planes, so we get 3^3 = 27 cases. */
2045 /* the interception point parameters: */
2046 double front_hit, this_hit, hit_in_poly;
2050 unsigned int full_class;
2052 /* find out which of the three edges would be intersected: */
2053 if ((p_side[0] > 0) == (p_side[1] > 0)) {
2054 /* first two vertices on the same side, the third
2055 * is on the 'other' side of the test edge */
2058 } else if ((p_side[0] > 0) == (p_side[2] > 0)) {
2059 /* vertex '1' on the other side */
2063 /* we now know that it must be the first vertex
2064 * that is on the 'other' side, as not all three
2065 * can be on different sides. */
2070 /* Carry out the classification into those 27 cases,
2071 * based upon classification bits precomputed above,
2072 * and calculate the intersection parameters, as
2073 * appropriate. Start by regarding the polygon's
2074 * plane: '1' means point is in front of plane.
2076 * First, some utility macros: */
2077 #define cleanup_hit(hit) \
2081 else if (EQ(hit,1)) \
2085 #define hit_in_edge(hit) ((hit >= 0) && (hit <= 1))
2087 /* find the intersection point through the front
2090 if (classification[3]) {
2091 if (SIGN(v1_rel_pplane - v2_rel_pplane)) {
2092 front_hit = v1_rel_pplane / (v1_rel_pplane - v2_rel_pplane);
2093 cleanup_hit(front_hit);
2095 if (!hit_in_edge(front_hit)) {
2097 classification[3] = 0; /* not really intersecting */
2102 /* Find intersection points with the two relevant side
2103 * planes of the shadow volume. A macro contains the
2104 * code, to be used for both side1 and side2, to
2105 * reduce code overhead: */
2106 #define check_out_hit(hit, side) \
2108 hit = 1e10; /* default is 'way off' */ \
2109 if (classification[side]) { \
2110 /* Not both inside, i.e. there has to be an \
2111 * intersection point: */ \
2112 hit_in_poly = - whichside * p_side[side] \
2113 / (e_side[0][side] - e_side[1][side]); \
2114 cleanup_hit(hit_in_poly); \
2115 if (hit_in_edge(hit_in_poly)) { \
2116 this_hit = e_side[0][side] \
2117 / (e_side[0][side] - e_side[1][side]); \
2118 cleanup_hit(this_hit); \
2119 if (hit_in_edge(this_hit)) { \
2125 /* doesn't really intersect */ \
2126 classification[side] = 0; \
2129 check_out_hit(hit1, side1);
2130 check_out_hit(hit2, side2);
2131 #undef check_out_hit
2133 /* OK, now we know the position of all the hits that
2134 * might be needed, given by their positions between
2135 * the test edge's vertices. */
2137 /* Macro: combine all the classifications into a
2138 * single classification bitpattern, for easier
2139 * listing of cases */
2140 #define makeclass(s2, s1, p) (16 * s2 + 4 * s1 + p)
2142 full_class = makeclass(classification[side2],
2143 classification[side1],
2147 /* all suspected intersections cleared, already! */
2150 /* First sort out all cases where one endpoint is
2151 * attributed to more than one intersecting
2152 * plane. Only one of those can be 'the real'
2153 * intersection of the edge with the surface of the
2154 * shadow volume surface. The others are intersections
2155 * with the planes defining the volumes, but outside
2158 * This cuts down the 27 to 13 cases (000, 001, 002,
2159 * 012, and their permutations). Start with v1: */
2160 switch (full_class & makeclass(1, 1, 1)) {
2161 case makeclass(0,1,1):
2162 /* v1 is out 'side1' and infront of 'p' */
2163 if (front_hit < hit1)
2164 classification[3] = 0;
2166 classification[side1] = 0;
2169 case makeclass(1,0,1):
2170 /* v1 is out 'side2' and infront of 'p' */
2171 if (front_hit < hit2)
2172 classification[3] = 0;
2174 classification[side2] = 0;
2177 /* The following two cases never trigger, even in
2178 * a full run through all.dem. Neither do the
2179 * corresponding 2 cases for the vertex2
2180 * classification. I think this is so because the
2181 * determination of 'side1' and 'side2' already
2182 * took care of this: if the edge vertex is out
2183 * two sides, those two will _not_ be chosen as
2184 * 'side1' and 'side2', because one of them is
2185 * completely on one side of the edge. */
2186 case makeclass(1,1,0):
2187 /* v1 out both sides, but behind the front */
2189 /* hit2 is closer to the hidden vertex v2, so
2190 * it's the relevant intersection: */
2191 classification[side1] = 0;
2193 classification[side2] = 0;
2196 case makeclass(1,1,1):
2197 /* v1 out both sides, and in front of p (--> v2 is
2199 if (front_hit < hit1) {
2200 classification[3] = 0;
2202 classification[side1] = 0;
2204 classification[side2] = 0;
2206 classification[side1] = 0;
2207 if (front_hit < hit2)
2208 classification[3] = 0;
2210 classification[side2] = 0;
2216 } /* switch(v1 classes) */
2219 /* And the same, for v2 */
2220 switch (full_class & makeclass(2, 2, 2)) {
2221 case makeclass(0,2,2):
2222 /* v2 is out 'side1' and infront of 'p' */
2223 if (front_hit > hit1)
2224 classification[3] = 0;
2226 classification[side1] = 0;
2229 case makeclass(2,0,2):
2230 /* v2 out side 'side2' and infront of 'p' */
2231 if (front_hit > hit2)
2232 classification[3] = 0;
2234 classification[side2] = 0;
2237 /* See note in v1 handling about these two cases
2238 * being impossible... */
2239 case makeclass(2,2,0):
2240 /* v2 out both sides, but behind the front */
2242 /* hit2 is closer to the hidden vertex v2, so
2243 * it's the relevant intersection: */
2244 classification[side1] = 0;
2246 classification[side2] = 0;
2249 case makeclass(2,2,2):
2250 /* v2 out both sides, and in front of p (--> v1 is
2252 if (front_hit > hit1) {
2253 classification[3] = 0;
2255 classification[side1] = 0;
2257 classification[side2] = 0;
2259 classification[side1] = 0;
2260 if (front_hit > hit2)
2261 classification[3] = 0;
2263 classification[side2] = 0;
2269 } /* switch(v2 classes) */
2272 /* recalculate full_class after all these possible changes: */
2273 full_class = makeclass(classification[side2],
2274 classification[side1],
2277 /* This monster switch catches all the 13 remaining cases
2279 switch (full_class) {
2281 printf("in_front: (T9) class %d is illegal. Should never happen.\n",
2284 case makeclass(0,0,0):
2285 /* can happen, by resetting of classification bits
2286 * inside this test */
2289 /*--------- The simplest cases first: only one intersection point -----*/
2290 /* Code is the same for all six cases, or nearly so: */
2291 #define handle_singleplane_hit(vert, hitpar) \
2292 newvert[0] = split_line_at_ratio(vnum1, vnum2, hitpar); \
2293 setup_edge(vert, newvert[0]); \
2296 case makeclass(0,0,1): /* v1 is in front of poly: */
2297 handle_singleplane_hit(vnum1, front_hit);
2298 case makeclass(0,0,2): /* v2 is in front of poly: */
2299 handle_singleplane_hit(vnum2, front_hit);
2300 case makeclass(0,1,0): /* v1 is out side1: */
2301 handle_singleplane_hit(vnum1,hit1);
2302 case makeclass(0,2,0): /* v2 is out side1: */
2303 handle_singleplane_hit(vnum2,hit1);
2304 case makeclass(1,0,0): /* v1 is out side2 */
2305 handle_singleplane_hit(vnum1,hit2);
2306 case makeclass(2,0,0): /* v2 is out side2 */
2307 handle_singleplane_hit(vnum2,hit2);
2309 /*----------- cases with 2 certain intersections: --------------*/
2310 case makeclass(1,2,0):
2311 case makeclass(2,1,0):
2312 /* Both behind the front, v1 out one side, v2 out
2313 * the other. These two cases can be handled by
2315 newvert[0] = split_line_at_ratio(vnum1, vnum2, hit1);
2316 newvert[1] = split_line_at_ratio(vnum1, vnum2, hit2);
2318 /* the fragments are v1--hit1 and hit2--v2 */
2319 handle_edge_fragment(edgenum, newvert[1], vnum2, polynum);
2320 setup_edge(vnum1, newvert[0]);
2322 /* the fragments are v2--hit1 and hit2--v1 */
2323 handle_edge_fragment(edgenum, vnum1, newvert[1], polynum);
2324 setup_edge(newvert[0], vnum2);
2328 /*----------- cases with either 2 or no intersections: --------------*/
2330 /* Mainly identical code block to be used 4 times
2332 /* HBB 20001108: up until today, this part of the
2333 * was severely buggy. But I do think I've got it
2334 * fixed up, this time */
2335 #define handle_outside_behind_vs_infront(hitvar1, hitvar2) \
2336 /* vnum1 out plane of 'hitvar1' and in back, vnum2 \
2338 if ((hitvar1) < (hitvar2)) { \
2339 newvert[0] = split_line_at_ratio(vnum1, vnum2, (hitvar1)); \
2340 newvert[1] = split_line_at_ratio(vnum1, vnum2, (hitvar2)); \
2341 handle_edge_fragment(edgenum, newvert[1], vnum2, polynum); \
2342 setup_edge(vnum1, newvert[0]); \
2344 /* otherwise, the edge missed the shadow volume \
2345 * --> nothing to do */ \
2348 case makeclass(0,1,2): /* v1 out side1 and in back, v2 in front */
2349 handle_outside_behind_vs_infront(hit1, front_hit);
2351 case makeclass(0,2,1): /* v2 out side1 and in back, v1 in front */
2352 handle_outside_behind_vs_infront(front_hit, hit1);
2354 case makeclass(1,0,2): /* v1 out side2 and in back, v2 in front */
2355 handle_outside_behind_vs_infront(hit2, front_hit);
2357 case makeclass(2,0,1): /* v2 out side2 and in back, v1 in front */
2358 handle_outside_behind_vs_infront(front_hit, hit2);
2360 #undef handle_outside_behind_vs_infront
2362 } /* end of switch through the cases */
2364 } /* end of part 'T9' */
2365 } /* for (polygons in list) */
2367 /* Came here, so there's something left of this edge, which needs
2368 * to be drawn. But the vertices are different, now, so copy our
2369 * new vertices back into 'e' */
2371 draw_edge(elist + edgenum, vlist + vnum1, vlist + vnum2);
2377 /* HBB 20000617: reimplemented this routine from scratch */
2378 /* Externally callable function to draw a line, but hide it behind the
2379 * visible surface. */
2380 /* NB: The p_vertex arguments are not allowed to be pointers into the
2381 * hidden3d 'vlist' structure. If they are, they may become invalid
2382 * before they're used, because of the nextfrom_dynarray() call. */
2385 p_vertex v1, p_vertex v2, /* pointers to the end vertices */
2386 struct lp_style_type *lp) /* line and point style to draw in */
2388 long int vstore1, vstore2;
2390 long int temp_pfirst;
2392 /* If no polygons have been stored, nothing can be hidden, and we
2393 * can't use in_front() because the datastructures are partly
2394 * invalid. So just draw the line and be done with it */
2395 if (!polygons.end) {
2396 draw3d_line_unconditional(v1, v2, lp, lp->l_type);
2400 /* Copy two vertices into hidden3d arrays: */
2401 nextfrom_dynarray(&vertices);
2402 vstore1 = vertices.end - 1;
2403 vlist[vstore1] = *v1;
2405 vlist[vstore1].lp_style = NULL;
2406 nextfrom_dynarray(&vertices);
2407 vstore2 = vertices.end - 1;
2408 vlist[vstore2] = *v2;
2409 vlist[vstore2].lp_style = NULL;
2411 /* v2 == NULL --> this is a point symbol to be drawn. Make two
2412 * vertex pointers the same, and set up the 'style' field */
2414 vlist[vstore2].lp_style = lp;
2417 /* store the edge into the hidden3d datastructures */
2418 edgenum = make_edge(vstore1, vstore2, lp, lp->l_type, -1);
2420 /* remove hidden portions of the line, and draw what remains */
2421 temp_pfirst = pfirst;
2422 in_front(edgenum, elist[edgenum].v1, elist[edgenum].v2, &temp_pfirst);
2424 /* release allocated storage slots: */
2425 droplast_dynarray(&edges);
2426 droplast_dynarray(&vertices);
2428 droplast_dynarray(&vertices);
2431 /***********************************************************************
2432 * and, finally, the 'mother function' that uses all these lots of tools
2433 ***********************************************************************/
2435 plot3d_hidden(struct surface_points *plots, int pcount)
2437 /* make vertices, edges and polygons out of all the plots */
2438 build_networks(plots, pcount);
2441 /* No drawable edges found. Free all storage and bail out. */
2442 term_hidden_line_removal();
2443 graph_error("*All* edges undefined or out of range, thus no plot.");
2446 if (! polygons.end) {
2447 /* No polygons anything could be hidden behind... */
2450 while (efirst >= 0) {
2451 draw_edge(elist+efirst, vlist + elist[efirst].v1, vlist + elist[efirst].v2);
2452 efirst = elist[efirst].next;
2455 long int temporary_pfirst;
2457 /* Presort edges in z order */
2459 /* Presort polygons in z order */
2462 temporary_pfirst = pfirst;
2464 while (efirst >=0) {
2465 if (elist[efirst].style >= -2) /* skip invisible edges */
2466 in_front(efirst, elist[efirst].v1, elist[efirst].v2,
2468 efirst = elist[efirst].next;
2473 /* FIXME: anything to free? */
2477 reset_hidden3doptions()
2479 hiddenBacksideLinetypeOffset = BACKSIDE_LINETYPE_OFFSET;
2480 hiddenTriangleLinesdrawnPattern = TRIANGLE_LINESDRAWN_PATTERN;
2481 hiddenHandleUndefinedPoints = HANDLE_UNDEFINED_POINTS;
2482 hiddenShowAlternativeDiagonal = SHOW_ALTERNATIVE_DIAGONAL;
2483 hiddenHandleBentoverQuadrangles = HANDLE_BENTOVER_QUADRANGLES;
2486 /* Emacs editing help for HBB:
2487 * Local Variables: ***
2488 * c-basic-offset: 4 ***