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