2 static char *RCSid() { return RCSid("$Id: plot3d.c,v 1.131.2.6 2008/03/20 09:39:46 sfeam Exp $"); }
5 /* GNUPLOT - plot3d.c */
8 * Copyright 1986 - 1993, 1998, 2004 Thomas Williams, Colin Kelley
10 * Permission to use, copy, and distribute this software and its
11 * documentation for any purpose with or without fee is hereby granted,
12 * provided that the above copyright notice appear in all copies and
13 * that both that copyright notice and this permission notice appear
14 * in supporting documentation.
16 * Permission to modify the software is granted, but not the right to
17 * distribute the complete modified source code. Modifications are to
18 * be distributed as patches to the released version. Permission to
19 * distribute binaries produced by compiling modified sources is granted,
21 * 1. distribute the corresponding source modifications from the
22 * released version in the form of a patch file along with the binaries,
23 * 2. add special version identification to distinguish your version
24 * in addition to the base release version number,
25 * 3. provide your name and address as the primary contact for the
26 * support of your modified version, and
27 * 4. retain our contact information in regard to use of the base
29 * Permission to distribute the released version of the source code along
30 * with corresponding source modifications in the form of a patch file is
31 * granted with same provisions 2 through 4 for binary distributions.
33 * This software is provided "as is" without express or implied warranty
34 * to the extent permitted by applicable law.
54 #ifdef BINARY_DATA_FILE
58 #ifdef EAM_DATASTRINGS
59 #include "plot2d.h" /* Only for store_label() */
62 #ifdef THIN_PLATE_SPLINES_GRID
70 /* global variables exported by this module */
72 t_data_mapping mapping3d = MAP3D_CARTESIAN;
74 int dgrid3d_row_fineness = 10;
75 int dgrid3d_col_fineness = 10;
76 int dgrid3d_norm_value = 1;
77 TBOOLEAN dgrid3d = FALSE;
79 /* static prototypes */
81 static void calculate_set_of_isolines __PROTO((AXIS_INDEX value_axis, TBOOLEAN cross, struct iso_curve **this_iso,
82 AXIS_INDEX iso_axis, double iso_min, double iso_step, int num_iso_to_use,
83 AXIS_INDEX sam_axis, double sam_min, double sam_step, int num_sam_to_use,
84 TBOOLEAN need_palette));
85 static void get_3ddata __PROTO((struct surface_points * this_plot));
86 static void print_3dtable __PROTO((int pcount));
87 static void eval_3dplots __PROTO((void));
88 static void grid_nongrid_data __PROTO((struct surface_points * this_plot));
89 static void parametric_3dfixup __PROTO((struct surface_points * start_plot, int *plot_num));
90 static struct surface_points * sp_alloc __PROTO((int num_samp_1, int num_iso_1, int num_samp_2, int num_iso_2));
91 static void sp_replace __PROTO((struct surface_points *sp, int num_samp_1, int num_iso_1, int num_samp_2, int num_iso_2));
92 #ifdef THIN_PLATE_SPLINES_GRID
93 static double splines_kernel __PROTO((double h));
96 /* the curves/surfaces of the plot */
97 struct surface_points *first_3dplot = NULL;
98 static struct udft_entry plot_func;
102 /* HBB 20000508: moved these functions to the only module that uses them
103 * so they can be turned 'static' */
105 * sp_alloc() allocates a surface_points structure that can hold 'num_iso_1'
106 * iso-curves with 'num_samp_2' samples and 'num_iso_2' iso-curves with
107 * 'num_samp_1' samples.
108 * If, however num_iso_2 or num_samp_1 is zero no iso curves are allocated.
110 static struct surface_points *
111 sp_alloc(int num_samp_1, int num_iso_1, int num_samp_2, int num_iso_2)
113 struct lp_style_type default_lp_properties = DEFAULT_LP_STYLE_TYPE;
115 struct surface_points *sp = gp_alloc(sizeof(*sp), "surface");
116 memset(sp,0,sizeof(struct surface_points));
118 /* Initialize various fields */
119 sp->lp_properties = default_lp_properties;
120 default_arrow_style(&(sp->arrow_properties));
122 if (num_iso_2 > 0 && num_samp_1 > 0) {
124 struct iso_curve *icrv;
126 for (i = 0; i < num_iso_1; i++) {
127 icrv = iso_alloc(num_samp_2);
128 icrv->next = sp->iso_crvs;
131 for (i = 0; i < num_iso_2; i++) {
132 icrv = iso_alloc(num_samp_1);
133 icrv->next = sp->iso_crvs;
142 * sp_replace() updates a surface_points structure so it can hold 'num_iso_1'
143 * iso-curves with 'num_samp_2' samples and 'num_iso_2' iso-curves with
144 * 'num_samp_1' samples.
145 * If, however num_iso_2 or num_samp_1 is zero no iso curves are allocated.
149 struct surface_points *sp,
150 int num_samp_1, int num_iso_1, int num_samp_2, int num_iso_2)
153 struct iso_curve *icrv, *icrvs = sp->iso_crvs;
162 if (num_iso_2 > 0 && num_samp_1 > 0) {
163 for (i = 0; i < num_iso_1; i++) {
164 icrv = iso_alloc(num_samp_2);
165 icrv->next = sp->iso_crvs;
168 for (i = 0; i < num_iso_2; i++) {
169 icrv = iso_alloc(num_samp_1);
170 icrv->next = sp->iso_crvs;
178 * sp_free() releases any memory which was previously malloc()'d to hold
181 /* HBB 20000506: don't risk stack havoc by recursion, use iterative list
184 sp_free(struct surface_points *sp)
187 struct surface_points *next = sp->next_sp;
191 while (sp->contours) {
192 struct gnuplot_contours *next_cntrs = sp->contours->next;
194 free(sp->contours->coords);
196 sp->contours = next_cntrs;
199 while (sp->iso_crvs) {
200 struct iso_curve *next_icrvs = sp->iso_crvs->next;
202 iso_free(sp->iso_crvs);
203 sp->iso_crvs = next_icrvs;
206 #ifdef EAM_DATASTRINGS
208 free_labels(sp->labels);
209 sp->labels = (struct text_label *)NULL;
220 /* support for dynamic size of input line */
225 * in the parametric case we would say splot [u= -Pi:Pi] [v= 0:2*Pi] [-1:1]
226 * [-1:1] [-1:1] sin(v)*cos(u),sin(v)*cos(u),sin(u) in the non-parametric
227 * case we would say only splot [x= -2:2] [y= -5:5] sin(x)*cos(y)
231 int dummy_token0 = -1, dummy_token1 = -1;
232 AXIS_INDEX u_axis, v_axis;
236 /* change view to become map if requested by 'set view map' */
237 if (splot_map == TRUE)
238 splot_map_activate();
240 if (parametric && strcmp(set_dummy_var[0], "t") == 0) {
241 strcpy(set_dummy_var[0], "u");
242 strcpy(set_dummy_var[1], "v");
245 /* put stuff into arrays to simplify access */
246 AXIS_INIT3D(FIRST_X_AXIS, 0, 0);
247 AXIS_INIT3D(FIRST_Y_AXIS, 0, 0);
248 AXIS_INIT3D(FIRST_Z_AXIS, 0, 1);
249 AXIS_INIT3D(U_AXIS, 1, 0);
250 AXIS_INIT3D(V_AXIS, 1, 0);
251 AXIS_INIT3D(COLOR_AXIS, 0, 1);
253 if (!term) /* unknown */
254 int_error(c_token, "use 'set term' to set terminal type first");
256 u_axis = (parametric ? U_AXIS : FIRST_X_AXIS);
257 v_axis = (parametric ? V_AXIS : FIRST_Y_AXIS);
259 PARSE_NAMED_RANGE(u_axis, dummy_token0);
260 if (splot_map == TRUE && !parametric) /* v_axis==FIRST_Y_AXIS */
261 splot_map_deactivate();
262 PARSE_NAMED_RANGE(v_axis, dummy_token1);
263 if (splot_map == TRUE && !parametric) /* v_axis==FIRST_Y_AXIS */
264 splot_map_activate();
267 PARSE_RANGE(FIRST_X_AXIS);
268 if (splot_map == TRUE)
269 splot_map_deactivate();
270 PARSE_RANGE(FIRST_Y_AXIS);
271 if (splot_map == TRUE)
272 splot_map_activate();
274 PARSE_RANGE(FIRST_Z_AXIS);
275 CHECK_REVERSE(FIRST_X_AXIS);
276 CHECK_REVERSE(FIRST_Y_AXIS);
277 CHECK_REVERSE(FIRST_Z_AXIS);
279 /* use the default dummy variable unless changed */
280 if (dummy_token0 >= 0)
281 copy_str(c_dummy_var[0], dummy_token0, MAX_ID_LEN);
283 (void) strcpy(c_dummy_var[0], set_dummy_var[0]);
285 if (dummy_token1 >= 0)
286 copy_str(c_dummy_var[1], dummy_token1, MAX_ID_LEN);
288 (void) strcpy(c_dummy_var[1], set_dummy_var[1]);
293 #ifdef THIN_PLATE_SPLINES_GRID
296 splines_kernel(double h)
299 /* this is normaly not useful ... */
306 return h * h * log(h);
315 grid_nongrid_data(struct surface_points *this_plot)
318 double x, y, z, w, dx, dy, xmin, xmax, ymin, ymax;
319 struct iso_curve *old_iso_crvs = this_plot->iso_crvs;
320 struct iso_curve *icrv, *oicrv, *oicrvs;
322 #ifdef THIN_PLATE_SPLINES_GRID
323 double *b, **K, *xx, *yy, *zz, d;
324 int *indx, numpoints;
327 /* Compute XY bounding box on the original data. */
328 /* FIXME HBB 20010424: Does this make any sense? Shouldn't we just
329 * use whatever the x and y ranges have been found to be, and
330 * that's that? The largest difference this is going to make is if
331 * we plot a datafile that doesn't span the whole x/y range
332 * used. Do we want a dgrid3d over the actual data rectangle, or
333 * over the xrange/yrange area? */
334 xmin = xmax = old_iso_crvs->points[0].x;
335 ymin = ymax = old_iso_crvs->points[0].y;
336 for (icrv = old_iso_crvs; icrv != NULL; icrv = icrv->next) {
337 struct coordinate GPHUGE *points = icrv->points;
339 for (i = 0; i < icrv->p_count; i++, points++) {
340 /* HBB 20010424: avoid crashing for undefined input */
341 if (points->type == UNDEFINED)
343 if (xmin > points->x)
345 if (xmax < points->x)
347 if (ymin > points->y)
349 if (ymax < points->y)
354 dx = (xmax - xmin) / (dgrid3d_col_fineness - 1);
355 dy = (ymax - ymin) / (dgrid3d_row_fineness - 1);
357 /* Create the new grid structure, and compute the low pass filtering from
358 * non grid to grid structure.
360 this_plot->iso_crvs = NULL;
361 this_plot->num_iso_read = dgrid3d_col_fineness;
362 this_plot->has_grid_topology = TRUE;
364 #ifdef THIN_PLATE_SPLINES_GRID
366 for (oicrv = old_iso_crvs; oicrv != NULL; oicrv = oicrv->next) {
367 numpoints += oicrv->p_count;
369 xx = gp_alloc(sizeof(xx[0]) * (numpoints + 3) * (numpoints + 8),
370 "thin plate splines in dgrid3d");
371 /* the memory needed is not really (n+3)*(n+8) for now,
372 but might be if I take into account errors ... */
373 K = gp_alloc(sizeof(K[0]) * (numpoints + 3),
374 "matrix : thin plate splines 2d");
379 /* HBB 20010424: Count actual input points without the UNDEFINED
380 * ones, as we copy them */
382 for (oicrv = old_iso_crvs; oicrv != NULL; oicrv = oicrv->next) {
383 struct coordinate GPHUGE *opoints = oicrv->points;
385 for (k = 0; k < oicrv->p_count; k++, opoints++) {
386 /* HBB 20010424: avoid crashing for undefined input */
387 if (opoints->type == UNDEFINED)
389 xx[numpoints] = opoints->x;
390 yy[numpoints] = opoints->y;
391 zz[numpoints] = opoints->z;
396 for (i = 0; i < numpoints + 3; i++) {
397 K[i] = b + (numpoints + 3) * (i + 1);
400 for (i = 0; i < numpoints; i++) {
401 for (j = i + 1; j < numpoints; j++) {
402 double dx = xx[i] - xx[j], dy = yy[i] - yy[j];
403 K[i][j] = K[j][i] = -splines_kernel(sqrt(dx * dx + dy * dy));
405 K[i][i] = 0.0; /* here will come the weights for errors */
408 for (i = 0; i < numpoints; i++) {
409 K[i][numpoints] = K[numpoints][i] = 1.0;
410 K[i][numpoints + 1] = K[numpoints + 1][i] = xx[i];
411 K[i][numpoints + 2] = K[numpoints + 2][i] = yy[i];
414 b[numpoints + 1] = 0.0;
415 b[numpoints + 2] = 0.0;
416 K[numpoints][numpoints] = 0.0;
417 K[numpoints][numpoints + 1] = 0.0;
418 K[numpoints][numpoints + 2] = 0.0;
419 K[numpoints + 1][numpoints] = 0.0;
420 K[numpoints + 1][numpoints + 1] = 0.0;
421 K[numpoints + 1][numpoints + 2] = 0.0;
422 K[numpoints + 2][numpoints] = 0.0;
423 K[numpoints + 2][numpoints + 1] = 0.0;
424 K[numpoints + 2][numpoints + 2] = 0.0;
425 indx = gp_alloc(sizeof(indx[0]) * (numpoints + 3), "indexes lu");
426 /* actually, K is *not* positive definite, but
427 has only non zero real eigenvalues ->
428 we can use an lu_decomp safely */
429 lu_decomp(K, numpoints + 3, indx, &d);
430 lu_backsubst(K, numpoints + 3, indx, b);
431 #endif /* THIN_PLATE_SPLINES_GRID */
433 for (i = 0, x = xmin; i < dgrid3d_col_fineness; i++, x += dx) {
434 struct coordinate GPHUGE *points;
436 icrv = iso_alloc(dgrid3d_row_fineness + 1);
437 icrv->p_count = dgrid3d_row_fineness;
438 icrv->next = this_plot->iso_crvs;
439 this_plot->iso_crvs = icrv;
440 points = icrv->points;
442 for (j = 0, y = ymin; j < dgrid3d_row_fineness; j++, y += dy, points++) {
445 /* as soon as ->type is changed to UNDEFINED, break out of
446 * two inner loops! */
447 points->type = INRANGE;
449 #ifdef THIN_PLATE_SPLINES_GRID
451 for (k = 0; k < numpoints; k++) {
452 double dx = xx[k] - x, dy = yy[k] - y;
453 z = z - b[k] * splines_kernel(sqrt(dx * dx + dy * dy));
455 z = z + b[numpoints + 1] * x + b[numpoints + 2] * y;
457 for (oicrv = old_iso_crvs; oicrv != NULL; oicrv = oicrv->next) {
458 struct coordinate GPHUGE *opoints = oicrv->points;
459 for (k = 0; k < oicrv->p_count; k++, opoints++) {
460 double dist, dist_x = fabs(opoints->x - x), dist_y = fabs(opoints->y - y);
461 switch (dgrid3d_norm_value) {
463 dist = dist_x + dist_y;
466 dist = dist_x * dist_x + dist_y * dist_y;
469 dist = dist_x * dist_x + dist_y * dist_y;
473 dist = dist_x * dist_x + dist_y * dist_y;
478 dist = dist_x * dist_x + dist_y * dist_y;
484 dist = pow(dist_x, (double) dgrid3d_norm_value) +
485 pow(dist_y, (double) dgrid3d_norm_value);
489 /* The weight of this point is inverse proportional
493 /* HBB 981209: revised flagging as undefined */
494 /* Supporting all those infinities on various
495 * platforms becomes tiresome, to say the least :-(
496 * Let's just return the first z where this happens,
497 * unchanged, and be done with this, period. */
498 points->type = UNDEFINED;
501 break; /* out of inner loop */
504 z += opoints->z * dist;
508 if (points->type != INRANGE)
509 break; /* out of the second-inner loop as well ... */
511 #endif /* THIN_PLATE_SPLINES_GRID */
513 /* Now that we've escaped the loops safely, we know that we
514 * do have a good value in z and w, so we can proceed just as
515 * if nothing had happened at all. Nice, isn't it? */
516 points->type = INRANGE;
518 /* HBB 20010424: if log x or log y axis, we don't want to
519 * log() the value again --> just store it, and trust that
520 * it's always inrange */
524 #ifndef THIN_PLATE_SPLINES_GRID
528 STORE_WITH_LOG_AND_UPDATE_RANGE(points->z, z, points->type, z_axis, NOOP, continue);
530 if (this_plot->pm3d_color_from_column)
531 int_error(NO_CARET, "Gridding of the color column is not implemented");
533 COLOR_STORE_WITH_LOG_AND_UPDATE_RANGE(points->CRD_COLOR, z, points->type, COLOR_AXIS, NOOP, continue);
538 #ifdef THIN_PLATE_SPLINES_GRID
544 /* Delete the old non grid data. */
545 for (oicrvs = old_iso_crvs; oicrvs != NULL;) {
547 oicrvs = oicrvs->next;
552 /* Get 3D data from file, and store into this_plot data
553 * structure. Takes care of 'set mapping' and 'set dgrid3d'.
555 * Notice: this_plot->token is end of datafile spec, before title etc
556 * will be moved past title etc after we return */
558 get_3ddata(struct surface_points *this_plot)
563 double v[MAXDATACOLS];
564 int pt_in_iso_crv = 0;
565 struct iso_curve *this_iso;
567 if (mapping3d == MAP3D_CARTESIAN) {
568 /* do this check only, if we have PM3D / PM3D-COLUMN not compiled in */
569 if (df_no_use_specs == 2)
570 int_error(this_plot->token, "Need 1 or 3 columns for cartesian data");
571 /* HBB NEW 20060427: if there's only one, explicit using
572 * column, it's z data. df_axis[] has to reflect that, so
573 * df_readline() will expect time/date input. */
574 if (df_no_use_specs == 1)
575 df_axis[0] = FIRST_Z_AXIS;
577 if (df_no_use_specs == 1)
578 int_error(this_plot->token, "Need 2 or 3 columns for polar data");
581 this_plot->num_iso_read = 0;
582 this_plot->has_grid_topology = TRUE;
583 this_plot->pm3d_color_from_column = FALSE;
585 /* we ought to keep old memory - most likely case
586 * is a replot, so it will probably exactly fit into
587 * memory already allocated ?
589 if (this_plot->iso_crvs != NULL) {
590 struct iso_curve *icrv, *icrvs = this_plot->iso_crvs;
597 this_plot->iso_crvs = NULL;
599 /* data file is already open */
601 #ifndef BINARY_DATA_FILE /* NO LONGER REQUIRED FOR GENERAL BINARY OR MATRIX BINARY DATA */
603 xdatum = df_3dmatrix(this_plot, NEED_PALETTE(this_plot));
607 this_plot->has_grid_topology = TRUE;
612 /*{{{ read surface from text file */
613 struct iso_curve *local_this_iso = iso_alloc(samples_1);
614 struct coordinate GPHUGE *cp;
615 struct coordinate GPHUGE *cptail = NULL; /* Only for VECTOR plots */
617 double xtail, ytail, ztail;
618 double color = VERYLARGE;
619 int pm3d_color_from_column = FALSE;
620 #define color_from_column(x) pm3d_color_from_column = x
622 #ifdef EAM_DATASTRINGS
623 if (this_plot->plot_style == LABELPOINTS)
627 if (this_plot->plot_style == VECTOR) {
628 local_this_iso->next = iso_alloc(samples_1);
629 local_this_iso->next->p_count = 0;
632 while ((j = df_readline(v,MAXDATACOLS)) != DF_EOF) {
633 if (j == DF_SECOND_BLANK)
634 break; /* two blank lines */
635 if (j == DF_FIRST_BLANK) {
637 #if defined(WITH_IMAGE) && defined(BINARY_DATA_FILE)
638 /* Images are in a sense similar to isocurves.
639 * However, the routine for images is written to
640 * compute the two dimensions of coordinates by
641 * examining the data alone. That way it can be used
642 * in the 2D plots, for which there is no isoline
643 * record. So, toss out isoline information for
646 if ((this_plot->plot_style == IMAGE)
647 || (this_plot->plot_style == RGBIMAGE))
650 if (this_plot->plot_style == VECTOR)
654 if (pt_in_iso_crv == 0) {
657 pt_in_iso_crv = xdatum;
660 local_this_iso->p_count = xdatum;
661 local_this_iso->next = this_plot->iso_crvs;
662 this_plot->iso_crvs = local_this_iso;
663 this_plot->num_iso_read++;
665 if (xdatum != pt_in_iso_crv)
666 this_plot->has_grid_topology = FALSE;
668 local_this_iso = iso_alloc(pt_in_iso_crv);
675 /* its a data point or undefined */
676 if (xdatum >= local_this_iso->p_max) {
677 /* overflow about to occur. Extend size of points[]
678 * array. Double the size, and add 1000 points, to
679 * avoid needlessly small steps. */
680 iso_extend(local_this_iso, xdatum + xdatum + 1000);
681 if (this_plot->plot_style == VECTOR) {
682 iso_extend(local_this_iso->next, xdatum + xdatum + 1000);
683 local_this_iso->next->p_count = 0;
686 cp = local_this_iso->points + xdatum;
688 if (this_plot->plot_style == VECTOR) {
690 cp->type = UNDEFINED;
693 cptail = local_this_iso->next->points + xdatum;
696 if (j == DF_UNDEFINED || j == DF_MISSING) {
697 cp->type = UNDEFINED;
698 goto come_here_if_undefined;
700 cp->type = INRANGE; /* unless we find out different */
702 /* EAM Oct 2004 - Substantially rework this section */
703 /* now that there are many more plot types. */
706 xtail = ytail = ztail = 0.0;
708 /* The x, y, z coordinates depend on the mapping type */
711 case MAP3D_CARTESIAN:
721 if (PM3DSURFACE != this_plot->plot_style)
722 int_error(this_plot->token,
723 "2 columns only possible with explicit pm3d style (line %d)",
728 color_from_column(TRUE);
734 /* Assume everybody agrees that x,y,z are the first three specs */
744 case MAP3D_SPHERICAL:
746 int_error(this_plot->token, "Need 2 or 3 columns");
748 v[2] = 1; /* default radius */
752 /* Convert to radians. */
756 x = v[2] * cos(v[0]) * cos(v[1]);
757 y = v[2] * sin(v[0]) * cos(v[1]);
758 z = v[2] * sin(v[1]);
762 case MAP3D_CYLINDRICAL:
764 int_error(this_plot->token, "Need 2 or 3 columns");
766 v[2] = 1; /* default radius */
770 /* Convert to radians. */
773 x = v[2] * cos(v[0]);
774 y = v[2] * sin(v[0]);
780 int_error(NO_CARET, "Internal error: Unknown mapping type");
784 if (j < df_no_use_specs)
785 int_error(this_plot->token,
786 "Wrong number of columns in input data - line %d",
789 /* After the first three columns it gets messy because */
790 /* different plot styles assume different contents in the columns */
792 if (( this_plot->plot_style == POINTSTYLE
793 || this_plot->plot_style == LINESPOINTS)
794 && this_plot->lp_properties.p_size == PTSZ_VARIABLE) {
795 cp->CRD_PTSIZE = v[3];
797 color_from_column(FALSE);
799 #ifdef EAM_DATASTRINGS
800 else if (this_plot->plot_style == LABELPOINTS) {
801 /* 4th column holds label text rather than color */
802 /* text = df_tokens[3]; */
804 color_from_column(FALSE);
809 color_from_column(TRUE);
814 if ((this_plot->plot_style == POINTSTYLE
815 || this_plot->plot_style == LINESPOINTS)
816 && this_plot->lp_properties.p_size == PTSZ_VARIABLE) {
818 color_from_column(TRUE);
820 #ifdef EAM_DATASTRINGS
821 if (this_plot->plot_style == LABELPOINTS) {
822 /* take color from an explicitly given 5th column */
824 color_from_column(TRUE);
830 if (this_plot->plot_style == VECTOR) {
834 color_from_column(FALSE);
837 #undef color_from_column
840 /* Adjust for logscales. Set min/max and point types. Store in cp.
841 * The macro cannot use continue, as it is wrapped in a loop.
842 * I regard this as correct goto use
845 STORE_WITH_LOG_AND_UPDATE_RANGE(cp->x, x, cp->type, x_axis, NOOP, goto come_here_if_undefined);
846 STORE_WITH_LOG_AND_UPDATE_RANGE(cp->y, y, cp->type, y_axis, NOOP, goto come_here_if_undefined);
847 if (this_plot->plot_style == VECTOR) {
848 cptail->type = INRANGE;
849 STORE_WITH_LOG_AND_UPDATE_RANGE(cptail->x, xtail, cp->type, x_axis, NOOP, goto come_here_if_undefined);
850 STORE_WITH_LOG_AND_UPDATE_RANGE(cptail->y, ytail, cp->type, y_axis, NOOP, goto come_here_if_undefined);
854 /* HBB 20010424: in dgrid3d mode, delay log() taking
855 * and scaling until after the dgrid process. Only for
856 * z, not for x and y, so we can layout the newly
857 * created created grid more easily. */
859 if (this_plot->plot_style == VECTOR)
862 STORE_WITH_LOG_AND_UPDATE_RANGE(cp->z, z, cp->type, z_axis, NOOP, goto come_here_if_undefined);
863 if (this_plot->plot_style == VECTOR)
864 STORE_WITH_LOG_AND_UPDATE_RANGE(cptail->z, ztail, cp->type, z_axis, NOOP, goto come_here_if_undefined);
866 if (NEED_PALETTE(this_plot)) {
867 if (pm3d_color_from_column) {
868 COLOR_STORE_WITH_LOG_AND_UPDATE_RANGE(cp->CRD_COLOR, color, cp->type, COLOR_AXIS, NOOP, goto come_here_if_undefined);
870 COLOR_STORE_WITH_LOG_AND_UPDATE_RANGE(cp->CRD_COLOR, z, cp->type, COLOR_AXIS, NOOP, goto come_here_if_undefined);
875 #ifdef EAM_DATASTRINGS
876 /* At this point we have stored the point coordinates. Now we need to copy */
877 /* x,y,z into the text_label structure and add the actual text string. */
878 if (this_plot->plot_style == LABELPOINTS)
879 store_label(this_plot->labels, cp, xdatum, df_tokens[3], color);
882 come_here_if_undefined:
883 /* some may complain, but I regard this as the correct use of goto */
885 } /* end of whileloop - end of surface */
887 if (pm3d_color_from_column) {
888 this_plot->pm3d_color_from_column = pm3d_color_from_column;
892 this_plot->num_iso_read++; /* Update last iso. */
893 local_this_iso->p_count = xdatum;
895 /* If this is a VECTOR plot then iso->next is already */
896 /* occupied by the vector tail coordinates. */
897 if (this_plot->plot_style != VECTOR)
898 local_this_iso->next = this_plot->iso_crvs;
899 this_plot->iso_crvs = local_this_iso;
901 if (xdatum != pt_in_iso_crv)
902 this_plot->has_grid_topology = FALSE;
904 } else { /* Free last allocation */
905 if (this_plot->plot_style == VECTOR)
906 iso_free(local_this_iso->next);
907 iso_free(local_this_iso);
913 if (dgrid3d && this_plot->num_iso_read > 0)
914 grid_nongrid_data(this_plot);
916 /* This check used to be done in graph3d */
917 if (X_AXIS.min == VERYLARGE || X_AXIS.max == -VERYLARGE ||
918 Y_AXIS.min == VERYLARGE || Y_AXIS.max == -VERYLARGE ||
919 Z_AXIS.min == VERYLARGE || Z_AXIS.max == -VERYLARGE)
921 "Axis range undefined due to improper data values. NaN? Inf?");
923 if (this_plot->num_iso_read <= 1)
924 this_plot->has_grid_topology = FALSE;
925 if (this_plot->has_grid_topology && !hidden3d) {
926 struct iso_curve *new_icrvs = NULL;
927 int num_new_iso = this_plot->iso_crvs->p_count, len_new_iso = this_plot->num_iso_read;
930 /* Now we need to set the other direction (pseudo) isolines. */
931 for (i = 0; i < num_new_iso; i++) {
932 struct iso_curve *new_icrv = iso_alloc(len_new_iso);
934 new_icrv->p_count = len_new_iso;
936 for (j = 0, this_iso = this_plot->iso_crvs;
938 j++, this_iso = this_iso->next) {
939 /* copy whole point struct to get type too.
940 * wasteful for windows, with padding */
941 /* more efficient would be extra pointer to same struct */
942 new_icrv->points[j] = this_iso->points[i];
945 new_icrv->next = new_icrvs;
946 new_icrvs = new_icrv;
949 /* Append the new iso curves after the read ones. */
950 for (this_iso = this_plot->iso_crvs;
951 this_iso->next != NULL;
952 this_iso = this_iso->next);
953 this_iso->next = new_icrvs;
958 print_3dtable(int pcount)
960 struct surface_points *this_plot;
962 struct coordinate GPHUGE *point;
963 char *buffer = gp_alloc(150, "print_3dtable output buffer");
964 FILE *outfile = (table_outfile) ? table_outfile : gpoutfile;
966 for (surface = 0, this_plot = first_3dplot;
968 this_plot = this_plot->next_sp, surface++) {
969 fprintf(outfile, "\n#Surface %d of %d surfaces\n", surface, pcount);
972 struct iso_curve *icrvs;
975 /* only the curves in one direction */
976 for (curve = 0, icrvs = this_plot->iso_crvs;
977 icrvs && curve < this_plot->num_iso_read;
978 icrvs = icrvs->next, curve++) {
979 fprintf(outfile, "\n#IsoCurve %d, %d points\n#x y z type\n",
980 curve, icrvs->p_count);
981 for (i = 0, point = icrvs->points;
984 /* HBB 20020405: new macro to use the 'set format'
985 * in their full flexibility */
986 #define OUTPUT_NUMBER(field, axis) \
987 gprintf(buffer, 150, axis_array[axis].formatstring, \
988 1.0, point->field); \
989 fputs(buffer, outfile); \
991 OUTPUT_NUMBER(x, FIRST_X_AXIS);
992 OUTPUT_NUMBER(y, FIRST_Y_AXIS);
993 OUTPUT_NUMBER(z, FIRST_Z_AXIS);
994 fprintf(outfile, "%c\n",
995 point->type == INRANGE
996 ? 'i' : point->type == OUTRANGE
1000 putc('\n', outfile);
1001 } /* if(draw_surface) */
1005 struct gnuplot_contours *c = this_plot->contours;
1008 int count = c->num_pts;
1009 struct coordinate GPHUGE *point = c->coords;
1012 /* don't display count - contour split across chunks */
1013 /* put # in case user wants to use it for a plot */
1014 /* double blank line to allow plot ... index ... */
1015 fprintf(outfile, "\n# Contour %d, label: %s\n",
1016 number++, c->label);
1018 for (; --count >= 0; ++point) {
1019 OUTPUT_NUMBER(x, FIRST_X_AXIS);
1020 OUTPUT_NUMBER(y, FIRST_Y_AXIS);
1021 OUTPUT_NUMBER(z, FIRST_Z_AXIS);
1022 putc('\n', outfile);
1025 /* blank line between segments of same contour */
1026 putc('\n', outfile);
1028 #undef OUTPUT_NUMBER
1029 } /* while (contour) */
1030 } /* if (draw_contour) */
1031 } /* for(surface) */
1037 /* HBB 20000501: code isolated from eval_3dplots(), where practically
1038 * identical code occured twice, for direct and crossing isolines,
1039 * respectively. The latter only are done for in non-hidden3d
1042 calculate_set_of_isolines(
1043 AXIS_INDEX value_axis,
1045 struct iso_curve **this_iso,
1046 AXIS_INDEX iso_axis,
1047 double iso_min, double iso_step,
1049 AXIS_INDEX sam_axis,
1050 double sam_min, double sam_step,
1052 TBOOLEAN need_palette)
1055 struct coordinate GPHUGE *points = (*this_iso)->points;
1056 int do_update_color = need_palette && (!parametric || (parametric && value_axis == FIRST_Z_AXIS));
1058 for (j = 0; j < num_iso_to_use; j++) {
1059 double iso = iso_min + j * iso_step;
1060 /* HBB 20000501: with the new code, it should
1061 * be safe to rely on the actual 'v' axis not
1062 * to be improperly logscaled... */
1063 (void) Gcomplex(&plot_func.dummy_values[cross ? 0 : 1],
1064 AXIS_DE_LOG_VALUE(iso_axis, iso), 0.0);
1066 for (i = 0; i < num_sam_to_use; i++) {
1067 double sam = sam_min + i * sam_step;
1071 (void) Gcomplex(&plot_func.dummy_values[cross ? 1 : 0],
1072 AXIS_DE_LOG_VALUE(sam_axis, sam), 0.0);
1082 evaluate_at(plot_func.at, &a);
1084 if (undefined || (fabs(imag(&a)) > zero)) {
1085 points[i].type = UNDEFINED;
1090 points[i].type = INRANGE;
1091 STORE_WITH_LOG_AND_UPDATE_RANGE(points[i].z, temp, points[i].type,
1092 value_axis, NOOP, NOOP);
1093 if (do_update_color) {
1094 COLOR_STORE_WITH_LOG_AND_UPDATE_RANGE(points[i].CRD_COLOR, temp, points[i].type, COLOR_AXIS, NOOP, NOOP);
1097 (*this_iso)->p_count = num_sam_to_use;
1098 *this_iso = (*this_iso)->next;
1099 points = (*this_iso) ? (*this_iso)->points : NULL;
1105 * This parses the splot command after any range specifications. To support
1106 * autoscaling on the x/z axis, we want any data files to define the x/y
1107 * range, then to plot any functions using that range. We thus parse the
1108 * input twice, once to pick up the data files, and again to pick up the
1109 * functions. Definitions are processed twice, but that won't hurt.
1110 * div - okay, it doesn't hurt, but every time an option as added for
1111 * datafiles, code to parse it has to be added here. Change so that
1112 * we store starting-token in the plot structure.
1118 struct surface_points **tp_3d_ptr;
1119 int start_token, end_token;
1121 TBOOLEAN some_data_files = FALSE, some_functions = FALSE;
1122 int plot_num, line_num, point_num;
1123 /* part number of parametric function triplet: 0 = z, 1 = y, 2 = x */
1127 legend_key *key = &keyT;
1129 /* Reset first_3dplot. This is usually done at the end of this function.
1130 * If there is an error within this function, the memory is left allocated,
1131 * since we cannot call sp_free if the list is incomplete
1133 if (first_3dplot && plot3d_num>0)
1134 sp_free(first_3dplot);
1136 first_3dplot = NULL;
1138 x_axis = FIRST_X_AXIS;
1139 y_axis = FIRST_Y_AXIS;
1140 z_axis = FIRST_Z_AXIS;
1142 tp_3d_ptr = &(first_3dplot);
1144 line_num = 0; /* default line type */
1145 point_num = 0; /* default point type */
1150 begin_token = c_token;
1152 /*** First Pass: Read through data files ***/
1154 * This pass serves to set the x/yranges and to parse the command, as
1155 * well as filling in every thing except the function data. That is done
1156 * after the x/yrange is defined.
1160 int_error(c_token, "function to plot expected");
1162 start_token = c_token;
1164 if (is_definition(c_token)) {
1168 struct surface_points *this_plot;
1171 TBOOLEAN duplication = FALSE;
1172 TBOOLEAN set_title = FALSE, set_with = FALSE;
1173 TBOOLEAN set_lpstyle = FALSE;
1174 TBOOLEAN checked_once = FALSE;
1175 #ifdef EAM_DATASTRINGS
1176 TBOOLEAN set_labelstyle = FALSE;
1179 dummy_func = &plot_func;
1180 /* WARNING: do NOT free name_str */
1181 /* FIXME: could also be saved in this_plot */
1182 name_str = string_or_express(NULL);
1185 /*{{{ data file to plot */
1186 if (parametric && crnt_param != 0)
1187 int_error(c_token, "previous parametric function not fully specified");
1189 if (!some_data_files) {
1190 if (axis_array[FIRST_X_AXIS].autoscale & AUTOSCALE_MIN) {
1191 axis_array[FIRST_X_AXIS].min = VERYLARGE;
1193 if (axis_array[FIRST_X_AXIS].autoscale & AUTOSCALE_MAX) {
1194 axis_array[FIRST_X_AXIS].max = -VERYLARGE;
1196 if (axis_array[FIRST_Y_AXIS].autoscale & AUTOSCALE_MIN) {
1197 axis_array[FIRST_Y_AXIS].min = VERYLARGE;
1199 if (axis_array[FIRST_Y_AXIS].autoscale & AUTOSCALE_MAX) {
1200 axis_array[FIRST_Y_AXIS].max = -VERYLARGE;
1202 some_data_files = TRUE;
1205 this_plot = *tp_3d_ptr;
1206 else { /* no memory malloc()'d there yet */
1207 /* Allocate enough isosamples and samples */
1208 this_plot = sp_alloc(0, 0, 0, 0);
1209 *tp_3d_ptr = this_plot;
1212 this_plot->plot_type = DATA3D;
1213 this_plot->plot_style = data_style;
1215 df_set_plot_mode(MODE_SPLOT);
1216 specs = df_open(name_str, MAXDATACOLS);
1217 #ifdef BINARY_DATA_FILE
1219 this_plot->has_grid_topology = TRUE;
1222 /* parses all datafile-specific modifiers */
1223 /* we will load the data after parsing title,with,... */
1225 /* for capture to key */
1226 this_plot->token = end_token = c_token - 1;
1228 /* this_plot->token is temporary, for errors in get_3ddata() */
1231 if (axis_array[FIRST_X_AXIS].is_timedata) {
1232 int_error(c_token, "Need full using spec for x time data");
1234 if (axis_array[FIRST_Y_AXIS].is_timedata) {
1235 int_error(c_token, "Need full using spec for y time data");
1238 df_axis[0] = FIRST_X_AXIS;
1239 df_axis[1] = FIRST_Y_AXIS;
1240 df_axis[2] = FIRST_Z_AXIS;
1244 } else { /* function to plot */
1249 /* Rotate between x/y/z axes */
1250 /* +2 same as -1, but beats -ve problem */
1251 crnt_param = (crnt_param + 2) % 3;
1254 this_plot = *tp_3d_ptr;
1256 sp_replace(this_plot, samples_1, iso_samples_1,
1257 samples_2, iso_samples_2);
1259 sp_replace(this_plot, iso_samples_1, 0,
1262 } else { /* no memory malloc()'d there yet */
1263 /* Allocate enough isosamples and samples */
1265 this_plot = sp_alloc(samples_1, iso_samples_1,
1266 samples_2, iso_samples_2);
1268 this_plot = sp_alloc(iso_samples_1, 0,
1270 *tp_3d_ptr = this_plot;
1273 this_plot->plot_type = FUNC3D;
1274 this_plot->has_grid_topology = TRUE;
1275 this_plot->plot_style = func_style;
1276 this_plot->num_iso_read = iso_samples_2;
1277 /* ignore it for now */
1278 some_functions = TRUE;
1279 end_token = c_token - 1;
1282 } /* end of IS THIS A FILE OR A FUNC block */
1284 /* clear current title, if exist */
1285 if (this_plot->title) {
1286 free(this_plot->title);
1287 this_plot->title = NULL;
1290 /* default line and point types, no palette */
1291 this_plot->lp_properties.use_palette = 0;
1292 this_plot->lp_properties.l_type = line_num;
1293 this_plot->lp_properties.p_type = point_num;
1295 /* user may prefer explicit line styles */
1296 if (prefer_line_styles)
1297 lp_use_properties(&this_plot->lp_properties, line_num+1, TRUE);
1299 /* pm 25.11.2001 allow any order of options */
1300 while (!END_OF_COMMAND || !checked_once) {
1302 /* deal with title */
1303 if (almost_equals(c_token, "t$itle")) {
1308 this_plot->title_no_enhanced = !key->enhanced;
1309 /* title can be enhanced if not explicitly disabled */
1311 if (crnt_param != 0)
1312 int_error(c_token, "\"title\" allowed only after parametric function fully specified");
1315 xtitle[0] = NUL; /* Remove default title . */
1317 ytitle[0] = NUL; /* Remove default title . */
1321 if (!(this_plot->title = try_to_get_string()))
1322 int_error(c_token, "expecting \"title\" for plot");
1327 if (almost_equals(c_token, "not$itle")) {
1333 if (isstringvalue(c_token))
1334 try_to_get_string(); /* ignore optionally given title string */
1339 /* this_plot->title = NULL; */
1344 /* deal with style */
1345 if (almost_equals(c_token, "w$ith")) {
1350 this_plot->plot_style = get_style();
1351 if ((this_plot->plot_type == FUNC3D) &&
1352 ((this_plot->plot_style & PLOT_STYLE_HAS_ERRORBAR)
1353 #ifdef EAM_DATASTRINGS
1354 || (this_plot->plot_style == LABELPOINTS)
1357 int_warn(c_token, "This plot style is only for datafiles , reverting to \"points\"");
1358 this_plot->plot_style = POINTSTYLE;
1361 if ((this_plot->plot_style | data_style) & PM3DSURFACE) {
1362 if (equals(c_token, "at")) {
1363 /* option 'with pm3d [at ...]' is explicitly specified */
1365 if (get_pm3d_at_option(&this_plot->pm3d_where[0]))
1374 /* EAM Dec 2005 - Hidden3D code by default includes points, */
1375 /* labels and vectors in the hidden3d processing. Check here */
1376 /* if this particular plot wants to be excluded. */
1377 if (almost_equals(c_token, "nohidden$3d")) {
1379 this_plot->opt_out_of_hidden3d = TRUE;
1383 /* "set contour" is global. Allow individual plots to opt out */
1384 if (almost_equals(c_token, "nocon$tours")) {
1386 this_plot->opt_out_of_contours = TRUE;
1390 #ifdef EAM_DATASTRINGS
1391 /* Labels can have font and text property info as plot options */
1392 /* In any case we must allocate one instance of the text style */
1393 /* that all labels in the plot will share. */
1394 if (this_plot->plot_style == LABELPOINTS) {
1395 int stored_token = c_token;
1396 if (this_plot->labels == NULL) {
1397 this_plot->labels = new_text_label(-1);
1398 this_plot->labels->pos = JUST_CENTRE;
1399 this_plot->labels->layer = LAYER_PLOTLABELS;
1401 parse_label_options(this_plot->labels);
1402 checked_once = TRUE;
1403 if (stored_token != c_token) {
1404 if (set_labelstyle) {
1408 set_labelstyle = TRUE;
1415 /* pick up line/point specs
1416 * - point spec allowed if style uses points, ie style&2 != 0
1417 * - keywords are optional
1419 if (this_plot->plot_style == VECTOR) {
1420 int stored_token = c_token;
1422 if (!checked_once) {
1423 default_arrow_style(&this_plot->arrow_properties);
1424 this_plot->arrow_properties.lp_properties.l_type = line_num;
1425 checked_once = TRUE;
1427 arrow_parse(&this_plot->arrow_properties, TRUE);
1428 if (stored_token != c_token) {
1434 this_plot->lp_properties = this_plot->arrow_properties.lp_properties;
1439 int stored_token = c_token;
1440 struct lp_style_type lp = DEFAULT_LP_STYLE_TYPE;
1442 lp.l_type = line_num;
1443 lp.p_type = point_num;
1445 /* user may prefer explicit line styles */
1446 if (prefer_line_styles)
1447 lp_use_properties(&lp, line_num+1, TRUE);
1450 this_plot->plot_style & PLOT_STYLE_HAS_POINT);
1451 checked_once = TRUE;
1452 if (stored_token != c_token) {
1457 this_plot->lp_properties = lp;
1464 break; /* unknown option */
1466 } /* while (!END_OF_COMMAND)*/
1469 int_error(c_token, "duplicated or contradicting arguments in plot options");
1471 /* set default values for title if this has not been specified */
1473 this_plot->title_no_enhanced = TRUE; /* filename or function cannot be enhanced */
1474 if (key->auto_titles == FILENAME_KEYTITLES) {
1475 #ifdef BINARY_DATA_FILE
1476 /* DJS (20 Aug 2004) I'd prefer that the df_binary flag be discarded. There
1477 * is nothing special about the file being binary that its title should be
1478 * different. Can't the decision to do this be based on some other criteria,
1479 * like the presence of a nonconventional `using`?
1482 if (this_plot->plot_type == DATA3D && df_binary==TRUE && end_token==start_token+1)
1483 /* let default title for splot 'a.dat' binary is 'a.dat'
1484 * while for 'a.dat' binary using 2:1:3 will be all 4 words */
1485 m_capture(&(this_plot->title), start_token, start_token);
1487 m_capture(&(this_plot->title), start_token, end_token);
1488 if (crnt_param == 2)
1489 xtitle = this_plot->title;
1490 else if (crnt_param == 1)
1491 ytitle = this_plot->title;
1497 /* this_plot->title = NULL; */
1501 /* No line/point style given. As lp_parse also supplies
1502 * the defaults for linewidth and pointsize, call it now
1503 * to define them. */
1504 if (! set_lpstyle) {
1505 if (this_plot->plot_style == VECTOR) {
1506 this_plot->arrow_properties.lp_properties.l_type = line_num;
1507 arrow_parse(&this_plot->arrow_properties, TRUE);
1508 this_plot->lp_properties = this_plot->arrow_properties.lp_properties;
1510 this_plot->lp_properties.l_type = line_num;
1511 this_plot->lp_properties.l_width = 1.0;
1512 this_plot->lp_properties.p_type = point_num;
1513 this_plot->lp_properties.p_size = pointsize;
1514 this_plot->lp_properties.use_palette = 0;
1516 /* user may prefer explicit line styles */
1517 if (prefer_line_styles)
1518 lp_use_properties(&this_plot->lp_properties, line_num+1, TRUE);
1520 lp_parse(&this_plot->lp_properties, TRUE,
1521 this_plot->plot_style & PLOT_STYLE_HAS_POINT);
1524 #ifdef BACKWARDS_COMPATIBLE
1525 /* allow old-style syntax - ignore case lt 3 4 for example */
1526 if (!END_OF_COMMAND && isanumber(c_token)) {
1529 this_plot->lp_properties.l_type =
1530 this_plot->lp_properties.p_type =
1531 (int) real(const_express(&t)) - 1;
1533 if (isanumber(c_token))
1534 this_plot->lp_properties.p_type =
1535 (int) real(const_express(&t)) - 1;
1541 /* Rule out incompatible line/point/style options */
1542 if (this_plot->plot_type == FUNC3D) {
1543 if ((this_plot->plot_style & PLOT_STYLE_HAS_POINT)
1544 && (this_plot->lp_properties.p_size == PTSZ_VARIABLE))
1545 this_plot->lp_properties.p_size = 1;
1549 && this_plot->plot_style != PM3DSURFACE
1550 /* don't increment the default line/point properties if
1551 * this_plot is an EXPLICIT pm3d surface plot */
1553 && this_plot->plot_style != IMAGE
1554 && this_plot->plot_style != RGBIMAGE
1555 /* same as above, for an (rgb)image plot */
1558 if (this_plot->plot_style & PLOT_STYLE_HAS_POINT)
1559 point_num += 1 + (draw_contour != 0) + (hidden3d != 0);
1560 line_num += 1 + (draw_contour != 0) + (hidden3d != 0);
1564 /* Styles that utilize palettes. */
1565 if (this_plot->plot_style == IMAGE)
1566 this_plot->lp_properties.use_palette = 1;
1569 /* now get the data... having to think hard here...
1570 * first time through, we fill in this_plot. For second
1571 * surface in file, we have to allocate another surface
1572 * struct. BUT we may allocate this store only to
1573 * find that it is merely some blank lines at end of file
1574 * tp_3d_ptr is still pointing at next field of prev. plot,
1575 * before : prev_or_first -> this_plot -> possible_preallocated_store
1577 * after : prev_or_first -> first -> second -> last -> possibly_more_store
1579 * if file is empty, tp_3d_ptr is not moved. this_plot continues
1580 * to point at allocated storage, but that will be reused later
1583 assert(this_plot == *tp_3d_ptr);
1585 if (this_plot->plot_type == DATA3D) {
1587 /* remember settings for second surface in file */
1588 struct lp_style_type *these_props = &(this_plot->lp_properties);
1589 enum PLOT_STYLE this_style = this_plot->plot_style;
1590 struct surface_points *first_dataset = this_plot;
1591 /* pointer to the plot of the first dataset (surface) in the file */
1592 int this_token = this_plot->token;
1595 this_plot = *tp_3d_ptr;
1596 assert(this_plot != NULL);
1598 /* dont move tp_3d_ptr until we are sure we
1599 * have read a surface
1602 /* used by get_3ddata() */
1603 this_plot->token = this_token;
1605 get_3ddata(this_plot);
1606 /* for second pass */
1607 this_plot->token = c_token;
1609 if (this_plot->num_iso_read == 0)
1610 this_plot->plot_type = NODATA;
1612 if (this_plot != first_dataset)
1613 /* copy (explicit) "with pm3d at ..." option from the first dataset in the file */
1614 strcpy(this_plot->pm3d_where, first_dataset->pm3d_where);
1616 /* okay, we have read a surface */
1618 tp_3d_ptr = &(this_plot->next_sp);
1622 /* there might be another surface so allocate
1623 * and prepare another surface structure
1624 * This does no harm if in fact there are
1625 * no more surfaces to read
1628 if ((this_plot = *tp_3d_ptr) != NULL) {
1629 if (this_plot->title) {
1630 free(this_plot->title);
1631 this_plot->title = NULL;
1634 /* Allocate enough isosamples and samples */
1635 this_plot = *tp_3d_ptr = sp_alloc(0, 0, 0, 0);
1638 this_plot->plot_type = DATA3D;
1639 this_plot->plot_style = this_style;
1641 this_plot->lp_properties = *these_props;
1647 } else { /* not a data file */
1648 tp_3d_ptr = &(this_plot->next_sp);
1649 this_plot->token = c_token; /* store for second pass */
1652 } /* !is_definition() : end of scope of this_plot */
1655 if (equals(c_token, ","))
1660 } /* while(TRUE), ie first pass */
1663 if (parametric && crnt_param != 0)
1664 int_error(NO_CARET, "parametric function not fully specified");
1667 /*** Second Pass: Evaluate the functions ***/
1669 * Everything is defined now, except the function data. We expect no
1670 * syntax errors, etc, since the above parsed it all. This makes the code
1671 * below simpler. If axis_array[FIRST_Y_AXIS].autoscale, the yrange may still change.
1672 * - eh ? - z can still change. x/y/z can change if we are parametric ??
1675 if (some_functions) {
1676 /* I've changed the controlled variable in fn plots to u_min etc since
1677 * it's easier for me to think parametric - 'normal' plot is after all
1678 * a special case. I was confused about x_min being both minimum of
1679 * x values found, and starting value for fn plots.
1681 double u_min, u_max, u_step, v_min, v_max, v_step;
1682 double u_isostep, v_isostep;
1683 AXIS_INDEX u_axis, v_axis;
1684 struct surface_points *this_plot;
1686 /* Make these point out the right 'u' and 'v' axis. In
1687 * non-parametric mode, x is used as u, and y as v */
1688 u_axis = parametric ? U_AXIS : FIRST_X_AXIS;
1689 v_axis = parametric ? V_AXIS : FIRST_Y_AXIS;
1692 /*{{{ check ranges */
1693 /* give error if xrange badly set from missing datafile error
1694 * parametric fn can still set ranges
1695 * if there are no fns, we'll report it later as 'nothing to plot'
1698 /* check that xmin -> xmax is not too small */
1699 axis_checked_extend_empty_range(FIRST_X_AXIS, "x range is invalid");
1700 axis_checked_extend_empty_range(FIRST_Y_AXIS, "y range is invalid");
1703 if (parametric && !some_data_files) {
1705 /* parametric fn can still change x/y range */
1706 if (axis_array[FIRST_X_AXIS].autoscale & AUTOSCALE_MIN)
1707 axis_array[FIRST_X_AXIS].min = VERYLARGE;
1708 if (axis_array[FIRST_X_AXIS].autoscale & AUTOSCALE_MAX)
1709 axis_array[FIRST_X_AXIS].max = -VERYLARGE;
1710 if (axis_array[FIRST_Y_AXIS].autoscale & AUTOSCALE_MIN)
1711 axis_array[FIRST_Y_AXIS].min = VERYLARGE;
1712 if (axis_array[FIRST_Y_AXIS].autoscale & AUTOSCALE_MAX)
1713 axis_array[FIRST_Y_AXIS].max = -VERYLARGE;
1717 /*{{{ figure ranges, taking logs etc into account */
1718 u_min = axis_log_value_checked(u_axis, axis_array[u_axis].min, "x range");
1719 u_max = axis_log_value_checked(u_axis, axis_array[u_axis].max, "x range");
1720 v_min = axis_log_value_checked(v_axis, axis_array[v_axis].min, "y range");
1721 v_max = axis_log_value_checked(v_axis, axis_array[v_axis].max, "y range");
1725 if (samples_1 < 2 || samples_2 < 2 || iso_samples_1 < 2 ||
1726 iso_samples_2 < 2) {
1727 int_error(NO_CARET, "samples or iso_samples < 2. Must be at least 2.");
1731 this_plot = first_3dplot;
1732 c_token = begin_token;
1734 /* why do attributes of this_plot matter ? */
1735 /* FIXME HBB 20000501: I think they don't, actually. I'm
1736 * taking out references to has_grid_topology in this part of
1737 * the code. It only deals with function, which always is
1741 u_step = (u_max - u_min) / (iso_samples_1 - 1);
1742 v_step = (v_max - v_min) / (iso_samples_2 - 1);
1744 u_step = (u_max - u_min) / (samples_1 - 1);
1745 v_step = (v_max - v_min) / (samples_2 - 1);
1748 u_isostep = (u_max - u_min) / (iso_samples_1 - 1);
1749 v_isostep = (v_max - v_min) / (iso_samples_2 - 1);
1752 /* Read through functions */
1754 if (is_definition(c_token)) {
1757 struct at_type *at_ptr;
1760 dummy_func = &plot_func;
1761 name_str = string_or_express(&at_ptr);
1763 if (!name_str) { /* func to plot */
1764 /*{{{ evaluate function */
1765 struct iso_curve *this_iso = this_plot->iso_crvs;
1766 int num_sam_to_use, num_iso_to_use;
1768 /* crnt_param is used as the axis number. As the
1769 * axis array indices are ordered z, y, x, we have
1770 * to count *backwards*, starting starting at 2,
1771 * to properly store away contents to x, y and
1772 * z. The following little gimmick does that. */
1774 crnt_param = (crnt_param + 2) % 3;
1776 plot_func.at = at_ptr;
1778 num_iso_to_use = iso_samples_2;
1779 num_sam_to_use = hidden3d ? iso_samples_1 : samples_1;
1781 calculate_set_of_isolines(crnt_param, FALSE, &this_iso,
1782 v_axis, v_min, v_isostep,
1784 u_axis, u_min, u_step,
1786 NEED_PALETTE(this_plot));
1789 num_iso_to_use = iso_samples_1;
1790 num_sam_to_use = samples_2;
1792 calculate_set_of_isolines(crnt_param, TRUE, &this_iso,
1793 u_axis, u_min, u_isostep,
1795 v_axis, v_min, v_step,
1797 NEED_PALETTE(this_plot));
1800 } /* end of ITS A FUNCTION TO PLOT */
1801 /* we saved it from first pass */
1802 c_token = this_plot->token;
1804 /* one data file can make several plots */
1806 this_plot = this_plot->next_sp;
1807 while (this_plot && this_plot->token == c_token);
1809 } /* !is_definition */
1811 if (equals(c_token, ","))
1820 /* Now actually fix the plot triplets to be single plots. */
1821 parametric_3dfixup(first_3dplot, &plot_num);
1823 } /* some functions */
1825 /* if first_3dplot is NULL, we have no functions or data at all.
1826 * This can happen, if you type "splot x=5", since x=5 is a
1827 * variable assignment
1829 if (plot_num == 0 || first_3dplot == NULL) {
1830 int_error(c_token, "no functions or data to plot");
1833 axis_checked_extend_empty_range(FIRST_X_AXIS, "All points x value undefined");
1834 axis_checked_extend_empty_range(FIRST_Y_AXIS, "All points y value undefined");
1836 axis_checked_extend_empty_range(FIRST_Z_AXIS, NULL); /* Suppress warning message */
1838 axis_checked_extend_empty_range(FIRST_Z_AXIS, "All points z value undefined");
1840 axis_revert_and_unlog_range(FIRST_X_AXIS);
1841 axis_revert_and_unlog_range(FIRST_Y_AXIS);
1842 axis_revert_and_unlog_range(FIRST_Z_AXIS);
1844 setup_tics(FIRST_X_AXIS, 20);
1845 setup_tics(FIRST_Y_AXIS, 20);
1846 setup_tics(FIRST_Z_AXIS, 20);
1848 set_plot_with_palette(plot_num, MODE_SPLOT);
1849 if (is_plot_with_palette()) {
1851 axis_checked_extend_empty_range(COLOR_AXIS, "All points of colorbox value undefined");
1852 setup_tics(COLOR_AXIS, 20);
1853 /* axis_revert_and_unlog_range(COLOR_AXIS); */
1854 /* fprintf(stderr,"plot3d.c: CB_AXIS.min=%g\tCB_AXIS.max=%g\n",CB_AXIS.min,CB_AXIS.max); */
1857 AXIS_WRITEBACK(FIRST_X_AXIS);
1859 if (plot_num == 0 || first_3dplot == NULL) {
1860 int_error(c_token, "no functions or data to plot");
1862 /* Creates contours if contours are to be plotted as well. */
1865 struct surface_points *this_plot;
1866 for (this_plot = first_3dplot, i = 0;
1868 this_plot = this_plot->next_sp, i++) {
1870 if (this_plot->contours) {
1871 struct gnuplot_contours *cntrs = this_plot->contours;
1874 struct gnuplot_contours *cntr = cntrs;
1875 cntrs = cntrs->next;
1879 this_plot->contours = NULL;
1882 /* Allow individual surfaces to opt out of contouring */
1883 if (this_plot->opt_out_of_contours)
1886 /* Make sure this one can be contoured. */
1887 if (!this_plot->has_grid_topology) {
1888 int_warn(NO_CARET, "Notice: Cannot contour non grid data. Please use \"set dgrid3d\".");
1889 } else if (this_plot->plot_type == DATA3D) {
1890 this_plot->contours = contour(this_plot->num_iso_read,
1891 this_plot->iso_crvs);
1893 this_plot->contours = contour(iso_samples_2,
1894 this_plot->iso_crvs);
1897 } /* draw_contour */
1899 /* the following ~9 lines were moved from the end of the
1900 * function to here, as do_3dplot calles term->text, which
1901 * itself might process input events in mouse enhanced
1902 * terminals. For redrawing to work, line capturing and
1903 * setting the plot3d_num must already be done before
1904 * entering do_3dplot(). Thu Jan 27 23:54:49 2000 (joze) */
1906 /* if we get here, all went well, so record the line for replot */
1907 if (plot_token != -1) {
1908 /* note that m_capture also frees the old replot_line */
1909 m_capture(&replot_line, plot_token, c_token - 1);
1913 /* record that all went well */
1914 plot3d_num=plot_num;
1916 /* perform the plot */
1918 print_3dtable(plot_num);
1920 START_LEAK_CHECK(); /* assert no memory leaks here ! */
1921 do_3dplot(first_3dplot, plot_num, 0);
1924 /* after do_3dplot(), axis_array[] and max_array[].min
1925 * contain the plotting range actually used (rounded
1926 * to tic marks, not only the min/max data values)
1927 * --> save them now for writeback if requested
1929 SAVE_WRITEBACK_ALL_AXES;
1930 /* update GPVAL_ variables available to user */
1931 update_gpval_variables(1);
1938 * The hardest part of this routine is collapsing the FUNC plot types in the
1939 * list (which are gauranteed to occur in (x,y,z) triplets while preserving
1940 * the non-FUNC type plots intact. This means we have to work our way
1941 * through various lists. Examples (hand checked):
1942 * start_plot:F1->F2->F3->NULL ==> F3->NULL
1943 * start_plot:F1->F2->F3->F4->F5->F6->NULL ==> F3->F6->NULL
1944 * start_plot:F1->F2->F3->D1->D2->F4->F5->F6->D3->NULL ==>
1945 * F3->D1->D2->F6->D3->NULL
1947 * x and y ranges now fixed in eval_3dplots
1950 parametric_3dfixup(struct surface_points *start_plot, int *plot_num)
1952 struct surface_points *xp, *new_list, *free_list = NULL;
1953 struct surface_points **last_pointer = &new_list;
1959 * Ok, go through all the plots and move FUNC3D types together. Note:
1960 * this originally was written to look for a NULL next pointer, but
1961 * gnuplot wants to be sticky in grabbing memory and the right number of
1962 * items in the plot list is controlled by the plot_num variable.
1964 * Since gnuplot wants to do this sticky business, a free_list of
1965 * surface_points is kept and then tagged onto the end of the plot list
1966 * as this seems more in the spirit of the original memory behavior than
1967 * simply freeing the memory. I'm personally not convinced this sort of
1968 * concern is worth it since the time spent computing points seems to
1969 * dominate any garbage collecting that might be saved here...
1971 new_list = xp = start_plot;
1972 for (surface = 0; surface < *plot_num; surface++) {
1973 if (xp->plot_type == FUNC3D) {
1974 struct surface_points *yp = xp->next_sp;
1975 struct surface_points *zp = yp->next_sp;
1977 /* Here's a FUNC3D parametric function defined as three parts.
1978 * Go through all the points and assign the x's and y's from xp and
1979 * yp to zp. min/max already done
1981 struct iso_curve *xicrvs = xp->iso_crvs;
1982 struct iso_curve *yicrvs = yp->iso_crvs;
1983 struct iso_curve *zicrvs = zp->iso_crvs;
1987 assert(INRANGE < OUTRANGE && OUTRANGE < UNDEFINED);
1990 struct coordinate GPHUGE *xpoints = xicrvs->points;
1991 struct coordinate GPHUGE *ypoints = yicrvs->points;
1992 struct coordinate GPHUGE *zpoints = zicrvs->points;
1994 for (i = 0; i < zicrvs->p_count; ++i) {
1995 zpoints[i].x = xpoints[i].z;
1996 zpoints[i].y = ypoints[i].z;
1997 if (zpoints[i].type < xpoints[i].type)
1998 zpoints[i].type = xpoints[i].type;
1999 if (zpoints[i].type < ypoints[i].type)
2000 zpoints[i].type = ypoints[i].type;
2003 xicrvs = xicrvs->next;
2004 yicrvs = yicrvs->next;
2005 zicrvs = zicrvs->next;
2008 #if 0 /* FIXME HBB 20001101: seems to cause a crash */
2010 sp_free(first_3dplot);
2011 first_3dplot = NULL;
2014 /* Ok, fix up the title to include xp and yp plots. */
2015 if (((xp->title && xp->title[0] != '\0') ||
2016 (yp->title && yp->title[0] != '\0')) && zp->title) {
2017 tlen = (xp->title ? strlen(xp->title) : 0) +
2018 (yp->title ? strlen(yp->title) : 0) +
2019 (zp->title ? strlen(zp->title) : 0) + 5;
2020 new_title = gp_alloc(tlen, "string");
2022 if (xp->title && xp->title[0] != '\0') {
2023 strcat(new_title, xp->title);
2024 strcat(new_title, ", "); /* + 2 */
2026 if (yp->title && yp->title[0] != '\0') {
2027 strcat(new_title, yp->title);
2028 strcat(new_title, ", "); /* + 2 */
2030 strcat(new_title, zp->title);
2032 zp->title = new_title;
2034 /* add xp and yp to head of free list */
2035 assert(xp->next_sp == yp);
2036 yp->next_sp = free_list;
2039 /* add zp to tail of new_list */
2041 last_pointer = &(zp->next_sp);
2043 } else { /* its a data plot */
2044 assert(*last_pointer == xp); /* think this is true ! */
2045 last_pointer = &(xp->next_sp);
2050 /* Ok, append free list and write first_plot */
2051 *last_pointer = free_list;
2052 first_3dplot = new_list;