1 /* GNUPLOT - graph3d.c */
2 
3 /*[
4  * Copyright 1986 - 1993, 1998, 2004   Thomas Williams, Colin Kelley
5  *
6  * Permission to use, copy, and distribute this software and its
7  * documentation for any purpose with or without fee is hereby granted,
8  * provided that the above copyright notice appear in all copies and
9  * that both that copyright notice and this permission notice appear
10  * in supporting documentation.
11  *
12  * Permission to modify the software is granted, but not the right to
13  * distribute the complete modified source code.  Modifications are to
14  * be distributed as patches to the released version.  Permission to
15  * distribute binaries produced by compiling modified sources is granted,
16  * provided you
17  *   1. distribute the corresponding source modifications from the
18  *    released version in the form of a patch file along with the binaries,
19  *   2. add special version identification to distinguish your version
20  *    in addition to the base release version number,
21  *   3. provide your name and address as the primary contact for the
22  *    support of your modified version, and
23  *   4. retain our contact information in regard to use of the base
24  *    software.
25  * Permission to distribute the released version of the source code along
26  * with corresponding source modifications in the form of a patch file is
27  * granted with same provisions 2 through 4 for binary distributions.
28  *
29  * This software is provided "as is" without express or implied warranty
30  * to the extent permitted by applicable law.
31 ]*/
32 
33 
34 /*
35  * AUTHORS
36  *
37  *   Original Software:
38  *       Gershon Elber and many others.
39  *
40  * 19 September 1992  Lawrence Crowl  (crowl@cs.orst.edu)
41  * Added user-specified bases for log scaling.
42  *
43  * 3.6 - split graph3d.c into graph3d.c (graph),
44  *                            util3d.c (intersections, etc)
45  *                            hidden3d.c (hidden-line removal code)
46  *
47  */
48 
49 #include "graph3d.h"
50 
51 #include "alloc.h"
52 #include "axis.h"
53 #include "command.h"
54 #include "contour.h"
55 #include "gadgets.h"
56 #include "hidden3d.h"
57 #include "misc.h"
58 #include "term_api.h"
59 #include "util3d.h"
60 #include "util.h"
61 #include "vplot.h"
62 
63 #include "pm3d.h"
64 #include "plot3d.h"
65 #include "color.h"
66 
67 #include "plot.h"
68 
69 static int key_entry_height;	/* bigger of t->v_char, pointsize*t->v_tick */
70 static int key_title_height;
71 static int key_title_extra;	/* allow room for subscript/superscript */
72 static int key_title_width;
73 
74 /* is contouring wanted ? */
75 t_contour_placement draw_contour = CONTOUR_NONE;
76 TBOOLEAN clabel_onecolor = FALSE;	/* use same linetype for all contours */
77 int clabel_interval = 20;		/* label every 20th contour segment */
78 int clabel_start = 5;			/*       starting with the 5th */
79 char *clabel_font = NULL;		/* default to current font */
80 
81 /* Draw the surface at all? (FALSE if only contours are wanted) */
82 TBOOLEAN draw_surface = TRUE;
83 /* Always create a gridded surface when lines are read from a data file */
84 TBOOLEAN implicit_surface = TRUE;
85 
86 /* Was hidden3d display selected by user? */
87 TBOOLEAN hidden3d = FALSE;
88 int hidden3d_layer = LAYER_BACK;
89 
90 /* Rotation and scale of the 3d view, as controlled by 'set view': */
91 float surface_rot_z = 30.0;
92 float surface_rot_x = 60.0;
93 float surface_scale = 1.0;
94 float surface_zscale = 1.0;
95 float surface_lscale = 0.0;
96 float mapview_scale = 1.0;
97 float azimuth = 0.0;
98 
99 /* These indicate projection onto the xy, xz or yz plane
100  * as requested by 'set view map' or 'set view projection'
101  */
102 TBOOLEAN splot_map = FALSE;
103 TBOOLEAN xz_projection = FALSE;
104 TBOOLEAN yz_projection = FALSE;
105 
106 /* position of the base plane, as given by 'set ticslevel' or 'set xyplane' */
107 t_xyplane xyplane = { 0.5, FALSE };
108 
109 /* 'set isosamples' settings */
110 int iso_samples_1 = ISO_SAMPLES;
111 int iso_samples_2 = ISO_SAMPLES;
112 
113 double xscale3d, yscale3d, zscale3d;
114 double xcenter3d = 0.0;
115 double ycenter3d = 0.0;
116 double zcenter3d = 0.0;
117 
118 typedef enum { ALLGRID, FRONTGRID, BACKGRID, BORDERONLY } WHICHGRID;
119 
120 static void do_3dkey_layout(legend_key *key, int *xinkey, int *yinkey);
121 static void plot3d_impulses(struct surface_points * plot);
122 static void plot3d_lines(struct surface_points * plot);
123 static void plot3d_points(struct surface_points * plot);
124 static void plot3d_polygons(struct surface_points * plot);
125 static void plot3d_zerrorfill(struct surface_points * plot);
126 static void plot3d_boxes(struct surface_points * plot);
127 static void plot3d_vectors(struct surface_points * plot);
128 static void plot3d_lines_pm3d(struct surface_points * plot);
129 static void get_surface_cbminmax(struct surface_points *plot, double *cbmin, double *cbmax);
130 static void cntr3d_impulses(struct gnuplot_contours * cntr, struct lp_style_type * lp);
131 static void cntr3d_lines(struct gnuplot_contours * cntr, struct lp_style_type * lp);
132 static void cntr3d_points(struct gnuplot_contours * cntr, struct lp_style_type * lp);
133 static void cntr3d_labels(struct gnuplot_contours * cntr, char * leveltext,
134 				   struct text_label * label);
135 static void check_corner_height(struct coordinate * point,
136 					 double height[2][2], double depth[2][2]);
137 static void setup_3d_box_corners(void);
138 static void draw_3d_graphbox(struct surface_points * plot,
139 				      int plot_count,
140 				      WHICHGRID whichgrid, int current_layer);
141 
142 static void xtick_callback(struct axis *, double place, char *text, int ticlevel,
143 			   struct lp_style_type grid, struct ticmark *userlabels);
144 static void ytick_callback(struct axis *, double place, char *text, int ticlevel,
145 			   struct lp_style_type grid, struct ticmark *userlabels);
146 static void ztick_callback(struct axis *, double place, char *text, int ticlevel,
147 			   struct lp_style_type grid, struct ticmark *userlabels);
148 
149 static int find_maxl_cntr(struct gnuplot_contours * contours, int *count);
150 static int find_maxl_keys3d(struct surface_points *plots, int count, int *kcnt);
151 static void boundary3d(struct surface_points * plots, int count);
152 
153 /* put entries in the key */
154 static void key_sample_line(int xl, int yl);
155 static void key_sample_point(struct surface_points *this_plot, int xl, int yl, int pointtype);
156 static void key_sample_line_pm3d(struct surface_points *plot, int xl, int yl);
157 static void key_sample_point_pm3d(struct surface_points *plot, int xl, int yl, int pointtype);
158 static void key_sample_fill(int xl, int yl, struct surface_points *this_plot);
159 static TBOOLEAN can_pm3d = FALSE;
160 static void key_text(int xl, int yl, char *text);
161 static void check3d_for_variable_color(struct surface_points *plot, struct coordinate *point);
162 
163 static TBOOLEAN get_arrow3d(struct arrow_def*, double*, double*, double*, double*);
164 static void place_arrows3d(int);
165 static void place_labels3d(struct text_label * listhead, int layer);
166 static int map3d_getposition(struct position* pos, const char* what, double* xpos, double* ypos, double* zpos);
167 
168 static void flip_projection_axis(struct axis *axis);
169 static void splot_map_activate(void);
170 static void splot_map_deactivate(void);
171 
172 # define f_max(a,b) GPMAX((a),(b))
173 # define f_min(a,b) GPMIN((a),(b))
174 # define i_inrange(z,a,b) inrange((z),(a),(b))
175 
176 #define apx_eq(x,y) (fabs(x-y) < 0.001)
177 #define ABS(x) ((x) >= 0 ? (x) : -(x))
178 #define SQR(x) ((x) * (x))
179 
180 /* Define the boundary of the plot
181  * These are computed at each call to do_plot, and are constant over
182  * the period of one do_plot. They actually only change when the term
183  * type changes and when the 'set size' factors change.
184  */
185 
186 int xmiddle, ymiddle, xscaler, yscaler;
187 double xyscaler;
188 double radius_scaler;
189 static int ptitl_cnt;
190 static int max_ptitl_len;
191 static int titlelin;
192 static int key_sample_width, key_rows, key_cols, key_col_wth, yl_ref;
193 static double ktitle_lines = 0;
194 
195 
196 /* Boundary and scale factors, in user coordinates */
197 
198 /* These positions assume a single linear scale encompassing the
199  * zrange plus extra space below for the baseplane.  This was messy but
200  * correct before the introduction of nonlinear axes. Now - not so much.
201  *
202  * ceiling_z is the highest z in use
203  * floor_z   is the lowest z in use
204  * base_z is the z of the base
205  * min3d_z is the lowest z of the graph area
206  * max3d_z is the highest z of the graph area
207  *
208  * ceiling_z is either max3d_z or base_z, and similarly for floor_z
209  * There should be no part of graph drawn outside
210  * min3d_z:max3d_z  - apart from arrows, perhaps
211  */
212 double floor_z;
213 double ceiling_z, base_z;	/* made exportable for PM3D */
214 
215 /* To handle a non-linear z axis we need to calculate these values on
216  * the other end of the linked linear:nonlinear axis pair.
217  */
218 double floor_z1;	/* Used also by map_z3d() */
219 static double ceiling_z1, base_z1;
220 
221 transform_matrix trans_mat;
222 
223 /* x and y input range endpoints where the three axes are to be
224  * displayed (left, front-left, and front-right edges of the cube) */
225 static double xaxis_y, yaxis_x, zaxis_x, zaxis_y;
226 
227 /* ... and the same for the back, right, and front corners */
228 static double back_x, back_y;
229 static double right_x, right_y;
230 static double front_x, front_y;
231 
232 /* The global flags splot_map, xz_projection, and yz_projection are specific views.
233  * These flag the more general case of looking down the x or y axis
234  */
235 static TBOOLEAN xz_plane, yz_plane;
236 
237 #ifdef USE_MOUSE
238 int axis3d_o_x, axis3d_o_y, axis3d_x_dx, axis3d_x_dy, axis3d_y_dx, axis3d_y_dy;
239 #endif
240 
241 /* the penalty for convenience of using tic_gen to make callbacks
242  * to tick routines is that we cannot pass parameters very easily.
243  * We communicate with the tick_callbacks using static variables
244  */
245 
246 /* unit vector (terminal coords) */
247 static double tic_unitx, tic_unity, tic_unitz;
248 
249 /* calculate the number and max-width of the keys for an splot.
250  * Note that a blank line is issued after each set of contours
251  */
252 static int
find_maxl_keys3d(struct surface_points * plots,int count,int * kcnt)253 find_maxl_keys3d(struct surface_points *plots, int count, int *kcnt)
254 {
255     int mlen, len, surf, cnt;
256     struct surface_points *this_plot;
257 
258     mlen = cnt = 0;
259     this_plot = plots;
260     for (surf = 0; surf < count; this_plot = this_plot->next_sp, surf++) {
261 
262 	/* we draw a main entry if there is one, and we are
263 	 * drawing either surface, or unlabeled contours
264 	 */
265 	if (this_plot->title && *this_plot->title
266 	&& !this_plot->title_is_suppressed && !this_plot->title_position) {
267 	    ++cnt;
268 	    len = estimate_strlen(this_plot->title);
269 	    if (len > mlen)
270 		mlen = len;
271 	}
272 	if (draw_contour && !clabel_onecolor && this_plot->contours != NULL
273 	&&  this_plot->plot_style != LABELPOINTS) {
274 	    len = find_maxl_cntr(this_plot->contours, &cnt);
275 	    if (len > mlen)
276 		mlen = len;
277 	}
278     }
279 
280     if (kcnt != NULL)
281 	*kcnt = cnt;
282     return (mlen);
283 }
284 
285 static int
find_maxl_cntr(struct gnuplot_contours * contours,int * count)286 find_maxl_cntr(struct gnuplot_contours *contours, int *count)
287 {
288     int cnt;
289     int mlen, len;
290     struct gnuplot_contours *cntrs = contours;
291 
292     mlen = cnt = 0;
293     while (cntrs) {
294 	if (cntrs->isNewLevel) {
295 	    len = estimate_strlen(cntrs->label)
296 		- strspn(cntrs->label," ");
297 	    if (len)
298 		cnt++;
299 	    if (len > mlen)
300 		mlen = len;
301 	}
302 	cntrs = cntrs->next;
303     }
304     *count += cnt;
305     return (mlen);
306 }
307 
308 
309 /* borders of plotting area */
310 /* computed once on every call to do_plot */
311 static void
boundary3d(struct surface_points * plots,int count)312 boundary3d(struct surface_points *plots, int count)
313 {
314     legend_key *key = &keyT;
315     struct termentry *t = term;
316     int i;
317 
318     titlelin = 0;
319 
320     if (key->swidth >= 0)
321 	key_sample_width = key->swidth * t->h_char + t->h_tic;
322     else
323 	key_sample_width = 0;
324     key_entry_height = t->v_tic * 1.25 * key->vert_factor;
325     if (key_entry_height < t->v_char) {
326 	/* is this reasonable ? */
327 	key_entry_height = t->v_char * key->vert_factor;
328     }
329 
330     /* Approximate width of titles is used to determine number of rows, cols
331      * The actual widths will be recalculated later
332      */
333     max_ptitl_len = find_maxl_keys3d(plots, count, &ptitl_cnt);
334     key_title_width = label_width(key->title.text, &i) * t->h_char;
335     ktitle_lines = i;
336     key_col_wth = (max_ptitl_len + 4) * t->h_char + key_sample_width;
337 
338     if (lmargin.scalex == screen)
339 	plot_bounds.xleft = lmargin.x * (double)t->xmax + 0.5;
340     else if (lmargin.x >= 0)
341 	plot_bounds.xleft = lmargin.x * (double)t->h_char + 0.5;
342     else
343 	plot_bounds.xleft = t->h_char * 2 + t->h_tic;
344 
345     if (rmargin.scalex == screen)
346 	plot_bounds.xright = rmargin.x * (double)t->xmax + 0.5;
347     else /* No tic label on the right side, so ignore rmargin */
348 	plot_bounds.xright = xsize * t->xmax - t->h_char * 2 - t->h_tic;
349 
350     key_rows = ptitl_cnt;
351     key_cols = 1;
352     if (key_rows > key->maxrows && key->maxrows > 0) {
353 	key_rows = key->maxrows;
354 	key_cols = (ptitl_cnt - 1)/key_rows + 1;
355     }
356 
357     if (key->visible)
358     if ((key->region == GPKEY_AUTO_EXTERIOR_MARGIN || key->region == GPKEY_AUTO_EXTERIOR_LRTBC)
359 	&& key->margin == GPKEY_BMARGIN) {
360 	if (ptitl_cnt > 0) {
361 	    /* calculate max no cols, limited by label-length */
362 	    key_cols = (int) (plot_bounds.xright - plot_bounds.xleft)
363 		     / ((max_ptitl_len + 4) * t->h_char + key_sample_width);
364 	    if (key_cols == 0)
365 		key_cols = 1;
366 	    key_rows = (int) ((ptitl_cnt - 1)/ key_cols) + 1;
367 	    /* Limit the number of rows if requested by user */
368 	    if (key_rows > key->maxrows && key->maxrows > 0)
369 		key_rows = key->maxrows;
370 	    /* now calculate actual no cols depending on no rows */
371 	    key_cols = (int) ((ptitl_cnt - 1)/ key_rows) + 1;
372 	    key_col_wth = (int) (plot_bounds.xright - plot_bounds.xleft) / key_cols;
373 	} else {
374 	    key_rows = key_cols = key_col_wth = 0;
375 	}
376     }
377 
378     /* Sanity check top and bottom margins, in case the user got confused */
379     if (bmargin.scalex == screen && tmargin.scalex == screen)
380 	if (bmargin.x > tmargin.x) {
381 	    double tmp = bmargin.x;
382 	    bmargin.x = tmargin.x;
383 	    tmargin.x = tmp;
384 	}
385 
386     /* this should also consider the view and number of lines in
387      * xformat || yformat || xlabel || ylabel */
388 
389     if (bmargin.scalex == screen)
390 	plot_bounds.ybot = bmargin.x * (double)t->ymax + 0.5;
391     else if (splot_map && bmargin.x >= 0)
392 	plot_bounds.ybot = (double)t->v_char * bmargin.x;
393     else
394 	plot_bounds.ybot = t->v_char * 2.5 + 1;
395 
396     if (key->visible)
397     if (key_rows && (key->region == GPKEY_AUTO_EXTERIOR_MARGIN || key->region == GPKEY_AUTO_EXTERIOR_LRTBC)
398 	&& key->margin == GPKEY_BMARGIN)
399 	plot_bounds.ybot += key_rows * key_entry_height + ktitle_lines * t->v_char;
400 
401     if (title.text) {
402 	titlelin++;
403 	for (i = 0; i < strlen(title.text); i++) {
404 	    if (title.text[i] == '\\')
405 		titlelin++;
406 	}
407     }
408 
409     if (tmargin.scalex == screen)
410 	plot_bounds.ytop = tmargin.x * (double)t->ymax + 0.5;
411     else /* FIXME: Why no provision for tmargin in terms of character height? */
412 	plot_bounds.ytop = ysize * t->ymax - t->v_char * (titlelin + 1.5) - 1;
413 
414     if (key->visible)
415     if (key->region == GPKEY_AUTO_INTERIOR_LRTBC
416 	|| ((key->region == GPKEY_AUTO_EXTERIOR_LRTBC || key->region == GPKEY_AUTO_EXTERIOR_MARGIN)
417 	    && key->margin == GPKEY_RMARGIN)) {
418 	/* calculate max no rows, limited by plot_bounds.ytop-plot_bounds.ybot */
419 	i = (int) (plot_bounds.ytop - plot_bounds.ybot) / t->v_char - 1 - ktitle_lines;
420 	if (i > key->maxrows && key->maxrows > 0)
421 	    i = key->maxrows;
422 	if (i <= 0)
423 	    i = 1;
424 	if (ptitl_cnt > i) {
425 	    key_cols = (int) ((ptitl_cnt - 1)/ i) + 1;
426 	    /* now calculate actual no rows depending on no cols */
427 	    key_rows = (int) ((ptitl_cnt - 1) / key_cols) + 1;
428 	}
429     }
430     if (key->visible)
431     if ((key->region == GPKEY_AUTO_EXTERIOR_LRTBC || key->region == GPKEY_AUTO_EXTERIOR_MARGIN)
432 	&& key->margin == GPKEY_RMARGIN) {
433 	int key_width = key_col_wth * key_cols - 2 * t->h_char;
434 	if (rmargin.scalex != screen)
435 	    plot_bounds.xright -= key_width;
436     }
437 
438     if (key->visible)
439     if ((key->region == GPKEY_AUTO_EXTERIOR_LRTBC || key->region == GPKEY_AUTO_EXTERIOR_MARGIN)
440 	&& key->margin == GPKEY_LMARGIN) {
441 	int key_width = key_col_wth * key_cols - 2 * t->h_char;
442 	if (lmargin.scalex != screen)
443 	    plot_bounds.xleft += key_width;
444     }
445 
446     if (!splot_map && aspect_ratio_3D > 0) {
447 	int height = (plot_bounds.ytop - plot_bounds.ybot);
448 	int width  = (plot_bounds.xright - plot_bounds.xleft);
449 	if (height > width) {
450 	    plot_bounds.ybot += (height-width)/2;
451 	    plot_bounds.ytop -= (height-width)/2;
452 	} else {
453 	    plot_bounds.xleft += (width-height)/2;
454 	    plot_bounds.xright -= (width-height)/2;
455 	}
456     }
457 
458     if (lmargin.scalex != screen)
459 	plot_bounds.xleft += t->xmax * xoffset;
460     if (rmargin.scalex != screen)
461 	plot_bounds.xright += t->xmax * xoffset;
462     if (tmargin.scalex != screen)
463 	plot_bounds.ytop += t->ymax * yoffset;
464     if (bmargin.scalex != screen)
465 	plot_bounds.ybot += t->ymax * yoffset;
466     xmiddle = (plot_bounds.xright + plot_bounds.xleft) / 2;
467     ymiddle = (plot_bounds.ytop + plot_bounds.ybot) / 2;
468 
469 
470     /* HBB: Magic number alert! */
471     xscaler = ((plot_bounds.xright - plot_bounds.xleft) * 4L) / 7L;
472     yscaler = ((plot_bounds.ytop - plot_bounds.ybot) * 4L) / 7L;
473 
474     /* Allow explicit control via set {}margin screen */
475     if (tmargin.scalex == screen || bmargin.scalex == screen)
476 	yscaler = (plot_bounds.ytop - plot_bounds.ybot) / surface_scale;
477     if (rmargin.scalex == screen || lmargin.scalex == screen)
478 	xscaler = (plot_bounds.xright - plot_bounds.xleft) / surface_scale;
479 
480     /* prevent infinite loop or divide-by-zero if scaling is bad */
481     if (yscaler == 0) yscaler = 1;
482     if (xscaler == 0) xscaler = 1;
483 
484     /* 'set size {square|ratio}' for splots */
485     if (splot_map && aspect_ratio != 0.0) {
486 	double current_aspect_ratio;
487 
488 	if (aspect_ratio < 0 && (X_AXIS.max - X_AXIS.min) != 0.0) {
489 	    current_aspect_ratio = - aspect_ratio
490 		* fabs((Y_AXIS.max - Y_AXIS.min) /
491 		       (X_AXIS.max - X_AXIS.min));
492 	} else
493 	    current_aspect_ratio = aspect_ratio;
494 
495 	/*{{{  set aspect ratio if valid and sensible */
496 	if (current_aspect_ratio >= 0.01 && current_aspect_ratio <= 100.0) {
497 	    double current = (double)yscaler / xscaler ;
498 	    double required = current_aspect_ratio * t->v_tic / t->h_tic;
499 
500 	    if (current > required)
501 		/* too tall */
502 		yscaler = xscaler * required;
503 	    else
504 		/* too wide */
505 		xscaler = yscaler / required;
506 	}
507     }
508 
509     /* For anything that really wants to be the same on x and y */
510     xyscaler = sqrt(xscaler*yscaler);
511 
512     /* This one is used to scale circles in 3D plots */
513     radius_scaler = xscaler * surface_scale / (X_AXIS.max - X_AXIS.min);
514 
515     /* Set default clipping */
516     if (splot_map)
517 	clip_area = &plot_bounds;
518     else if (term->flags & TERM_CAN_CLIP)
519 	clip_area = NULL;
520     else
521 	clip_area = &canvas;
522 }
523 
524 static TBOOLEAN
get_arrow3d(struct arrow_def * arrow,double * dsx,double * dsy,double * dex,double * dey)525 get_arrow3d(
526     struct arrow_def* arrow,
527     double* dsx, double* dsy,
528     double* dex, double* dey)
529 {
530     map3d_position_double(&(arrow->start), dsx, dsy, "arrow");
531 
532     if (arrow->type == arrow_end_relative) {
533 	map3d_position_r_double(&(arrow->end), dex, dey, "arrow");
534 	*dex += *dsx;
535 	*dey += *dsy;
536     } else if (arrow->type == arrow_end_oriented) {
537 	double aspect = (double)term->v_tic / (double)term->h_tic;
538 	double radius;
539 	double junkw, junkh;
540 
541 #ifdef _WIN32
542 	if (strcmp(term->name, "windows") == 0)
543 	    aspect = 1.;
544 #endif
545 	if (arrow->end.scalex != screen && arrow->end.scalex != character && !splot_map)
546 	    return FALSE;
547 	map3d_position_r_double(&arrow->end, &junkw, &junkh, "arrow");
548 	radius = junkw;
549 	*dex = *dsx + cos(DEG2RAD * arrow->angle) * radius;
550 	*dey = *dsy + sin(DEG2RAD * arrow->angle) * radius * aspect;
551     } else {
552 	map3d_position_double(&(arrow->end), dex, dey, "arrow");
553     }
554 
555     return TRUE;
556 }
557 
558 static void
place_labels3d(struct text_label * listhead,int layer)559 place_labels3d(struct text_label *listhead, int layer)
560 {
561     struct text_label *this_label;
562     int x, y;
563 
564     term->pointsize(pointsize);
565 
566     for (this_label = listhead;
567 	 this_label != NULL;
568 	 this_label = this_label->next) {
569 
570 	if (this_label->layer != layer)
571 	    continue;
572 
573 	if (layer == LAYER_PLOTLABELS) {
574 	    double xx, yy;
575 	    map3d_xy_double(this_label->place.x, this_label->place.y,
576 		     this_label->place.z, &xx, &yy);
577 	    x = xx;
578 	    y = yy;
579 	    /* Only clip in 2D */
580 	    if (splot_map && clip_point(x, y))
581 		continue;
582 	} else
583 	    map3d_position(&this_label->place, &x, &y, "label");
584 
585 	write_label(x, y, this_label);
586     }
587 }
588 
589 static void
place_arrows3d(int layer)590 place_arrows3d(int layer)
591 {
592     struct arrow_def *this_arrow;
593     BoundingBox *clip_save = clip_area;
594 
595     /* Allow arrows to run off the plot, so long as they are still on the canvas */
596     if (term->flags & TERM_CAN_CLIP)
597       clip_area = NULL;
598     else
599       clip_area = &canvas;
600 
601     for (this_arrow = first_arrow; this_arrow != NULL;
602 	 this_arrow = this_arrow->next) {
603 	double dsx, dsy, dex, dey;
604 
605 	if (this_arrow->arrow_properties.layer != layer)
606 	    continue;
607 	if (get_arrow3d(this_arrow, &dsx, &dsy, &dex, &dey)) {
608 	    term_apply_lp_properties(&(this_arrow->arrow_properties.lp_properties));
609 	    apply_head_properties(&(this_arrow->arrow_properties));
610 	    draw_clip_arrow(dsx, dsy, dex, dey, this_arrow->arrow_properties.head);
611 	} else {
612 	    FPRINTF((stderr,"place_arrows3d: skipping out-of-bounds arrow\n"));
613 	}
614     }
615     clip_area = clip_save;
616 }
617 
618 /* we precalculate features of the key, to save lots of nested
619  * ifs in code - x,y = user supplied or computed position of key
620  * taken to be inner edge of a line sample
621  */
622 static int key_sample_left;	/* offset from x for left of line sample */
623 static int key_sample_right;	/* offset from x for right of line sample */
624 static int key_point_offset;	/* offset from x for point sample */
625 static int key_text_left;	/* offset from x for left-justified text */
626 static int key_text_right;	/* offset from x for right-justified text */
627 static int key_size_left;	/* distance from x to left edge of box */
628 static int key_size_right;	/* distance from x to right edge of box */
629 
630 void
do_3dplot(struct surface_points * plots,int pcount,REPLOT_TYPE replot_mode)631 do_3dplot(
632     struct surface_points *plots,
633     int pcount,			/* count of plots in linked list */
634     REPLOT_TYPE replot_mode) 	/* replot/refresh/axes-only/quick-refresh */
635 {
636     struct termentry *t = term;
637     int surface;
638     struct surface_points *this_plot = NULL;
639     int xl = 0, yl = 0;
640     int xl_save, yl_save;
641     int xl_prev = 0, yl_prev = 0;
642     int title_x = 0, title_y = 0;
643     transform_matrix mat;
644     int key_count;
645     TBOOLEAN key_pass = FALSE;
646     legend_key *key = &keyT;
647     TBOOLEAN pm3d_order_depth = FALSE;
648     AXIS *primary_z;
649 
650     /* Initiate transformation matrix using the global view variables. */
651     if (splot_map) {
652 	splot_map_activate();
653     } else if (xz_projection) {
654 	surface_rot_x = 270.;
655 	surface_rot_z = 0.;
656 	surface_scale = 1.425 * mapview_scale;
657     } else if (yz_projection) {
658 	surface_rot_x = 90.;
659 	surface_rot_z = 90.;
660 	surface_scale = 1.425 * mapview_scale;
661 	flip_projection_axis(&axis_array[FIRST_Z_AXIS]);
662     }
663     mat_rot_z(surface_rot_z, trans_mat);
664     mat_rot_x(surface_rot_x, mat);
665     mat_mult(trans_mat, trans_mat, mat);
666     mat_scale(surface_scale / 2.0, surface_scale / 2.0, surface_scale / 2.0, mat);
667     mat_mult(trans_mat, trans_mat, mat);
668 
669     /* The azimuth is applied as a rotation about the line of sight */
670     if (azimuth !=0 && !splot_map) {
671 	mat_rot_z(azimuth, mat);
672 	mat_mult(trans_mat, trans_mat, mat);
673     }
674 
675     if (polar)
676 	int_error(NO_CARET,"Cannot splot in polar coordinate system.");
677 
678     /* In the case of a nonlinear z axis this points to the linear version */
679     /* that shadows it.  Otherwise it just points to FIRST_Z_AXIS.         */
680     primary_z = (nonlinear(&Z_AXIS)) ? Z_AXIS.linked_to_primary : &Z_AXIS;
681 
682     /* absolute or relative placement of xyplane along z */
683     if (nonlinear(&Z_AXIS)) {
684 	if (xyplane.absolute) {
685 	    if (primary_z->log && xyplane.z <= 0) {
686 		base_z1 = eval_link_function(primary_z, Z_AXIS.min);
687 	    } else {
688 		base_z1 = eval_link_function(primary_z, xyplane.z);
689 	    }
690 	} else {
691 	    base_z1 = primary_z->min - (primary_z->max - primary_z->min) * xyplane.z;
692 	}
693 	base_z = eval_link_function(&Z_AXIS, base_z1);
694     } else {
695 	if (xyplane.absolute)
696 	    base_z1 = xyplane.z;
697 	else
698 	    base_z1 = primary_z->min - (primary_z->max - primary_z->min) * xyplane.z;
699 	base_z = base_z1;
700     }
701 
702     /* If we are to draw some portion of the xyplane make sure zmin is updated properly. */
703     if (X_AXIS.ticmode || Y_AXIS.ticmode || draw_border & 0x00F) {
704 	if (primary_z->min > primary_z->max) {
705 	    floor_z1 = GPMAX(primary_z->min, base_z1);
706 	    ceiling_z1 = GPMIN(primary_z->max, base_z1);
707 	} else {
708 	    floor_z1 = GPMIN(primary_z->min, base_z1);
709 	    ceiling_z1 = GPMAX(primary_z->max, base_z1);
710 	}
711     } else {
712 	floor_z1 = primary_z->min;
713 	ceiling_z1 = primary_z->max;
714     }
715 
716     if (nonlinear(&Z_AXIS)) {
717 	floor_z = eval_link_function(&Z_AXIS, floor_z1);
718 	ceiling_z = eval_link_function(&Z_AXIS, ceiling_z1);
719     } else {
720 	floor_z = floor_z1;
721 	ceiling_z = ceiling_z1;
722     }
723 
724     if (X_AXIS.min == X_AXIS.max)
725 	int_error(NO_CARET,"x_min3d should not equal x_max3d!");
726     if (Y_AXIS.min == Y_AXIS.max)
727 	int_error(NO_CARET,"y_min3d should not equal y_max3d!");
728     if (Z_AXIS.min == Z_AXIS.max)
729 	int_error(NO_CARET,"z_min3d should not equal z_max3d!");
730 
731     /* Special case projections of the xz or yz plane */
732     /* Place x or y axis to the left of the plot */
733     xz_plane = yz_plane = FALSE;
734     if (!splot_map && (surface_rot_x == 90 || surface_rot_x == 270)) {
735 	if (surface_rot_z ==  0 || surface_rot_z == 180) {
736 	    xz_plane = TRUE;
737 	    base_z = floor_z;
738 	}
739 	if (surface_rot_z == 90 || surface_rot_z == 270) {
740 	    yz_plane = TRUE;
741 	    if (surface_rot_x == 270 || yz_projection)
742 		base_z = ceiling_z;
743 	}
744     }
745 
746     term_start_plot();
747     (term->layer)(TERM_LAYER_3DPLOT);
748 
749     screen_ok = FALSE;
750 
751     /* Sync point for epslatex text positioning */
752     (term->layer)(TERM_LAYER_BACKTEXT);
753 
754     /* now compute boundary for plot */
755     boundary3d(plots, pcount);
756 
757     axis_set_scale_and_range(&axis_array[FIRST_X_AXIS], plot_bounds.xleft, plot_bounds.xright);
758     axis_set_scale_and_range(&axis_array[FIRST_Y_AXIS], plot_bounds.ybot, plot_bounds.ytop);
759     axis_set_scale_and_range(&axis_array[FIRST_Z_AXIS], floor_z, ceiling_z);
760 
761     /* SCALE FACTORS */
762     zscale3d = 2.0 / (ceiling_z - floor_z) * surface_zscale;
763     yscale3d = 2.0 / (Y_AXIS.max - Y_AXIS.min);
764     xscale3d = 2.0 / (X_AXIS.max - X_AXIS.min);
765     if (nonlinear(&X_AXIS))
766 	xscale3d = 2.0 / (X_AXIS.linked_to_primary->max - X_AXIS.linked_to_primary->min);
767     if (nonlinear(&Y_AXIS))
768 	yscale3d = 2.0 / (Y_AXIS.linked_to_primary->max - Y_AXIS.linked_to_primary->min);
769     if (nonlinear(&Z_AXIS))
770 	zscale3d = 2.0 / (ceiling_z1 - floor_z1) * surface_zscale;
771 
772     /* Allow 'set view equal xy' to adjust rendered length of the X and/or Y axes.  */
773     /* NB: only works correctly for terminals whose coordinate system is isotropic. */
774     xcenter3d = ycenter3d = zcenter3d = 0.0;
775     if (aspect_ratio_3D >= 2) {
776 	if (yscale3d > xscale3d) {
777 	    ycenter3d = 1.0 - xscale3d/yscale3d;
778 	    yscale3d = xscale3d;
779 	} else if (xscale3d > yscale3d) {
780 	    xcenter3d = 1.0 - yscale3d/xscale3d;
781 	    xscale3d = yscale3d;
782 	}
783 	if (aspect_ratio_3D >= 3)
784 	    zscale3d = xscale3d;
785     }
786 
787     /* FIXME: I do not understand why this is correct */
788     if (nonlinear(&Z_AXIS))
789 	zcenter3d = 0.0;
790     /* Without this the rotation center would be located at */
791     /* the bottom of the plot. This places it in the middle.*/
792     else
793 	zcenter3d =  -(ceiling_z - floor_z) / 2.0 * zscale3d + 1;
794 
795     /* Needed for mousing by outboard terminal drivers */
796     if (splot_map) {
797 	AXIS *X = &axis_array[FIRST_X_AXIS];
798 	AXIS *Y = &axis_array[FIRST_Y_AXIS];
799 	int xl, xr, yb, yt;
800 	map3d_xy(X->min, Y->min, 0.0, &xl, &yb);
801 	map3d_xy(X->max, Y->max, 0.0, &xr, &yt);
802 	axis_set_scale_and_range(&axis_array[FIRST_X_AXIS], xl, xr);
803 	axis_set_scale_and_range(&axis_array[FIRST_Y_AXIS], yb, yt);
804     }
805 
806     /* Initialize palette */
807     if (replot_mode != AXIS_ONLY_ROTATE) {
808 	can_pm3d = is_plot_with_palette() && !make_palette()
809 		   && ((term->flags & TERM_NULL_SET_COLOR) == 0);
810     }
811 
812     /* Give a chance for rectangles to be behind everything else */
813     place_objects( first_object, LAYER_BEHIND, 3);
814     if (replot_mode != AXIS_ONLY_ROTATE)
815 	place_pixmaps(LAYER_BEHIND, 3);
816 
817     term_apply_lp_properties(&border_lp);	/* border linetype */
818 
819     /* must come before using draw_3d_graphbox() the first time */
820     setup_3d_box_corners();
821 
822     /* DRAW GRID AND BORDER */
823     /* Original behaviour: draw entire grid in back, if 'set grid back': */
824     /* HBB 20040331: but not if in hidden3d mode */
825     if (splot_map && border_layer != LAYER_FRONT)
826 	draw_3d_graphbox(plots, pcount, BORDERONLY, LAYER_BACK);
827 
828     else if (!hidden3d && (grid_layer == LAYER_BACK))
829 	draw_3d_graphbox(plots, pcount, ALLGRID, LAYER_BACK);
830 
831     else if (!hidden3d && (grid_layer == LAYER_BEHIND))
832 	/* Default layering mode.  Draw the back part now, but not if
833 	 * hidden3d is in use, because that relies on all isolated
834 	 * lines being output after all surfaces have been defined. */
835 	draw_3d_graphbox(plots, pcount, BACKGRID, LAYER_BACK);
836 
837     else if (hidden3d && border_layer == LAYER_BEHIND)
838 	draw_3d_graphbox(plots, pcount, ALLGRID, LAYER_BACK);
839 
840     /* Save state of plot_bounds before applying rotations, etc */
841     memcpy(&page_bounds, &plot_bounds, sizeof(page_bounds));
842 
843     /* Clipping in 'set view map' mode should be like 2D clipping */
844     /* FIXME:  Wasn't this already done in boundary3d?            */
845     if (splot_map) {
846 	int map_x1, map_y1, map_x2, map_y2;
847 	map3d_xy(X_AXIS.min, Y_AXIS.min, base_z, &map_x1, &map_y1);
848 	map3d_xy(X_AXIS.max, Y_AXIS.max, base_z, &map_x2, &map_y2);
849 	plot_bounds.xleft = map_x1;
850 	plot_bounds.xright = map_x2;
851 	plot_bounds.ybot = map_y2;
852 	plot_bounds.ytop = map_y1;
853     }
854 
855     /* Define the clipping area in 3D to lie between the left-most and
856      * right-most graph box edges.  This is introduced for the benefit of
857      * zooming in the canvas terminal.  It may or may not make any practical
858      * difference for other terminals.  If it causes problems, then we will need
859      * a separate BoundingBox structure to track the actual 3D graph box.
860      */
861     else if (azimuth == 0) {
862 	int xl, xb, xr, xf, yl, yb, yr, yf;
863 
864 	map3d_xy(zaxis_x, zaxis_y, base_z, &xl, &yl);
865 	map3d_xy(back_x , back_y , base_z, &xb, &yb);
866 	map3d_xy(right_x, right_y, base_z, &xr, &yr);
867 	map3d_xy(front_x, front_y, base_z, &xf, &yf);
868 	plot_bounds.xleft = GPMIN(xl, xb);	/* Always xl? */
869 	plot_bounds.xright = GPMAX(xb, xr);	/* Always xr? */
870     }
871 
872     /* PLACE TITLE */
873     if (title.text != 0) {
874 	int x, y;
875 	if (splot_map) { /* case 'set view map' */
876 	    int map_x1, map_y1, map_x2, map_y2;
877 	    int tics_len = 0;
878 	    if (X_AXIS.ticmode & TICS_MIRROR) {
879 		tics_len = (int)(X_AXIS.ticscale * (X_AXIS.tic_in ? -1 : 1) * (term->v_tic));
880 		if (tics_len < 0) tics_len = 0; /* take care only about upward tics */
881 	    }
882 	    map3d_xy(X_AXIS.min, Y_AXIS.min, base_z, &map_x1, &map_y1);
883 	    map3d_xy(X_AXIS.max, Y_AXIS.max, base_z, &map_x2, &map_y2);
884 	    /* Distance between the title base line and graph top line or the upper part of
885 	       tics is as given by character height: */
886 	    x = ((map_x1 + map_x2) / 2);
887 	    y = (map_y1 + tics_len + (titlelin + 0.5) * (t->v_char));
888 	} else { /* usual 3d set view ... */
889 	    x = (plot_bounds.xleft + plot_bounds.xright) / 2;
890 	    y = (plot_bounds.ytop + titlelin * (t->h_char));
891 	}
892 
893 	/* Save title position for later */
894 	title_x = x;
895 	title_y = y;
896     }
897 
898     /* PLACE TIMELABEL */
899     if (timelabel.text) {
900 	int x, y;
901 	x = t->v_char;
902 	y = timelabel_bottom
903 	  ? yoffset * Y_AXIS.max + t->v_char
904 	  : plot_bounds.ytop - t->v_char;
905 	do_timelabel(x,y);
906     }
907 
908     /* Add 'back' color box */
909     if ((replot_mode != AXIS_ONLY_ROTATE)
910     && can_pm3d && is_plot_with_colorbox() && color_box.layer == LAYER_BACK)
911 	draw_color_smooth_box(MODE_SPLOT);
912 
913     /* Grid walls */
914     place_objects(grid_wall, LAYER_BACK, 3);
915 
916     /* pixmaps before objects so that a rectangle can be used as a border */
917     place_pixmaps(LAYER_BACK, 3);
918 
919     /* Add 'back' rectangles */
920     place_objects(first_object, LAYER_BACK, 3);
921 
922     /* PLACE LABELS */
923     place_labels3d(first_label, LAYER_BACK);
924 
925     /* PLACE ARROWS */
926     place_arrows3d(LAYER_BACK);
927 
928     /* Sync point for epslatex text positioning */
929     (term->layer)(TERM_LAYER_FRONTTEXT);
930 
931     if (hidden3d && draw_surface && (replot_mode != AXIS_ONLY_ROTATE)) {
932 	init_hidden_line_removal();
933 	reset_hidden_line_removal();
934     }
935 
936     /* WORK OUT KEY POSITION AND SIZE */
937     do_3dkey_layout(key, &xl, &yl);
938 
939     /* "set key opaque" requires two passes, with the key drawn in the second pass */
940     xl_save = xl; yl_save = yl;
941     SECOND_KEY_PASS:
942 
943     /* This tells the canvas, qt, and svg terminals to restart the plot   */
944     /* count so that key titles are in sync with the plots they describe. */
945     (term->layer)(TERM_LAYER_RESET_PLOTNO);
946 
947     /* Key box */
948     if (key->visible) {
949 	(term->layer)(TERM_LAYER_KEYBOX);
950 
951 	/* In two-pass mode, we blank out the key area after the graph	*/
952 	/* is drawn and then redo the key in the blank area.		*/
953 	if (key_pass && t->fillbox && !(t->flags & TERM_NULL_SET_COLOR)) {
954 	    t_colorspec background_fill = BACKGROUND_COLORSPEC;
955 	    (*t->set_color)(&background_fill);
956 	    (*t->fillbox)(FS_OPAQUE, key->bounds.xleft, key->bounds.ybot,
957 		    key->bounds.xright - key->bounds.xleft,
958 		    key->bounds.ytop - key->bounds.ybot);
959 	}
960 
961 	if (key->box.l_type > LT_NODRAW
962 	&&  key->bounds.ytop != key->bounds.ybot) {
963 	    term_apply_lp_properties(&key->box);
964 	    newpath();
965 	    clip_move(key->bounds.xleft, key->bounds.ybot);
966 	    clip_vector(key->bounds.xleft, key->bounds.ytop);
967 	    clip_vector(key->bounds.xright, key->bounds.ytop);
968 	    clip_vector(key->bounds.xright, key->bounds.ybot);
969 	    clip_vector(key->bounds.xleft, key->bounds.ybot);
970 	    closepath();
971 
972 	    /* draw a horizontal line between key title and first entry  JFi */
973 	    clip_move(key->bounds.xleft,
974 			key->bounds.ytop - key_title_height - key_title_extra);
975 	    clip_vector(key->bounds.xright,
976 			key->bounds.ytop - key_title_height - key_title_extra);
977 	}
978 
979 	if (key->title.text) {
980 	    int center = (key->bounds.xright + key->bounds.xleft) / 2;
981 	    int titley = key->bounds.ytop - (key_title_extra + t->v_char)/2;
982 	    write_label(center, titley, &key->title);
983 	    (*t->linetype)(LT_BLACK);
984 	}
985     }
986 
987 
988     /* DRAW SURFACES AND CONTOURS */
989 
990     if (!key_pass)
991     if (hidden3d && (hidden3d_layer == LAYER_BACK) && draw_surface
992     && (replot_mode != AXIS_ONLY_ROTATE)) {
993 	(term->layer)(TERM_LAYER_BEFORE_PLOT);
994 	plot3d_hidden(plots, pcount);
995 	(term->layer)(TERM_LAYER_AFTER_PLOT);
996     }
997 
998     /* Set up bookkeeping for the individual key titles */
999 #define NEXT_KEY_LINE()					\
1000     do {                                                \
1001     if ( ++key_count >= key_rows ) {			\
1002 	yl = yl_ref; xl += key_col_wth; key_count = 0;	\
1003     } else						\
1004 	yl -= key_entry_height;                         \
1005     } while (0)
1006     key_count = 0;
1007     yl_ref = yl -= key_entry_height / 2;	/* centralise the keys */
1008 
1009 
1010     /* PM January 2005: The mistake of missing blank lines in the data file is
1011      * so frequently made (see questions at comp.graphics.apps.gnuplot) that it
1012      * really deserves this warning. But don't show it too often --- only if it
1013      * is a single surface in the plot.
1014      */
1015     if (plots->plot_style != BOXES)
1016     if (pcount == 1 && plots->num_iso_read == 1 && can_pm3d &&
1017 	(plots->plot_style == PM3DSURFACE || PM3D_IMPLICIT == pm3d.implicit))
1018 	    fprintf(stderr, "  Warning: Single isoline (scan) is not enough for a pm3d plot.\n\t   Hint: Missing blank lines in the data file? See 'help pm3d' and FAQ.\n");
1019 
1020 
1021     pm3d_order_depth = (can_pm3d && !draw_contour && pm3d.direction == PM3D_DEPTH);
1022 
1023     /* TODO:
1024      *   During "refresh" from rotation it would be better to re-use previously
1025      *   built quadrangle list rather than clearing and rebuilding it.
1026      */
1027     if (pm3d_order_depth || track_pm3d_quadrangles) {
1028 	pm3d_depth_queue_clear();
1029 	place_objects(first_object, LAYER_DEPTHORDER, 3);
1030     }
1031 
1032     this_plot = plots;
1033     if (replot_mode != AXIS_ONLY_ROTATE)
1034 	for (surface = 0;
1035 	     surface < pcount;
1036 	     this_plot = this_plot->next_sp, surface++) {
1037 	    /* just an abbreviation */
1038 	    TBOOLEAN lkey, draw_this_surface;
1039 
1040 	    /* Skip over abortive data structures */
1041 	    if (this_plot->plot_type == NODATA)
1042 		continue;
1043 
1044 	    /* Sync point for start of new curve (used by svg, post, ...) */
1045 	    (term->layer)(TERM_LAYER_BEFORE_PLOT);
1046 
1047 	    if (!key_pass && this_plot->plot_type != KEYENTRY)
1048 	    if (can_pm3d && PM3D_IMPLICIT == pm3d.implicit)
1049 		pm3d_draw_one(this_plot);
1050 
1051 	    lkey = (key->visible && this_plot->title && this_plot->title[0]
1052 				 && !this_plot->title_is_suppressed);
1053 	    draw_this_surface = (draw_surface && !this_plot->opt_out_of_surface);
1054 	    if (this_plot->plot_type == KEYENTRY)
1055 		draw_this_surface = TRUE;
1056 
1057 	    /* User-specified key locations can use the 2D code */
1058 	    if (this_plot->title_position) {
1059 		xl_prev = xl;
1060 		yl_prev = yl;
1061 		if (this_plot->title_position->scalex != character) {
1062 		    map3d_position(this_plot->title_position, &xl, &yl, "key sample");
1063 		    xl -=  (key->just == GPKEY_LEFT) ? key_text_left : key_text_right;
1064 		} else {
1065 		    /* Option to label the end of the curve on the plot itself */
1066 		    attach_title_to_plot((struct curve_points *)this_plot, key);
1067 		}
1068 	    }
1069 
1070 	    if (lkey
1071 	    &&  (!this_plot->title_position || this_plot->title_position->scalex != character)) {
1072 		char *title = this_plot->title;
1073 
1074 		if (this_plot->title_is_automated && (term->flags & TERM_IS_LATEX))
1075 		    title = texify_title(title, this_plot->plot_type);
1076 
1077 		if (key->textcolor.type != TC_DEFAULT)
1078 		    /* Draw key text in same color as key title */
1079 		    apply_pm3dcolor(&key->textcolor);
1080 		else
1081 		    /* Draw key text in black */
1082 		    (*t->linetype)(LT_BLACK);
1083 		ignore_enhanced(this_plot->title_no_enhanced);
1084 		key_text(xl, yl, title);
1085 		ignore_enhanced(FALSE);
1086 	    }
1087 	    term_apply_lp_properties(&(this_plot->lp_properties));
1088 
1089 	    /* Voxel data is a special case.
1090 	     * what about hidden3d mode? pm3d?
1091 	     */
1092 	    if (!key_pass && this_plot->plot_type == VOXELDATA) {
1093 		switch (this_plot->plot_style) {
1094 		default:
1095 		    /* style should default to DOTS */
1096 		    this_plot->plot_style = DOTS;
1097 		case DOTS:
1098 		case POINTSTYLE:
1099 		    vplot_points(this_plot, this_plot->iso_level);
1100 		    break;
1101 		case ISOSURFACE:
1102 		    if (replot_mode == QUICK_REFRESH)
1103 			vplot_isosurface(this_plot, 4);
1104 		    else
1105 			vplot_isosurface(this_plot, 1);
1106 		    break;
1107 		}
1108 	    }
1109 
1110 	    /* First draw the graph plot itself */
1111 	    if (!key_pass && this_plot->plot_type != KEYENTRY && this_plot->plot_type != VOXELDATA)
1112 	    switch (this_plot->plot_style) {
1113 	    case FILLEDCURVES:	/* same, but maybe we could dummy up ZERRORFILL? */
1114 	    case IMPULSES:
1115 		if (!hidden3d)
1116 		    plot3d_impulses(this_plot);
1117 		break;
1118 	    case STEPS:	/* HBB: I think these should be here */
1119 	    case FILLSTEPS:
1120 	    case FSTEPS:
1121 	    case HISTEPS:
1122 	    case SURFACEGRID:
1123 	    case LINES:
1124 		if (draw_this_surface) {
1125 		    if (!hidden3d || this_plot->opt_out_of_hidden3d)
1126 			plot3d_lines_pm3d(this_plot);
1127 		}
1128 		break;
1129 	    case YERRORLINES:	/* ignored; treat like points */
1130 	    case XERRORLINES:	/* ignored; treat like points */
1131 	    case XYERRORLINES:	/* ignored; treat like points */
1132 	    case YERRORBARS:	/* ignored; treat like points */
1133 	    case XERRORBARS:	/* ignored; treat like points */
1134 	    case XYERRORBARS:	/* ignored; treat like points */
1135 	    case BOXXYERROR:	/* HBB: ignore these as well */
1136 	    case BOXERROR:
1137 	    case ARROWS:
1138 	    case CANDLESTICKS:	/* HBB: ditto */
1139 	    case BOXPLOT:
1140 	    case FINANCEBARS:
1141 	    case CIRCLES:
1142 	    case ELLIPSES:
1143 	    case POINTSTYLE:
1144 	    case DOTS:
1145 		if (draw_this_surface) {
1146 		    if (!hidden3d || this_plot->opt_out_of_hidden3d)
1147 			plot3d_points(this_plot);
1148 		}
1149 		break;
1150 
1151 	    case LINESPOINTS:
1152 		if (draw_this_surface) {
1153 		    if (!hidden3d || this_plot->opt_out_of_hidden3d) {
1154 			plot3d_lines_pm3d(this_plot);
1155 			plot3d_points(this_plot);
1156 		    }
1157 		}
1158 		break;
1159 
1160 	    case VECTOR:
1161 		if (!hidden3d || this_plot->opt_out_of_hidden3d)
1162 		    plot3d_vectors(this_plot);
1163 		break;
1164 
1165 	    case ZERRORFILL:
1166 		/* Always draw filled areas even if we _also_ do hidden3d processing */
1167 		if (term->filled_polygon)
1168 		    plot3d_zerrorfill(this_plot);
1169 		term_apply_lp_properties(&(this_plot->lp_properties));
1170 		plot3d_lines(this_plot);
1171 		break;
1172 
1173 	    case BOXES:
1174 		if (term->filled_polygon)
1175 		    plot3d_boxes(this_plot);
1176 		else
1177 		    plot3d_impulses(this_plot);
1178 		break;
1179 
1180 	    case PM3DSURFACE:
1181 		if (draw_this_surface) {
1182 		    if (can_pm3d && PM3D_IMPLICIT != pm3d.implicit) {
1183 			pm3d_draw_one(this_plot);
1184 			if (!pm3d_order_depth)
1185 			    pm3d_depth_queue_flush(); /* draw plot immediately */
1186 		    }
1187 		}
1188 		break;
1189 
1190 	    case POLYGONS:
1191 		if (term->filled_polygon)
1192 		    plot3d_polygons(this_plot);
1193 		else
1194 		    plot3d_lines(this_plot);
1195 		break;
1196 
1197 	    case LABELPOINTS:
1198 		if (draw_this_surface) {
1199 		    if (hidden3d && !(this_plot->opt_out_of_hidden3d))
1200 			break;
1201 		    if (draw_contour && !(this_plot->opt_out_of_contours))
1202 			break;
1203 		    place_labels3d(this_plot->labels->next, LAYER_PLOTLABELS);
1204 		}
1205 		break;
1206 
1207 	    case HISTOGRAMS: /* Cannot happen */
1208 		break;
1209 
1210 	    case IMAGE:
1211 		/* Plot image using projection of 3D plot coordinates to 2D viewing coordinates. */
1212 		this_plot->image_properties.type = IC_PALETTE;
1213 		process_image(this_plot, IMG_PLOT);
1214 		break;
1215 
1216 	    case RGBIMAGE:
1217 		/* Plot image using projection of 3D plot coordinates to 2D viewing coordinates. */
1218 		this_plot->image_properties.type = IC_RGB;
1219 		process_image(this_plot, IMG_PLOT);
1220 		break;
1221 
1222 	    case RGBA_IMAGE:
1223 		this_plot->image_properties.type = IC_RGBA;
1224 		process_image(this_plot, IMG_PLOT);
1225 		break;
1226 
1227 	    case PARALLELPLOT:
1228 	    case SPIDERPLOT:
1229 		int_error(NO_CARET, "plot style not supported in 3D");
1230 		break;
1231 
1232 	    case ISOSURFACE:
1233 	    case PLOT_STYLE_NONE:
1234 	    case TABLESTYLE:
1235 		/* cannot happen */
1236 		break;
1237 	    }			/* switch(plot-style) plot proper */
1238 
1239 	    /* Next draw the key sample */
1240 	    if (lkey
1241 	    &&  (!this_plot->title_position || this_plot->title_position->scalex != character))
1242 	    switch (this_plot->plot_style) {
1243 	    case FILLEDCURVES:
1244 	    case IMPULSES:
1245 		if (!(hidden3d && draw_this_surface))
1246 		    key_sample_line(xl, yl);
1247 		break;
1248 	    case STEPS:	/* HBB: I think these should be here */
1249 	    case FILLSTEPS:
1250 	    case FSTEPS:
1251 	    case HISTEPS:
1252 	    case SURFACEGRID:
1253 	    case LINES:
1254 		/* Normal case (surface) */
1255 		if (draw_this_surface)
1256 		    key_sample_line_pm3d(this_plot, xl, yl);
1257 		/* Contour plot with no surface, all contours use the same linetype */
1258 		else if (this_plot->contours != NULL && clabel_onecolor) {
1259 		    key_sample_line(xl, yl);
1260 		}
1261 		break;
1262 	    case YERRORLINES:	/* ignored; treat like points */
1263 	    case XERRORLINES:	/* ignored; treat like points */
1264 	    case XYERRORLINES:	/* ignored; treat like points */
1265 	    case YERRORBARS:	/* ignored; treat like points */
1266 	    case XERRORBARS:	/* ignored; treat like points */
1267 	    case XYERRORBARS:	/* ignored; treat like points */
1268 	    case BOXXYERROR:	/* HBB: ignore these as well */
1269 	    case BOXERROR:
1270 	    case CANDLESTICKS:	/* HBB: ditto */
1271 	    case BOXPLOT:
1272 	    case FINANCEBARS:
1273 	    case ELLIPSES:
1274 	    case POINTSTYLE:
1275 		if (this_plot->plot_type == VOXELDATA) {
1276 		    if (this_plot->lp_properties.pm3d_color.type == TC_Z)
1277 			set_color(0.5);
1278 		    key_sample_point(this_plot, xl, yl, this_plot->lp_properties.p_type);
1279 		} else
1280 
1281 		if (draw_this_surface)
1282 		    key_sample_point_pm3d(this_plot, xl, yl, this_plot->lp_properties.p_type);
1283 		break;
1284 
1285 	    case LABELPOINTS:
1286 		if ((this_plot->labels->lp_properties.flags & LP_SHOW_POINTS)) {
1287 		    term_apply_lp_properties(&this_plot->labels->lp_properties);
1288 		    key_sample_point(this_plot, xl, yl, this_plot->labels->lp_properties.p_type);
1289 		}
1290 		break;
1291 
1292 	    case LINESPOINTS:
1293 		if (draw_this_surface) {
1294 		    if (this_plot->lp_properties.l_type != LT_NODRAW)
1295 			key_sample_line_pm3d(this_plot, xl, yl);
1296 		    key_sample_point_pm3d(this_plot, xl, yl, this_plot->lp_properties.p_type);
1297 		}
1298 		break;
1299 
1300 	    case DOTS:
1301 		if (draw_this_surface)
1302 		    key_sample_point_pm3d(this_plot, xl, yl, -1);
1303 		break;
1304 
1305 	    case VECTOR:
1306 		key_sample_line_pm3d(this_plot, xl, yl);
1307 		break;
1308 
1309 	    case ZERRORFILL:
1310 		apply_pm3dcolor(&this_plot->fill_properties.border_color);
1311 		key_sample_fill(xl, yl, this_plot);
1312 		term_apply_lp_properties(&this_plot->lp_properties);
1313 		key_sample_line(xl, yl);
1314 		break;
1315 
1316 	    case BOXES:
1317 	    case CIRCLES:
1318 		apply_pm3dcolor(&this_plot->lp_properties.pm3d_color);
1319 		if (this_plot->iso_crvs)
1320 		    check3d_for_variable_color(this_plot, this_plot->iso_crvs->points);
1321 		key_sample_fill(xl, yl, this_plot);
1322 		break;
1323 
1324 	    case ISOSURFACE:
1325 		apply_pm3dcolor(&this_plot->fill_properties.border_color);
1326 		key_sample_fill(xl, yl, this_plot);
1327 		break;
1328 
1329 	    case PLOT_STYLE_NONE:
1330 		/* cannot happen */
1331 	    default:
1332 		break;
1333 
1334 	    }			/* switch(plot-style) key sample */
1335 
1336 	    /* If the title went somewhere other than the key,
1337 	     * restore the previous key position.
1338 	     * Else move down one line in the key.
1339 	     */
1340 	    if (this_plot->title_position) {
1341 		xl = xl_prev;
1342 		yl = yl_prev;
1343 	    } else if (lkey)
1344 		NEXT_KEY_LINE();
1345 
1346 	    /* Draw contours for previous surface */
1347 	    if (draw_contour && this_plot->contours != NULL) {
1348 		struct gnuplot_contours *cntrs = this_plot->contours;
1349 		struct lp_style_type thiscontour_lp_properties;
1350 		static char *thiscontour_label = NULL;
1351 		TBOOLEAN save_hidden3d;
1352 		int ic = 1;	/* ic will index the contour linetypes */
1353 
1354 		thiscontour_lp_properties = this_plot->lp_properties;
1355 		term_apply_lp_properties(&(thiscontour_lp_properties));
1356 
1357 		while (cntrs) {
1358 		    if (!clabel_onecolor && cntrs->isNewLevel) {
1359 			if (key->visible && !this_plot->title_is_suppressed
1360 			&&  this_plot->plot_style != LABELPOINTS) {
1361 			    (*t->linetype)(LT_BLACK);
1362 			    key_text(xl, yl, cntrs->label);
1363 			}
1364 			if (thiscontour_lp_properties.pm3d_color.type == TC_Z)
1365 			    set_color( cb2gray(cntrs->z) );
1366 			else {
1367 			    struct lp_style_type ls = thiscontour_lp_properties;
1368 			    int contour_linetype;
1369 			    ic++;	/* Increment linetype used for contour */
1370 
1371 			    /* First contour line type defaults to surface linetype + 1  */
1372 			    /* but can be changed using 'set cntrparams firstlinetype N' */
1373 			    if (contour_firstlinetype > 0)
1374 				contour_linetype = contour_firstlinetype + ic - 2;
1375 			    else
1376 				contour_linetype = this_plot->hidden3d_top_linetype + ic;
1377 
1378 			    /* hidden3d processing looks directly at l_type */
1379 			    /* for other purposes the line color is set by load_linetype */
1380 			    if (hidden3d)
1381 				thiscontour_lp_properties.l_type = contour_linetype - 1;
1382 			    load_linetype(&ls, contour_linetype);
1383 
1384 			    thiscontour_lp_properties.pm3d_color = ls.pm3d_color;
1385 			    thiscontour_lp_properties.l_width = ls.l_width
1386 							      * this_plot->lp_properties.l_width;
1387 			    thiscontour_lp_properties.d_type = ls.d_type;
1388 			    thiscontour_lp_properties.custom_dash_pattern = ls.custom_dash_pattern;
1389 			    term_apply_lp_properties(&thiscontour_lp_properties);
1390 			}
1391 
1392 			if (key->visible && !this_plot->title_is_suppressed
1393 			&& !(this_plot->plot_style == LABELPOINTS)) {
1394 
1395 			    switch (this_plot->plot_style) {
1396 			    case IMPULSES:
1397 			    case LINES:
1398 			    case LINESPOINTS:
1399 			    case FILLEDCURVES:
1400 			    case VECTOR:
1401 			    case STEPS:
1402 			    case FSTEPS:
1403 			    case HISTEPS:
1404 			    case PM3DSURFACE:
1405 				key_sample_line(xl, yl);
1406 				break;
1407 			    case POINTSTYLE:
1408 				key_sample_point(this_plot, xl, yl, this_plot->lp_properties.p_type);
1409 				break;
1410 			    case DOTS:
1411 				key_sample_point(this_plot, xl, yl, -1);
1412 				break;
1413 			    default:
1414 				break;
1415 			    }	/* switch */
1416 
1417 			    NEXT_KEY_LINE();
1418 
1419 			} /* key */
1420 		    } /* clabel_onecolor */
1421 
1422 		    /* now draw the contour */
1423 		    if (!key_pass)
1424 		    switch (this_plot->plot_style) {
1425 			/* treat boxes like impulses: */
1426 		    case BOXES:
1427 		    case FILLEDCURVES:
1428 		    case VECTOR:
1429 		    case IMPULSES:
1430 			cntr3d_impulses(cntrs, &thiscontour_lp_properties);
1431 			break;
1432 
1433 		    case STEPS:
1434 		    case FSTEPS:
1435 		    case HISTEPS:
1436 			/* treat all the above like 'lines' */
1437 		    case LINES:
1438 		    case PM3DSURFACE:
1439 			save_hidden3d = hidden3d;
1440 			if (this_plot->opt_out_of_hidden3d)
1441 			    hidden3d = FALSE;
1442 			cntr3d_lines(cntrs, &thiscontour_lp_properties);
1443 			hidden3d = save_hidden3d;
1444 			break;
1445 
1446 		    case LINESPOINTS:
1447 			cntr3d_lines(cntrs, &thiscontour_lp_properties);
1448 			/* Fall through to draw the points */
1449 		    case DOTS:
1450 		    case POINTSTYLE:
1451 			cntr3d_points(cntrs, &thiscontour_lp_properties);
1452 			break;
1453 
1454 		    case LABELPOINTS:
1455 			if (cntrs->isNewLevel) {
1456 			    char *c = &cntrs->label[strspn(cntrs->label," ")];
1457 			    free(thiscontour_label);
1458 			    thiscontour_label = gp_strdup(c);
1459 			}
1460 			cntr3d_labels(cntrs, thiscontour_label, this_plot->labels);
1461 			break;
1462 
1463 		    default:
1464 			break;
1465 		    } /*switch */
1466 
1467 		    cntrs = cntrs->next;
1468 		} /* loop over contours */
1469 	    } /* draw contours */
1470 
1471 	    /* Sync point for end of this curve (used by svg, post, ...) */
1472 	    (term->layer)(TERM_LAYER_AFTER_PLOT);
1473 
1474 	} /* loop over surfaces */
1475 
1476     if (!key_pass)
1477     if (pm3d_order_depth || track_pm3d_quadrangles) {
1478 	pm3d_depth_queue_flush(); /* draw pending plots */
1479     }
1480 
1481     if (!key_pass)
1482     if (hidden3d && (hidden3d_layer == LAYER_FRONT) && draw_surface
1483     &&  (replot_mode != AXIS_ONLY_ROTATE)) {
1484 	(term->layer)(TERM_LAYER_BEFORE_PLOT);
1485 	plot3d_hidden(plots, pcount);
1486 	(term->layer)(TERM_LAYER_AFTER_PLOT);
1487     }
1488 
1489     /* Draw grid and border.
1490      * The 1st case allows "set border behind" to override hidden3d processing.
1491      * The 2nd case either leaves everything to hidden3d or forces it to the front.
1492      * The 3rd case is the non-hidden3d default - draw back pieces (done earlier),
1493      * then the graph, and now the front pieces.
1494      */
1495     if (hidden3d && border_layer == LAYER_BEHIND)
1496 	/* the important thing is _not_ to draw the back grid */
1497 	/* draw_3d_graphbox(plots, pcount, FRONTGRID, LAYER_FRONT) */
1498 	;
1499 
1500     else if (hidden3d || grid_layer == LAYER_FRONT)
1501 	draw_3d_graphbox(plots, pcount, ALLGRID, LAYER_FRONT);
1502 
1503     else if (grid_layer == LAYER_BEHIND)
1504 	draw_3d_graphbox(plots, pcount, FRONTGRID, LAYER_FRONT);
1505 
1506     /* Go back and draw the legend in a separate pass if "key opaque" */
1507     if (key->visible && key->front && !key_pass) {
1508 	key_pass = TRUE;
1509 	xl = xl_save; yl = yl_save;
1510 	goto SECOND_KEY_PASS;
1511     }
1512 
1513     /* Add 'front' color box */
1514     if ((replot_mode != AXIS_ONLY_ROTATE)
1515     &&  can_pm3d && is_plot_with_colorbox() && color_box.layer == LAYER_FRONT)
1516 	draw_color_smooth_box(MODE_SPLOT);
1517 
1518     /* Add 'front' rectangles */
1519     place_pixmaps(LAYER_FRONT, 3);
1520     place_objects(first_object, LAYER_FRONT, 3);
1521 
1522     /* Grid walls */
1523     place_objects(grid_wall, LAYER_FRONT, 3);
1524 
1525     /* PLACE LABELS */
1526     place_labels3d(first_label, LAYER_FRONT);
1527 
1528     /* PLACE ARROWS */
1529     place_arrows3d(LAYER_FRONT);
1530 
1531     /* PLACE TITLE LAST */
1532     if (title.text != 0)
1533 	place_title(title_x, title_y);
1534 
1535 #ifdef USE_MOUSE
1536     /* finally, store the 2d projection of the x and y axis, to enable zooming by mouse */
1537     {
1538 	int x,y;
1539 	map3d_xy(X_AXIS.min, Y_AXIS.min, base_z, &axis3d_o_x, &axis3d_o_y);
1540 	map3d_xy(X_AXIS.max, Y_AXIS.min, base_z, &x, &y);
1541 	axis3d_x_dx = x - axis3d_o_x;
1542 	axis3d_x_dy = y - axis3d_o_y;
1543 	map3d_xy(X_AXIS.min, Y_AXIS.max, base_z, &x, &y);
1544 	axis3d_y_dx = x - axis3d_o_x;
1545 	axis3d_y_dy = y - axis3d_o_y;
1546     }
1547 #endif
1548 
1549     /* Release the palette if we have used one (PostScript only?) */
1550     if (is_plot_with_palette() && term->previous_palette)
1551 	term->previous_palette();
1552 
1553     term_end_plot();
1554 
1555     if (hidden3d && draw_surface) {
1556 	term_hidden_line_removal();
1557     }
1558 
1559     if (splot_map)
1560 	splot_map_deactivate();
1561     else if (xz_projection || yz_projection)
1562 	surface_scale = 1.0;
1563     else if (yz_projection)
1564 	flip_projection_axis(&axis_array[FIRST_Z_AXIS]);
1565 }
1566 
1567 
1568 /* plot3d_impulses:
1569  * Plot the surfaces in IMPULSES style
1570  */
1571 static void
plot3d_impulses(struct surface_points * plot)1572 plot3d_impulses(struct surface_points *plot)
1573 {
1574     int i;				/* point index */
1575     int x, y, xx0, yy0;			/* point in terminal coordinates */
1576     struct iso_curve *icrvs = plot->iso_crvs;
1577     int colortype = plot->lp_properties.pm3d_color.type;
1578 
1579     if (colortype == TC_RGB)
1580 	set_rgbcolor_const(plot->lp_properties.pm3d_color.lt);
1581 
1582     while (icrvs) {
1583 	struct coordinate *points = icrvs->points;
1584 
1585 	for (i = 0; i < icrvs->p_count; i++) {
1586 
1587 	    check3d_for_variable_color(plot, &points[i]);
1588 
1589 	    switch (points[i].type) {
1590 	    case INRANGE:
1591 		{
1592 		    double z = 0.0;
1593 		    map3d_xy(points[i].x, points[i].y, points[i].z, &x, &y);
1594 		    cliptorange(z, Z_AXIS.min, Z_AXIS.max);
1595 		    map3d_xy(points[i].x, points[i].y, z, &xx0, &yy0);
1596 
1597 		    clip_move(xx0, yy0);
1598 		    clip_vector(x, y);
1599 
1600 		    break;
1601 		}
1602 	    case OUTRANGE:
1603 		{
1604 		    if (!inrange(points[i].x, X_AXIS.min, X_AXIS.max) ||
1605 			!inrange(points[i].y, Y_AXIS.min, Y_AXIS.max))
1606 			break;
1607 
1608 		    if (inrange(0.0, Z_AXIS.min, Z_AXIS.max)) {
1609 			/* zero point is INRANGE */
1610 			map3d_xy(points[i].x, points[i].y, 0.0, &xx0, &yy0);
1611 
1612 			/* must cross z = Z_AXIS.min or Z_AXIS.max limits */
1613 			if (inrange(Z_AXIS.min, 0.0, points[i].z) &&
1614 			    Z_AXIS.min != 0.0 && Z_AXIS.min != points[i].z) {
1615 			    map3d_xy(points[i].x, points[i].y, Z_AXIS.min, &x, &y);
1616 			} else {
1617 			    map3d_xy(points[i].x, points[i].y, Z_AXIS.max, &x, &y);
1618 			}
1619 		    } else {
1620 			/* zero point is also OUTRANGE */
1621 			if (inrange(Z_AXIS.min, 0.0, points[i].z) &&
1622 			    inrange(Z_AXIS.max, 0.0, points[i].z)) {
1623 			    /* crosses z = Z_AXIS.min or Z_AXIS.max limits */
1624 			    map3d_xy(points[i].x, points[i].y, Z_AXIS.max, &x, &y);
1625 			    map3d_xy(points[i].x, points[i].y, Z_AXIS.min, &xx0, &yy0);
1626 			} else {
1627 			    /* doesn't cross z = Z_AXIS.min or Z_AXIS.max limits */
1628 			    break;
1629 			}
1630 		    }
1631 
1632 		    clip_move(xx0, yy0);
1633 		    clip_vector(x, y);
1634 
1635 		    break;
1636 		}
1637 	    default:		/* just a safety */
1638 	    case UNDEFINED:{
1639 		    break;
1640 		}
1641 	    }
1642 	}
1643 
1644 	icrvs = icrvs->next;
1645     }
1646 }
1647 
1648 
1649 /* plot3d_lines:
1650  * Plot the surfaces in LINES style
1651  */
1652 /* We want to always draw the lines in the same direction, otherwise when
1653    we draw an adjacent box we might get the line drawn a little differently
1654    and we get splotches.  */
1655 
1656 static void
plot3d_lines(struct surface_points * plot)1657 plot3d_lines(struct surface_points *plot)
1658 {
1659     int i;
1660     int x, y, xx0, yy0;	/* point in terminal coordinates */
1661     double clip_x, clip_y, clip_z;
1662     struct iso_curve *icrvs = plot->iso_crvs;
1663     struct coordinate *points;
1664     TBOOLEAN rgb_from_column;
1665 
1666     /* These are handled elsewhere.  */
1667     if (plot->has_grid_topology && hidden3d)
1668 	return;
1669 
1670     /* These don't need to be drawn at all */
1671     if (plot->lp_properties.l_type == LT_NODRAW)
1672 	return;
1673 
1674     rgb_from_column = plot->pm3d_color_from_column
1675 			&& plot->lp_properties.pm3d_color.type == TC_RGB
1676 			&& plot->lp_properties.pm3d_color.value < 0.0;
1677 
1678     while (icrvs) {
1679 	enum coord_type prev = UNDEFINED;	/* type of previous plot */
1680 
1681 	for (i = 0, points = icrvs->points; i < icrvs->p_count; i++) {
1682 
1683 	    if (rgb_from_column)
1684 		set_rgbcolor_var((unsigned int)points[i].CRD_COLOR);
1685 	    else if (plot->lp_properties.pm3d_color.type == TC_LINESTYLE) {
1686 		plot->lp_properties.pm3d_color.lt = (int)(points[i].CRD_COLOR);
1687 		apply_pm3dcolor(&(plot->lp_properties.pm3d_color));
1688 	    }
1689 
1690 	    switch (points[i].type) {
1691 	    case INRANGE:{
1692 		    map3d_xy(points[i].x, points[i].y, points[i].z, &x, &y);
1693 
1694 		    if (prev == INRANGE) {
1695 			clip_vector(x, y);
1696 		    } else {
1697 			if (prev == OUTRANGE) {
1698 			    /* from outrange to inrange */
1699 			    if (!clip_lines1) {
1700 				clip_move(x, y);
1701 			    } else {
1702 				/*
1703 				 * Calculate intersection point and draw
1704 				 * vector from there
1705 				 */
1706 				edge3d_intersect(&points[i-1], &points[i], &clip_x, &clip_y, &clip_z);
1707 
1708 				map3d_xy(clip_x, clip_y, clip_z, &xx0, &yy0);
1709 
1710 				clip_move(xx0, yy0);
1711 				clip_vector(x, y);
1712 			    }
1713 			} else {
1714 			    clip_move(x, y);
1715 			}
1716 		    }
1717 
1718 		    break;
1719 		}
1720 	    case OUTRANGE:{
1721 		    if (prev == INRANGE) {
1722 			/* from inrange to outrange */
1723 			if (clip_lines1) {
1724 			    /*
1725 			     * Calculate intersection point and draw
1726 			     * vector to it
1727 			     */
1728 			    edge3d_intersect(&points[i-1], &points[i], &clip_x, &clip_y, &clip_z);
1729 
1730 			    map3d_xy(clip_x, clip_y, clip_z, &xx0, &yy0);
1731 
1732 			    clip_vector(xx0, yy0);
1733 			}
1734 		    } else if (prev == OUTRANGE) {
1735 			/* from outrange to outrange */
1736 			if (clip_lines2) {
1737 			    double lx[2], ly[2], lz[2];	/* two edge points */
1738 			    /*
1739 			     * Calculate the two 3D intersection points if present
1740 			     */
1741 			    if (two_edge3d_intersect(&points[i-1], &points[i], lx, ly, lz)) {
1742 				map3d_xy(lx[0], ly[0], lz[0], &x, &y);
1743 				map3d_xy(lx[1], ly[1], lz[1], &xx0, &yy0);
1744 				clip_move(x, y);
1745 				clip_vector(xx0, yy0);
1746 			    }
1747 			}
1748 		    }
1749 		    break;
1750 		}
1751 	    case UNDEFINED:{
1752 		    break;
1753 		}
1754 	    default:
1755 		    int_warn(NO_CARET,"Unknown point type in plot3d_lines");
1756 	    }
1757 
1758 	    prev = points[i].type;
1759 	}
1760 
1761 	icrvs = icrvs->next;
1762     }
1763 }
1764 
1765 /* this is basically the same function as above, but:
1766  *  - it splits the bunch of scans in two sets corresponding to
1767  *    the two scan directions.
1768  *  - reorders the two sets -- from behind to front
1769  *  - checks if inside on scan of a set the order should be inverted
1770  */
1771 static void
plot3d_lines_pm3d(struct surface_points * plot)1772 plot3d_lines_pm3d(struct surface_points *plot)
1773 {
1774     struct iso_curve** icrvs_pair[2];
1775     int invert[2] = {0,0};
1776     int n[2] = {0,0};
1777 
1778     int i, set, scan;
1779     int x, y, xx0, yy0;	/* point in terminal coordinates */
1780     double clip_x, clip_y, clip_z;
1781     struct coordinate *points;
1782     enum coord_type prev = UNDEFINED;
1783     double z;
1784 
1785     /* just a shortcut */
1786     TBOOLEAN color_from_column = plot->pm3d_color_from_column;
1787 
1788     /* If plot really uses RGB rather than pm3d colors, let plot3d_lines take over */
1789     if (plot->lp_properties.pm3d_color.type == TC_RGB) {
1790 	apply_pm3dcolor(&(plot->lp_properties.pm3d_color));
1791 	plot3d_lines(plot);
1792 	return;
1793     } else if (plot->lp_properties.pm3d_color.type == TC_LT) {
1794 	plot3d_lines(plot);
1795 	return;
1796     } else if (plot->lp_properties.pm3d_color.type == TC_LINESTYLE) {
1797 	plot3d_lines(plot);
1798 	return;
1799     }
1800 
1801     /* These are handled elsewhere.  */
1802     if (plot->has_grid_topology && hidden3d)
1803 	return;
1804 
1805     /* split the bunch of scans in two sets in
1806      * which the scans are already depth ordered */
1807     pm3d_rearrange_scan_array(plot,
1808 	icrvs_pair, &n[0], &invert[0],
1809 	icrvs_pair + 1, &n[1], &invert[1]);
1810 
1811     for (set = 0; set < 2; set++) {
1812 
1813 	int begin = 0;
1814 	int step;
1815 
1816 	if (invert[set]) {
1817 	    /* begin is set below to the length of the scan - 1 */
1818 	    step = -1;
1819 	} else {
1820 	    step = 1;
1821 	}
1822 
1823 	for (scan = 0; scan < n[set] && icrvs_pair[set]; scan++) {
1824 
1825 	    int cnt;
1826 	    struct iso_curve *icrvs = icrvs_pair[set][scan];
1827 
1828 	    if (invert[set]) {
1829 		begin = icrvs->p_count - 1;
1830 	    }
1831 
1832 	    prev = UNDEFINED;	/* type of previous plot */
1833 
1834 	    for (cnt = 0, i = begin, points = icrvs->points; cnt < icrvs->p_count; cnt++, i += step) {
1835 		switch (points[i].type) {
1836 		    case INRANGE:
1837 			map3d_xy(points[i].x, points[i].y, points[i].z, &x, &y);
1838 
1839 			if (prev == INRANGE) {
1840 			    if (color_from_column)
1841 				z =  (points[i - step].CRD_COLOR + points[i].CRD_COLOR) * 0.5;
1842 			    else
1843 				z =  (points[i - step].z + points[i].z) * 0.5;
1844 			    set_color( cb2gray(z) );
1845 			    clip_vector(x, y);
1846 			} else {
1847 			    if (prev == OUTRANGE) {
1848 				/* from outrange to inrange */
1849 				if (!clip_lines1) {
1850 				    clip_move(x, y);
1851 				} else {
1852 				    /*
1853 				     * Calculate intersection point and draw
1854 				     * vector from there
1855 				     */
1856 				    edge3d_intersect(&points[i-step], &points[i], &clip_x, &clip_y, &clip_z);
1857 
1858 				    map3d_xy(clip_x, clip_y, clip_z, &xx0, &yy0);
1859 
1860 				    clip_move(xx0, yy0);
1861 				    if (color_from_column)
1862 					z =  (points[i - step].CRD_COLOR + points[i].CRD_COLOR) * 0.5;
1863 				    else
1864 					z =  (points[i - step].z + points[i].z) * 0.5;
1865 				    set_color( cb2gray(z) );
1866 				    clip_vector(x, y);
1867 				}
1868 			    } else {
1869 				clip_move(x, y);
1870 			    }
1871 			}
1872 
1873 			break;
1874 		    case OUTRANGE:
1875 			if (prev == INRANGE) {
1876 			    /* from inrange to outrange */
1877 			    if (clip_lines1) {
1878 				/*
1879 				 * Calculate intersection point and draw
1880 				 * vector to it
1881 				 */
1882 
1883 				edge3d_intersect(&points[i-step], &points[i], &clip_x, &clip_y, &clip_z);
1884 
1885 				map3d_xy(clip_x, clip_y, clip_z, &xx0, &yy0);
1886 
1887 				if (color_from_column)
1888 				    z =  (points[i - step].CRD_COLOR + points[i].CRD_COLOR) * 0.5;
1889 				else
1890 				    z =  (points[i - step].z + points[i].z) * 0.5;
1891 				set_color( cb2gray(z));
1892 				clip_vector(xx0, yy0);
1893 			    }
1894 			} else if (prev == OUTRANGE) {
1895 			    /* from outrange to outrange */
1896 			    if (clip_lines2) {
1897 				/*
1898 				 * Calculate the two 3D intersection points if present
1899 				 */
1900 				double lx[2], ly[2], lz[2];
1901 				if (two_edge3d_intersect(&points[i-step], &points[i], lx, ly, lz)) {
1902 				    map3d_xy(lx[0], ly[0], lz[0], &x, &y);
1903 				    map3d_xy(lx[1], ly[1], lz[1], &xx0, &yy0);
1904 
1905 				    clip_move(x, y);
1906 				    if (color_from_column)
1907 					z =  (points[i - step].CRD_COLOR + points[i].CRD_COLOR) * 0.5;
1908 				    else
1909 					z =  (points[i - step].z + points[i].z) * 0.5;
1910 				    set_color( cb2gray(z) );
1911 				    clip_vector(xx0, yy0);
1912 				}
1913 			    }
1914 			}
1915 			break;
1916 		    case UNDEFINED:
1917 			break;
1918 		    default:
1919 			int_warn(NO_CARET,"Unknown point type in plot3d_lines");
1920 		}
1921 
1922 		prev = points[i].type;
1923 
1924 	    } /* one scan */
1925 
1926 	} /* while (icrvs)  */
1927 
1928     } /* for (scan = 0; scan < 2; scan++) */
1929 
1930     if (icrvs_pair[0])
1931 	free(icrvs_pair[0]);
1932     if (icrvs_pair[1])
1933 	free(icrvs_pair[1]);
1934 }
1935 
1936 /* plot3d_points:
1937  * Plot the surfaces in POINTSTYLE style
1938  */
1939 static void
plot3d_points(struct surface_points * plot)1940 plot3d_points(struct surface_points *plot)
1941 {
1942     int i;
1943     int x, y;
1944     struct termentry *t = term;
1945     struct iso_curve *icrvs = plot->iso_crvs;
1946     int interval = plot->lp_properties.p_interval;
1947 
1948     /* Set whatever we can that applies to every point in the loop */
1949     if (plot->lp_properties.p_type == PT_CHARACTER) {
1950 	ignore_enhanced(TRUE);
1951 	if (plot->labels->font && plot->labels->font[0])
1952 	    (*t->set_font) (plot->labels->font);
1953 	(*t->justify_text) (CENTRE);
1954     }
1955 
1956     while (icrvs) {
1957 	struct coordinate *point;
1958 	int colortype = plot->lp_properties.pm3d_color.type;
1959 	const char *ptchar;
1960 
1961 	/* Apply constant color outside of the loop */
1962 	if (plot->plot_style == CIRCLES)
1963 	    set_rgbcolor_const( plot->fill_properties.border_color.lt );
1964 	else if (colortype == TC_RGB)
1965 	    set_rgbcolor_const( plot->lp_properties.pm3d_color.lt );
1966 
1967 	for (i = 0; i < icrvs->p_count; i++) {
1968 
1969 	    /* Only print 1 point per interval */
1970 	    if ((plot->plot_style == LINESPOINTS) && (interval) && (i % interval))
1971 		continue;
1972 
1973 	    point = &(icrvs->points[i]);
1974 	    if (point->type == INRANGE) {
1975 		map3d_xy(point->x, point->y, point->z, &x, &y);
1976 
1977 		if (!clip_point(x, y)) {
1978 
1979 		    /* A negative interval indicates we should blank */
1980 		    /* out the area behind the point symbol          */
1981 		    if (plot->plot_style == LINESPOINTS && interval < 0) {
1982 			(*t->set_color)(&background_fill);
1983 			(*t->pointsize)(pointsize * pointintervalbox);
1984 			(*t->point) (x, y, 6);
1985 			term_apply_lp_properties(&(plot->lp_properties));
1986 		    }
1987 
1988 		    check3d_for_variable_color(plot, point);
1989 
1990 		    if ((plot->plot_style == POINTSTYLE || plot->plot_style == LINESPOINTS)
1991 		    &&  plot->lp_properties.p_size == PTSZ_VARIABLE)
1992 			(*t->pointsize)(pointsize * point->CRD_PTSIZE);
1993 
1994 		    /* We could dummy up circles as a point of type 7, but this way */
1995 		    /* the radius can use x-axis coordinates rather than pointsize. */
1996 		    /* FIXME: track per-plot fillstyle */
1997 		    if (plot->plot_style == CIRCLES) {
1998 			double radius = point->CRD_PTSIZE * radius_scaler;
1999 			do_arc(x, y, radius, 0., 360.,
2000 				style_from_fill(&default_fillstyle), FALSE);
2001 			/* Retrace the border if the style requests it */
2002 			if (need_fill_border(&default_fillstyle))
2003 			    do_arc(x, y, radius, 0., 360., 0, FALSE);
2004 			continue;
2005 		    }
2006 
2007 		    /* This code is also used for "splot ... with dots" */
2008 		    if (plot->plot_style == DOTS) {
2009 			(*t->point) (x, y, -1);
2010 			continue;
2011 		    }
2012 
2013 		    /* variable point type */
2014 		    if ((plot->lp_properties.p_type == PT_VARIABLE)
2015 			 &&  !(isnan(point->CRD_PTTYPE))) {
2016 			(*t->point) (x, y, (int)(point->CRD_PTTYPE) - 1);
2017 		    }
2018 
2019 		    /* Print special character rather than drawn symbol */
2020 		    if (plot->lp_properties.p_type == PT_CHARACTER)
2021 			ptchar = plot->lp_properties.p_char;
2022 		    else if (plot->lp_properties.p_type == PT_VARIABLE
2023 			 &&  isnan(point->CRD_PTTYPE))
2024 			ptchar = (char *)(&point->CRD_PTCHAR);
2025 		    else
2026 			ptchar = NULL;
2027 		    if (ptchar) {
2028 			if (plot->labels)
2029 			    apply_pm3dcolor(&(plot->labels->textcolor));
2030 			(*t->put_text)(x, y, ptchar);
2031 		    }
2032 
2033 		    /* The normal case */
2034 		    else if (plot->lp_properties.p_type >= -1)
2035 			(*t->point) (x, y, plot->lp_properties.p_type);
2036 
2037 		}
2038 	    }
2039 	}
2040 
2041 	icrvs = icrvs->next;
2042     }
2043 
2044     /* Return to initial state */
2045     if (plot->lp_properties.p_type == PT_CHARACTER) {
2046 	if (plot->labels->font && plot->labels->font[0])
2047 	    (*t->set_font) ("");
2048 	ignore_enhanced(FALSE);
2049     }
2050 }
2051 
2052 /* cntr3d_impulses:
2053  * Plot a surface contour in IMPULSES style
2054  */
2055 static void
cntr3d_impulses(struct gnuplot_contours * cntr,struct lp_style_type * lp)2056 cntr3d_impulses(struct gnuplot_contours *cntr, struct lp_style_type *lp)
2057 {
2058     int i;				/* point index */
2059     vertex vertex_on_surface, vertex_on_base;
2060 
2061     if (draw_contour & CONTOUR_SRF) {
2062 	for (i = 0; i < cntr->num_pts; i++) {
2063 	    map3d_xyz(cntr->coords[i].x, cntr->coords[i].y, cntr->coords[i].z,
2064 		      &vertex_on_surface);
2065 	    map3d_xyz(cntr->coords[i].x, cntr->coords[i].y, 0.0,
2066 		      &vertex_on_base);
2067 	    /* HBB 20010822: Provide correct color-coding for
2068 	     * "linetype palette" PM3D mode */
2069 	    vertex_on_base.real_z = cntr->coords[i].z;
2070 	    draw3d_line(&vertex_on_surface, &vertex_on_base, lp);
2071 	}
2072     } else {
2073 	/* Must be on base grid, so do points. */
2074 	cntr3d_points(cntr, lp);
2075     }
2076 }
2077 
2078 /* cntr3d_lines:
2079  * Plot a surface contour in LINES style
2080  */
2081 static void
cntr3d_lines(struct gnuplot_contours * cntr,struct lp_style_type * lp)2082 cntr3d_lines(struct gnuplot_contours *cntr, struct lp_style_type *lp)
2083 {
2084     int i;			/* point index */
2085     vertex this_vertex;
2086 
2087     /* In the case of "set view map" (only) clip the contour lines to the graph */
2088     BoundingBox *clip_save = clip_area;
2089     if (splot_map)
2090 	clip_area = &plot_bounds;
2091 
2092     if (draw_contour & CONTOUR_SRF) {
2093 	map3d_xyz(cntr->coords[0].x, cntr->coords[0].y, cntr->coords[0].z,
2094 		  &this_vertex);
2095 	/* move slightly frontward, to make sure the contours are
2096 	 * visible in front of the the triangles they're in, if this
2097 	 * is a hidden3d plot */
2098 	if (hidden3d && !VERTEX_IS_UNDEFINED(this_vertex))
2099 	    this_vertex.z += 1e-2;
2100 
2101 	polyline3d_start(&this_vertex);
2102 
2103 	for (i = 1; i < cntr->num_pts; i++) {
2104 	    map3d_xyz(cntr->coords[i].x, cntr->coords[i].y, cntr->coords[i].z,
2105 		      &this_vertex);
2106 	    /* move slightly frontward, to make sure the contours are
2107 	     * visible in front of the the triangles they're in, if this
2108 	     * is a hidden3d plot */
2109 	    if (hidden3d && !VERTEX_IS_UNDEFINED(this_vertex))
2110 		this_vertex.z += 1e-2;
2111 	    polyline3d_next(&this_vertex, lp);
2112 	}
2113     }
2114 
2115     if (draw_contour & CONTOUR_BASE) {
2116 	map3d_xyz(cntr->coords[0].x, cntr->coords[0].y, base_z,
2117 		  &this_vertex);
2118 	this_vertex.real_z = cntr->coords[0].z;
2119 	polyline3d_start(&this_vertex);
2120 
2121 	for (i = 1; i < cntr->num_pts; i++) {
2122 	    map3d_xyz(cntr->coords[i].x, cntr->coords[i].y, base_z,
2123 		      &this_vertex);
2124 	    this_vertex.real_z = cntr->coords[i].z;
2125 	    polyline3d_next(&this_vertex, lp);
2126 	}
2127     }
2128 
2129     if (splot_map)
2130 	clip_area = clip_save;
2131 }
2132 
2133 /* cntr3d_points:
2134  * Plot a surface contour in POINTSTYLE style
2135  */
2136 static void
cntr3d_points(struct gnuplot_contours * cntr,struct lp_style_type * lp)2137 cntr3d_points(struct gnuplot_contours *cntr, struct lp_style_type *lp)
2138 {
2139     int i;
2140     vertex v;
2141 
2142     if (draw_contour & CONTOUR_SRF) {
2143 	for (i = 0; i < cntr->num_pts; i++) {
2144 	    map3d_xyz(cntr->coords[i].x, cntr->coords[i].y, cntr->coords[i].z,
2145 		      &v);
2146 	    /* move slightly frontward, to make sure the contours and
2147 	     * points are visible in front of the triangles they're
2148 	     * in, if this is a hidden3d plot */
2149 	    if (hidden3d && !VERTEX_IS_UNDEFINED(v))
2150 		v.z += 1e-2;
2151 	    draw3d_point(&v, lp);
2152 	}
2153     }
2154     if (draw_contour & CONTOUR_BASE) {
2155 	for (i = 0; i < cntr->num_pts; i++) {
2156 	    map3d_xyz(cntr->coords[i].x, cntr->coords[i].y, base_z,
2157 		     &v);
2158 	    /* HBB 20010822: see above */
2159 	    v.real_z = cntr->coords[i].z;
2160 	    draw3d_point(&v, lp);
2161 	}
2162     }
2163 }
2164 
2165 /* cntr3d_labels:
2166  * Place contour labels on a contour line at the base.
2167  * These are the same labels that would be used in the key.
2168  * The label density is controlled by the point interval property
2169  *     splot FOO with labels point pi 20 nosurface
2170  */
2171 static void
cntr3d_labels(struct gnuplot_contours * cntr,char * level_text,struct text_label * label)2172 cntr3d_labels(struct gnuplot_contours *cntr, char *level_text, struct text_label *label)
2173 {
2174     int i;
2175     int interval;
2176     int x, y;
2177     vertex v;
2178     struct lp_style_type *lp = &(label->lp_properties);
2179 
2180     /* Drawing a label at every point would be too crowded */
2181     interval = lp->p_interval;
2182     if (interval <= 0) interval = 999;	/* Place label only at start point */
2183 
2184     if (draw_contour & CONTOUR_BASE) {
2185 	for (i = 0; i < cntr->num_pts; i++) {
2186 	    if ((i-clabel_start) % interval)	/* Offset to avoid sitting on the border */
2187 		continue;
2188 	    map3d_xy(cntr->coords[i].x, cntr->coords[i].y, base_z, &x, &y);
2189 	    label->text = level_text;
2190 	    label->font = clabel_font;
2191 	    if (hidden3d) {
2192 		map3d_xyz(cntr->coords[i].x, cntr->coords[i].y, base_z, &v);
2193 		v.real_z = cntr->coords[i].z;
2194 		v.label = label;
2195 		draw_label_hidden(&v, lp, x, y);
2196 	    } else {
2197 		write_label(x, y, label);
2198 	    }
2199 	    label->text = NULL;		/* Otherwise someone will try to free it */
2200 	    label->font = NULL;
2201 	}
2202     }
2203 }
2204 
2205 /* map xmin | xmax to 0 | 1 and same for y
2206  * 0.1 avoids any rounding errors
2207  */
2208 #define MAP_HEIGHT_X(x) ( ((x)-X_AXIS.min)/(X_AXIS.max-X_AXIS.min) > 0.9 ? 1 : 0 )
2209 #define MAP_HEIGHT_Y(y) ( ((y)-Y_AXIS.min)/(Y_AXIS.max-Y_AXIS.min) > 0.9 ? 1 : 0 )
2210 
2211 /* if point is at corner, update height[][] and depth[][]
2212  * we are still assuming that extremes of surfaces are at corners,
2213  * but we are not assuming order of corners
2214  */
2215 static void
check_corner_height(struct coordinate * p,double height[2][2],double depth[2][2])2216 check_corner_height(
2217     struct coordinate *p,
2218     double height[2][2], double depth[2][2])
2219 {
2220     if (p->type != INRANGE)
2221 	return;
2222     /* FIXME HBB 20010121: don't compare 'zero' to data values in
2223      * absolute terms. */
2224     if ((fabs(p->x - X_AXIS.min) < zero || fabs(p->x - X_AXIS.max) < zero) &&
2225 	(fabs(p->y - Y_AXIS.min) < zero || fabs(p->y - Y_AXIS.max) < zero)) {
2226 	int x = MAP_HEIGHT_X(p->x);
2227 	int y = MAP_HEIGHT_Y(p->y);
2228 	if (height[x][y] < p->z)
2229 	    height[x][y] = p->z;
2230 	if (depth[x][y] > p->z)
2231 	    depth[x][y] = p->z;
2232     }
2233 }
2234 
2235 /* work out where the axes and tics are drawn */
2236 static void
setup_3d_box_corners()2237 setup_3d_box_corners()
2238 {
2239     int quadrant = surface_rot_z / 90;
2240     if ((quadrant + 1) & 2) {
2241 	zaxis_x = X_AXIS.max;
2242 	right_x = X_AXIS.min;
2243 	back_y  = Y_AXIS.min;
2244 	front_y  = Y_AXIS.max;
2245     } else {
2246 	zaxis_x = X_AXIS.min;
2247 	right_x = X_AXIS.max;
2248 	back_y  = Y_AXIS.max;
2249 	front_y  = Y_AXIS.min;
2250     }
2251 
2252     if (quadrant & 2) {
2253 	zaxis_y = Y_AXIS.max;
2254 	right_y = Y_AXIS.min;
2255 	back_x  = X_AXIS.max;
2256 	front_x  = X_AXIS.min;
2257     } else {
2258 	zaxis_y = Y_AXIS.min;
2259 	right_y = Y_AXIS.max;
2260 	back_x  = X_AXIS.min;
2261 	front_x  = X_AXIS.max;
2262     }
2263 
2264     quadrant = surface_rot_x / 90;
2265     if ((quadrant & 2) && !splot_map) {
2266 	double temp;
2267 	temp = front_y;
2268 	front_y = back_y;
2269 	back_y = temp;
2270 	temp = front_x;
2271 	front_x = back_x;
2272 	back_x = temp;
2273     }
2274 
2275     if ((quadrant + 1) & 2) {
2276 	/* labels on the back axes */
2277 	yaxis_x = back_x;
2278 	xaxis_y = back_y;
2279     } else {
2280 	yaxis_x = front_x;
2281 	xaxis_y = front_y;
2282     }
2283 }
2284 
2285 /* Draw all elements of the 3d graph box, including borders, zeroaxes,
2286  * tics, gridlines, ticmarks, axis labels and the base plane. */
2287 static void
draw_3d_graphbox(struct surface_points * plot,int plot_num,WHICHGRID whichgrid,int current_layer)2288 draw_3d_graphbox(struct surface_points *plot, int plot_num, WHICHGRID whichgrid, int current_layer)
2289 {
2290     int x, y;		/* point in terminal coordinates */
2291     struct termentry *t = term;
2292     BoundingBox *clip_save = clip_area;
2293 
2294     FPRINTF((stderr,
2295 	"draw_3d_graphbox: whichgrid = %d current_layer = %d border_layer = %d\n",
2296 	whichgrid,current_layer,border_layer));
2297 
2298     clip_area = &canvas;
2299     if (draw_border && splot_map) {
2300 	if (border_layer == current_layer) {
2301 	    term_apply_lp_properties(&border_lp);
2302 	    if ((draw_border & 15) == 15)
2303 		newpath();
2304 	    map3d_xy(zaxis_x, zaxis_y, base_z, &x, &y);
2305 	    clip_move(x, y);
2306 	    map3d_xy(back_x , back_y , base_z, &x, &y);
2307 	    if (draw_border & 2)
2308 		clip_vector(x, y);
2309 	    else
2310 		clip_move(x, y);
2311 	    map3d_xy(right_x, right_y, base_z, &x, &y);
2312 	    if (draw_border & 8)
2313 		clip_vector(x, y);
2314 	    else
2315 		clip_move(x, y);
2316 	    map3d_xy(front_x, front_y, base_z, &x, &y);
2317 	    if (draw_border & 4)
2318 		clip_vector(x, y);
2319 	    else
2320 		clip_move(x, y);
2321 	    map3d_xy(zaxis_x, zaxis_y, base_z, &x, &y);
2322 	    if (draw_border & 1)
2323 		clip_vector(x, y);
2324 	    else
2325 		clip_move(x, y);
2326 	    if ((draw_border & 15) == 15)
2327 		closepath();
2328 	}
2329 
2330     } else if (draw_border && yz_projection) {
2331 	if (border_layer == current_layer) {
2332 	    struct axis *yaxis = &axis_array[FIRST_Y_AXIS];
2333 	    struct axis *zaxis = &axis_array[FIRST_Z_AXIS];
2334 	    term_apply_lp_properties(&border_lp);
2335 	    if ((draw_border & 15) == 15)
2336 		newpath();
2337 	    map3d_xy(0.0, yaxis->min, zaxis->min, &x, &y);
2338 	    clip_move(x, y);
2339 	    map3d_xy(0.0, yaxis->max, zaxis->min, &x, &y);
2340 	    if (draw_border & 8)
2341 		clip_vector(x, y);
2342 	    else
2343 		clip_move(x, y);
2344 	    map3d_xy(0.0, yaxis->max, zaxis->max, &x, &y);
2345 	    if (draw_border & 4)
2346 		clip_vector(x, y);
2347 	    else
2348 		clip_move(x, y);
2349 	    map3d_xy(0.0, yaxis->min, zaxis->max, &x, &y);
2350 	    if (draw_border & 2)
2351 		clip_vector(x, y);
2352 	    else
2353 		clip_move(x, y);
2354 	    map3d_xy(0.0, yaxis->min, zaxis->min, &x, &y);
2355 	    if (draw_border & 1)
2356 		clip_vector(x, y);
2357 	    else
2358 		clip_move(x, y);
2359 	    if ((draw_border & 15) == 15)
2360 		closepath();
2361 	}
2362 
2363     } else if (draw_border && xz_projection) {
2364 	if (border_layer == current_layer) {
2365 	    struct axis *xaxis = &axis_array[FIRST_X_AXIS];
2366 	    struct axis *zaxis = &axis_array[FIRST_Z_AXIS];
2367 	    term_apply_lp_properties(&border_lp);
2368 	    if ((draw_border & 15) == 15)
2369 		newpath();
2370 	    map3d_xy(xaxis->min, 0.0, zaxis->min, &x, &y);
2371 	    clip_move(x, y);
2372 	    map3d_xy(xaxis->max, 0.0, zaxis->min, &x, &y);
2373 	    if (draw_border & 2)
2374 		clip_vector(x, y);
2375 	    else
2376 		clip_move(x, y);
2377 	    map3d_xy(xaxis->max, 0.0, zaxis->max, &x, &y);
2378 	    if (draw_border & 4)
2379 		clip_vector(x, y);
2380 	    else
2381 		clip_move(x, y);
2382 	    map3d_xy(xaxis->min, 0.0, zaxis->max, &x, &y);
2383 	    if (draw_border & 8)
2384 		clip_vector(x, y);
2385 	    else
2386 		clip_move(x, y);
2387 	    map3d_xy(xaxis->min, 0.0, zaxis->min, &x, &y);
2388 	    if (draw_border & 1)
2389 		clip_vector(x, y);
2390 	    else
2391 		clip_move(x, y);
2392 	    if ((draw_border & 15) == 15)
2393 		closepath();
2394 	}
2395 
2396     } else if (draw_border) {
2397 	/* the four corners of the base plane, in normalized view
2398 	 * coordinates (-1..1) on all three axes. */
2399 	vertex bl, bb, br, bf;
2400 
2401 	/* map to normalized view coordinates the corners of the
2402 	 * baseplane: left, back, right and front, in that order: */
2403 	map3d_xyz(zaxis_x, zaxis_y, base_z, &bl);
2404 	map3d_xyz(back_x , back_y , base_z, &bb);
2405 	map3d_xyz(right_x, right_y, base_z, &br);
2406 	map3d_xyz(front_x, front_y, base_z, &bf);
2407 
2408 	if (BACKGRID != whichgrid) {
2409 	    /* Draw front part of base grid, right to front corner: */
2410 	    if (draw_border & 4)
2411 		draw3d_line(&br, &bf, &border_lp);
2412 	    /* ... and left to front: */
2413 	    if (draw_border & 1)
2414 		draw3d_line(&bl, &bf, &border_lp);
2415 	}
2416 	if (FRONTGRID != whichgrid) {
2417 	    /* Draw back part of base grid: left to back corner: */
2418 	    if (draw_border & 2)
2419 		draw3d_line(&bl, &bb, &border_lp);
2420 	    /* ... and right to back: */
2421 	    if (draw_border & 8)
2422 		draw3d_line(&br, &bb, &border_lp);
2423 	}
2424 
2425 	/* if surface is drawn, draw the rest of the graph box, too: */
2426 	if (draw_surface || (draw_contour & CONTOUR_SRF)
2427 	    || (pm3d.implicit == PM3D_IMPLICIT && strpbrk(pm3d.where,"st") != NULL)
2428 	   ) {
2429 	    vertex fl, fb, fr, ff; /* floor left/back/right/front corners */
2430 	    vertex tl, tb, tr, tf; /* top left/back/right/front corners */
2431 
2432 	    map3d_xyz(zaxis_x, zaxis_y, floor_z, &fl);
2433 	    map3d_xyz(back_x , back_y , floor_z, &fb);
2434 	    map3d_xyz(right_x, right_y, floor_z, &fr);
2435 	    map3d_xyz(front_x, front_y, floor_z, &ff);
2436 
2437 	    map3d_xyz(zaxis_x, zaxis_y, ceiling_z, &tl);
2438 	    map3d_xyz(back_x , back_y , ceiling_z, &tb);
2439 	    map3d_xyz(right_x, right_y, ceiling_z, &tr);
2440 	    map3d_xyz(front_x, front_y, ceiling_z, &tf);
2441 
2442 	    if ((draw_border & 0xf0) == 0xf0) {
2443 		/* all four verticals are drawn - save some time by
2444 		 * drawing them to the full height, regardless of
2445 		 * where the surface lies */
2446 		if (FRONTGRID != whichgrid) {
2447 		    /* Draw the back verticals floor-to-ceiling, left: */
2448 		    draw3d_line(&fl, &tl, &border_lp);
2449 		    /* ... back: */
2450 		    draw3d_line(&fb, &tb, &border_lp);
2451 		    /* ... and right */
2452 		    draw3d_line(&fr, &tr, &border_lp);
2453 		}
2454 		if (BACKGRID != whichgrid) {
2455 		    /* Draw the front vertical: floor-to-ceiling, front: */
2456 		    draw3d_line(&ff, &tf, &border_lp);
2457 		}
2458 	    } else {
2459 		/* find heights of surfaces at the corners of the xy
2460 		 * rectangle */
2461 		double height[2][2];
2462 		double depth[2][2];
2463 		int zaxis_i = MAP_HEIGHT_X(zaxis_x);
2464 		int zaxis_j = MAP_HEIGHT_Y(zaxis_y);
2465 		int back_i = MAP_HEIGHT_X(back_x);
2466 		int back_j = MAP_HEIGHT_Y(back_y);
2467 
2468 		height[0][0] = height[0][1]
2469 		    = height[1][0] = height[1][1] = base_z;
2470 		depth[0][0] = depth[0][1]
2471 		    = depth[1][0] = depth[1][1] = base_z;
2472 
2473 		/* FIXME HBB 20000617: this method contains the
2474 		 * assumption that the topological corners of the
2475 		 * surface mesh(es) are also the geometrical ones of
2476 		 * their xy projections. This is only true for
2477 		 * 'explicit' surface datasets, i.e. z(x,y) */
2478 		for (; --plot_num >= 0; plot = plot->next_sp) {
2479 		    struct iso_curve *curve = plot->iso_crvs;
2480 		    int count;
2481 		    int iso;
2482 
2483 		    if (plot->plot_type == NODATA || plot->plot_type == KEYENTRY)
2484 			continue;
2485 		    if (plot->plot_type == VOXELDATA)
2486 			continue;
2487 		    if (plot->plot_type == DATA3D) {
2488 			if (!plot->has_grid_topology)
2489 			    continue;
2490 			iso = plot->num_iso_read;
2491 		    } else
2492 			iso = iso_samples_2;
2493 
2494 		    count = curve->p_count;
2495 		    if (count == 0)
2496 			continue;
2497 
2498 		    check_corner_height(curve->points, height, depth);
2499 		    check_corner_height(curve->points + count - 1, height, depth);
2500 		    while (--iso)
2501 			curve = curve->next;
2502 		    check_corner_height(curve->points, height, depth);
2503 		    check_corner_height(curve->points + count - 1, height, depth);
2504 		}
2505 
2506 #define VERTICAL(mask,x,y,i,j,bottom,top)			\
2507 		if (draw_border&mask) {				\
2508 		    draw3d_line(bottom,top, &border_lp);	\
2509 		} else if (height[i][j] != depth[i][j] &&	\
2510 			   (X_AXIS.ticmode || Y_AXIS.ticmode ||	\
2511 			    draw_border & 0x00F)) {		\
2512 		    vertex a, b;				\
2513 		    map3d_xyz(x,y,depth[i][j],&a);		\
2514 		    map3d_xyz(x,y,height[i][j],&b);		\
2515 		    draw3d_line(&a, &b, &border_lp);		\
2516 		}
2517 
2518 		if (FRONTGRID != whichgrid) {
2519 		    /* Draw back verticals: floor-to-ceiling left: */
2520 		    VERTICAL(0x10, zaxis_x, zaxis_y, zaxis_i, zaxis_j, &fl, &tl);
2521 		    /* ... back: */
2522 		    VERTICAL(0x20, back_x, back_y, back_i, back_j, &fb, &tb);
2523 		    /* ... and right: */
2524 		    VERTICAL(0x40, right_x, right_y, 1 - zaxis_i, 1 - zaxis_j,
2525 			     &fr, &tr);
2526 		}
2527 		if (BACKGRID != whichgrid) {
2528 		    /* Draw front verticals: floor-to-ceiling front */
2529 		    VERTICAL(0x80, front_x, front_y, 1 - back_i, 1 - back_j,
2530 			     &ff, &tf);
2531 		}
2532 #undef VERTICAL
2533 	    } /* else (all 4 verticals drawn?) */
2534 
2535 	    /* now border lines on top */
2536 	    if (FRONTGRID != whichgrid) {
2537 		/* Draw back part of top of box: top left to back corner: */
2538 		if (draw_border & 0x100)
2539 		    draw3d_line(&tl, &tb, &border_lp);
2540 		/* ... and top right to back: */
2541 		if (draw_border & 0x200)
2542 		    draw3d_line(&tr, &tb, &border_lp);
2543 	    }
2544 	    if (BACKGRID != whichgrid) {
2545 		/* Draw front part of top of box: top left to front corner: */
2546 		if (draw_border & 0x400)
2547 		    draw3d_line(&tl, &tf, &border_lp);
2548 		/* ... and top right to front: */
2549 		if (draw_border & 0x800)
2550 		    draw3d_line(&tr, &tf, &border_lp);
2551 	    }
2552 	} /* else (surface is drawn) */
2553     } /* if (draw_border) */
2554 
2555     /* In 'set view map' mode, treat grid as in 2D plots */
2556     if (splot_map && current_layer != abs(grid_layer)) {
2557 	clip_area = clip_save;
2558 	return;
2559     }
2560     if (whichgrid == BORDERONLY) {
2561 	clip_area = clip_save;
2562 	return;
2563     }
2564 
2565     /* Draw ticlabels and axis labels */
2566 
2567     /* x axis */
2568     if ((X_AXIS.ticmode || X_AXIS.label.text) && !yz_plane) {
2569 	vertex v0, v1;
2570 	double other_end = Y_AXIS.min + Y_AXIS.max - xaxis_y;
2571 	double mid_x;
2572 
2573 	if (nonlinear(&X_AXIS)) {
2574 	    AXIS *primary = X_AXIS.linked_to_primary;
2575 	    mid_x = (primary->max + primary->min) / 2.;
2576 	    mid_x = eval_link_function(&X_AXIS, mid_x);
2577 	} else {
2578 	    mid_x = (X_AXIS.max + X_AXIS.min) / 2.;
2579 	}
2580 
2581 	map3d_xyz(mid_x, xaxis_y, base_z, &v0);
2582 	map3d_xyz(mid_x, other_end, base_z, &v1);
2583 
2584 	/* Unusual case: 2D projection of the xz plane */
2585 	if (!splot_map && xz_plane)
2586 	    map3d_xyz(mid_x, xaxis_y, Z_AXIS.max+Z_AXIS.min-base_z, &v1);
2587 
2588 	tic_unitx = (v1.x - v0.x) / xyscaler;
2589 	tic_unity = (v1.y - v0.y) / xyscaler;
2590 	tic_unitz = (v1.z - v0.z) / xyscaler;
2591 
2592 	/* Don't output tics and grids if this is the front part of a
2593 	 * two-part grid drawing process: */
2594 	if ((surface_rot_x <= 90 && FRONTGRID != whichgrid) ||
2595 	    (surface_rot_x > 90 && BACKGRID != whichgrid))
2596 
2597 	if (X_AXIS.ticmode)
2598 	    gen_tics(&axis_array[FIRST_X_AXIS], xtick_callback);
2599 
2600 	if (X_AXIS.label.text) {
2601 
2602 	    if ((surface_rot_x <= 90 && BACKGRID != whichgrid) ||
2603 		(surface_rot_x > 90 && FRONTGRID != whichgrid) ||
2604 		splot_map) {
2605 
2606 	    int x1, y1;
2607 
2608 	    if (splot_map) { /* case 'set view map' */
2609 		/* copied from xtick_callback(): baseline of tics labels */
2610 		vertex v1, v2;
2611 		map3d_xyz(mid_x, xaxis_y, base_z, &v1);
2612 		v2.x = v1.x;
2613 		v2.y = v1.y - tic_unity * t->v_char;
2614 		if (!X_AXIS.tic_in)
2615 		    v2.y -= tic_unity * t->v_tic * X_AXIS.ticscale;
2616 		TERMCOORD(&v2, x1, y1);
2617 		/* Default displacement with respect to baseline of tics labels */
2618 		y1 -= (1.5 * t->v_char);
2619 	    } else { /* usual 3d set view ... */
2620 		if (X_AXIS.label.tag == ROTATE_IN_3D_LABEL_TAG) {
2621 		    double ang, angx0, angx1, angy0, angy1;
2622 		    map3d_xy_double(X_AXIS.min, xaxis_y, base_z, &angx0, &angy0);
2623 		    map3d_xy_double(X_AXIS.max, xaxis_y, base_z, &angx1, &angy1);
2624 		    ang = atan2(angy1-angy0, angx1-angx0) / DEG2RAD;
2625 		    if (ang < -90) ang += 180;
2626 		    if (ang > 90) ang -= 180;
2627 		    X_AXIS.label.rotate = (ang > 0) ? floor(ang + 0.5) : floor(ang - 0.5);
2628 		}
2629 
2630 		if (X_AXIS.ticmode & TICS_ON_AXIS) {
2631 		    map3d_xyz(mid_x, 0.0, base_z, &v1);
2632 		} else {
2633 		    map3d_xyz(mid_x, xaxis_y, base_z, &v1);
2634 		}
2635 
2636 		if (xz_projection) {
2637 		    v1.x -= 3. * t->h_tic * tic_unitx;
2638 		    v1.y -= 3. * t->h_tic * tic_unity;
2639 		} else if (X_AXIS.ticmode & TICS_ON_AXIS) {
2640 		    v1.x += 2. * t->h_tic * ((X_AXIS.tic_in) ? 1.0 : -1.0) * tic_unitx;
2641 		    v1.y += 2. * t->h_tic * ((X_AXIS.tic_in) ? 1.0 : -1.0) * tic_unity;
2642 		} else {
2643 		    v1.x -= 10. * t->h_tic * tic_unitx;
2644 		    v1.y -= 10. * t->h_tic * tic_unity;
2645 		}
2646 
2647 		if (!X_AXIS.tic_in) {
2648 		    v1.x -= tic_unitx * X_AXIS.ticscale * t->h_tic;
2649 		    v1.y -= tic_unity * X_AXIS.ticscale * t->h_tic;
2650 		}
2651 		TERMCOORD(&v1, x1, y1);
2652 	    }
2653 
2654 	    write_label(x1, y1, &X_AXIS.label);
2655 	    }
2656 	}
2657 
2658 	if (splot_map && axis_array[SECOND_X_AXIS].ticmode)
2659 	    gen_tics(&axis_array[SECOND_X_AXIS], xtick_callback);
2660     }
2661 
2662     /* y axis */
2663     if ((Y_AXIS.ticmode || Y_AXIS.label.text) && !xz_plane) {
2664 	vertex v0, v1;
2665 	double other_end = X_AXIS.min + X_AXIS.max - yaxis_x;
2666 	double mid_y;
2667 
2668 	if (nonlinear(&Y_AXIS)) {
2669 	    AXIS *primary = Y_AXIS.linked_to_primary;
2670 	    mid_y = (primary->max + primary->min) / 2.;
2671 	    mid_y = eval_link_function(&Y_AXIS, mid_y);
2672 	} else {
2673 	    mid_y = (Y_AXIS.max + Y_AXIS.min) / 2.;
2674 	}
2675 
2676 	map3d_xyz(yaxis_x, mid_y, base_z, &v0);
2677 	map3d_xyz(other_end, mid_y, base_z, &v1);
2678 
2679 	/* Unusual case: 2D projection of the yz plane */
2680 	if (!splot_map && yz_plane)
2681 	    map3d_xyz(yaxis_x, mid_y, Z_AXIS.max+Z_AXIS.min-base_z, &v1);
2682 
2683 	tic_unitx = (v1.x - v0.x) / xyscaler;
2684 	tic_unity = (v1.y - v0.y) / xyscaler;
2685 	tic_unitz = (v1.z - v0.z) / xyscaler;
2686 
2687 	/* Don't output tics and grids if this is the front part of a
2688 	 * two-part grid drawing process: */
2689 	if ((surface_rot_x <= 90 && FRONTGRID != whichgrid) ||
2690 	    (surface_rot_x > 90 && BACKGRID != whichgrid))
2691 
2692 	if (Y_AXIS.ticmode)
2693 	    gen_tics(&axis_array[FIRST_Y_AXIS], ytick_callback);
2694 
2695 	if (Y_AXIS.label.text) {
2696 	    if ((surface_rot_x <= 90 && BACKGRID != whichgrid) ||
2697 		(surface_rot_x > 90 && FRONTGRID != whichgrid) ||
2698 		splot_map) {
2699 		int x1, y1;
2700 		int save_rotate = Y_AXIS.label.rotate;
2701 
2702 		if (splot_map) { /* case 'set view map' */
2703 		    /* copied from ytick_callback(): baseline of tics labels */
2704 		    vertex v1, v2;
2705 		    map3d_xyz(yaxis_x, mid_y, base_z, &v1);
2706 		    if (Y_AXIS.ticmode & TICS_ON_AXIS
2707 			    && !X_AXIS.log
2708 			    && inrange (0.0, X_AXIS.min, X_AXIS.max)
2709 		       ) {
2710 			map3d_xyz(0.0, yaxis_x, base_z, &v1);
2711 		    }
2712 		    v2.x = v1.x - tic_unitx * t->h_char * 1;
2713 		    v2.y = v1.y;
2714 		    if (!X_AXIS.tic_in)
2715 			v2.x -= tic_unitx * t->h_tic * X_AXIS.ticscale;
2716 		    TERMCOORD(&v2, x1, y1);
2717 		    /* calculate max length of y-tics labels */
2718 		    widest_tic_strlen = 0;
2719 		    if (Y_AXIS.ticmode & TICS_ON_BORDER) {
2720 			widest_tic_strlen = 0; /* reset the global variable */
2721 			gen_tics(&axis_array[FIRST_Y_AXIS], widest_tic_callback);
2722 		    }
2723 		    /* Default displacement with respect to baseline of tics labels */
2724 		    x1 -= (0.5 + widest_tic_strlen) * t->h_char;
2725 		} else { /* usual 3d set view ... */
2726 		    if (Y_AXIS.label.tag == ROTATE_IN_3D_LABEL_TAG) {
2727 			double ang, angx0, angx1, angy0, angy1;
2728 			map3d_xy_double(yaxis_x, Y_AXIS.min, base_z, &angx0, &angy0);
2729 			map3d_xy_double(yaxis_x, Y_AXIS.max, base_z, &angx1, &angy1);
2730 			ang = atan2(angy1-angy0, angx1-angx0) / DEG2RAD;
2731 			if (ang < -90) ang += 180;
2732 			if (ang > 90) ang -= 180;
2733 			Y_AXIS.label.rotate = (ang > 0) ? floor(ang + 0.5) : floor(ang - 0.5);
2734 		    } else if (!yz_projection) {
2735 			/* The 2D default state (ylabel rotate) is not wanted in 3D */
2736 			Y_AXIS.label.rotate = 0;
2737 		    }
2738 
2739 		    if (Y_AXIS.ticmode & TICS_ON_AXIS) {
2740 			map3d_xyz(0.0, mid_y, base_z, &v1);
2741 		    } else {
2742 			map3d_xyz(yaxis_x, mid_y, base_z, &v1);
2743 		    }
2744 
2745 		    if (yz_projection) {
2746 			v1.x -= 3. * t->h_tic * tic_unitx;
2747 			v1.y -= 3. * t->h_tic * tic_unity;
2748 		    } else if (Y_AXIS.ticmode & TICS_ON_AXIS) {
2749 			v1.x += 2. * t->h_tic * ((Y_AXIS.tic_in) ? 1.0 : -1.0) * tic_unitx;
2750 			v1.y += 2. * t->h_tic * ((Y_AXIS.tic_in) ? 1.0 : -1.0) * tic_unity;
2751 		    } else {
2752 			v1.x -= 10. * t->h_tic * tic_unitx;
2753 			v1.y -= 10. * t->h_tic * tic_unity;
2754 		    }
2755 
2756 		    if (!Y_AXIS.tic_in) {
2757 			v1.x -= tic_unitx * Y_AXIS.ticscale * t->v_tic;
2758 			v1.y -= tic_unity * Y_AXIS.ticscale * t->v_tic;
2759 		    }
2760 		    TERMCOORD(&v1, x1, y1);
2761 		}
2762 
2763 		write_label(x1, y1, &Y_AXIS.label);
2764 		Y_AXIS.label.rotate = save_rotate;
2765 	    }
2766 	}
2767 
2768 	if (splot_map && axis_array[SECOND_Y_AXIS].ticmode)
2769 	    gen_tics(&axis_array[SECOND_Y_AXIS], ytick_callback);
2770     }
2771 
2772     /* do z tics */
2773     if (Z_AXIS.ticmode
2774 	/* Don't output tics and grids if this is the front part of a
2775 	 * two-part grid drawing process: */
2776 	&& (FRONTGRID != whichgrid)
2777 	&& (splot_map == FALSE)
2778 	&& (surface_rot_x != 0)
2779 	&& (draw_surface
2780 	    || (draw_contour & CONTOUR_SRF)
2781 	    || strchr(pm3d.where,'s') != NULL
2782 	    )
2783 	) {
2784 	gen_tics(&axis_array[FIRST_Z_AXIS], ztick_callback);
2785     }
2786     if ((Y_AXIS.zeroaxis)
2787 	&& !X_AXIS.log
2788 	&& inrange(0, X_AXIS.min, X_AXIS.max)
2789 	) {
2790 	vertex v1, v2;
2791 
2792 	/* line through x=0 */
2793 	map3d_xyz(0.0, Y_AXIS.min, base_z, &v1);
2794 	map3d_xyz(0.0, Y_AXIS.max, base_z, &v2);
2795 	draw3d_line(&v1, &v2, Y_AXIS.zeroaxis);
2796     }
2797     if ((Z_AXIS.zeroaxis)
2798 	&& !X_AXIS.log
2799 	&& inrange(0, X_AXIS.min, X_AXIS.max)
2800 	) {
2801 	vertex v1, v2;
2802 
2803 	/* line through x=0 y=0 */
2804 	map3d_xyz(0.0, 0.0, Z_AXIS.min, &v1);
2805 	map3d_xyz(0.0, 0.0, Z_AXIS.max, &v2);
2806 	draw3d_line(&v1, &v2, Z_AXIS.zeroaxis);
2807     }
2808     if ((X_AXIS.zeroaxis)
2809 	&& !Y_AXIS.log
2810 	&& inrange(0, Y_AXIS.min, Y_AXIS.max)
2811 	) {
2812 	vertex v1, v2;
2813 
2814 	term_apply_lp_properties(X_AXIS.zeroaxis);
2815 	/* line through y=0 */
2816 	map3d_xyz(X_AXIS.min, 0.0, base_z, &v1);
2817 	map3d_xyz(X_AXIS.max, 0.0, base_z, &v2);
2818 	draw3d_line(&v1, &v2, X_AXIS.zeroaxis);
2819     }
2820 
2821     /* PLACE ZLABEL - along the middle grid Z axis - eh ? */
2822     if (Z_AXIS.label.text
2823 	&& (splot_map == FALSE)
2824 	&& (current_layer == LAYER_FRONT || whichgrid == ALLGRID)
2825 	&& (draw_surface
2826 	    || (draw_contour & CONTOUR_SRF)
2827 	    || strpbrk(pm3d.where,"st") != NULL
2828 	    )
2829 	) {
2830 	vertex v1;
2831 	double mid_z;
2832 
2833 	if (nonlinear(&Z_AXIS)) {
2834 	    mid_z = (Z_AXIS.linked_to_primary->max + Z_AXIS.linked_to_primary->min) / 2.;
2835 	    mid_z = eval_link_function(&Z_AXIS, mid_z);
2836 	} else
2837 	    mid_z = (Z_AXIS.max + Z_AXIS.min) / 2.;
2838 
2839 	if (Z_AXIS.ticmode & TICS_ON_AXIS) {
2840 	    map3d_xyz(0, 0, mid_z, &v1);
2841 	    TERMCOORD(&v1, x, y);
2842 	    x -= 5 * t->h_char;
2843 	} else {
2844 	    map3d_xyz(zaxis_x, zaxis_y, mid_z, &v1);
2845 	    TERMCOORD(&v1, x, y);
2846 	    if (fabs(azimuth) > 80)
2847 		y += 2 * sgn(azimuth) * t->v_char;
2848 	    else
2849 		x -= 7 * t->h_char;
2850 	}
2851 
2852 	if (Z_AXIS.label.tag == ROTATE_IN_3D_LABEL_TAG) {
2853 	    double ang, angx0, angx1, angy0, angy1;
2854 	    map3d_xy_double(zaxis_x, zaxis_y, Z_AXIS.min, &angx0, &angy0);
2855 	    map3d_xy_double(zaxis_x, zaxis_y, Z_AXIS.max, &angx1, &angy1);
2856 	    ang = atan2(angy1-angy0, angx1-angx0) / DEG2RAD;
2857 	    if (ang < -90) ang += 180;
2858 	    if (ang > 90) ang -= 180;
2859 	    Z_AXIS.label.rotate = (ang > 0) ? floor(ang + 0.5) : floor(ang - 0.5);
2860 	}
2861 
2862 	write_label(x, y, &Z_AXIS.label);
2863     }
2864 
2865     clip_area = clip_save;
2866 }
2867 
2868 static void
xtick_callback(struct axis * this_axis,double place,char * text,int ticlevel,struct lp_style_type grid,struct ticmark * userlabels)2869 xtick_callback(
2870     struct axis *this_axis,
2871     double place,
2872     char *text,
2873     int ticlevel,
2874     struct lp_style_type grid,		/* linetype or -2 for none */
2875     struct ticmark *userlabels)
2876 {
2877     double scale = tic_scale(ticlevel, this_axis) * (this_axis->tic_in ? 1 : -1);
2878     double other_end = Y_AXIS.min + Y_AXIS.max - xaxis_y;
2879     struct termentry *t = term;
2880     vertex v1, v2, v3, v4;
2881 
2882     /* Draw full-length grid line */
2883     map3d_xyz(place, xaxis_y, base_z, &v1);
2884     if (grid.l_type > LT_NODRAW) {
2885 	(t->layer)(TERM_LAYER_BEGIN_GRID);
2886 	/* to save mapping twice, map non-axis y */
2887 	map3d_xyz(place, other_end, base_z, &v3);
2888 	draw3d_line(&v1, &v3, &grid);
2889 	(t->layer)(TERM_LAYER_END_GRID);
2890     }
2891     /* Vertical grid lines (in yz plane) */
2892     if (grid_vertical_lines && grid.l_type > LT_NODRAW) {
2893 	vertex v4, v5;
2894 	double which_face = (surface_rot_x > 90 && surface_rot_x < 270) ? xaxis_y : other_end;
2895 	(t->layer)(TERM_LAYER_BEGIN_GRID);
2896 	map3d_xyz(place, which_face, Z_AXIS.min, &v4);
2897 	map3d_xyz(place, which_face, ceiling_z, &v5);
2898 	draw3d_line(&v4, &v5, &grid);
2899 	(t->layer)(TERM_LAYER_END_GRID);
2900     }
2901     if ((X_AXIS.ticmode & TICS_ON_AXIS)
2902 	&& !Y_AXIS.log
2903 	&& inrange (0.0, Y_AXIS.min, Y_AXIS.max)
2904 	) {
2905 	map3d_xyz(place, 0.0, base_z, &v1);
2906     }
2907 
2908     /* NB: secondary axis must be linked to primary */
2909     if (this_axis->index == SECOND_X_AXIS
2910     &&  this_axis->linked_to_primary
2911     &&  this_axis->link_udf->at != NULL) {
2912 	place = eval_link_function(&axis_array[FIRST_X_AXIS], place);
2913     }
2914 
2915     /* Draw bottom tic mark */
2916     if ((this_axis->index == FIRST_X_AXIS)
2917     ||  (this_axis->index == SECOND_X_AXIS && (this_axis->ticmode & TICS_MIRROR))) {
2918 	v2.x = v1.x + tic_unitx * scale * t->v_tic ;
2919 	v2.y = v1.y + tic_unity * scale * t->v_tic ;
2920 	v2.z = v1.z + tic_unitz * scale * t->v_tic ;
2921 	v2.real_z = v1.real_z;
2922 	draw3d_line(&v1, &v2, &border_lp);
2923     }
2924 
2925     /* Draw top tic mark */
2926     if ((this_axis->index == SECOND_X_AXIS)
2927     ||  (this_axis->index == FIRST_X_AXIS && (this_axis->ticmode & TICS_MIRROR))) {
2928 	if (xz_projection)
2929 	    map3d_xyz(place, other_end, Z_AXIS.max, &v3);
2930 	else
2931 	    map3d_xyz(place, other_end, base_z, &v3);
2932 	v4.x = v3.x - tic_unitx * scale * t->v_tic;
2933 	v4.y = v3.y - tic_unity * scale * t->v_tic;
2934 	v4.z = v3.z - tic_unitz * scale * t->v_tic;
2935 	v4.real_z = v3.real_z;
2936 	draw3d_line(&v3, &v4, &border_lp);
2937     }
2938 
2939     /* Draw tic label */
2940     if (text) {
2941 	int just;
2942 	int x2, y2;
2943 	int angle;
2944 	int offsetx, offsety;
2945 
2946 	/* Skip label if we've already written a user-specified one here */
2947 #	define MINIMUM_SEPARATION 0.001
2948 	while (userlabels) {
2949 	    if (fabs((place - userlabels->position) / (X_AXIS.max - X_AXIS.min))
2950 		<= MINIMUM_SEPARATION) {
2951 		text = NULL;
2952 		break;
2953 	    }
2954 	    userlabels = userlabels->next;
2955 	}
2956 #	undef MINIMUM_SEPARATION
2957 
2958 	/* get offset */
2959 	map3d_position_r(&(this_axis->ticdef.offset), &offsetx, &offsety, "xtics");
2960 
2961 	/* allow manual justification of tick labels, but only for projections */
2962 	if ((splot_map || xz_projection) && this_axis->manual_justify)
2963 	    just = this_axis->tic_pos;
2964 	else if (tic_unitx * xscaler < -0.9)
2965 	    just = LEFT;
2966 	else if (tic_unitx * xscaler < 0.9)
2967 	    just = CENTRE;
2968 	else
2969 	    just = RIGHT;
2970 
2971 	if (this_axis->index == SECOND_X_AXIS) {
2972 	    v4.x = v3.x + tic_unitx * t->h_char * 1;
2973 	    v4.y = v3.y + tic_unity * t->v_char * 1;
2974 	    if (!this_axis->tic_in) {
2975 		v4.x += tic_unitx * t->v_tic * this_axis->ticscale;
2976 		v4.y += tic_unity * t->v_tic * this_axis->ticscale;
2977 	    }
2978 	    TERMCOORD(&v4, x2, y2);
2979 	} else {
2980 	    v2.x = v1.x - tic_unitx * t->h_char * 1;
2981 	    v2.y = v1.y - tic_unity * t->v_char * 1;
2982 	    if (!this_axis->tic_in) {
2983 		v2.x -= tic_unitx * t->v_tic * this_axis->ticscale;
2984 		v2.y -= tic_unity * t->v_tic * this_axis->ticscale;
2985 	    }
2986 	    TERMCOORD(&v2, x2, y2);
2987 	}
2988 
2989 	/* User-specified different color for the tics text */
2990 	if (this_axis->ticdef.textcolor.type != TC_DEFAULT)
2991 	    apply_pm3dcolor(&(this_axis->ticdef.textcolor));
2992 	angle = this_axis->tic_rotate;
2993 	if (!(splot_map && angle && term->text_angle(angle)))
2994 	    angle = 0;
2995 	ignore_enhanced(!this_axis->ticdef.enhanced);
2996 	write_multiline(x2+offsetx, y2+offsety, text, just, JUST_TOP,
2997 			    angle, this_axis->ticdef.font);
2998 	ignore_enhanced(FALSE);
2999 	term->text_angle(0);
3000 	term_apply_lp_properties(&border_lp);
3001     }
3002 }
3003 
3004 static void
ytick_callback(struct axis * this_axis,double place,char * text,int ticlevel,struct lp_style_type grid,struct ticmark * userlabels)3005 ytick_callback(
3006     struct axis *this_axis,
3007     double place,
3008     char *text,
3009     int ticlevel,
3010     struct lp_style_type grid,
3011     struct ticmark *userlabels)
3012 {
3013     double scale = tic_scale(ticlevel, this_axis) * (this_axis->tic_in ? 1 : -1);
3014     double other_end = X_AXIS.min + X_AXIS.max - yaxis_x;
3015     struct termentry *t = term;
3016     vertex v1, v2, v3, v4;
3017 
3018     /* Draw full-length grid line */
3019     map3d_xyz(yaxis_x, place, base_z, &v1);
3020     if (grid.l_type > LT_NODRAW) {
3021 	(t->layer)(TERM_LAYER_BEGIN_GRID);
3022 	map3d_xyz(other_end, place, base_z, &v3);
3023 	draw3d_line(&v1, &v3, &grid);
3024 	(t->layer)(TERM_LAYER_END_GRID);
3025     }
3026     /* Vertical grid lines (in xz plane) */
3027     if (grid_vertical_lines && grid.l_type > LT_NODRAW) {
3028 	vertex v4, v5;
3029 	double which_face = (surface_rot_x > 90 && surface_rot_x < 270) ? yaxis_x : other_end;
3030 	(t->layer)(TERM_LAYER_BEGIN_GRID);
3031 	map3d_xyz(which_face, place, Z_AXIS.min, &v4);
3032 	map3d_xyz(which_face, place, ceiling_z, &v5);
3033 	draw3d_line(&v4, &v5, &grid);
3034 	(t->layer)(TERM_LAYER_END_GRID);
3035     }
3036     if (Y_AXIS.ticmode & TICS_ON_AXIS
3037 	&& !X_AXIS.log
3038 	&& inrange (0.0, X_AXIS.min, X_AXIS.max)
3039 	) {
3040 	map3d_xyz(0.0, place, base_z, &v1);
3041     }
3042 
3043     /* NB: secondary axis must be linked to primary */
3044     if (this_axis->index == SECOND_Y_AXIS
3045     &&  this_axis->linked_to_primary
3046     &&  this_axis->link_udf->at != NULL) {
3047 	place = eval_link_function(&axis_array[FIRST_Y_AXIS], place);
3048     }
3049 
3050     /* Draw left tic mark */
3051     if ((this_axis->index == FIRST_Y_AXIS)
3052     ||  (this_axis->index == SECOND_Y_AXIS && (this_axis->ticmode & TICS_MIRROR))) {
3053 	v2.x = v1.x + tic_unitx * scale * t->h_tic;
3054 	v2.y = v1.y + tic_unity * scale * t->h_tic;
3055 	v2.z = v1.z + tic_unitz * scale * t->h_tic;
3056 	v2.real_z = v1.real_z;
3057 	draw3d_line(&v1, &v2, &border_lp);
3058     }
3059 
3060     /* Draw right tic mark */
3061     if ((this_axis->index == SECOND_Y_AXIS)
3062     ||  (this_axis->index == FIRST_Y_AXIS && (this_axis->ticmode & TICS_MIRROR))) {
3063 	if (yz_projection)
3064 	    map3d_xyz(other_end, place, Z_AXIS.min, &v3);
3065 	else
3066 	    map3d_xyz(other_end, place, base_z, &v3);
3067 	v4.x = v3.x - tic_unitx * scale * t->h_tic;
3068 	v4.y = v3.y - tic_unity * scale * t->h_tic;
3069 	v4.z = v3.z - tic_unitz * scale * t->h_tic;
3070 	v4.real_z = v3.real_z;
3071 	draw3d_line(&v3, &v4, &border_lp);
3072     }
3073 
3074     /* Draw tic label */
3075     if (text) {
3076 	int just;
3077 	int x2, y2;
3078 	int angle;
3079 	int offsetx, offsety;
3080 
3081 	/* Skip label if we've already written a user-specified one here */
3082 #	define MINIMUM_SEPARATION 0.001
3083 	while (userlabels) {
3084 	    if (fabs((place - userlabels->position) / (Y_AXIS.max - Y_AXIS.min))
3085 		<= MINIMUM_SEPARATION) {
3086 		text = NULL;
3087 		break;
3088 	    }
3089 	    userlabels = userlabels->next;
3090 	}
3091 #	undef MINIMUM_SEPARATION
3092 
3093 	/* get offset */
3094 	map3d_position_r(&(this_axis->ticdef.offset), &offsetx, &offsety, "ytics");
3095 
3096 	/* allow manual justification of tick labels, but only for projections */
3097 	if ((splot_map || yz_projection) && this_axis->manual_justify)
3098 	    just = this_axis->tic_pos;
3099 	else if (tic_unitx * xscaler < -0.9)
3100 	    just = (this_axis->index == FIRST_Y_AXIS) ? LEFT : RIGHT;
3101 	else if (tic_unitx * xscaler < 0.9)
3102 	    just = CENTRE;
3103 	else
3104 	    just = (this_axis->index == FIRST_Y_AXIS) ? RIGHT : LEFT;
3105 
3106 	if (this_axis->index == SECOND_Y_AXIS) {
3107 	    v4.x = v3.x + tic_unitx * t->h_char * 1;
3108 	    v4.y = v3.y + tic_unity * t->v_char * 1;
3109 	    if (!this_axis->tic_in) {
3110 		v4.x += tic_unitx * t->h_tic * this_axis->ticscale;
3111 		v4.y += tic_unity * t->v_tic * this_axis->ticscale;
3112 	    }
3113 	    TERMCOORD(&v4, x2, y2);
3114 	} else {
3115 	    v2.x = v1.x - tic_unitx * t->h_char * 1;
3116 	    v2.y = v1.y - tic_unity * t->v_char * 1;
3117 	    if (!this_axis->tic_in) {
3118 		v2.x -= tic_unitx * t->h_tic * this_axis->ticscale;
3119 		v2.y -= tic_unity * t->v_tic * this_axis->ticscale;
3120 	    }
3121 	    TERMCOORD(&v2, x2, y2);
3122 	}
3123 
3124 	/* User-specified different color for the tics text */
3125 	if (this_axis->ticdef.textcolor.type != TC_DEFAULT)
3126 	    apply_pm3dcolor(&(this_axis->ticdef.textcolor));
3127 	angle = this_axis->tic_rotate;
3128 	if (!(splot_map && angle && term->text_angle(angle)))
3129 	    angle = 0;
3130 	ignore_enhanced(!this_axis->ticdef.enhanced);
3131 	write_multiline(x2+offsetx, y2+offsety, text, just, JUST_TOP,
3132 			angle, this_axis->ticdef.font);
3133 	ignore_enhanced(FALSE);
3134 	term->text_angle(0);
3135 	term_apply_lp_properties(&border_lp);
3136     }
3137 }
3138 
3139 static void
ztick_callback(struct axis * this_axis,double place,char * text,int ticlevel,struct lp_style_type grid,struct ticmark * userlabels)3140 ztick_callback(
3141     struct axis *this_axis,
3142     double place,
3143     char *text,
3144     int ticlevel,
3145     struct lp_style_type grid,
3146     struct ticmark *userlabels)
3147 {
3148     struct termentry *t = term;
3149     int len = tic_scale(ticlevel, this_axis)
3150 	* (this_axis->tic_in ? 1 : -1) * (term->h_tic);
3151     vertex v1, v2, v3;
3152 
3153     if (this_axis->ticmode & TICS_ON_AXIS)
3154 	map3d_xyz(0., 0., place, &v1);
3155     else
3156 	map3d_xyz(zaxis_x, zaxis_y, place, &v1);
3157 
3158     /* Needed both for grid and for azimuth ztics */
3159     map3d_xyz(right_x, right_y, place, &v3);
3160 
3161     if (grid.l_type > LT_NODRAW) {
3162 	(t->layer)(TERM_LAYER_BEGIN_GRID);
3163 	map3d_xyz(back_x, back_y, place, &v2);
3164 	draw3d_line(&v1, &v2, &grid);
3165 	draw3d_line(&v2, &v3, &grid);
3166 	(t->layer)(TERM_LAYER_END_GRID);
3167     }
3168     if (azimuth != 0) {
3169 	v2.x = v1.x + (v3.x - v1.x) * len / xyscaler;
3170 	v2.y = v1.y + (v3.y - v1.y) * len / xyscaler;
3171 	v2.z = v1.z + (v3.z - v1.z) * len / xyscaler;
3172     } else {
3173 	v2.x = v1.x + len / (double)xscaler;
3174 	v2.y = v1.y;
3175 	v2.z = v1.z;
3176     }
3177     v2.real_z = v1.real_z;
3178     draw3d_line(&v1, &v2, &border_lp);
3179 
3180     if (text) {
3181 	int x1, y1;
3182 	int just;
3183 	int offsetx, offsety;
3184 
3185 	/* Skip label if we've already written a user-specified one here */
3186 #	define MINIMUM_SEPARATION 0.001
3187 	while (userlabels) {
3188 	    if (fabs((place - userlabels->position) / (Z_AXIS.max - Z_AXIS.min))
3189 		<= MINIMUM_SEPARATION) {
3190 		text = NULL;
3191 		break;
3192 	    }
3193 	    userlabels = userlabels->next;
3194 	}
3195 #	undef MINIMUM_SEPARATION
3196 
3197 	/* get offset */
3198 	map3d_position_r(&(this_axis->ticdef.offset), &offsetx, &offsety, "ztics");
3199 	TERMCOORD(&v1, x1, y1);
3200 
3201 	if (fabs(azimuth) > 80) {
3202 	    /* Z axis is (nearly) horizontal */
3203 	    y1 += sgn(azimuth) * (term->v_tic) * 2;
3204 	} else {
3205 	    /* the normal case */
3206 	    x1 -= (term->h_tic) * 2;
3207 	    if (!this_axis->tic_in)
3208 		x1 -= (term->h_tic) * this_axis->ticscale;
3209 	}
3210 
3211 	/* allow manual justification of tick labels, but only for projections */
3212 	if ((xz_projection || yz_projection) && this_axis->manual_justify)
3213 	    just = this_axis->tic_pos;
3214 	else
3215 	    just = RIGHT;
3216 
3217 	/* User-specified different color for the tics text */
3218 	if (this_axis->ticdef.textcolor.type == TC_Z)
3219 	    this_axis->ticdef.textcolor.value = place;
3220 	if (this_axis->ticdef.textcolor.type != TC_DEFAULT)
3221 	    apply_pm3dcolor(&(this_axis->ticdef.textcolor));
3222 	ignore_enhanced(!this_axis->ticdef.enhanced);
3223 	write_multiline(x1+offsetx, y1+offsety, text, just, JUST_CENTRE,
3224 			0, this_axis->ticdef.font);
3225 	ignore_enhanced(FALSE);
3226 	term_apply_lp_properties(&border_lp);
3227     }
3228 
3229     if (Z_AXIS.ticmode & TICS_MIRROR) {
3230 	if (azimuth != 0) {
3231 	    v2.x = v3.x + (v1.x - v3.x) * len / xyscaler;
3232 	    v2.y = v3.y + (v1.y - v3.y) * len / xyscaler;
3233 	    v2.z = v3.z + (v1.z - v3.z) * len / xyscaler;
3234 	    draw3d_line(&v3, &v2, &border_lp);
3235 	} else {
3236 	    map3d_xyz(right_x, right_y, place, &v1);
3237 	    v2.x = v1.x - len / (double)xscaler;
3238 	    v2.y = v1.y;
3239 	    v2.z = v1.z;
3240 	    v2.real_z = v1.real_z;
3241 	    draw3d_line(&v1, &v2, &border_lp);
3242 	}
3243     }
3244 }
3245 
3246 static int
map3d_getposition(struct position * pos,const char * what,double * xpos,double * ypos,double * zpos)3247 map3d_getposition(
3248     struct position *pos,
3249     const char *what,
3250     double *xpos, double *ypos, double *zpos)
3251 {
3252     TBOOLEAN screen_coords = FALSE;
3253     TBOOLEAN char_coords = FALSE;
3254     TBOOLEAN plot_coords = FALSE;
3255     double xx, yy;
3256 
3257     switch (pos->scalex) {
3258     case first_axes:
3259     case second_axes:
3260 	*xpos = axis_log_value_checked(FIRST_X_AXIS, *xpos, what);
3261 	plot_coords = TRUE;
3262 	break;
3263     case graph:
3264 	*xpos = X_AXIS.min + *xpos * (X_AXIS.max - X_AXIS.min);
3265 	plot_coords = TRUE;
3266 	break;
3267     case screen:
3268 	*xpos = *xpos * (term->xmax -1) + 0.5;
3269 	screen_coords = TRUE;
3270 	break;
3271     case character:
3272 	*xpos = *xpos * term->h_char + 0.5;
3273 	char_coords = TRUE;
3274 	break;
3275     case polar_axes:
3276 	(void) polar_to_xy(*xpos, *ypos, &xx, &yy, FALSE);
3277 	*xpos = axis_log_value_checked(FIRST_X_AXIS, xx, what);
3278 	*ypos = axis_log_value_checked(FIRST_Y_AXIS, yy, what);
3279 	plot_coords = TRUE;
3280 	pos->scaley = polar_axes;	/* Just to make sure */
3281 	break;
3282     }
3283 
3284     switch (pos->scaley) {
3285     case first_axes:
3286     case second_axes:
3287 	*ypos = axis_log_value_checked(FIRST_Y_AXIS, *ypos, what);
3288 	plot_coords = TRUE;
3289 	break;
3290     case graph:
3291 	if (splot_map)
3292 	    *ypos = Y_AXIS.max - *ypos * (Y_AXIS.max - Y_AXIS.min);
3293 	else
3294 	    *ypos = Y_AXIS.min + *ypos * (Y_AXIS.max - Y_AXIS.min);
3295 	plot_coords = TRUE;
3296 	break;
3297     case screen:
3298 	*ypos = *ypos * (term->ymax -1) + 0.5;
3299 	screen_coords = TRUE;
3300 	break;
3301     case character:
3302 	*ypos = *ypos * term->v_char + 0.5;
3303 	char_coords = TRUE;
3304 	break;
3305     case polar_axes:
3306 	break;
3307     }
3308 
3309     switch (pos->scalez) {
3310     case first_axes:
3311     case second_axes:
3312     case polar_axes:
3313 	if (splot_map)
3314 	    *zpos = 1;	/* Avoid failure if z=0 with logscale z */
3315 	else
3316 	    *zpos = axis_log_value_checked(FIRST_Z_AXIS, *zpos, what);
3317 	plot_coords = TRUE;
3318 	break;
3319     case graph:
3320 	*zpos = Z_AXIS.min + *zpos * (Z_AXIS.max - Z_AXIS.min);
3321 	plot_coords = TRUE;
3322 	break;
3323     case screen:
3324 	screen_coords = TRUE;
3325 	break;
3326     case character:
3327 	char_coords = TRUE;
3328 	break;
3329     }
3330 
3331     if (plot_coords && (screen_coords || char_coords))
3332 	int_error(NO_CARET,"Cannot mix screen or character coords with plot coords");
3333 
3334     return (screen_coords || char_coords);
3335 }
3336 
3337 /*
3338  * map3d_position()  wrapper for map3d_position_double
3339  */
3340 void
map3d_position(struct position * pos,int * x,int * y,const char * what)3341 map3d_position(
3342     struct position *pos,
3343     int *x, int *y,
3344     const char *what)
3345 {
3346     double xx, yy;
3347 
3348     map3d_position_double(pos, &xx, &yy, what);
3349     *x = xx;
3350     *y = yy;
3351 }
3352 
3353 void
map3d_position_double(struct position * pos,double * x,double * y,const char * what)3354 map3d_position_double(
3355     struct position *pos,
3356     double *x, double *y,
3357     const char *what)
3358 {
3359     double xpos = pos->x;
3360     double ypos = pos->y;
3361     double zpos = pos->z;
3362 
3363     if (map3d_getposition(pos, what, &xpos, &ypos, &zpos) == 0) {
3364 	map3d_xy_double(xpos, ypos, zpos, x, y);
3365     } else {
3366 	/* Screen or character coordinates */
3367 	*x = xpos;
3368 	*y = ypos;
3369     }
3370 }
3371 
3372 void
map3d_position_r(struct position * pos,int * x,int * y,const char * what)3373 map3d_position_r(
3374     struct position *pos,
3375     int *x, int *y,
3376     const char *what)
3377 {
3378     double xx, yy;
3379     map3d_position_r_double(pos, &xx, &yy, what);
3380     *x = xx;
3381     *y = yy;
3382 }
3383 
3384 void
map3d_position_r_double(struct position * pos,double * xx,double * yy,const char * what)3385 map3d_position_r_double(
3386     struct position *pos,
3387     double *xx, double *yy,
3388     const char *what)
3389 {
3390     double xpos = pos->x;
3391     double ypos = pos->y;
3392     double zpos = (splot_map) ? Z_AXIS.min : pos->z;
3393 
3394     /* startpoint in graph coordinates */
3395     if (map3d_getposition(pos, what, &xpos, &ypos, &zpos) == 0) {
3396 	int xoriginlocal, yoriginlocal;
3397 	map3d_xy_double(xpos, ypos, zpos, xx, yy);
3398 	if (pos->scalex == graph)
3399 	    xpos = X_AXIS.min;
3400 	else
3401 	    xpos = 0;
3402 	if (pos->scaley == graph)
3403 	    ypos = (splot_map) ? Y_AXIS.max : Y_AXIS.min;
3404 	else
3405 	    ypos = 0;
3406 	if (pos->scalez == graph)
3407 	    zpos = Z_AXIS.min;
3408 	else if (splot_map)
3409 	    zpos = Z_AXIS.min;
3410 	else
3411 	    zpos = 0;
3412 	map3d_xy(xpos, ypos, zpos, &xoriginlocal, &yoriginlocal);
3413 	*xx -= xoriginlocal;
3414 	*yy -= yoriginlocal;
3415     } else {
3416     /* endpoint `screen' or 'character' coordinates */
3417 	*xx = xpos;
3418 	*yy = ypos;
3419     }
3420     return;
3421 }
3422 
3423 /*
3424  * these code blocks were moved to functions, to make the code simpler
3425  */
3426 
3427 static void
key_text(int xl,int yl,char * text)3428 key_text(int xl, int yl, char *text)
3429 {
3430     legend_key *key = &keyT;
3431 
3432     (term->layer)(TERM_LAYER_BEGIN_KEYSAMPLE);
3433     if (key->just == GPKEY_LEFT && key->region != GPKEY_USER_PLACEMENT) {
3434 	write_multiline(xl + key_text_left, yl, text, LEFT, JUST_TOP, 0, key->font);
3435     } else {
3436 	if ((*term->justify_text) (RIGHT)) {
3437 	    write_multiline(xl + key_text_right, yl, text, RIGHT, JUST_TOP, 0, key->font);
3438 	} else {
3439 	    int x = xl + key_text_right - (term->h_char) * estimate_strlen(text);
3440 	    write_multiline(x, yl, text, LEFT, JUST_TOP, 0, key->font);
3441 	}
3442     }
3443     (term->layer)(TERM_LAYER_END_KEYSAMPLE);
3444 }
3445 
3446 static void
key_sample_line(int xl,int yl)3447 key_sample_line(int xl, int yl)
3448 {
3449     BoundingBox *clip_save = clip_area;
3450 
3451     /* Clip against canvas */
3452     if (term->flags & TERM_CAN_CLIP)
3453 	clip_area = NULL;
3454     else
3455 	clip_area = &canvas;
3456 
3457     (term->layer)(TERM_LAYER_BEGIN_KEYSAMPLE);
3458     draw_clip_line(xl + key_sample_left, yl, xl + key_sample_right, yl);
3459     (term->layer)(TERM_LAYER_END_KEYSAMPLE);
3460 
3461     clip_area = clip_save;
3462 }
3463 
3464 static void
key_sample_point(struct surface_points * this_plot,int xl,int yl,int pointtype)3465 key_sample_point(struct surface_points *this_plot, int xl, int yl, int pointtype)
3466 {
3467     BoundingBox *clip_save = clip_area;
3468 
3469     /* Clip against canvas */
3470     if (term->flags & TERM_CAN_CLIP)
3471 	clip_area = NULL;
3472     else
3473 	clip_area = &canvas;
3474 
3475     (term->layer)(TERM_LAYER_BEGIN_KEYSAMPLE);
3476     if (!clip_point(xl + key_point_offset, yl)) {
3477 	if (pointtype == PT_CHARACTER && this_plot) {
3478 	    apply_pm3dcolor(&(this_plot->labels->textcolor));
3479 	    (*term->put_text) (xl + key_point_offset, yl,
3480 			       this_plot->lp_properties.p_char);
3481 	    apply_pm3dcolor(&(this_plot->lp_properties.pm3d_color));
3482 	} else {
3483 	    (*term->point) (xl + key_point_offset, yl, pointtype);
3484 	}
3485     }
3486     (term->layer)(TERM_LAYER_END_KEYSAMPLE);
3487 
3488     clip_area = clip_save;
3489 }
3490 
3491 static void
key_sample_fill(int xl,int yl,struct surface_points * this_plot)3492 key_sample_fill(int xl, int yl, struct surface_points *this_plot)
3493 {
3494     struct fill_style_type *fs = &this_plot->fill_properties;
3495     int style = style_from_fill(fs);
3496     int x = xl + key_sample_left;
3497     int y = yl - key_entry_height/4;
3498     int w = key_sample_right - key_sample_left;
3499     int h = key_entry_height/2;
3500 
3501     if (!(term->fillbox))
3502 	return;
3503     (term->layer)(TERM_LAYER_BEGIN_KEYSAMPLE);
3504 
3505     if (this_plot->plot_style == CIRCLES) {
3506 	do_arc(x+w/2, yl, key_entry_height/4, 0., 360., style, FALSE);
3507 	/* Retrace the border if the style requests it */
3508 	if (need_fill_border(fs))
3509 	    do_arc(x+w/2, yl, key_entry_height/4, 0., 360., 0, FALSE);
3510 
3511     } else if (w > 0) {
3512 	(term->fillbox)(style,x,y,w,h);
3513 
3514 	/* FIXME:  what other plot styles want a border on the key sample? */
3515 	if ((this_plot->plot_style & PLOT_STYLE_HAS_PM3DBORDER)) {
3516 	    if (pm3d.border.l_type != LT_NODRAW && pm3d.border.l_type != LT_DEFAULT)
3517 		term_apply_lp_properties(&pm3d.border);
3518 	    newpath();
3519 	    draw_clip_line( x, y, x+w, y);
3520 	    draw_clip_line( x+w, y, x+w, y+h);
3521 	    draw_clip_line( x+w, y+h, x, y+h);
3522 	    draw_clip_line( x, y+h, x, y);
3523 	    closepath();
3524 	}
3525     }
3526 
3527     (term->layer)(TERM_LAYER_END_KEYSAMPLE);
3528 }
3529 
3530 
3531 /*
3532  * returns minimal and maximal values of the cb-range (or z-range if taking the
3533  * color from the z value) of the given surface
3534  */
3535 static void
get_surface_cbminmax(struct surface_points * plot,double * cbmin,double * cbmax)3536 get_surface_cbminmax(struct surface_points *plot, double *cbmin, double *cbmax)
3537 {
3538     int i, curve = 0;
3539     TBOOLEAN color_from_column = plot->pm3d_color_from_column; /* just a shortcut */
3540     coordval cb;
3541     struct iso_curve *icrvs = plot->iso_crvs;
3542     struct coordinate *points;
3543 
3544     *cbmin = VERYLARGE;
3545     *cbmax = -VERYLARGE;
3546 
3547     while (icrvs && curve < plot->num_iso_read) {
3548 	/* fprintf(stderr,"**** NEW ISOCURVE - nb of pts: %i ****\n", icrvs->p_count); */
3549 	for (i = 0, points = icrvs->points; i < icrvs->p_count; i++) {
3550 		/* fprintf(stderr,"  point i=%i => x=%4g y=%4g z=%4lg cb=%4lg\n",i, points[i].x,points[i].y,points[i].z,points[i].CRD_COLOR); */
3551 		if (points[i].type == INRANGE) {
3552 		    /* ?? if (!clip_point(x, y)) ... */
3553 		    cb = color_from_column ? points[i].CRD_COLOR : points[i].z;
3554 		    if (cb < *cbmin) *cbmin = cb;
3555 		    if (cb > *cbmax) *cbmax = cb;
3556 		}
3557 	} /* points on one scan */
3558 	icrvs = icrvs->next;
3559 	curve++;
3560     } /* surface */
3561 }
3562 
3563 
3564 /*
3565  * Draw a gradient color line for a key (legend).
3566  */
3567 static void
key_sample_line_pm3d(struct surface_points * plot,int xl,int yl)3568 key_sample_line_pm3d(struct surface_points *plot, int xl, int yl)
3569 {
3570     int steps = GPMIN(24, abs(key_sample_right - key_sample_left));
3571     /* don't multiply by key->swidth --- could be >> palette.maxcolors */
3572     int x_to = xl + key_sample_right;
3573     double step = ((double)(key_sample_right - key_sample_left)) / steps;
3574     int i = 1, x1 = xl + key_sample_left, x2;
3575     double cbmin, cbmax;
3576     double gray, gray_from, gray_to, gray_step;
3577     int colortype = plot->lp_properties.pm3d_color.type;
3578 
3579     /* If plot uses a constant color, set it here and then let simpler routine take over */
3580     if ((colortype == TC_RGB && plot->lp_properties.pm3d_color.value >= 0.0)
3581     || (colortype == TC_LT)
3582     || (colortype == TC_LINESTYLE)) {
3583 	lp_style_type lptmp = plot->lp_properties;
3584 	if (plot->lp_properties.l_type == LT_COLORFROMCOLUMN)
3585 		lp_use_properties(&lptmp, (int)(plot->iso_crvs->points[0].CRD_COLOR));
3586 	apply_pm3dcolor(&lptmp.pm3d_color);
3587 	key_sample_line(xl,yl);
3588 	return;
3589     }
3590 
3591     /* color gradient only over the cb-values of the surface, if smaller than the
3592      * cb-axis range (the latter are gray values [0:1]) */
3593     get_surface_cbminmax(plot, &cbmin, &cbmax);
3594     if (cbmin > cbmax) return; /* splot 1/0, for example */
3595     cbmin = GPMAX(cbmin, CB_AXIS.min);
3596     cbmax = GPMIN(cbmax, CB_AXIS.max);
3597     gray_from = cb2gray(cbmin);
3598     gray_to = cb2gray(cbmax);
3599     gray_step = (gray_to - gray_from)/steps;
3600 
3601     clip_move(x1, yl);
3602     x2 = x1;
3603     while (i <= steps) {
3604 	/* if (i>1) set_color( i==steps ? 1 : (i-0.5)/steps ); ... range [0:1] */
3605 	gray = (i==steps) ? gray_to : gray_from+i*gray_step;
3606 	set_color(gray);
3607 	clip_move(x2, yl);
3608 	x2 = (i==steps) ? x_to : x1 + (int)(i*step+0.5);
3609 	clip_vector(x2, yl);
3610 	i++;
3611     }
3612 }
3613 
3614 
3615 /*
3616  * Draw a sequence of points with gradient color a key (legend).
3617  */
3618 static void
key_sample_point_pm3d(struct surface_points * plot,int xl,int yl,int pointtype)3619 key_sample_point_pm3d(
3620     struct surface_points *plot,
3621     int xl, int yl,
3622     int pointtype)
3623 {
3624     BoundingBox *clip_save = clip_area;
3625     int x_to = xl + key_sample_right;
3626     int i = 0, x1 = xl + key_sample_left, x2;
3627     double cbmin, cbmax;
3628     double gray, gray_from, gray_to, gray_step;
3629     int colortype = plot->lp_properties.pm3d_color.type;
3630     /* rule for number of steps: 3*char_width*pointsize or char_width for dots,
3631      * but at least 3 points */
3632     double step = term->h_char * (pointtype == -1 ? 1 : 3*(1+(pointsize-1)/2));
3633     int steps = (int)(((double)(key_sample_right - key_sample_left)) / step + 0.5);
3634 
3635     if (steps < 2) steps = 2;
3636     step = ((double)(key_sample_right - key_sample_left)) / steps;
3637 
3638     /* If plot uses a constant color, set it here and then let simpler routine take over */
3639     if ((colortype == TC_RGB && plot->lp_properties.pm3d_color.value >= 0.0)
3640     || (colortype == TC_LT)
3641     || (colortype == TC_LINESTYLE)) {
3642 	lp_style_type lptmp = plot->lp_properties;
3643 	if (plot->lp_properties.l_type == LT_COLORFROMCOLUMN)
3644 		lp_use_properties(&lptmp, (int)(plot->iso_crvs->points[0].CRD_COLOR));
3645 	apply_pm3dcolor(&lptmp.pm3d_color);
3646 	key_sample_point(plot, xl, yl, pointtype);
3647 	return;
3648     }
3649 
3650     /* color gradient only over the cb-values of the surface, if smaller than the
3651      * cb-axis range (the latter are gray values [0:1]) */
3652     get_surface_cbminmax(plot, &cbmin, &cbmax);
3653     if (cbmin > cbmax) return; /* splot 1/0, for example */
3654     cbmin = GPMAX(cbmin, CB_AXIS.min);
3655     cbmax = GPMIN(cbmax, CB_AXIS.max);
3656     gray_from = cb2gray(cbmin);
3657     gray_to = cb2gray(cbmax);
3658     gray_step = (gray_to - gray_from)/steps;
3659     /* Clip to canvas */
3660     if (term->flags & TERM_CAN_CLIP)
3661 	clip_area = NULL;
3662     else
3663 	clip_area = &canvas;
3664 
3665     while (i <= steps) {
3666 	/* if (i>0) set_color( i==steps ? gray_to : (i-0.5)/steps ); ... range [0:1] */
3667 	gray = (i==steps) ? gray_to : gray_from+i*gray_step;
3668 	set_color(gray);
3669 	x2 = i==0 ? x1 : (i==steps ? x_to : x1 + (int)(i*step+0.5));
3670 	/* x2 += key_point_offset; ... that's if there is only 1 point */
3671 	if (!clip_point(x2, yl))
3672 	    (*term->point) (x2, yl, pointtype);
3673 	i++;
3674     }
3675 
3676     clip_area = clip_save;
3677 }
3678 
3679 
3680 /* plot_vectors:
3681  * Plot the curves in VECTORS style
3682  */
3683 static void
plot3d_vectors(struct surface_points * plot)3684 plot3d_vectors(struct surface_points *plot)
3685 {
3686     int i;
3687     double x1, y1, x2, y2;
3688     arrow_style_type ap;
3689     struct coordinate *tails = plot->iso_crvs->points;
3690     struct coordinate *heads = plot->iso_crvs->next->points;
3691 
3692     /* Only necessary once, unless variable arrow style */
3693     ap = plot->arrow_properties;
3694     term_apply_lp_properties(&ap.lp_properties);
3695     apply_head_properties(&ap);
3696 
3697     for (i = 0; i < plot->iso_crvs->p_count; i++) {
3698 
3699 	if (heads[i].type == UNDEFINED || tails[i].type == UNDEFINED)
3700 	    continue;
3701 
3702 	/* variable arrow style read from extra data column */
3703 	if (plot->arrow_properties.tag == AS_VARIABLE) {
3704 	    int as= heads[i].CRD_COLOR;
3705 	    arrow_use_properties(&ap, as);
3706 	    term_apply_lp_properties(&ap.lp_properties);
3707 	    apply_head_properties(&ap);
3708 	} else {
3709 	    check3d_for_variable_color(plot, &heads[i]);
3710 	}
3711 
3712 	/* The normal case: both ends in range */
3713 	if (heads[i].type == INRANGE && tails[i].type == INRANGE) {
3714 	    map3d_xy_double(tails[i].x, tails[i].y, tails[i].z, &x1, &y1);
3715 	    map3d_xy_double(heads[i].x, heads[i].y, heads[i].z, &x2, &y2);
3716 	    draw_clip_arrow(x1, y1, x2, y2, ap.head);
3717 
3718 	/* "set clip two" - both ends out of range */
3719 	} else if (heads[i].type != INRANGE && tails[i].type != INRANGE) {
3720 	    double lx[2], ly[2], lz[2];
3721 	    if (!clip_lines2)
3722 		continue;
3723 	    two_edge3d_intersect(&tails[i], &heads[i], lx, ly, lz);
3724 	    map3d_xy_double(lx[0], ly[0], lz[0], &x1, &y1);
3725 	    map3d_xy_double(lx[1], ly[1], lz[1], &x2, &y2);
3726 	    draw_clip_arrow(x1, y1, x2, y2, ap.head);
3727 
3728 	/* "set clip one" - one end out of range */
3729 	} else if (clip_lines1) {
3730 	    double clip_x, clip_y, clip_z;
3731 	    edge3d_intersect(&heads[i], &tails[i], &clip_x, &clip_y, &clip_z);
3732 	    if (tails[i].type == INRANGE) {
3733 		map3d_xy_double(tails[i].x, tails[i].y, tails[i].z, &x1, &y1);
3734 		map3d_xy_double(clip_x, clip_y, clip_z, &x2, &y2);
3735 	    } else {
3736 		map3d_xy_double(clip_x, clip_y, clip_z, &x1, &y1);
3737 		map3d_xy_double(heads[i].x, heads[i].y, heads[i].z, &x2, &y2);
3738 	    }
3739 	    draw_clip_arrow(x1, y1, x2, y2, ap.head);
3740 	}
3741     }
3742 }
3743 
3744 /*
3745  * splot with zerrorfill
3746  * This 3D style is similar to a 2D filledcurves plot between two lines.
3747  * Put together a list of the component quadrangles using the data structures
3748  * normally used by pm3d routines pm3d_plot(), pm3d_depth_queue_flush().
3749  * The component quadrangles from all plots are sorted and flushed together.
3750  */
3751 static void
plot3d_zerrorfill(struct surface_points * plot)3752 plot3d_zerrorfill(struct surface_points *plot)
3753 {
3754     struct iso_curve *curve = plot->iso_crvs;
3755     int i1, i2;		/* index leading and trailing coord of current quadrangle */
3756     int count = 0;
3757     gpdPoint corner[4];
3758 
3759     /* Find leading edge of first quadrangle */
3760     for (i1=0; i1 < curve->p_count; i1++) {
3761 	if (curve->points[i1].type == INRANGE)
3762 	    break;
3763     }
3764 
3765     for (i2=i1+1; i2 < curve->p_count; i2++) {
3766 	if (curve->points[i2].type != INRANGE)
3767 	    continue;
3768 	count++;	/* Found one */
3769 	corner[0].x = corner[1].x = curve->points[i1].x;
3770 	corner[0].y = corner[1].y = curve->points[i1].y;
3771 	corner[0].z = curve->points[i1].CRD_ZLOW;
3772 	corner[1].z = curve->points[i1].CRD_ZHIGH;
3773 	corner[2].x = corner[3].x = curve->points[i2].x;
3774 	corner[2].y = corner[3].y = curve->points[i2].y;
3775 	corner[3].z = curve->points[i2].CRD_ZLOW;
3776 	corner[2].z = curve->points[i2].CRD_ZHIGH;
3777 	pm3d_add_quadrangle(plot, corner);
3778 	i1 = i2;
3779     }
3780 
3781     if (count == 0)
3782 	int_error(NO_CARET, "all points out of range");
3783 
3784     /* Default is to write out each zerror plot as we come to it     */
3785     /* (most recent plot occludes all previous plots). To get proper */
3786     /* sorting, use "set pm3d depthorder".                           */
3787     if (pm3d.direction != PM3D_DEPTH)
3788 	pm3d_depth_queue_flush();
3789 
3790 }
3791 
3792 /*
3793  * 3D version of plot with boxes.
3794  * By default only a flat rectangle is drawn.  "set boxdepth <depth>"
3795  * changes this to draw real boxes (4 sides + top).
3796  * The boxes are drawn as pm3d rectangles. This means that depth-cueing
3797  * must be done with "set pm3d depth base" rather than with "set hidden3d".
3798  */
3799 static void
plot3d_boxes(struct surface_points * plot)3800 plot3d_boxes(struct surface_points *plot)
3801 {
3802     int i;			/* point index */
3803     double dxl, dxh;		/* rectangle extent along X axis */
3804     double dyl, dyh;		/* rectangle extent along Y axis */
3805     double zbase, dz;		/* box base and height */
3806     fill_style_type save_fillstyle;
3807 
3808     struct iso_curve *icrvs = plot->iso_crvs;
3809     gpdPoint corner[4];
3810 
3811     /* This initialization is normally done via pm3d_plot()
3812      * but 3D boxes are drawn in a parallel code path.
3813      */
3814     if (pm3d_shade.strength > 0)
3815 	pm3d_init_lighting_model();
3816 
3817     /* FIXME: fillstyle and border color always come from "set style fill" */
3818     pm3d.border = plot->lp_properties;
3819     pm3d.border.pm3d_color = default_fillstyle.border_color;
3820 
3821     while (icrvs) {
3822 	struct coordinate *points = icrvs->points;
3823 
3824 	for (i = 0; i < icrvs->p_count; i++) {
3825 
3826 	    if (points[i].type == UNDEFINED)
3827 		continue;
3828 
3829 	    dxh = points[i].xhigh;
3830 	    dxl = points[i].xlow;
3831 	    dyl = points[i].y;
3832 	    dyh = points[i].y;
3833 	    dz = points[i].z;
3834 
3835 	    /* Box is out of range on y */
3836 	    if ((dyl > Y_AXIS.min && dyl > Y_AXIS.max)
3837 	    ||  (dyl < Y_AXIS.min && dyl < Y_AXIS.max))
3838 		continue;
3839 
3840 	    if (boxdepth != 0) {
3841 		double depth = (boxdepth > 0) ? boxdepth : boxwidth * yscaler/xscaler;
3842 		if (Y_AXIS.log) {
3843 		    dyl *= pow(Y_AXIS.base, -depth/2.);
3844 		    dyh *= pow(Y_AXIS.base, depth/2.);
3845 		} else {
3846 		    dyl -= depth / 2.;
3847 		    dyh += depth / 2.;
3848 		}
3849 		cliptorange(dyl, Y_AXIS.min, Y_AXIS.max);
3850 		cliptorange(dyh, Y_AXIS.min, Y_AXIS.max);
3851 	    }
3852 
3853 	    /* clip to border */
3854 	    cliptorange(dxl, X_AXIS.min, X_AXIS.max);
3855 	    cliptorange(dxh, X_AXIS.min, X_AXIS.max);
3856 
3857 	    /* Entire box is out of range on x */
3858 	    if (dxl == dxh && (dxl == X_AXIS.min || dxl == X_AXIS.max))
3859 		continue;
3860 
3861 	    zbase = 0;
3862 	    cliptorange(zbase, Z_AXIS.min, Z_AXIS.max);
3863 
3864 	    /* Copy variable color value into plot header for pm3d_add_quadrangle */
3865 	    if (plot->pm3d_color_from_column)
3866 		plot->lp_properties.pm3d_color.lt =  points[i].CRD_COLOR;
3867 
3868 	    /* Construct and store single pm3d rectangle (front of box) */
3869 	    /* Z	corner1	corner2	*/
3870 	    /* 0	corner0 corner3 */
3871 	    corner[0].x = corner[1].x = dxl;
3872 	    corner[2].x = corner[3].x = dxh;
3873 	    corner[0].y = corner[1].y = corner[2].y = corner[3].y = dyl;
3874 	    corner[0].z = corner[3].z = zbase;
3875 	    corner[1].z = corner[2].z = dz;
3876 	    pm3d_add_quadrangle(plot, corner);
3877 
3878 	    /* The normal case is to draw the front only (boxdepth = 0) */
3879 	    if (boxdepth == 0)
3880 		continue;
3881 
3882 	    /* Back side of the box */
3883 	    corner[0].y = corner[1].y = corner[2].y = corner[3].y = dyh;
3884 	    pm3d_add_quadrangle(plot, corner);
3885 
3886 	    /* Left side of box */
3887 	    corner[2].x = corner[3].x = dxl;
3888 	    corner[0].y = corner[1].y = dyl;
3889 	    corner[0].z = corner[3].z = zbase;
3890 	    corner[1].z = corner[2].z = dz;
3891 	    pm3d_add_quadrangle(plot, corner);
3892 
3893 	    /* Right side of box */
3894 	    corner[0].x = corner[1].x = corner[2].x = corner[3].x = dxh;
3895 	    pm3d_add_quadrangle(plot, corner);
3896 
3897 	    /* Top of box */
3898 	    corner[0].x = corner[1].x = dxl;
3899 	    corner[0].y = corner[3].y = dyl;
3900 	    corner[1].y = corner[2].y = dyh;
3901 	    corner[0].z = corner[3].z = dz;
3902 	    pm3d_add_quadrangle(plot, corner);
3903 
3904 	}	/* loop over points */
3905 
3906 	icrvs = icrvs->next;
3907     }
3908 
3909     /* FIXME The only way to get the pm3d flush code to see our fill */
3910     /* style is to temporarily copy it to the global fillstyle.      */
3911     save_fillstyle = default_fillstyle;
3912     default_fillstyle = plot->fill_properties;
3913 
3914     /* By default we write out each set of boxes as it is seen.  */
3915     /* The other option is to let them accummulate and then sort */
3916     /* them together with all other pm3d elements to draw later. */
3917     if (pm3d.direction != PM3D_DEPTH) {
3918 	pm3d.base_sort = TRUE;
3919 	pm3d_depth_queue_flush();
3920 	pm3d.base_sort = FALSE;
3921     }
3922 
3923     /* Restore global fillstyle */
3924     default_fillstyle = save_fillstyle;
3925 }
3926 
3927 /*
3928  * Plot the data as a set of polygons.
3929  * Successive lines of input data provide vertex coordinates.
3930  * A blank line separates polygons.
3931  * E.g. two triangles:
3932  *	x1 y1 z1
3933  *	x2 y2 z2
3934  *	x3 y3 z3
3935  *
3936  *	x1 y1 z1
3937  *	x2 y2 z2
3938  *	x3 y3 z3
3939  */
3940 static void
plot3d_polygons(struct surface_points * plot)3941 plot3d_polygons(struct surface_points *plot)
3942 {
3943     int nv;
3944     struct iso_curve *icrvs;
3945     struct coordinate *points;
3946     int style;
3947 
3948     static gpdPoint *quad = NULL;
3949     static int quadmax = 0;
3950 
3951     /* These don't need to be drawn at all */
3952     if (plot->lp_properties.l_type == LT_NODRAW)
3953 	return;
3954 
3955     /* This initialization is normally done in pm3d_plot()
3956      * but polygons do not necessarily use that code path.
3957      */
3958     if (pm3d_shade.strength > 0)
3959 	pm3d_init_lighting_model();
3960     style = style_from_fill(&plot->fill_properties);
3961 
3962     /* Most polygons are small */
3963     quadmax = 8;
3964     quad = gp_realloc(quad, quadmax * sizeof(gpdPoint), NULL);
3965 
3966     for (icrvs = plot->iso_crvs; icrvs; icrvs = icrvs->next) {
3967 
3968 	/* Allow for very large polygons (e.g. cartographic outlines) */
3969 	int npoints = icrvs->p_count;
3970 	if (npoints > quadmax) {
3971 	    quadmax = npoints;
3972 	    quad = gp_realloc(quad, quadmax * sizeof(gpdPoint), NULL);
3973 	}
3974 
3975 	/* Copy the vertex coordinates into a pm3d quadrangle */
3976 	for (nv = 0, points = icrvs->points; nv < npoints; nv++) {
3977 	    quad[nv].x = points[nv].x;
3978 	    quad[nv].y = points[nv].y;
3979 	    quad[nv].z = points[nv].z;
3980 	}
3981 	/* Treat triangle as a degenerate quadrangle */
3982 	if (nv == 3) {
3983 	    quad[3].x = points[0].x;
3984 	    quad[3].y = points[0].y;
3985 	    quad[3].z = points[0].z;
3986 	}
3987 	/* Ignore lines and points */
3988 	if (nv < 3)
3989 	    continue;
3990 
3991 	/* Coloring piggybacks on options for isosurface */
3992 	quad[0].c = plot->fill_properties.border_color.lt;
3993 	quad[1].c = style;
3994 	pm3d_add_polygon( plot, quad, nv );
3995     }
3996 
3997     /* Default is to write out each polygon as we come to it. */
3998     /* To get proper sorting, use "set pm3d depthorder".      */
3999     if (pm3d.direction != PM3D_DEPTH)
4000 	pm3d_depth_queue_flush();
4001 
4002     /* Clean up */
4003     free(quad);
4004     quadmax = 0;
4005     quad = NULL;
4006 }
4007 
4008 
4009 static void
check3d_for_variable_color(struct surface_points * plot,struct coordinate * point)4010 check3d_for_variable_color(struct surface_points *plot, struct coordinate *point)
4011 {
4012     int colortype = plot->lp_properties.pm3d_color.type;
4013 
4014     switch( colortype ) {
4015     case TC_RGB:
4016 	if (plot->pm3d_color_from_column && plot->lp_properties.pm3d_color.value < 0.0)
4017 	    set_rgbcolor_var( (unsigned int)point->CRD_COLOR );
4018 	break;
4019     case TC_Z:
4020     case TC_DEFAULT:   /* pm3d mode assumes this is default */
4021 	if (plot->pm3d_color_from_column)
4022 	    set_color( cb2gray(point->CRD_COLOR) );
4023 	else
4024 	    set_color( cb2gray(point->z) );
4025 	break;
4026     case TC_LINESTYLE:	/* color from linestyle in data column */
4027 	plot->lp_properties.pm3d_color.lt = (int)(point->CRD_COLOR);
4028 	apply_pm3dcolor(&(plot->lp_properties.pm3d_color));
4029 	break;
4030     default:
4031 	/* The other cases were taken care of already */
4032 	break;
4033     }
4034 }
4035 
4036 void
do_3dkey_layout(legend_key * key,int * xinkey,int * yinkey)4037 do_3dkey_layout(legend_key *key, int *xinkey, int *yinkey)
4038 {
4039     struct termentry *t = term;
4040     int key_height, key_width;
4041 
4042     /* NOTE: All of these had better not change after being calculated here! */
4043     if (key->reverse) {
4044 	key_sample_left = -key_sample_width;
4045 	key_sample_right = 0;
4046 	key_text_left = t->h_char;
4047 	key_text_right = t->h_char * (max_ptitl_len + 1);
4048 	key_size_right = t->h_char * (max_ptitl_len + 2 + key->width_fix);
4049 	key_size_left = t->h_char + key_sample_width;
4050     } else {
4051 	key_sample_left = 0;
4052 	key_sample_right = key_sample_width;
4053 	key_text_left = -(int) (t->h_char * (max_ptitl_len + 1));
4054 	key_text_right = -(int) t->h_char;
4055 	key_size_left = t->h_char * (max_ptitl_len + 2 + key->width_fix);
4056 	key_size_right = t->h_char + key_sample_width;
4057     }
4058     key_point_offset = (key_sample_left + key_sample_right) / 2;
4059 
4060     key_title_height = ktitle_lines * t->v_char;
4061     key_title_extra = 0;
4062     if ((key->title.text) && (t->flags & TERM_ENHANCED_TEXT)
4063     &&  (strchr(key->title.text,'^') || strchr(key->title.text,'_')))
4064 	    key_title_extra = t->v_char;
4065 
4066     key_width = key_col_wth * (key_cols - 1) + key_size_right + key_size_left;
4067     key_height = key_title_height + key_title_extra
4068 		+ key_entry_height * key_rows + key->height_fix * t->v_char;
4069 
4070     /* Make room for extra long title */
4071     if (key_width < key_title_width)
4072 	key_width = key_title_width;
4073 
4074     /* Now that we know the size of the key, we can position it as requested */
4075     if (key->region == GPKEY_USER_PLACEMENT) {
4076 	int corner_x, corner_y;
4077 	t_position keypos = key->user_pos;
4078 
4079 	/* Translate request for graph coordinates from x/y
4080 	 * to whatever the equivalent is for an xz or yz projection
4081 	 */
4082 	if (yz_projection && key->user_pos.scalex == graph) {
4083 	    keypos.scalez = graph;
4084 	    keypos.z = 1.0 - key->user_pos.x;
4085 	    keypos.x = 0;
4086 	}
4087 	if (xz_projection && key->user_pos.scalex == graph) {
4088 	    keypos.scalez = graph;
4089 	    keypos.z = key->user_pos.x;
4090 	    keypos.x = 0;
4091 	}
4092 	if (xz_projection && key->user_pos.scaley == graph) {
4093 	    keypos.scalex = graph;
4094 	    keypos.x = key->user_pos.y;
4095 	    keypos.y = 0;
4096 	}
4097 
4098 	map3d_position(&keypos, &corner_x, &corner_y, "key");
4099 
4100 	if (key->hpos == CENTRE)
4101 	    key->bounds.xleft = corner_x - key_width / 2;
4102 	else if (key->hpos == RIGHT)
4103 	    key->bounds.xleft = corner_x - key_width;
4104 	else
4105 	    key->bounds.xleft = corner_x;
4106 	key->bounds.xright = key->bounds.xleft + key_width;
4107 
4108 	key->bounds.ytop = corner_y;
4109 	key->bounds.ybot = corner_y - key_height;
4110 
4111 	*xinkey = key->bounds.xleft + key_size_left;
4112 	*yinkey = key->bounds.ytop - key_title_height - key_title_extra;
4113 
4114     } else {
4115 	BoundingBox *bounds;
4116 	if (key->fixed && !splot_map)
4117 	    bounds = &page_bounds;
4118 	else
4119 	    bounds = &plot_bounds;
4120 
4121 	if (key->region != GPKEY_AUTO_INTERIOR_LRTBC && key->margin == GPKEY_BMARGIN) {
4122 	    if (ptitl_cnt > 0) {
4123 		/* we divide into columns, then centre in column by considering
4124 		 * ratio of key_left_size to key_right_size
4125 		 * key_size_left / (key_size_left+key_size_right)
4126 		 *               * (bounds->xright-bounds->xleft)/key_cols
4127 		 * do one integer division to maximise accuracy (hope we dont overflow!)
4128 		 */
4129 		*xinkey = bounds->xleft
4130 		   + ((bounds->xright - bounds->xleft) * key_size_left)
4131 		   / (key_cols * (key_size_left + key_size_right));
4132 		key->bounds.xleft = *xinkey - key_size_left;
4133 		key->bounds.xright = key->bounds.xleft + key_width;
4134 
4135 		key->bounds.ytop = bounds->ybot;
4136 		key->bounds.ybot = bounds->ybot - key_height;
4137 		*yinkey = key->bounds.ytop - key_title_height - key_title_extra;
4138 	    }
4139 
4140 	} else {
4141 	    if (key->vpos == JUST_TOP) {
4142 		key->bounds.ytop = bounds->ytop - t->v_tic;
4143 		key->bounds.ybot = key->bounds.ytop - key_height;
4144 		*yinkey = key->bounds.ytop - key_title_height - key_title_extra;
4145 	    } else {
4146 		key->bounds.ybot = bounds->ybot + t->v_tic;
4147 		key->bounds.ytop = key->bounds.ybot + key_height;
4148 		*yinkey = key->bounds.ytop - key_title_height - key_title_extra;
4149 	    }
4150 	    if (key->region != GPKEY_AUTO_INTERIOR_LRTBC && key->margin == GPKEY_RMARGIN) {
4151 		/* keys outside plot border (right) */
4152 		key->bounds.xleft = bounds->xright + t->h_tic;
4153 		key->bounds.xright = key->bounds.xleft + key_width;
4154 		*xinkey = key->bounds.xleft + key_size_left;
4155 	    } else if (key->region != GPKEY_AUTO_INTERIOR_LRTBC && key->margin == GPKEY_LMARGIN) {
4156 		/* keys outside plot border (left) */
4157 		key->bounds.xright = bounds->xleft - t->h_tic;
4158 		key->bounds.xleft = key->bounds.xright - key_width;
4159 		*xinkey = key->bounds.xleft + key_size_left;
4160 	    } else if (key->hpos == LEFT) {
4161 		key->bounds.xleft = bounds->xleft + t->h_tic;
4162 		key->bounds.xright = key->bounds.xleft + key_width;
4163 		*xinkey = key->bounds.xleft + key_size_left;
4164 	    } else {
4165 		key->bounds.xright = bounds->xright - t->h_tic;
4166 		key->bounds.xleft = key->bounds.xright - key_width;
4167 		*xinkey = key->bounds.xleft + key_size_left;
4168 	    }
4169 	}
4170 	yl_ref = *yinkey - key_title_height - key_title_extra;
4171 
4172     }
4173 
4174     /* Center the key entries vertically, allowing for requested extra space */
4175     *yinkey -= (key->height_fix * t->v_char) / 2;
4176 }
4177 
4178 
4179 /*
4180  * Support routines for "set view map"
4181  */
4182 static int splot_map_active = 0;
4183 static float splot_map_surface_rot_x;
4184 static float splot_map_surface_rot_z;
4185 static float splot_map_surface_scale;
4186 
4187 static void
flip_projection_axis(struct axis * axis)4188 flip_projection_axis(struct axis *axis)
4189 {
4190     double temp = axis->min;
4191     axis->min = axis->max;
4192     axis->max = temp;
4193     if (axis->linked_to_primary) {
4194 	axis = axis->linked_to_primary;
4195 	temp = axis->min;
4196 	axis->min = axis->max;
4197 	axis->max = temp;
4198     }
4199 }
4200 
4201 void
splot_map_activate()4202 splot_map_activate()
4203 {
4204     if (splot_map_active)
4205 	return;
4206     splot_map_active = 1;
4207     /* save current values */
4208     splot_map_surface_rot_x = surface_rot_x;
4209     splot_map_surface_rot_z = surface_rot_z ;
4210     splot_map_surface_scale = surface_scale;
4211     /* set new values */
4212     surface_rot_x = 180;
4213     surface_rot_z = 0;
4214     /* version 4 had constant value surface_scale = 1.3 */
4215     surface_scale = 1.425 * mapview_scale;
4216     /* The Y axis runs backwards from a normal 2D plot */
4217     flip_projection_axis(&axis_array[FIRST_Y_AXIS]);
4218 }
4219 
4220 void
splot_map_deactivate()4221 splot_map_deactivate()
4222 {
4223     if (!splot_map_active)
4224 	return;
4225     splot_map_active = 0;
4226     /* restore the original values */
4227     surface_rot_x = splot_map_surface_rot_x;
4228     surface_rot_z = splot_map_surface_rot_z;
4229     surface_scale = splot_map_surface_scale;
4230     /* The Y axis runs backwards from a normal 2D plot */
4231     flip_projection_axis(&axis_array[FIRST_Y_AXIS]);
4232 }
4233 
4234