/* GNUPLOT - graph3d.c */
/*[
* Copyright 1986 - 1993, 1998, 2004 Thomas Williams, Colin Kelley
*
* Permission to use, copy, and distribute this software and its
* documentation for any purpose with or without fee is hereby granted,
* provided that the above copyright notice appear in all copies and
* that both that copyright notice and this permission notice appear
* in supporting documentation.
*
* Permission to modify the software is granted, but not the right to
* distribute the complete modified source code. Modifications are to
* be distributed as patches to the released version. Permission to
* distribute binaries produced by compiling modified sources is granted,
* provided you
* 1. distribute the corresponding source modifications from the
* released version in the form of a patch file along with the binaries,
* 2. add special version identification to distinguish your version
* in addition to the base release version number,
* 3. provide your name and address as the primary contact for the
* support of your modified version, and
* 4. retain our contact information in regard to use of the base
* software.
* Permission to distribute the released version of the source code along
* with corresponding source modifications in the form of a patch file is
* granted with same provisions 2 through 4 for binary distributions.
*
* This software is provided "as is" without express or implied warranty
* to the extent permitted by applicable law.
]*/
/*
* AUTHORS
*
* Original Software:
* Gershon Elber and many others.
*
* 19 September 1992 Lawrence Crowl (crowl@cs.orst.edu)
* Added user-specified bases for log scaling.
*
* 3.6 - split graph3d.c into graph3d.c (graph),
* util3d.c (intersections, etc)
* hidden3d.c (hidden-line removal code)
*
*/
#include "graph3d.h"
#include "alloc.h"
#include "axis.h"
#include "command.h"
#include "contour.h"
#include "gadgets.h"
#include "hidden3d.h"
#include "misc.h"
#include "term_api.h"
#include "util3d.h"
#include "util.h"
#include "pm3d.h"
#include "plot3d.h"
#include "color.h"
#include "plot.h"
static int key_entry_height; /* bigger of t->v_char, pointsize*t->v_tick */
static int key_title_height;
static int key_title_extra; /* allow room for subscript/superscript */
/* is contouring wanted ? */
t_contour_placement draw_contour = CONTOUR_NONE;
TBOOLEAN clabel_onecolor = FALSE; /* use same linetype for all contours */
int clabel_interval = 20; /* label every 20th contour segment */
int clabel_start = 5; /* starting with the 5th */
char *clabel_font = NULL; /* default to current font */
/* Draw the surface at all? (FALSE if only contours are wanted) */
TBOOLEAN draw_surface = TRUE;
/* Always create a gridded surface when lines are read from a data file */
TBOOLEAN implicit_surface = TRUE;
/* Was hidden3d display selected by user? */
TBOOLEAN hidden3d = FALSE;
int hidden3d_layer = LAYER_BACK;
/* Rotation and scale of the 3d view, as controlled by 'set view': */
float surface_rot_z = 30.0;
float surface_rot_x = 60.0;
float surface_scale = 1.0;
float surface_zscale = 1.0;
float surface_lscale = 0.0;
float mapview_scale = 1.0;
float azimuth = 0.0;
/* Set by 'set view map': */
int splot_map = FALSE;
/* position of the base plane, as given by 'set ticslevel' or 'set xyplane' */
t_xyplane xyplane = { 0.5, FALSE };
/* 'set isosamples' settings */
int iso_samples_1 = ISO_SAMPLES;
int iso_samples_2 = ISO_SAMPLES;
double xscale3d, yscale3d, zscale3d;
double xcenter3d = 0.0;
double ycenter3d = 0.0;
double zcenter3d = 0.0;
typedef enum { ALLGRID, FRONTGRID, BACKGRID, BORDERONLY } WHICHGRID;
static void do_3dkey_layout __PROTO((legend_key *key, int *xinkey, int *yinkey));
static void plot3d_impulses __PROTO((struct surface_points * plot));
static void plot3d_lines __PROTO((struct surface_points * plot));
static void plot3d_points __PROTO((struct surface_points * plot));
static void plot3d_zerrorfill __PROTO((struct surface_points * plot));
static void plot3d_vectors __PROTO((struct surface_points * plot));
/* no pm3d for impulses */
static void plot3d_lines_pm3d __PROTO((struct surface_points * plot));
static void get_surface_cbminmax __PROTO((struct surface_points *plot, double *cbmin, double *cbmax));
static void cntr3d_impulses __PROTO((struct gnuplot_contours * cntr,
struct lp_style_type * lp));
static void cntr3d_lines __PROTO((struct gnuplot_contours * cntr,
struct lp_style_type * lp));
static void cntr3d_points __PROTO((struct gnuplot_contours * cntr,
struct lp_style_type * lp));
static void cntr3d_labels __PROTO((struct gnuplot_contours * cntr, char * leveltext,
struct text_label * label));
static void check_corner_height __PROTO((struct coordinate GPHUGE * point,
double height[2][2], double depth[2][2]));
static void setup_3d_box_corners __PROTO((void));
static void draw_3d_graphbox __PROTO((struct surface_points * plot,
int plot_count,
WHICHGRID whichgrid, int current_layer));
static void xtick_callback __PROTO((struct axis *, double place, char *text, int ticlevel,
struct lp_style_type grid, struct ticmark *userlabels));
static void ytick_callback __PROTO((struct axis *, double place, char *text, int ticlevel,
struct lp_style_type grid, struct ticmark *userlabels));
static void ztick_callback __PROTO((struct axis *, double place, char *text, int ticlevel,
struct lp_style_type grid, struct ticmark *userlabels));
static int find_maxl_cntr __PROTO((struct gnuplot_contours * contours, int *count));
static int find_maxl_keys3d __PROTO((struct surface_points *plots, int count, int *kcnt));
static void boundary3d __PROTO((struct surface_points * plots, int count));
/* put entries in the key */
static void key_sample_line __PROTO((int xl, int yl));
static void key_sample_point __PROTO((int xl, int yl, int pointtype));
static void key_sample_line_pm3d __PROTO((struct surface_points *plot, int xl, int yl));
static void key_sample_point_pm3d __PROTO((struct surface_points *plot, int xl, int yl, int pointtype));
static void key_sample_fill __PROTO((int xl, int yl, struct fill_style_type *fs));
static TBOOLEAN can_pm3d = FALSE;
static void key_text __PROTO((int xl, int yl, char *text));
static void check3d_for_variable_color __PROTO((struct surface_points *plot, struct coordinate *point));
static TBOOLEAN get_arrow3d __PROTO((struct arrow_def*, int*, int*, int*, int*));
static void place_arrows3d __PROTO((int));
static void place_labels3d __PROTO((struct text_label * listhead, int layer));
static int map3d_getposition __PROTO((struct position* pos, const char* what, double* xpos, double* ypos, double* zpos));
# define f_max(a,b) GPMAX((a),(b))
# define f_min(a,b) GPMIN((a),(b))
# define i_inrange(z,a,b) inrange((z),(a),(b))
#define apx_eq(x,y) (fabs(x-y) < 0.001)
#define ABS(x) ((x) >= 0 ? (x) : -(x))
#define SQR(x) ((x) * (x))
/* Define the boundary of the plot
* These are computed at each call to do_plot, and are constant over
* the period of one do_plot. They actually only change when the term
* type changes and when the 'set size' factors change.
*/
int xmiddle, ymiddle, xscaler, yscaler;
double xyscaler;
static int ptitl_cnt;
static int max_ptitl_len;
static int titlelin;
static int key_sample_width, key_rows, key_cols, key_col_wth, yl_ref;
static double ktitle_lines = 0;
/* Boundary and scale factors, in user coordinates */
/* These positions assume a single linear scale encompassing the
* zrange plus extra space below for the baseplane. This was messy but
* correct before the introduction of nonlinear axes. Now - not so much.
*
* ceiling_z is the highest z in use
* floor_z is the lowest z in use
* base_z is the z of the base
* min3d_z is the lowest z of the graph area
* max3d_z is the highest z of the graph area
*
* ceiling_z is either max3d_z or base_z, and similarly for floor_z
* There should be no part of graph drawn outside
* min3d_z:max3d_z - apart from arrows, perhaps
*/
double floor_z;
double ceiling_z, base_z; /* made exportable for PM3D */
/* To handle a non-linear z axis we need to calculate these values on
* the other end of the linked linear:nonlinear axis pair.
*/
double floor_z1; /* Used also by map_z3d() */
static double ceiling_z1, base_z1;
transform_matrix trans_mat;
/* x and y input range endpoints where the three axes are to be
* displayed (left, front-left, and front-right edges of the cube) */
static double xaxis_y, yaxis_x, zaxis_x, zaxis_y;
/* ... and the same for the back, right, and front corners */
static double back_x, back_y;
static double right_x, right_y;
static double front_x, front_y;
#ifdef USE_MOUSE
int axis3d_o_x, axis3d_o_y, axis3d_x_dx, axis3d_x_dy, axis3d_y_dx, axis3d_y_dy;
#endif
/* the penalty for convenience of using tic_gen to make callbacks
* to tick routines is that we cannot pass parameters very easily.
* We communicate with the tick_callbacks using static variables
*/
/* unit vector (terminal coords) */
static double tic_unitx, tic_unity, tic_unitz;
/* calculate the number and max-width of the keys for an splot.
* Note that a blank line is issued after each set of contours
*/
static int
find_maxl_keys3d(struct surface_points *plots, int count, int *kcnt)
{
int mlen, len, surf, cnt;
struct surface_points *this_plot;
mlen = cnt = 0;
this_plot = plots;
for (surf = 0; surf < count; this_plot = this_plot->next_sp, surf++) {
/* we draw a main entry if there is one, and we are
* drawing either surface, or unlabeled contours
*/
if (this_plot->title && *this_plot->title
&& !this_plot->title_is_suppressed && !this_plot->title_position) {
++cnt;
len = estimate_strlen(this_plot->title);
if (len > mlen)
mlen = len;
}
if (draw_contour && !clabel_onecolor && this_plot->contours != NULL
&& this_plot->plot_style != LABELPOINTS) {
len = find_maxl_cntr(this_plot->contours, &cnt);
if (len > mlen)
mlen = len;
}
}
if (kcnt != NULL)
*kcnt = cnt;
return (mlen);
}
static int
find_maxl_cntr(struct gnuplot_contours *contours, int *count)
{
int cnt;
int mlen, len;
struct gnuplot_contours *cntrs = contours;
mlen = cnt = 0;
while (cntrs) {
if (cntrs->isNewLevel) {
len = estimate_strlen(cntrs->label)
- strspn(cntrs->label," ");
if (len)
cnt++;
if (len > mlen)
mlen = len;
}
cntrs = cntrs->next;
}
*count += cnt;
return (mlen);
}
/* borders of plotting area */
/* computed once on every call to do_plot */
static void
boundary3d(struct surface_points *plots, int count)
{
legend_key *key = &keyT;
struct termentry *t = term;
int ytlen, i;
titlelin = 0;
if (key->swidth >= 0)
key_sample_width = key->swidth * t->h_char + t->h_tic;
else
key_sample_width = 0;
key_entry_height = t->v_tic * 1.25 * key->vert_factor;
if (key_entry_height < t->v_char) {
/* is this reasonable ? */
key_entry_height = t->v_char * key->vert_factor;
}
/* count max_len key and number keys (plot-titles and contour labels) with len > 0 */
max_ptitl_len = find_maxl_keys3d(plots, count, &ptitl_cnt);
ytlen = label_width(key->title.text, &i) - (key->swidth + 2);
ktitle_lines = i;
if (ytlen > max_ptitl_len)
max_ptitl_len = ytlen;
key_col_wth = (max_ptitl_len + 4) * t->h_char + key_sample_width;
if (lmargin.scalex == screen)
plot_bounds.xleft = lmargin.x * (float)t->xmax + 0.5;
else if (lmargin.x >= 0)
plot_bounds.xleft = lmargin.x * (float)t->h_char + 0.5;
else
plot_bounds.xleft = t->h_char * 2 + t->h_tic;
if (rmargin.scalex == screen)
plot_bounds.xright = rmargin.x * (float)t->xmax + 0.5;
else /* No tic label on the right side, so ignore rmargin */
plot_bounds.xright = xsize * t->xmax - t->h_char * 2 - t->h_tic;
key_rows = ptitl_cnt;
key_cols = 1;
if (key_rows > key->maxrows && key->maxrows > 0) {
key_rows = key->maxrows;
key_cols = (ptitl_cnt - 1)/key_rows + 1;
}
if (key->visible)
if ((key->region == GPKEY_AUTO_EXTERIOR_MARGIN || key->region == GPKEY_AUTO_EXTERIOR_LRTBC)
&& key->margin == GPKEY_BMARGIN) {
if (ptitl_cnt > 0) {
/* calculate max no cols, limited by label-length */
key_cols = (int) (plot_bounds.xright - plot_bounds.xleft)
/ ((max_ptitl_len + 4) * t->h_char + key_sample_width);
/* HBB 991019: fix division by zero problem */
if (key_cols == 0)
key_cols = 1;
key_rows = (int) ((ptitl_cnt - 1)/ key_cols) + 1;
/* Limit the number of rows if requested by user */
if (key_rows > key->maxrows && key->maxrows > 0)
key_rows = key->maxrows;
/* now calculate actual no cols depending on no rows */
key_cols = (int) ((ptitl_cnt - 1)/ key_rows) + 1;
key_col_wth = (int) (plot_bounds.xright - plot_bounds.xleft) / key_cols;
} else {
key_rows = key_cols = key_col_wth = 0;
}
}
/* Sanity check top and bottom margins, in case the user got confused */
if (bmargin.scalex == screen && tmargin.scalex == screen)
if (bmargin.x > tmargin.x) {
double tmp = bmargin.x;
bmargin.x = tmargin.x;
tmargin.x = tmp;
}
/* this should also consider the view and number of lines in
* xformat || yformat || xlabel || ylabel */
if (bmargin.scalex == screen)
plot_bounds.ybot = bmargin.x * (float)t->ymax + 0.5;
else if (splot_map && bmargin.x >= 0)
plot_bounds.ybot = (float)t->v_char * bmargin.x;
else
plot_bounds.ybot = t->v_char * 2.5 + 1;
if (key->visible)
if (key_rows && (key->region == GPKEY_AUTO_EXTERIOR_MARGIN || key->region == GPKEY_AUTO_EXTERIOR_LRTBC)
&& key->margin == GPKEY_BMARGIN)
plot_bounds.ybot += key_rows * key_entry_height + ktitle_lines * t->v_char;
if (title.text) {
titlelin++;
for (i = 0; i < strlen(title.text); i++) {
if (title.text[i] == '\\')
titlelin++;
}
}
if (tmargin.scalex == screen)
plot_bounds.ytop = tmargin.x * (float)t->ymax + 0.5;
else /* FIXME: Why no provision for tmargin in terms of character height? */
plot_bounds.ytop = ysize * t->ymax - t->v_char * (titlelin + 1.5) - 1;
if (key->visible)
if (key->region == GPKEY_AUTO_INTERIOR_LRTBC
|| ((key->region == GPKEY_AUTO_EXTERIOR_LRTBC || key->region == GPKEY_AUTO_EXTERIOR_MARGIN)
&& key->margin == GPKEY_RMARGIN)) {
/* calculate max no rows, limited by plot_bounds.ytop-plot_bounds.ybot */
i = (int) (plot_bounds.ytop - plot_bounds.ybot) / t->v_char - 1 - ktitle_lines;
if (i > key->maxrows && key->maxrows > 0)
i = key->maxrows;
/* HBB 20030321: div by 0 fix like above */
if (i == 0)
i = 1;
if (ptitl_cnt > i) {
key_cols = (int) ((ptitl_cnt - 1)/ i) + 1;
/* now calculate actual no rows depending on no cols */
key_rows = (int) ((ptitl_cnt - 1) / key_cols) + 1;
}
}
if (key->visible)
if ((key->region == GPKEY_AUTO_EXTERIOR_LRTBC || key->region == GPKEY_AUTO_EXTERIOR_MARGIN)
&& key->margin == GPKEY_RMARGIN) {
int key_width = key_col_wth * (key_cols - 1) + key_col_wth - 2 * t->h_char;
if (rmargin.scalex != screen)
plot_bounds.xright -= key_width;
}
if (key->visible)
if ((key->region == GPKEY_AUTO_EXTERIOR_LRTBC || key->region == GPKEY_AUTO_EXTERIOR_MARGIN)
&& key->margin == GPKEY_LMARGIN) {
int key_width = key_col_wth * (key_cols - 1) + key_col_wth - 2 * t->h_char;
if (lmargin.scalex != screen)
plot_bounds.xleft += key_width;
}
if (!splot_map && aspect_ratio_3D > 0) {
int height = (plot_bounds.ytop - plot_bounds.ybot);
int width = (plot_bounds.xright - plot_bounds.xleft);
if (height > width) {
plot_bounds.ybot += (height-width)/2;
plot_bounds.ytop -= (height-width)/2;
} else {
plot_bounds.xleft += (width-height)/2;
plot_bounds.xright -= (width-height)/2;
}
}
if (lmargin.scalex != screen)
plot_bounds.xleft += t->xmax * xoffset;
if (rmargin.scalex != screen)
plot_bounds.xright += t->xmax * xoffset;
if (tmargin.scalex != screen)
plot_bounds.ytop += t->ymax * yoffset;
if (bmargin.scalex != screen)
plot_bounds.ybot += t->ymax * yoffset;
xmiddle = (plot_bounds.xright + plot_bounds.xleft) / 2;
ymiddle = (plot_bounds.ytop + plot_bounds.ybot) / 2;
/* HBB: Magic number alert! */
xscaler = ((plot_bounds.xright - plot_bounds.xleft) * 4L) / 7L;
yscaler = ((plot_bounds.ytop - plot_bounds.ybot) * 4L) / 7L;
/* EAM Aug 2006 - Allow explicit control via set {}margin screen */
if (tmargin.scalex == screen || bmargin.scalex == screen)
yscaler = (plot_bounds.ytop - plot_bounds.ybot) / surface_scale;
if (rmargin.scalex == screen || lmargin.scalex == screen)
xscaler = (plot_bounds.xright - plot_bounds.xleft) / surface_scale;
/* EAM Jul 2010 - prevent infinite loop or divide-by-zero if scaling is bad */
if (yscaler == 0) yscaler = 1;
if (xscaler == 0) xscaler = 1;
/* HBB 20011011: 'set size {square|ratio}' for splots */
if (splot_map && aspect_ratio != 0.0) {
double current_aspect_ratio;
if (aspect_ratio < 0 && (X_AXIS.max - X_AXIS.min) != 0.0) {
current_aspect_ratio = - aspect_ratio
* fabs((Y_AXIS.max - Y_AXIS.min) /
(X_AXIS.max - X_AXIS.min));
} else
current_aspect_ratio = aspect_ratio;
/*{{{ set aspect ratio if valid and sensible */
if (current_aspect_ratio >= 0.01 && current_aspect_ratio <= 100.0) {
double current = (double)yscaler / xscaler ;
double required = current_aspect_ratio * t->v_tic / t->h_tic;
if (current > required)
/* too tall */
yscaler = xscaler * required;
else
/* too wide */
xscaler = yscaler / required;
}
}
/* For anything that really wants to be the same on x and y */
xyscaler = sqrt(xscaler*yscaler);
/* Set default clipping */
if (splot_map)
clip_area = &plot_bounds;
else if (term->flags & TERM_CAN_CLIP)
clip_area = NULL;
else
clip_area = &canvas;
}
static TBOOLEAN
get_arrow3d(
struct arrow_def* arrow,
int* sx, int* sy,
int* ex, int* ey)
{
map3d_position(&(arrow->start), sx, sy, "arrow");
if (arrow->type == arrow_end_relative) {
map3d_position_r(&(arrow->end), ex, ey, "arrow");
*ex += *sx;
*ey += *sy;
} else if (arrow->type == arrow_end_oriented) {
double aspect = (double)term->v_tic / (double)term->h_tic;
double radius;
int junkw, junkh;
#ifdef WIN32
if (strcmp(term->name, "windows") == 0)
aspect = 1.;
#endif
if (arrow->end.scalex != screen && arrow->end.scalex != character && !splot_map)
return FALSE;
map3d_position_r(&arrow->end, &junkw, &junkh, "arrow");
radius = junkw;
*ex = *sx + cos(DEG2RAD * arrow->angle) * radius;
*ey = *sy + sin(DEG2RAD * arrow->angle) * radius * aspect;
} else {
map3d_position(&(arrow->end), ex, ey, "arrow");
}
return TRUE;
}
static void
place_labels3d(struct text_label *listhead, int layer)
{
struct text_label *this_label;
int x, y;
term->pointsize(pointsize);
for (this_label = listhead;
this_label != NULL;
this_label = this_label->next) {
if (this_label->layer != layer)
continue;
if (layer == LAYER_PLOTLABELS) {
double xx, yy;
map3d_xy_double(this_label->place.x, this_label->place.y,
this_label->place.z, &xx, &yy);
x = xx;
y = yy;
/* Only clip in 2D */
if (splot_map && clip_point(x, y))
continue;
} else
map3d_position(&this_label->place, &x, &y, "label");
write_label(x, y, this_label);
}
}
static void
place_arrows3d(int layer)
{
struct arrow_def *this_arrow;
BoundingBox *clip_save = clip_area;
/* Allow arrows to run off the plot, so long as they are still on the canvas */
if (term->flags & TERM_CAN_CLIP)
clip_area = NULL;
else
clip_area = &canvas;
for (this_arrow = first_arrow; this_arrow != NULL;
this_arrow = this_arrow->next) {
int sx, sy, ex, ey;
if (this_arrow->arrow_properties.layer != layer)
continue;
if (get_arrow3d(this_arrow, &sx, &sy, &ex, &ey)) {
term_apply_lp_properties(&(this_arrow->arrow_properties.lp_properties));
apply_head_properties(&(this_arrow->arrow_properties));
draw_clip_arrow(sx, sy, ex, ey, this_arrow->arrow_properties.head);
} else {
FPRINTF((stderr,"place_arrows3d: skipping out-of-bounds arrow\n"));
}
}
clip_area = clip_save;
}
/* we precalculate features of the key, to save lots of nested
* ifs in code - x,y = user supplied or computed position of key
* taken to be inner edge of a line sample
*/
static int key_sample_left; /* offset from x for left of line sample */
static int key_sample_right; /* offset from x for right of line sample */
static int key_point_offset; /* offset from x for point sample */
static int key_text_left; /* offset from x for left-justified text */
static int key_text_right; /* offset from x for right-justified text */
static int key_size_left; /* distance from x to left edge of box */
static int key_size_right; /* distance from x to right edge of box */
void
do_3dplot(
struct surface_points *plots,
int pcount, /* count of plots in linked list */
int quick) /* !=0 means plot only axes etc., for quick rotation */
{
struct termentry *t = term;
int surface;
struct surface_points *this_plot = NULL;
int xl = 0, yl = 0;
int xl_prev = 0, yl_prev = 0;
int xl_save, yl_save;
transform_matrix mat;
int key_count;
TBOOLEAN key_pass = FALSE;
legend_key *key = &keyT;
TBOOLEAN pm3d_order_depth = FALSE;
AXIS *primary_z;
/* Initiate transformation matrix using the global view variables. */
if (splot_map)
splot_map_activate();
mat_rot_z(surface_rot_z, trans_mat);
mat_rot_x(surface_rot_x, mat);
mat_mult(trans_mat, trans_mat, mat);
mat_scale(surface_scale / 2.0, surface_scale / 2.0, surface_scale / 2.0, mat);
mat_mult(trans_mat, trans_mat, mat);
/* The azimuth is applied as a rotation about the line of sight */
if (azimuth !=0 && !splot_map) {
mat_rot_z(azimuth, mat);
mat_mult(trans_mat, trans_mat, mat);
}
if (polar)
int_error(NO_CARET,"Cannot splot in polar coordinate system.");
/* In the case of a nonlinear z axis this points to the linear version */
/* that shadows it. Otherwise it just points to FIRST_Z_AXIS. */
primary_z = (nonlinear(&Z_AXIS)) ? Z_AXIS.linked_to_primary : &Z_AXIS;
/* absolute or relative placement of xyplane along z */
if (nonlinear(&Z_AXIS)) {
if (xyplane.absolute) {
if (primary_z->log && xyplane.z <= 0) {
base_z1 = eval_link_function(primary_z, Z_AXIS.min);
} else {
base_z1 = eval_link_function(primary_z, xyplane.z);
}
} else {
base_z1 = primary_z->min - (primary_z->max - primary_z->min) * xyplane.z;
}
base_z = eval_link_function(&Z_AXIS, base_z1);
} else {
if (xyplane.absolute)
base_z1 = AXIS_LOG_VALUE(0, xyplane.z);
else
base_z1 = primary_z->min - (primary_z->max - primary_z->min) * xyplane.z;
base_z = base_z1;
}
/* If we are to draw some portion of the xyplane make sure zmin is updated properly. */
if (X_AXIS.ticmode || Y_AXIS.ticmode || draw_border & 0x00F) {
if (primary_z->min > primary_z->max) {
floor_z1 = GPMAX(primary_z->min, base_z1);
ceiling_z1 = GPMIN(primary_z->max, base_z1);
} else {
floor_z1 = GPMIN(primary_z->min, base_z1);
ceiling_z1 = GPMAX(primary_z->max, base_z1);
}
} else {
floor_z1 = primary_z->min;
ceiling_z1 = primary_z->max;
}
if (nonlinear(&Z_AXIS)) {
floor_z = eval_link_function(&Z_AXIS, floor_z1);
ceiling_z = eval_link_function(&Z_AXIS, ceiling_z1);
} else {
floor_z = floor_z1;
ceiling_z = ceiling_z1;
}
if (X_AXIS.min == X_AXIS.max)
int_error(NO_CARET,"x_min3d should not equal x_max3d!");
if (Y_AXIS.min == Y_AXIS.max)
int_error(NO_CARET,"y_min3d should not equal y_max3d!");
if (Z_AXIS.min == Z_AXIS.max)
int_error(NO_CARET,"z_min3d should not equal z_max3d!");
term_start_plot();
(term->layer)(TERM_LAYER_3DPLOT);
screen_ok = FALSE;
/* Sync point for epslatex text positioning */
(term->layer)(TERM_LAYER_BACKTEXT);
/* now compute boundary for plot */
boundary3d(plots, pcount);
axis_set_scale_and_range(&axis_array[FIRST_X_AXIS], plot_bounds.xleft, plot_bounds.xright);
axis_set_scale_and_range(&axis_array[FIRST_Y_AXIS], plot_bounds.ybot, plot_bounds.ytop);
axis_set_scale_and_range(&axis_array[FIRST_Z_AXIS], floor_z, ceiling_z);
/* SCALE FACTORS */
zscale3d = 2.0 / (ceiling_z - floor_z) * surface_zscale;
yscale3d = 2.0 / (Y_AXIS.max - Y_AXIS.min);
xscale3d = 2.0 / (X_AXIS.max - X_AXIS.min);
if (nonlinear(&X_AXIS))
xscale3d = 2.0 / (X_AXIS.linked_to_primary->max - X_AXIS.linked_to_primary->min);
if (nonlinear(&Y_AXIS))
yscale3d = 2.0 / (Y_AXIS.linked_to_primary->max - Y_AXIS.linked_to_primary->min);
if (nonlinear(&Z_AXIS))
zscale3d = 2.0 / (ceiling_z1 - floor_z1) * surface_zscale;
/* Allow 'set view equal xy' to adjust rendered length of the X and/or Y axes. */
/* NB: only works correctly for terminals whose coordinate system is isotropic. */
xcenter3d = ycenter3d = zcenter3d = 0.0;
if (aspect_ratio_3D >= 2) {
if (yscale3d > xscale3d) {
ycenter3d = 1.0 - xscale3d/yscale3d;
yscale3d = xscale3d;
} else if (xscale3d > yscale3d) {
xcenter3d = 1.0 - yscale3d/xscale3d;
xscale3d = yscale3d;
}
if (aspect_ratio_3D >= 3)
zscale3d = xscale3d;
}
/* FIXME: I do not understand why this is correct */
if (nonlinear(&Z_AXIS))
zcenter3d = 0.0;
/* Without this the rotation center would be located at */
/* the bottom of the plot. This places it in the middle.*/
else
zcenter3d = -(ceiling_z - floor_z) / 2.0 * zscale3d + 1;
/* Needed for mousing by outboard terminal drivers */
if (splot_map) {
AXIS *X = &axis_array[FIRST_X_AXIS];
AXIS *Y = &axis_array[FIRST_Y_AXIS];
int xl, xr, yb, yt;
map3d_xy(X->min, Y->min, 0.0, &xl, &yb);
map3d_xy(X->max, Y->max, 0.0, &xr, &yt);
axis_set_scale_and_range(&axis_array[FIRST_X_AXIS], xl, xr);
axis_set_scale_and_range(&axis_array[FIRST_Y_AXIS], yb, yt);
}
/* Initialize palette */
if (!quick) {
can_pm3d = is_plot_with_palette() && !make_palette()
&& ((term->flags & TERM_NULL_SET_COLOR) == 0);
}
/* Give a chance for rectangles to be behind everything else */
place_objects( first_object, LAYER_BEHIND, 3);
term_apply_lp_properties(&border_lp); /* border linetype */
/* must come before using draw_3d_graphbox() the first time */
setup_3d_box_corners();
/* DRAW GRID AND BORDER */
/* Original behaviour: draw entire grid in back, if 'set grid back': */
/* HBB 20040331: but not if in hidden3d mode */
if (splot_map && border_layer != LAYER_FRONT)
draw_3d_graphbox(plots, pcount, BORDERONLY, LAYER_BACK);
else if (!hidden3d && (grid_layer == LAYER_BACK))
draw_3d_graphbox(plots, pcount, ALLGRID, LAYER_BACK);
else if (!hidden3d && (grid_layer == LAYER_BEHIND))
/* Default layering mode. Draw the back part now, but not if
* hidden3d is in use, because that relies on all isolated
* lines being output after all surfaces have been defined. */
draw_3d_graphbox(plots, pcount, BACKGRID, LAYER_BACK);
else if (hidden3d && border_layer == LAYER_BEHIND)
draw_3d_graphbox(plots, pcount, ALLGRID, LAYER_BACK);
/* Save state of plot_bounds before applying rotations, etc */
memcpy(&page_bounds, &plot_bounds, sizeof(page_bounds));
/* Clipping in 'set view map' mode should be like 2D clipping */
/* FIXME: Wasn't this already done in boundary3d? */
if (splot_map) {
int map_x1, map_y1, map_x2, map_y2;
map3d_xy(X_AXIS.min, Y_AXIS.min, base_z, &map_x1, &map_y1);
map3d_xy(X_AXIS.max, Y_AXIS.max, base_z, &map_x2, &map_y2);
plot_bounds.xleft = map_x1;
plot_bounds.xright = map_x2;
plot_bounds.ybot = map_y2;
plot_bounds.ytop = map_y1;
}
/* Mar 2009 - This is a change!
* Define the clipping area in 3D to lie between the left-most and
* right-most graph box edges. This is introduced for the benefit of
* zooming in the canvas terminal. It may or may not make any practical
* difference for other terminals. If it causes problems, then we will need
* a separate BoundingBox structure to track the actual 3D graph box.
*/
else if (azimuth == 0) {
int xl, xb, xr, xf, yl, yb, yr, yf;
map3d_xy(zaxis_x, zaxis_y, base_z, &xl, &yl);
map3d_xy(back_x , back_y , base_z, &xb, &yb);
map3d_xy(right_x, right_y, base_z, &xr, &yr);
map3d_xy(front_x, front_y, base_z, &xf, &yf);
plot_bounds.xleft = GPMIN(xl, xb); /* Always xl? */
plot_bounds.xright = GPMAX(xb, xr); /* Always xr? */
}
/* PLACE TITLE */
if (title.text != 0) {
unsigned int x, y;
if (splot_map) { /* case 'set view map' */
int map_x1, map_y1, map_x2, map_y2;
int tics_len = 0;
if (X_AXIS.ticmode & TICS_MIRROR) {
tics_len = (int)(X_AXIS.ticscale * (X_AXIS.tic_in ? -1 : 1) * (term->v_tic));
if (tics_len < 0) tics_len = 0; /* take care only about upward tics */
}
map3d_xy(X_AXIS.min, Y_AXIS.min, base_z, &map_x1, &map_y1);
map3d_xy(X_AXIS.max, Y_AXIS.max, base_z, &map_x2, &map_y2);
/* Distance between the title base line and graph top line or the upper part of
tics is as given by character height: */
x = (unsigned int) ((map_x1 + map_x2) / 2);
y = (unsigned int) (map_y1 + tics_len + (titlelin + 0.5) * (t->v_char));
} else { /* usual 3d set view ... */
x = (unsigned int) ((plot_bounds.xleft + plot_bounds.xright) / 2);
y = (unsigned int) (plot_bounds.ytop + titlelin * (t->h_char));
}
/* NB: write_label applies text color but does not reset it */
write_label(x, y, &title);
reset_textcolor(&(title.textcolor));
}
/* PLACE TIMELABEL */
if (timelabel.text) {
unsigned int x, y;
x = t->v_char;
y = timelabel_bottom
? yoffset * Y_AXIS.max + t->v_char
: plot_bounds.ytop - t->v_char;
do_timelabel(x,y);
}
/* Add 'back' color box */
if (!quick && can_pm3d && is_plot_with_colorbox() && color_box.layer == LAYER_BACK)
draw_color_smooth_box(MODE_SPLOT);
/* Add 'back' rectangles */
place_objects(first_object, LAYER_BACK, 3);
/* PLACE LABELS */
place_labels3d(first_label, LAYER_BACK);
/* PLACE ARROWS */
place_arrows3d(LAYER_BACK);
/* Sync point for epslatex text positioning */
(term->layer)(TERM_LAYER_FRONTTEXT);
if (hidden3d && draw_surface && !quick) {
init_hidden_line_removal();
reset_hidden_line_removal();
}
/* WORK OUT KEY POSITION AND SIZE */
do_3dkey_layout(key, &xl, &yl);
/* "set key opaque" requires two passes, with the key drawn in the second pass */
xl_save = xl; yl_save = yl;
SECOND_KEY_PASS:
/* This tells the canvas, qt, and svg terminals to restart the plot */
/* count so that key titles are in sync with the plots they describe. */
(*t->layer)(TERM_LAYER_RESET_PLOTNO);
/* Key box */
if (key->visible) {
/* In two-pass mode, we blank out the key area after the graph */
/* is drawn and then redo the key in the blank area. */
if (key_pass && t->fillbox && !(t->flags & TERM_NULL_SET_COLOR)) {
t_colorspec background_fill = BACKGROUND_COLORSPEC;
(*t->set_color)(&background_fill);
(*t->fillbox)(FS_OPAQUE, key->bounds.xleft, key->bounds.ybot,
key->bounds.xright - key->bounds.xleft,
key->bounds.ytop - key->bounds.ybot);
}
if (key->box.l_type > LT_NODRAW
&& key->bounds.ytop != key->bounds.ybot) {
term_apply_lp_properties(&key->box);
newpath();
clip_move(key->bounds.xleft, key->bounds.ybot);
clip_vector(key->bounds.xleft, key->bounds.ytop);
clip_vector(key->bounds.xright, key->bounds.ytop);
clip_vector(key->bounds.xright, key->bounds.ybot);
clip_vector(key->bounds.xleft, key->bounds.ybot);
closepath();
/* draw a horizontal line between key title and first entry JFi */
clip_move(key->bounds.xleft,
key->bounds.ytop - key_title_height - key_title_extra);
clip_vector(key->bounds.xright,
key->bounds.ytop - key_title_height - key_title_extra);
}
if (key->title.text) {
int center = (key->bounds.xright + key->bounds.xleft) / 2;
int titley = key->bounds.ytop - (key_title_extra + t->v_char)/2;
write_label(center, titley, &key->title);
(*t->linetype)(LT_BLACK);
}
}
/* DRAW SURFACES AND CONTOURS */
if (!key_pass)
if (hidden3d && (hidden3d_layer == LAYER_BACK) && draw_surface && !quick) {
(term->layer)(TERM_LAYER_BEFORE_PLOT);
plot3d_hidden(plots, pcount);
(term->layer)(TERM_LAYER_AFTER_PLOT);
}
/* Set up bookkeeping for the individual key titles */
#define NEXT_KEY_LINE() \
do { \
if ( ++key_count >= key_rows ) { \
yl = yl_ref; xl += key_col_wth; key_count = 0; \
} else \
yl -= key_entry_height; \
} while (0)
key_count = 0;
yl_ref = yl -= key_entry_height / 2; /* centralise the keys */
/* PM January 2005: The mistake of missing blank lines in the data file is
* so frequently made (see questions at comp.graphics.apps.gnuplot) that it
* really deserves this warning. But don't show it too often --- only if it
* is a single surface in the plot.
*/
if (pcount == 1 && plots->num_iso_read == 1 && can_pm3d &&
(plots->plot_style == PM3DSURFACE || PM3D_IMPLICIT == pm3d.implicit))
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");
pm3d_order_depth = (can_pm3d && !draw_contour && pm3d.direction == PM3D_DEPTH);
if (pm3d_order_depth || track_pm3d_quadrangles) {
pm3d_depth_queue_clear();
}
this_plot = plots;
if (!quick)
for (surface = 0;
surface < pcount;
this_plot = this_plot->next_sp, surface++) {
/* just an abbreviation */
TBOOLEAN lkey, draw_this_surface;
/* Skip over abortive data structures */
if (this_plot->plot_type == NODATA)
continue;
/* Sync point for start of new curve (used by svg, post, ...) */
(term->layer)(TERM_LAYER_BEFORE_PLOT);
if (!key_pass)
if (can_pm3d && PM3D_IMPLICIT == pm3d.implicit)
pm3d_draw_one(this_plot);
lkey = (key->visible && this_plot->title && this_plot->title[0]
&& !this_plot->title_is_suppressed);
draw_this_surface = (draw_surface && !this_plot->opt_out_of_surface);
/* User-specified key locations can use the 2D code */
if (this_plot->title_position) {
xl_prev = xl;
yl_prev = yl;
if (this_plot->title_position->scalex != character) {
map3d_position(this_plot->title_position, &xl, &yl, "key sample");
xl -= (key->just == GPKEY_LEFT) ? key_text_left : key_text_right;
} else {
/* Option to label the end of the curve on the plot itself */
attach_title_to_plot((struct curve_points *)this_plot, key);
}
}
if (lkey
&& (!this_plot->title_position || this_plot->title_position->scalex != character)) {
if (key->textcolor.type != TC_DEFAULT)
/* Draw key text in same color as key title */
apply_pm3dcolor(&key->textcolor);
else
/* Draw key text in black */
(*t->linetype)(LT_BLACK);
ignore_enhanced(this_plot->title_no_enhanced);
key_text(xl, yl, this_plot->title);
ignore_enhanced(FALSE);
}
term_apply_lp_properties(&(this_plot->lp_properties));
/* First draw the graph plot itself */
if (!key_pass)
switch (this_plot->plot_style) {
case BOXES: /* can't do boxes in 3d yet so use impulses */
case FILLEDCURVES: /* same, but maybe we could dummy up ZERRORFILL? */
case IMPULSES:
if (!hidden3d)
plot3d_impulses(this_plot);
break;
case STEPS: /* HBB: I think these should be here */
case FILLSTEPS:
case FSTEPS:
case HISTEPS:
case SURFACEGRID:
case LINES:
if (draw_this_surface) {
if (!hidden3d || this_plot->opt_out_of_hidden3d)
plot3d_lines_pm3d(this_plot);
}
break;
case YERRORLINES: /* ignored; treat like points */
case XERRORLINES: /* ignored; treat like points */
case XYERRORLINES: /* ignored; treat like points */
case YERRORBARS: /* ignored; treat like points */
case XERRORBARS: /* ignored; treat like points */
case XYERRORBARS: /* ignored; treat like points */
case BOXXYERROR: /* HBB: ignore these as well */
case BOXERROR:
case CANDLESTICKS: /* HBB: ditto */
case BOXPLOT:
case FINANCEBARS:
#ifdef EAM_OBJECTS
case CIRCLES:
case ELLIPSES:
#endif
case POINTSTYLE:
case DOTS:
if (draw_this_surface) {
if (!hidden3d || this_plot->opt_out_of_hidden3d)
plot3d_points(this_plot);
}
break;
case LINESPOINTS:
if (draw_this_surface) {
if (!hidden3d || this_plot->opt_out_of_hidden3d) {
plot3d_lines_pm3d(this_plot);
plot3d_points(this_plot);
}
}
break;
case VECTOR:
if (!hidden3d || this_plot->opt_out_of_hidden3d)
plot3d_vectors(this_plot);
break;
case ZERRORFILL:
/* Always draw filled areas even if we _also_ do hidden3d processing */
if (term->filled_polygon)
plot3d_zerrorfill(this_plot);
term_apply_lp_properties(&(this_plot->lp_properties));
plot3d_lines(this_plot);
break;
case PM3DSURFACE:
if (draw_this_surface) {
if (can_pm3d && PM3D_IMPLICIT != pm3d.implicit) {
pm3d_draw_one(this_plot);
if (!pm3d_order_depth)
pm3d_depth_queue_flush(); /* draw plot immediately */
}
}
break;
case LABELPOINTS:
if (draw_this_surface) {
if (!hidden3d || this_plot->opt_out_of_hidden3d)
place_labels3d(this_plot->labels->next, LAYER_PLOTLABELS);
}
break;
case HISTOGRAMS: /* Cannot happen */
break;
case IMAGE:
/* Plot image using projection of 3D plot coordinates to 2D viewing coordinates. */
this_plot->image_properties.type = IC_PALETTE;
process_image(this_plot, IMG_PLOT);
break;
case RGBIMAGE:
/* Plot image using projection of 3D plot coordinates to 2D viewing coordinates. */
this_plot->image_properties.type = IC_RGB;
process_image(this_plot, IMG_PLOT);
break;
case RGBA_IMAGE:
this_plot->image_properties.type = IC_RGBA;
process_image(this_plot, IMG_PLOT);
break;
case PARALLELPLOT:
int_error(NO_CARET, "plot style parallelaxes not supported in 3D");
break;
case PLOT_STYLE_NONE:
case TABLESTYLE:
/* cannot happen */
break;
} /* switch(plot-style) plot proper */
/* Next draw the key sample */
if (lkey
&& (!this_plot->title_position || this_plot->title_position->scalex != character))
switch (this_plot->plot_style) {
case BOXES: /* can't do boxes in 3d yet so use impulses */
case FILLEDCURVES:
case IMPULSES:
if (!(hidden3d && draw_this_surface))
key_sample_line(xl, yl);
break;
case STEPS: /* HBB: I think these should be here */
case FILLSTEPS:
case FSTEPS:
case HISTEPS:
case SURFACEGRID:
case LINES:
/* Normal case (surface) */
if (draw_this_surface)
key_sample_line_pm3d(this_plot, xl, yl);
/* Contour plot with no surface, all contours use the same linetype */
else if (this_plot->contours != NULL && clabel_onecolor) {
key_sample_line(xl, yl);
}
break;
case YERRORLINES: /* ignored; treat like points */
case XERRORLINES: /* ignored; treat like points */
case XYERRORLINES: /* ignored; treat like points */
case YERRORBARS: /* ignored; treat like points */
case XERRORBARS: /* ignored; treat like points */
case XYERRORBARS: /* ignored; treat like points */
case BOXXYERROR: /* HBB: ignore these as well */
case BOXERROR:
case CANDLESTICKS: /* HBB: ditto */
case BOXPLOT:
case FINANCEBARS:
#ifdef EAM_OBJECTS
case CIRCLES:
case ELLIPSES:
#endif
case POINTSTYLE:
if (draw_this_surface)
key_sample_point_pm3d(this_plot, xl, yl, this_plot->lp_properties.p_type);
break;
case LABELPOINTS:
if ((this_plot->labels->lp_properties.flags & LP_SHOW_POINTS)) {
term_apply_lp_properties(&this_plot->labels->lp_properties);
key_sample_point(xl, yl, this_plot->labels->lp_properties.p_type);
}
break;
case LINESPOINTS:
if (draw_this_surface) {
if (this_plot->lp_properties.l_type != LT_NODRAW)
key_sample_line_pm3d(this_plot, xl, yl);
key_sample_point_pm3d(this_plot, xl, yl, this_plot->lp_properties.p_type);
}
break;
case DOTS:
if (draw_this_surface)
key_sample_point_pm3d(this_plot, xl, yl, -1);
break;
case VECTOR:
key_sample_line_pm3d(this_plot, xl, yl);
break;
case ZERRORFILL:
key_sample_fill(xl, yl, &this_plot->fill_properties);
term_apply_lp_properties(&this_plot->lp_properties);
key_sample_line(xl, yl);
break;
case PLOT_STYLE_NONE:
/* cannot happen */
default:
break;
} /* switch(plot-style) key sample */
/* If the title went somewhere other than the key,
* restore the previous key position.
* Else move down one line in the key.
*/
if (this_plot->title_position) {
xl = xl_prev;
yl = yl_prev;
} else if (lkey)
NEXT_KEY_LINE();
/* Draw contours for previous surface */
if (draw_contour && this_plot->contours != NULL) {
struct gnuplot_contours *cntrs = this_plot->contours;
struct lp_style_type thiscontour_lp_properties;
static char *thiscontour_label = NULL;
TBOOLEAN save_hidden3d;
int ic = 1; /* ic will index the contour linetypes */
thiscontour_lp_properties = this_plot->lp_properties;
term_apply_lp_properties(&(thiscontour_lp_properties));
while (cntrs) {
if (!clabel_onecolor && cntrs->isNewLevel) {
if (key->visible && !this_plot->title_is_suppressed
&& this_plot->plot_style != LABELPOINTS) {
(*t->linetype)(LT_BLACK);
key_text(xl, yl, cntrs->label);
}
if (thiscontour_lp_properties.pm3d_color.type == TC_Z)
set_color( cb2gray( z2cb(cntrs->z) ) );
else {
struct lp_style_type ls = thiscontour_lp_properties;
int contour_linetype;
ic++; /* Increment linetype used for contour */
/* First contour line type defaults to surface linetype + 1 */
/* but can be changed using 'set cntrparams firstlinetype N' */
if (contour_firstlinetype > 0)
contour_linetype = contour_firstlinetype + ic - 2;
else
contour_linetype = this_plot->hidden3d_top_linetype + ic;
/* hidden3d processing looks directly at l_type */
/* for other purposes the line color is set by load_linetype */
if (hidden3d)
thiscontour_lp_properties.l_type = contour_linetype - 1;
load_linetype(&ls, contour_linetype);
thiscontour_lp_properties.pm3d_color = ls.pm3d_color;
thiscontour_lp_properties.l_width = ls.l_width
* this_plot->lp_properties.l_width;
thiscontour_lp_properties.d_type = ls.d_type;
thiscontour_lp_properties.custom_dash_pattern = ls.custom_dash_pattern;
term_apply_lp_properties(&thiscontour_lp_properties);
}
if (key->visible && !this_plot->title_is_suppressed
&& !(this_plot->plot_style == LABELPOINTS)) {
switch (this_plot->plot_style) {
case IMPULSES:
case LINES:
case LINESPOINTS:
case BOXES:
case FILLEDCURVES:
case VECTOR:
case STEPS:
case FSTEPS:
case HISTEPS:
case PM3DSURFACE:
key_sample_line(xl, yl);
break;
case POINTSTYLE:
key_sample_point(xl, yl, this_plot->lp_properties.p_type);
break;
case DOTS:
key_sample_point(xl, yl, -1);
break;
default:
break;
} /* switch */
NEXT_KEY_LINE();
} /* key */
} /* clabel_onecolor */
/* now draw the contour */
if (!key_pass)
switch (this_plot->plot_style) {
/* treat boxes like impulses: */
case BOXES:
case FILLEDCURVES:
case VECTOR:
case IMPULSES:
cntr3d_impulses(cntrs, &thiscontour_lp_properties);
break;
case STEPS:
case FSTEPS:
case HISTEPS:
/* treat all the above like 'lines' */
case LINES:
case PM3DSURFACE:
save_hidden3d = hidden3d;
if (this_plot->opt_out_of_hidden3d)
hidden3d = FALSE;
cntr3d_lines(cntrs, &thiscontour_lp_properties);
hidden3d = save_hidden3d;
break;
case LINESPOINTS:
cntr3d_lines(cntrs, &thiscontour_lp_properties);
/* Fall through to draw the points */
case DOTS:
case POINTSTYLE:
cntr3d_points(cntrs, &thiscontour_lp_properties);
break;
case LABELPOINTS:
if (cntrs->isNewLevel) {
char *c = &cntrs->label[strspn(cntrs->label," ")];
free(thiscontour_label);
thiscontour_label = gp_strdup(c);
}
/* cntr3d_lines(cntrs, &thiscontour_lp_properties); */
cntr3d_labels(cntrs, thiscontour_label, this_plot->labels);
break;
default:
break;
} /*switch */
cntrs = cntrs->next;
} /* loop over contours */
} /* draw contours */
/* Sync point for end of this curve (used by svg, post, ...) */
(term->layer)(TERM_LAYER_AFTER_PLOT);
} /* loop over surfaces */
if (!key_pass)
if (pm3d_order_depth || track_pm3d_quadrangles) {
pm3d_depth_queue_flush(); /* draw pending plots */
}
if (!key_pass)
if (hidden3d && (hidden3d_layer == LAYER_FRONT) && draw_surface && !quick) {
(term->layer)(TERM_LAYER_BEFORE_PLOT);
plot3d_hidden(plots, pcount);
(term->layer)(TERM_LAYER_AFTER_PLOT);
}
/* Draw grid and border.
* The 1st case allows "set border behind" to override hidden3d processing.
* The 2nd case either leaves everything to hidden3d or forces it to the front.
* The 3rd case is the non-hidden3d default - draw back pieces (done earlier),
* then the graph, and now the front pieces.
*/
if (hidden3d && border_layer == LAYER_BEHIND)
/* the important thing is _not_ to draw the back grid */
/* draw_3d_graphbox(plots, pcount, FRONTGRID, LAYER_FRONT) */
;
else if (hidden3d || grid_layer == LAYER_FRONT)
draw_3d_graphbox(plots, pcount, ALLGRID, LAYER_FRONT);
else if (grid_layer == LAYER_BEHIND)
draw_3d_graphbox(plots, pcount, FRONTGRID, LAYER_FRONT);
/* Go back and draw the legend in a separate pass if "key opaque" */
if (key->visible && key->front && !key_pass) {
key_pass = TRUE;
xl = xl_save; yl = yl_save;
goto SECOND_KEY_PASS;
}
/* Add 'front' color box */
if (!quick && can_pm3d && is_plot_with_colorbox() && color_box.layer == LAYER_FRONT)
draw_color_smooth_box(MODE_SPLOT);
/* Add 'front' rectangles */
place_objects(first_object, LAYER_FRONT, 3);
/* PLACE LABELS */
place_labels3d(first_label, LAYER_FRONT);
/* PLACE ARROWS */
place_arrows3d(LAYER_FRONT);
#ifdef USE_MOUSE
/* finally, store the 2d projection of the x and y axis, to enable zooming by mouse */
{
int x,y;
map3d_xy(X_AXIS.min, Y_AXIS.min, base_z, &axis3d_o_x, &axis3d_o_y);
map3d_xy(X_AXIS.max, Y_AXIS.min, base_z, &x, &y);
axis3d_x_dx = x - axis3d_o_x;
axis3d_x_dy = y - axis3d_o_y;
map3d_xy(X_AXIS.min, Y_AXIS.max, base_z, &x, &y);
axis3d_y_dx = x - axis3d_o_x;
axis3d_y_dy = y - axis3d_o_y;
}
#endif
/* Release the palette if we have used one (PostScript only?) */
if (is_plot_with_palette() && term->previous_palette)
term->previous_palette();
term_end_plot();
if (hidden3d && draw_surface) {
term_hidden_line_removal();
}
if (splot_map)
splot_map_deactivate();
}
/* plot3d_impulses:
* Plot the surfaces in IMPULSES style
*/
static void
plot3d_impulses(struct surface_points *plot)
{
int i; /* point index */
int x, y, xx0, yy0; /* point in terminal coordinates */
struct iso_curve *icrvs = plot->iso_crvs;
int colortype = plot->lp_properties.pm3d_color.type;
if (colortype == TC_RGB)
set_rgbcolor_const(plot->lp_properties.pm3d_color.lt);
while (icrvs) {
struct coordinate GPHUGE *points = icrvs->points;
for (i = 0; i < icrvs->p_count; i++) {
check3d_for_variable_color(plot, &points[i]);
switch (points[i].type) {
case INRANGE:
{
map3d_xy(points[i].x, points[i].y, points[i].z, &x, &y);
if (inrange(0.0, Z_AXIS.min, Z_AXIS.max)) {
map3d_xy(points[i].x, points[i].y, 0.0, &xx0, &yy0);
} else if (inrange(Z_AXIS.min, 0.0, points[i].z)) {
map3d_xy(points[i].x, points[i].y, Z_AXIS.min, &xx0, &yy0);
} else {
map3d_xy(points[i].x, points[i].y, Z_AXIS.max, &xx0, &yy0);
}
clip_move(xx0, yy0);
clip_vector(x, y);
break;
}
case OUTRANGE:
{
if (!inrange(points[i].x, X_AXIS.min, X_AXIS.max) ||
!inrange(points[i].y, Y_AXIS.min, Y_AXIS.max))
break;
if (inrange(0.0, Z_AXIS.min, Z_AXIS.max)) {
/* zero point is INRANGE */
map3d_xy(points[i].x, points[i].y, 0.0, &xx0, &yy0);
/* must cross z = Z_AXIS.min or Z_AXIS.max limits */
if (inrange(Z_AXIS.min, 0.0, points[i].z) &&
Z_AXIS.min != 0.0 && Z_AXIS.min != points[i].z) {
map3d_xy(points[i].x, points[i].y, Z_AXIS.min, &x, &y);
} else {
map3d_xy(points[i].x, points[i].y, Z_AXIS.max, &x, &y);
}
} else {
/* zero point is also OUTRANGE */
if (inrange(Z_AXIS.min, 0.0, points[i].z) &&
inrange(Z_AXIS.max, 0.0, points[i].z)) {
/* crosses z = Z_AXIS.min or Z_AXIS.max limits */
map3d_xy(points[i].x, points[i].y, Z_AXIS.max, &x, &y);
map3d_xy(points[i].x, points[i].y, Z_AXIS.min, &xx0, &yy0);
} else {
/* doesn't cross z = Z_AXIS.min or Z_AXIS.max limits */
break;
}
}
clip_move(xx0, yy0);
clip_vector(x, y);
break;
}
default: /* just a safety */
case UNDEFINED:{
break;
}
}
}
icrvs = icrvs->next;
}
}
/* plot3d_lines:
* Plot the surfaces in LINES style
*/
/* We want to always draw the lines in the same direction, otherwise when
we draw an adjacent box we might get the line drawn a little differently
and we get splotches. */
static void
plot3d_lines(struct surface_points *plot)
{
int i;
int x, y, xx0, yy0; /* point in terminal coordinates */
double clip_x, clip_y, clip_z;
struct iso_curve *icrvs = plot->iso_crvs;
struct coordinate GPHUGE *points;
TBOOLEAN rgb_from_column;
/* These are handled elsewhere. */
if (plot->has_grid_topology && hidden3d)
return;
/* These don't need to be drawn at all */
if (plot->lp_properties.l_type == LT_NODRAW)
return;
rgb_from_column = plot->pm3d_color_from_column
&& plot->lp_properties.pm3d_color.type == TC_RGB
&& plot->lp_properties.pm3d_color.value < 0.0;
while (icrvs) {
enum coord_type prev = UNDEFINED; /* type of previous plot */
for (i = 0, points = icrvs->points; i < icrvs->p_count; i++) {
if (rgb_from_column)
set_rgbcolor_var((unsigned int)points[i].CRD_COLOR);
else if (plot->lp_properties.pm3d_color.type == TC_LINESTYLE) {
plot->lp_properties.pm3d_color.lt = (int)(points[i].CRD_COLOR);
apply_pm3dcolor(&(plot->lp_properties.pm3d_color));
}
switch (points[i].type) {
case INRANGE:{
map3d_xy(points[i].x, points[i].y, points[i].z, &x, &y);
if (prev == INRANGE) {
clip_vector(x, y);
} else {
if (prev == OUTRANGE) {
/* from outrange to inrange */
if (!clip_lines1) {
clip_move(x, y);
} else {
/*
* Calculate intersection point and draw
* vector from there
*/
edge3d_intersect(&points[i-1], &points[i], &clip_x, &clip_y, &clip_z);
map3d_xy(clip_x, clip_y, clip_z, &xx0, &yy0);
clip_move(xx0, yy0);
clip_vector(x, y);
}
} else {
clip_move(x, y);
}
}
break;
}
case OUTRANGE:{
if (prev == INRANGE) {
/* from inrange to outrange */
if (clip_lines1) {
/*
* Calculate intersection point and draw
* vector to it
*/
edge3d_intersect(&points[i-1], &points[i], &clip_x, &clip_y, &clip_z);
map3d_xy(clip_x, clip_y, clip_z, &xx0, &yy0);
clip_vector(xx0, yy0);
}
} else if (prev == OUTRANGE) {
/* from outrange to outrange */
if (clip_lines2) {
double lx[2], ly[2], lz[2]; /* two edge points */
/*
* Calculate the two 3D intersection points if present
*/
if (two_edge3d_intersect(&points[i-1], &points[i], lx, ly, lz)) {
map3d_xy(lx[0], ly[0], lz[0], &x, &y);
map3d_xy(lx[1], ly[1], lz[1], &xx0, &yy0);
clip_move(x, y);
clip_vector(xx0, yy0);
}
}
}
break;
}
case UNDEFINED:{
break;
}
default:
int_warn(NO_CARET,"Unknown point type in plot3d_lines");
}
prev = points[i].type;
}
icrvs = icrvs->next;
}
}
/* this is basically the same function as above, but:
* - it splits the bunch of scans in two sets corresponding to
* the two scan directions.
* - reorders the two sets -- from behind to front
* - checks if inside on scan of a set the order should be inverted
*/
static void
plot3d_lines_pm3d(struct surface_points *plot)
{
struct iso_curve** icrvs_pair[2];
int invert[2] = {0,0};
int n[2] = {0,0};
int i, set, scan;
int x, y, xx0, yy0; /* point in terminal coordinates */
double clip_x, clip_y, clip_z;
struct coordinate GPHUGE *points;
enum coord_type prev = UNDEFINED;
double z;
/* just a shortcut */
TBOOLEAN color_from_column = plot->pm3d_color_from_column;
/* If plot really uses RGB rather than pm3d colors, let plot3d_lines take over */
if (plot->lp_properties.pm3d_color.type == TC_RGB) {
apply_pm3dcolor(&(plot->lp_properties.pm3d_color));
plot3d_lines(plot);
return;
} else if (plot->lp_properties.pm3d_color.type == TC_LT) {
plot3d_lines(plot);
return;
} else if (plot->lp_properties.pm3d_color.type == TC_LINESTYLE) {
plot3d_lines(plot);
return;
}
/* These are handled elsewhere. */
if (plot->has_grid_topology && hidden3d)
return;
/* split the bunch of scans in two sets in
* which the scans are already depth ordered */
pm3d_rearrange_scan_array(plot,
icrvs_pair, &n[0], &invert[0],
icrvs_pair + 1, &n[1], &invert[1]);
for (set = 0; set < 2; set++) {
int begin = 0;
int step;
if (invert[set]) {
/* begin is set below to the length of the scan - 1 */
step = -1;
} else {
step = 1;
}
for (scan = 0; scan < n[set] && icrvs_pair[set]; scan++) {
int cnt;
struct iso_curve *icrvs = icrvs_pair[set][scan];
if (invert[set]) {
begin = icrvs->p_count - 1;
}
prev = UNDEFINED; /* type of previous plot */
for (cnt = 0, i = begin, points = icrvs->points; cnt < icrvs->p_count; cnt++, i += step) {
switch (points[i].type) {
case INRANGE:
map3d_xy(points[i].x, points[i].y, points[i].z, &x, &y);
if (prev == INRANGE) {
if (color_from_column)
z = (points[i - step].CRD_COLOR + points[i].CRD_COLOR) * 0.5;
else
z = (z2cb(points[i - step].z) + z2cb(points[i].z)) * 0.5;
set_color( cb2gray(z) );
clip_vector(x, y);
} else {
if (prev == OUTRANGE) {
/* from outrange to inrange */
if (!clip_lines1) {
clip_move(x, y);
} else {
/*
* Calculate intersection point and draw
* vector from there
*/
edge3d_intersect(&points[i-step], &points[i], &clip_x, &clip_y, &clip_z);
map3d_xy(clip_x, clip_y, clip_z, &xx0, &yy0);
clip_move(xx0, yy0);
if (color_from_column)
z = (points[i - step].CRD_COLOR + points[i].CRD_COLOR) * 0.5;
else
z = (z2cb(points[i - step].z) + z2cb(points[i].z)) * 0.5;
set_color( cb2gray(z) );
clip_vector(x, y);
}
} else {
clip_move(x, y);
}
}
break;
case OUTRANGE:
if (prev == INRANGE) {
/* from inrange to outrange */
if (clip_lines1) {
/*
* Calculate intersection point and draw
* vector to it
*/
edge3d_intersect(&points[i-step], &points[i], &clip_x, &clip_y, &clip_z);
map3d_xy(clip_x, clip_y, clip_z, &xx0, &yy0);
if (color_from_column)
z = (points[i - step].CRD_COLOR + points[i].CRD_COLOR) * 0.5;
else
z = (z2cb(points[i - step].z) + z2cb(points[i].z)) * 0.5;
set_color( cb2gray(z));
clip_vector(xx0, yy0);
}
} else if (prev == OUTRANGE) {
/* from outrange to outrange */
if (clip_lines2) {
/*
* Calculate the two 3D intersection points if present
*/
double lx[2], ly[2], lz[2];
if (two_edge3d_intersect(&points[i-step], &points[i], lx, ly, lz)) {
map3d_xy(lx[0], ly[0], lz[0], &x, &y);
map3d_xy(lx[1], ly[1], lz[1], &xx0, &yy0);
clip_move(x, y);
if (color_from_column)
z = (points[i - step].CRD_COLOR + points[i].CRD_COLOR) * 0.5;
else
z = (z2cb(points[i - step].z) + z2cb(points[i].z)) * 0.5;
set_color( cb2gray(z) );
clip_vector(xx0, yy0);
}
}
}
break;
case UNDEFINED:
break;
default:
int_warn(NO_CARET,"Unknown point type in plot3d_lines");
}
prev = points[i].type;
} /* one scan */
} /* while (icrvs) */
} /* for (scan = 0; scan < 2; scan++) */
if (icrvs_pair[0])
free(icrvs_pair[0]);
if (icrvs_pair[1])
free(icrvs_pair[1]);
}
/* plot3d_points:
* Plot the surfaces in POINTSTYLE style
*/
static void
plot3d_points(struct surface_points *plot)
{
int i;
int x, y;
struct termentry *t = term;
struct iso_curve *icrvs = plot->iso_crvs;
int interval = plot->lp_properties.p_interval;
/* Set whatever we can that applies to every point in the loop */
if (plot->lp_properties.p_type == PT_CHARACTER) {
ignore_enhanced(TRUE);
if (plot->labels->font && plot->labels->font[0])
(*t->set_font) (plot->labels->font);
(*t->justify_text) (CENTRE);
}
while (icrvs) {
struct coordinate GPHUGE *point;
int colortype = plot->lp_properties.pm3d_color.type;
/* Apply constant color outside of the loop */
if (colortype == TC_RGB)
set_rgbcolor_const( plot->lp_properties.pm3d_color.lt );
for (i = 0; i < icrvs->p_count; i++) {
/* Only print 1 point per interval */
if ((plot->plot_style == LINESPOINTS) && (interval) && (i % interval))
continue;
point = &(icrvs->points[i]);
if (point->type == INRANGE) {
map3d_xy(point->x, point->y, point->z, &x, &y);
if (!clip_point(x, y)) {
/* A negative interval indicates we should blank */
/* out the area behind the point symbol */
if (plot->plot_style == LINESPOINTS && interval < 0) {
(*t->set_color)(&background_fill);
(*t->pointsize)(pointsize * pointintervalbox);
(*t->point) (x, y, 6);
term_apply_lp_properties(&(plot->lp_properties));
}
check3d_for_variable_color(plot, point);
if ((plot->plot_style == POINTSTYLE || plot->plot_style == LINESPOINTS)
&& plot->lp_properties.p_size == PTSZ_VARIABLE)
(*t->pointsize)(pointsize * point->CRD_PTSIZE);
/* This code is also used for "splot ... with dots" */
if (plot->plot_style == DOTS) {
(*t->point) (x, y, -1);
}
/* Print special character rather than drawn symbol */
else if (plot->lp_properties.p_type == PT_CHARACTER) {
apply_pm3dcolor(&(plot->labels->textcolor));
(*t->put_text)(x, y, plot->lp_properties.p_char);
}
/* variable point type */
else if (plot->lp_properties.p_type == PT_VARIABLE) {
(*t->point) (x, y, (int)(point->CRD_PTTYPE) - 1);
}
/* The normal case */
else if (plot->lp_properties.p_type >= 0)
(*t->point) (x, y, plot->lp_properties.p_type);
}
}
}
icrvs = icrvs->next;
}
/* Return to initial state */
if (plot->lp_properties.p_type == PT_CHARACTER) {
if (plot->labels->font && plot->labels->font[0])
(*t->set_font) ("");
ignore_enhanced(FALSE);
}
}
/* cntr3d_impulses:
* Plot a surface contour in IMPULSES style
*/
static void
cntr3d_impulses(struct gnuplot_contours *cntr, struct lp_style_type *lp)
{
int i; /* point index */
vertex vertex_on_surface, vertex_on_base;
if (draw_contour & CONTOUR_SRF) {
for (i = 0; i < cntr->num_pts; i++) {
map3d_xyz(cntr->coords[i].x, cntr->coords[i].y, cntr->coords[i].z,
&vertex_on_surface);
map3d_xyz(cntr->coords[i].x, cntr->coords[i].y, 0.0,
&vertex_on_base);
/* HBB 20010822: Provide correct color-coding for
* "linetype palette" PM3D mode */
vertex_on_base.real_z = cntr->coords[i].z;
draw3d_line(&vertex_on_surface, &vertex_on_base, lp);
}
} else {
/* Must be on base grid, so do points. */
cntr3d_points(cntr, lp);
}
}
/* cntr3d_lines: Plot a surface contour in LINES style */
/* HBB NEW 20031218: changed to use move/vector() style polyline
* drawing. Gets rid of variable "previous_vertex" */
static void
cntr3d_lines(struct gnuplot_contours *cntr, struct lp_style_type *lp)
{
int i; /* point index */
vertex this_vertex;
/* In the case of "set view map" (only) clip the contour lines to the graph */
BoundingBox *clip_save = clip_area;
if (splot_map)
clip_area = &plot_bounds;
if (draw_contour & CONTOUR_SRF) {
map3d_xyz(cntr->coords[0].x, cntr->coords[0].y, cntr->coords[0].z,
&this_vertex);
/* move slightly frontward, to make sure the contours are
* visible in front of the the triangles they're in, if this
* is a hidden3d plot */
if (hidden3d && !VERTEX_IS_UNDEFINED(this_vertex))
this_vertex.z += 1e-2;
polyline3d_start(&this_vertex);
for (i = 1; i < cntr->num_pts; i++) {
map3d_xyz(cntr->coords[i].x, cntr->coords[i].y, cntr->coords[i].z,
&this_vertex);
/* move slightly frontward, to make sure the contours are
* visible in front of the the triangles they're in, if this
* is a hidden3d plot */
if (hidden3d && !VERTEX_IS_UNDEFINED(this_vertex))
this_vertex.z += 1e-2;
polyline3d_next(&this_vertex, lp);
}
}
if (draw_contour & CONTOUR_BASE) {
map3d_xyz(cntr->coords[0].x, cntr->coords[0].y, base_z,
&this_vertex);
this_vertex.real_z = cntr->coords[0].z;
polyline3d_start(&this_vertex);
for (i = 1; i < cntr->num_pts; i++) {
map3d_xyz(cntr->coords[i].x, cntr->coords[i].y, base_z,
&this_vertex);
this_vertex.real_z = cntr->coords[i].z;
polyline3d_next(&this_vertex, lp);
}
}
if (splot_map)
clip_area = clip_save;
}
/* cntr3d_points:
* Plot a surface contour in POINTSTYLE style
*/
static void
cntr3d_points(struct gnuplot_contours *cntr, struct lp_style_type *lp)
{
int i;
vertex v;
if (draw_contour & CONTOUR_SRF) {
for (i = 0; i < cntr->num_pts; i++) {
map3d_xyz(cntr->coords[i].x, cntr->coords[i].y, cntr->coords[i].z,
&v);
/* move slightly frontward, to make sure the contours and
* points are visible in front of the triangles they're
* in, if this is a hidden3d plot */
if (hidden3d && !VERTEX_IS_UNDEFINED(v))
v.z += 1e-2;
draw3d_point(&v, lp);
}
}
if (draw_contour & CONTOUR_BASE) {
for (i = 0; i < cntr->num_pts; i++) {
map3d_xyz(cntr->coords[i].x, cntr->coords[i].y, base_z,
&v);
/* HBB 20010822: see above */
v.real_z = cntr->coords[i].z;
draw3d_point(&v, lp);
}
}
}
/* cntr3d_labels:
* Place contour labels on a contour line at the base.
* These are the same labels that would be used in the key.
* The label density is controlled by the point interval property
* splot FOO with labels point pi 20 nosurface
*/
static void
cntr3d_labels(struct gnuplot_contours *cntr, char *level_text, struct text_label *label)
{
int i;
int interval;
int x, y;
vertex v;
struct lp_style_type *lp = &(label->lp_properties);
/* Drawing a label at every point would be too crowded */
interval = lp->p_interval;
if (interval <= 0) interval = 999; /* Place label only at start point */
if (draw_contour & CONTOUR_BASE) {
for (i = 0; i < cntr->num_pts; i++) {
if ((i-clabel_start) % interval) /* Offset to avoid sitting on the border */
continue;
map3d_xy(cntr->coords[i].x, cntr->coords[i].y, base_z, &x, &y);
label->text = level_text;
if (hidden3d) {
map3d_xyz(cntr->coords[i].x, cntr->coords[i].y, base_z, &v);
v.real_z = cntr->coords[i].z;
v.label = label;
draw_label_hidden(&v, lp, x, y);
} else {
write_label(x, y, label);
}
label->text = NULL; /* Otherwise someone will try to free it */
}
}
}
/* map xmin | xmax to 0 | 1 and same for y
* 0.1 avoids any rounding errors
*/
#define MAP_HEIGHT_X(x) ( ((x)-X_AXIS.min)/(X_AXIS.max-X_AXIS.min) > 0.9 ? 1 : 0 )
#define MAP_HEIGHT_Y(y) ( ((y)-Y_AXIS.min)/(Y_AXIS.max-Y_AXIS.min) > 0.9 ? 1 : 0 )
/* if point is at corner, update height[][] and depth[][]
* we are still assuming that extremes of surfaces are at corners,
* but we are not assuming order of corners
*/
static void
check_corner_height(
struct coordinate GPHUGE *p,
double height[2][2], double depth[2][2])
{
if (p->type != INRANGE)
return;
/* FIXME HBB 20010121: don't compare 'zero' to data values in
* absolute terms. */
if ((fabs(p->x - X_AXIS.min) < zero || fabs(p->x - X_AXIS.max) < zero) &&
(fabs(p->y - Y_AXIS.min) < zero || fabs(p->y - Y_AXIS.max) < zero)) {
unsigned int x = MAP_HEIGHT_X(p->x);
unsigned int y = MAP_HEIGHT_Y(p->y);
if (height[x][y] < p->z)
height[x][y] = p->z;
if (depth[x][y] > p->z)
depth[x][y] = p->z;
}
}
/* work out where the axes and tics are drawn */
static void
setup_3d_box_corners()
{
int quadrant = surface_rot_z / 90;
if ((quadrant + 1) & 2) {
zaxis_x = X_AXIS.max;
right_x = X_AXIS.min;
back_y = Y_AXIS.min;
front_y = Y_AXIS.max;
} else {
zaxis_x = X_AXIS.min;
right_x = X_AXIS.max;
back_y = Y_AXIS.max;
front_y = Y_AXIS.min;
}
if (quadrant & 2) {
zaxis_y = Y_AXIS.max;
right_y = Y_AXIS.min;
back_x = X_AXIS.max;
front_x = X_AXIS.min;
} else {
zaxis_y = Y_AXIS.min;
right_y = Y_AXIS.max;
back_x = X_AXIS.min;
front_x = X_AXIS.max;
}
quadrant = surface_rot_x / 90;
if ((quadrant & 2) && !splot_map) {
double temp;
temp = front_y;
front_y = back_y;
back_y = temp;
temp = front_x;
front_x = back_x;
back_x = temp;
}
if ((quadrant + 1) & 2) {
/* labels on the back axes */
yaxis_x = back_x;
xaxis_y = back_y;
} else {
yaxis_x = front_x;
xaxis_y = front_y;
}
}
/* Draw all elements of the 3d graph box, including borders, zeroaxes,
* tics, gridlines, ticmarks, axis labels and the base plane. */
static void
draw_3d_graphbox(struct surface_points *plot, int plot_num, WHICHGRID whichgrid, int current_layer)
{
int x, y; /* point in terminal coordinates */
struct termentry *t = term;
BoundingBox *clip_save = clip_area;
FPRINTF((stderr,
"draw_3d_graphbox: whichgrid = %d current_layer = %d border_layer = %d\n",
whichgrid,current_layer,border_layer));
clip_area = &canvas;
if (draw_border && splot_map) {
if (border_layer == current_layer) {
term_apply_lp_properties(&border_lp);
if ((draw_border & 15) == 15)
newpath();
map3d_xy(zaxis_x, zaxis_y, base_z, &x, &y);
clip_move(x, y);
map3d_xy(back_x , back_y , base_z, &x, &y);
if (draw_border & 2)
clip_vector(x, y);
else
clip_move(x, y);
map3d_xy(right_x, right_y, base_z, &x, &y);
if (draw_border & 8)
clip_vector(x, y);
else
clip_move(x, y);
map3d_xy(front_x, front_y, base_z, &x, &y);
if (draw_border & 4)
clip_vector(x, y);
else
clip_move(x, y);
map3d_xy(zaxis_x, zaxis_y, base_z, &x, &y);
if (draw_border & 1)
clip_vector(x, y);
else
clip_move(x, y);
if ((draw_border & 15) == 15)
closepath();
}
} else
if (draw_border) {
/* the four corners of the base plane, in normalized view
* coordinates (-1..1) on all three axes. */
vertex bl, bb, br, bf;
/* map to normalized view coordinates the corners of the
* baseplane: left, back, right and front, in that order: */
map3d_xyz(zaxis_x, zaxis_y, base_z, &bl);
map3d_xyz(back_x , back_y , base_z, &bb);
map3d_xyz(right_x, right_y, base_z, &br);
map3d_xyz(front_x, front_y, base_z, &bf);
if (BACKGRID != whichgrid) {
/* Draw front part of base grid, right to front corner: */
if (draw_border & 4)
draw3d_line(&br, &bf, &border_lp);
/* ... and left to front: */
if (draw_border & 1)
draw3d_line(&bl, &bf, &border_lp);
}
if (FRONTGRID != whichgrid) {
/* Draw back part of base grid: left to back corner: */
if (draw_border & 2)
draw3d_line(&bl, &bb, &border_lp);
/* ... and right to back: */
if (draw_border & 8)
draw3d_line(&br, &bb, &border_lp);
}
/* if surface is drawn, draw the rest of the graph box, too: */
if (draw_surface || (draw_contour & CONTOUR_SRF)
|| (pm3d.implicit == PM3D_IMPLICIT && strpbrk(pm3d.where,"st") != NULL)
) {
vertex fl, fb, fr, ff; /* floor left/back/right/front corners */
vertex tl, tb, tr, tf; /* top left/back/right/front corners */
map3d_xyz(zaxis_x, zaxis_y, floor_z, &fl);
map3d_xyz(back_x , back_y , floor_z, &fb);
map3d_xyz(right_x, right_y, floor_z, &fr);
map3d_xyz(front_x, front_y, floor_z, &ff);
map3d_xyz(zaxis_x, zaxis_y, ceiling_z, &tl);
map3d_xyz(back_x , back_y , ceiling_z, &tb);
map3d_xyz(right_x, right_y, ceiling_z, &tr);
map3d_xyz(front_x, front_y, ceiling_z, &tf);
if ((draw_border & 0xf0) == 0xf0) {
/* all four verticals are drawn - save some time by
* drawing them to the full height, regardless of
* where the surface lies */
if (FRONTGRID != whichgrid) {
/* Draw the back verticals floor-to-ceiling, left: */
draw3d_line(&fl, &tl, &border_lp);
/* ... back: */
draw3d_line(&fb, &tb, &border_lp);
/* ... and right */
draw3d_line(&fr, &tr, &border_lp);
}
if (BACKGRID != whichgrid) {
/* Draw the front vertical: floor-to-ceiling, front: */
draw3d_line(&ff, &tf, &border_lp);
}
} else {
/* find heights of surfaces at the corners of the xy
* rectangle */
double height[2][2];
double depth[2][2];
unsigned int zaxis_i = MAP_HEIGHT_X(zaxis_x);
unsigned int zaxis_j = MAP_HEIGHT_Y(zaxis_y);
unsigned int back_i = MAP_HEIGHT_X(back_x);
unsigned int back_j = MAP_HEIGHT_Y(back_y);
height[0][0] = height[0][1]
= height[1][0] = height[1][1] = base_z;
depth[0][0] = depth[0][1]
= depth[1][0] = depth[1][1] = base_z;
/* FIXME HBB 20000617: this method contains the
* assumption that the topological corners of the
* surface mesh(es) are also the geometrical ones of
* their xy projections. This is only true for
* 'explicit' surface datasets, i.e. z(x,y) */
for (; --plot_num >= 0; plot = plot->next_sp) {
struct iso_curve *curve = plot->iso_crvs;
int count;
int iso;
if (plot->plot_type == NODATA)
continue;
if (plot->plot_type == DATA3D) {
if (!plot->has_grid_topology)
continue;
iso = plot->num_iso_read;
} else
iso = iso_samples_2;
count = curve->p_count;
check_corner_height(curve->points, height, depth);
check_corner_height(curve->points + count - 1, height, depth);
while (--iso)
curve = curve->next;
check_corner_height(curve->points, height, depth);
check_corner_height(curve->points + count - 1, height, depth);
}
#define VERTICAL(mask,x,y,i,j,bottom,top) \
if (draw_border&mask) { \
draw3d_line(bottom,top, &border_lp); \
} else if (height[i][j] != depth[i][j] && \
(X_AXIS.ticmode || Y_AXIS.ticmode || \
draw_border & 0x00F)) { \
vertex a, b; \
map3d_xyz(x,y,depth[i][j],&a); \
map3d_xyz(x,y,height[i][j],&b); \
draw3d_line(&a, &b, &border_lp); \
}
if (FRONTGRID != whichgrid) {
/* Draw back verticals: floor-to-ceiling left: */
VERTICAL(0x10, zaxis_x, zaxis_y, zaxis_i, zaxis_j, &fl, &tl);
/* ... back: */
VERTICAL(0x20, back_x, back_y, back_i, back_j, &fb, &tb);
/* ... and right: */
VERTICAL(0x40, right_x, right_y, 1 - zaxis_i, 1 - zaxis_j,
&fr, &tr);
}
if (BACKGRID != whichgrid) {
/* Draw front verticals: floor-to-ceiling front */
VERTICAL(0x80, front_x, front_y, 1 - back_i, 1 - back_j,
&ff, &tf);
}
#undef VERTICAL
} /* else (all 4 verticals drawn?) */
/* now border lines on top */
if (FRONTGRID != whichgrid) {
/* Draw back part of top of box: top left to back corner: */
if (draw_border & 0x100)
draw3d_line(&tl, &tb, &border_lp);
/* ... and top right to back: */
if (draw_border & 0x200)
draw3d_line(&tr, &tb, &border_lp);
}
if (BACKGRID != whichgrid) {
/* Draw front part of top of box: top left to front corner: */
if (draw_border & 0x400)
draw3d_line(&tl, &tf, &border_lp);
/* ... and top right to front: */
if (draw_border & 0x800)
draw3d_line(&tr, &tf, &border_lp);
}
} /* else (surface is drawn) */
} /* if (draw_border) */
/* In 'set view map' mode, treat grid as in 2D plots */
if (splot_map && current_layer != abs(grid_layer)) {
clip_area = clip_save;
return;
}
if (whichgrid == BORDERONLY) {
clip_area = clip_save;
return;
}
/* Draw ticlabels and axis labels */
/* x axis */
if (X_AXIS.ticmode || X_AXIS.label.text) {
vertex v0, v1;
double other_end = Y_AXIS.min + Y_AXIS.max - xaxis_y;
double mid_x;
if (nonlinear(&X_AXIS)) {
AXIS *primary = X_AXIS.linked_to_primary;
mid_x = (primary->max + primary->min) / 2.;
mid_x = eval_link_function(&X_AXIS, mid_x);
} else {
mid_x = (X_AXIS.max + X_AXIS.min) / 2.;
}
map3d_xyz(mid_x, xaxis_y, base_z, &v0);
map3d_xyz(mid_x, other_end, base_z, &v1);
tic_unitx = (v1.x - v0.x) / xyscaler;
tic_unity = (v1.y - v0.y) / xyscaler;
tic_unitz = (v1.z - v0.z) / xyscaler;
/* Don't output tics and grids if this is the front part of a
* two-part grid drawing process: */
if ((surface_rot_x <= 90 && FRONTGRID != whichgrid) ||
(surface_rot_x > 90 && BACKGRID != whichgrid))
if (X_AXIS.ticmode)
gen_tics(&axis_array[FIRST_X_AXIS], xtick_callback);
if (X_AXIS.label.text) {
if ((surface_rot_x <= 90 && BACKGRID != whichgrid) ||
(surface_rot_x > 90 && FRONTGRID != whichgrid) ||
splot_map) {
unsigned int x1, y1;
if (splot_map) { /* case 'set view map' */
/* copied from xtick_callback(): baseline of tics labels */
vertex v1, v2;
map3d_xyz(mid_x, xaxis_y, base_z, &v1);
v2.x = v1.x;
v2.y = v1.y - tic_unity * t->v_char;
if (!X_AXIS.tic_in)
v2.y -= tic_unity * t->v_tic * X_AXIS.ticscale;
TERMCOORD(&v2, x1, y1);
/* Default displacement with respect to baseline of tics labels */
y1 -= (unsigned int) ((1.5) * t->v_char);
} else { /* usual 3d set view ... */
if (X_AXIS.label.tag == ROTATE_IN_3D_LABEL_TAG) {
double ang, angx0, angx1, angy0, angy1;
map3d_xy_double(X_AXIS.min, xaxis_y, base_z, &angx0, &angy0);
map3d_xy_double(X_AXIS.max, xaxis_y, base_z, &angx1, &angy1);
ang = atan2(angy1-angy0, angx1-angx0) / DEG2RAD;
if (ang < -90) ang += 180;
if (ang > 90) ang -= 180;
X_AXIS.label.rotate = (ang > 0) ? floor(ang + 0.5) : floor(ang - 0.5);
}
if (X_AXIS.ticmode & TICS_ON_AXIS) {
map3d_xyz(mid_x, 0.0, base_z, &v1);
} else {
map3d_xyz(mid_x, xaxis_y, base_z, &v1);
}
if (X_AXIS.ticmode & TICS_ON_AXIS) {
v1.x += 2. * t->h_tic * ((X_AXIS.tic_in) ? 1.0 : -1.0) * tic_unitx;
v1.y += 2. * t->h_tic * ((X_AXIS.tic_in) ? 1.0 : -1.0) * tic_unity;
} else {
v1.x -= 10. * t->h_tic * tic_unitx;
v1.y -= 10. * t->h_tic * tic_unity;
}
if (!X_AXIS.tic_in) {
v1.x -= tic_unitx * X_AXIS.ticscale * t->h_tic;
v1.y -= tic_unity * X_AXIS.ticscale * t->h_tic;
}
TERMCOORD(&v1, x1, y1);
}
write_label(x1, y1, &X_AXIS.label);
}
}
if (splot_map && axis_array[SECOND_X_AXIS].ticmode)
gen_tics(&axis_array[SECOND_X_AXIS], xtick_callback);
}
/* y axis */
if (Y_AXIS.ticmode || Y_AXIS.label.text) {
vertex v0, v1;
double other_end = X_AXIS.min + X_AXIS.max - yaxis_x;
double mid_y;
if (nonlinear(&Y_AXIS)) {
AXIS *primary = Y_AXIS.linked_to_primary;
mid_y = (primary->max + primary->min) / 2.;
mid_y = eval_link_function(&Y_AXIS, mid_y);
} else {
mid_y = (Y_AXIS.max + Y_AXIS.min) / 2.;
}
map3d_xyz(yaxis_x, mid_y, base_z, &v0);
map3d_xyz(other_end, mid_y, base_z, &v1);
tic_unitx = (v1.x - v0.x) / xyscaler;
tic_unity = (v1.y - v0.y) / xyscaler;
tic_unitz = (v1.z - v0.z) / xyscaler;
/* Don't output tics and grids if this is the front part of a
* two-part grid drawing process: */
if ((surface_rot_x <= 90 && FRONTGRID != whichgrid) ||
(surface_rot_x > 90 && BACKGRID != whichgrid))
if (Y_AXIS.ticmode)
gen_tics(&axis_array[FIRST_Y_AXIS], ytick_callback);
if (Y_AXIS.label.text) {
if ((surface_rot_x <= 90 && BACKGRID != whichgrid) ||
(surface_rot_x > 90 && FRONTGRID != whichgrid) ||
splot_map) {
unsigned int x1, y1;
int save_rotate = Y_AXIS.label.rotate;
if (splot_map) { /* case 'set view map' */
/* copied from ytick_callback(): baseline of tics labels */
vertex v1, v2;
map3d_xyz(yaxis_x, mid_y, base_z, &v1);
if (Y_AXIS.ticmode & TICS_ON_AXIS
&& !X_AXIS.log
&& inrange (0.0, X_AXIS.min, X_AXIS.max)
) {
map3d_xyz(0.0, yaxis_x, base_z, &v1);
}
v2.x = v1.x - tic_unitx * t->h_char * 1;
v2.y = v1.y;
if (!X_AXIS.tic_in)
v2.x -= tic_unitx * t->h_tic * X_AXIS.ticscale;
TERMCOORD(&v2, x1, y1);
/* calculate max length of y-tics labels */
widest_tic_strlen = 0;
if (Y_AXIS.ticmode & TICS_ON_BORDER) {
widest_tic_strlen = 0; /* reset the global variable */
gen_tics(&axis_array[FIRST_Y_AXIS], widest_tic_callback);
}
/* Default displacement with respect to baseline of tics labels */
x1 -= (unsigned int) ((0.5 + widest_tic_strlen) * t->h_char);
} else { /* usual 3d set view ... */
if (Y_AXIS.label.tag == ROTATE_IN_3D_LABEL_TAG) {
double ang, angx0, angx1, angy0, angy1;
map3d_xy_double(yaxis_x, Y_AXIS.min, base_z, &angx0, &angy0);
map3d_xy_double(yaxis_x, Y_AXIS.max, base_z, &angx1, &angy1);
ang = atan2(angy1-angy0, angx1-angx0) / DEG2RAD;
if (ang < -90) ang += 180;
if (ang > 90) ang -= 180;
Y_AXIS.label.rotate = (ang > 0) ? floor(ang + 0.5) : floor(ang - 0.5);
} else {
/* The 2D default state (ylabel rotate) is not wanted in 3D */
Y_AXIS.label.rotate = 0;
}
if (Y_AXIS.ticmode & TICS_ON_AXIS) {
map3d_xyz(0.0, mid_y, base_z, &v1);
} else {
map3d_xyz(yaxis_x, mid_y, base_z, &v1);
}
if (Y_AXIS.ticmode & TICS_ON_AXIS) {
v1.x += 2. * t->h_tic * ((Y_AXIS.tic_in) ? 1.0 : -1.0) * tic_unitx;
v1.y += 2. * t->h_tic * ((Y_AXIS.tic_in) ? 1.0 : -1.0) * tic_unity;
} else {
v1.x -= 10. * t->h_tic * tic_unitx;
v1.y -= 10. * t->h_tic * tic_unity;
}
if (!Y_AXIS.tic_in) {
v1.x -= tic_unitx * Y_AXIS.ticscale * t->v_tic;
v1.y -= tic_unity * Y_AXIS.ticscale * t->v_tic;
}
TERMCOORD(&v1, x1, y1);
}
write_label(x1, y1, &Y_AXIS.label);
Y_AXIS.label.rotate = save_rotate;
}
}
if (splot_map && axis_array[SECOND_Y_AXIS].ticmode)
gen_tics(&axis_array[SECOND_Y_AXIS], ytick_callback);
}
/* do z tics */
if (Z_AXIS.ticmode
/* Don't output tics and grids if this is the front part of a
* two-part grid drawing process: */
&& (FRONTGRID != whichgrid)
&& (splot_map == FALSE)
&& (surface_rot_x != 0)
&& (draw_surface
|| (draw_contour & CONTOUR_SRF)
|| strchr(pm3d.where,'s') != NULL
)
) {
gen_tics(&axis_array[FIRST_Z_AXIS], ztick_callback);
}
if ((Y_AXIS.zeroaxis)
&& !X_AXIS.log
&& inrange(0, X_AXIS.min, X_AXIS.max)
) {
vertex v1, v2;
/* line through x=0 */
map3d_xyz(0.0, Y_AXIS.min, base_z, &v1);
map3d_xyz(0.0, Y_AXIS.max, base_z, &v2);
draw3d_line(&v1, &v2, Y_AXIS.zeroaxis);
}
if ((Z_AXIS.zeroaxis)
&& !X_AXIS.log
&& inrange(0, X_AXIS.min, X_AXIS.max)
) {
vertex v1, v2;
/* line through x=0 y=0 */
map3d_xyz(0.0, 0.0, Z_AXIS.min, &v1);
map3d_xyz(0.0, 0.0, Z_AXIS.max, &v2);
draw3d_line(&v1, &v2, Z_AXIS.zeroaxis);
}
if ((X_AXIS.zeroaxis)
&& !Y_AXIS.log
&& inrange(0, Y_AXIS.min, Y_AXIS.max)
) {
vertex v1, v2;
term_apply_lp_properties(X_AXIS.zeroaxis);
/* line through y=0 */
map3d_xyz(X_AXIS.min, 0.0, base_z, &v1);
map3d_xyz(X_AXIS.max, 0.0, base_z, &v2);
draw3d_line(&v1, &v2, X_AXIS.zeroaxis);
}
/* PLACE ZLABEL - along the middle grid Z axis - eh ? */
if (Z_AXIS.label.text
&& (splot_map == FALSE)
&& (current_layer == LAYER_FRONT || whichgrid == ALLGRID)
&& (draw_surface
|| (draw_contour & CONTOUR_SRF)
|| strpbrk(pm3d.where,"st") != NULL
)
) {
vertex v1;
double mid_z;
if (nonlinear(&Z_AXIS)) {
mid_z = (Z_AXIS.linked_to_primary->max + Z_AXIS.linked_to_primary->min) / 2.;
mid_z = eval_link_function(&Z_AXIS, mid_z);
} else
mid_z = (Z_AXIS.max + Z_AXIS.min) / 2.;
if (Z_AXIS.ticmode & TICS_ON_AXIS) {
map3d_xyz(0, 0, mid_z, &v1);
TERMCOORD(&v1, x, y);
x -= 5 * t->h_char;
} else {
map3d_xyz(zaxis_x, zaxis_y, mid_z, &v1);
TERMCOORD(&v1, x, y);
x -= 7 * t->h_char;
}
if (Z_AXIS.label.tag == ROTATE_IN_3D_LABEL_TAG) {
double ang, angx0, angx1, angy0, angy1;
map3d_xy_double(zaxis_x, zaxis_y, Z_AXIS.min, &angx0, &angy0);
map3d_xy_double(zaxis_x, zaxis_y, Z_AXIS.max, &angx1, &angy1);
ang = atan2(angy1-angy0, angx1-angx0) / DEG2RAD;
if (ang < -90) ang += 180;
if (ang > 90) ang -= 180;
Z_AXIS.label.rotate = (ang > 0) ? floor(ang + 0.5) : floor(ang - 0.5);
}
write_label(x, y, &Z_AXIS.label);
}
clip_area = clip_save;
}
static void
xtick_callback(
struct axis *this_axis,
double place,
char *text,
int ticlevel,
struct lp_style_type grid, /* linetype or -2 for none */
struct ticmark *userlabels)
{
double scale = tic_scale(ticlevel, this_axis) * (this_axis->tic_in ? 1 : -1);
double other_end = Y_AXIS.min + Y_AXIS.max - xaxis_y;
struct termentry *t = term;
vertex v1, v2, v3, v4;
/* Draw full-length grid line */
map3d_xyz(place, xaxis_y, base_z, &v1);
if (grid.l_type > LT_NODRAW) {
(t->layer)(TERM_LAYER_BEGIN_GRID);
/* to save mapping twice, map non-axis y */
map3d_xyz(place, other_end, base_z, &v3);
draw3d_line(&v1, &v3, &grid);
(t->layer)(TERM_LAYER_END_GRID);
}
/* Vertical grid lines (in yz plane) */
if (grid_vertical_lines && grid.l_type > LT_NODRAW) {
vertex v4, v5;
double which_face = (surface_rot_x > 90 && surface_rot_x < 270) ? xaxis_y : other_end;
(t->layer)(TERM_LAYER_BEGIN_GRID);
map3d_xyz(place, which_face, Z_AXIS.min, &v4);
map3d_xyz(place, which_face, ceiling_z, &v5);
draw3d_line(&v4, &v5, &grid);
(t->layer)(TERM_LAYER_END_GRID);
}
if ((X_AXIS.ticmode & TICS_ON_AXIS)
&& !Y_AXIS.log
&& inrange (0.0, Y_AXIS.min, Y_AXIS.max)
) {
map3d_xyz(place, 0.0, base_z, &v1);
}
/* NB: secondary axis must be linked to primary */
if (this_axis->index == SECOND_X_AXIS
&& this_axis->linked_to_primary
&& this_axis->link_udf->at != NULL) {
place = eval_link_function(&axis_array[FIRST_X_AXIS], place);
}
/* Draw bottom tic mark */
if ((this_axis->index == FIRST_X_AXIS)
|| (this_axis->index == SECOND_X_AXIS && (this_axis->ticmode & TICS_MIRROR))) {
v2.x = v1.x + tic_unitx * scale * t->v_tic ;
v2.y = v1.y + tic_unity * scale * t->v_tic ;
v2.z = v1.z + tic_unitz * scale * t->v_tic ;
v2.real_z = v1.real_z;
draw3d_line(&v1, &v2, &border_lp);
}
/* Draw top tic mark */
if ((this_axis->index == SECOND_X_AXIS)
|| (this_axis->index == FIRST_X_AXIS && (this_axis->ticmode & TICS_MIRROR))) {
map3d_xyz(place, other_end, base_z, &v3);
v4.x = v3.x - tic_unitx * scale * t->v_tic;
v4.y = v3.y - tic_unity * scale * t->v_tic;
v4.z = v3.z - tic_unitz * scale * t->v_tic;
v4.real_z = v3.real_z;
draw3d_line(&v3, &v4, &border_lp);
}
/* Draw tic label */
if (text) {
int just;
unsigned int x2, y2;
int angle;
int offsetx, offsety;
/* Skip label if we've already written a user-specified one here */
# define MINIMUM_SEPARATION 0.001
while (userlabels) {
if (fabs((place - userlabels->position) / (X_AXIS.max - X_AXIS.min))
<= MINIMUM_SEPARATION) {
text = NULL;
break;
}
userlabels = userlabels->next;
}
# undef MINIMUM_SEPARATION
/* get offset */
map3d_position_r(&(this_axis->ticdef.offset), &offsetx, &offsety, "xtics");
/* allow manual justification of tick labels, but only for "set view map" */
if (splot_map && this_axis->manual_justify)
just = this_axis->tic_pos;
else if (tic_unitx * xscaler < -0.9)
just = LEFT;
else if (tic_unitx * xscaler < 0.9)
just = CENTRE;
else
just = RIGHT;
if (this_axis->index == SECOND_X_AXIS) {
v4.x = v3.x + tic_unitx * t->h_char * 1;
v4.y = v3.y + tic_unity * t->v_char * 1;
if (!this_axis->tic_in) {
v4.x += tic_unitx * t->v_tic * this_axis->ticscale;
v4.y += tic_unity * t->v_tic * this_axis->ticscale;
}
TERMCOORD(&v4, x2, y2);
} else {
v2.x = v1.x - tic_unitx * t->h_char * 1;
v2.y = v1.y - tic_unity * t->v_char * 1;
if (!this_axis->tic_in) {
v2.x -= tic_unitx * t->v_tic * this_axis->ticscale;
v2.y -= tic_unity * t->v_tic * this_axis->ticscale;
}
TERMCOORD(&v2, x2, y2);
}
/* User-specified different color for the tics text */
if (this_axis->ticdef.textcolor.type != TC_DEFAULT)
apply_pm3dcolor(&(this_axis->ticdef.textcolor));
angle = this_axis->tic_rotate;
if (!(splot_map && angle && term->text_angle(angle)))
angle = 0;
ignore_enhanced(!this_axis->ticdef.enhanced);
write_multiline(x2+offsetx, y2+offsety, text, just, JUST_TOP,
angle, this_axis->ticdef.font);
ignore_enhanced(FALSE);
term->text_angle(0);
term_apply_lp_properties(&border_lp);
}
}
static void
ytick_callback(
struct axis *this_axis,
double place,
char *text,
int ticlevel,
struct lp_style_type grid,
struct ticmark *userlabels)
{
double scale = tic_scale(ticlevel, this_axis) * (this_axis->tic_in ? 1 : -1);
double other_end = X_AXIS.min + X_AXIS.max - yaxis_x;
struct termentry *t = term;
vertex v1, v2, v3, v4;
/* Draw full-length grid line */
map3d_xyz(yaxis_x, place, base_z, &v1);
if (grid.l_type > LT_NODRAW) {
(t->layer)(TERM_LAYER_BEGIN_GRID);
map3d_xyz(other_end, place, base_z, &v3);
draw3d_line(&v1, &v3, &grid);
(t->layer)(TERM_LAYER_END_GRID);
}
/* Vertical grid lines (in xz plane) */
if (grid_vertical_lines && grid.l_type > LT_NODRAW) {
vertex v4, v5;
double which_face = (surface_rot_x > 90 && surface_rot_x < 270) ? yaxis_x : other_end;
(t->layer)(TERM_LAYER_BEGIN_GRID);
map3d_xyz(which_face, place, Z_AXIS.min, &v4);
map3d_xyz(which_face, place, ceiling_z, &v5);
draw3d_line(&v4, &v5, &grid);
(t->layer)(TERM_LAYER_END_GRID);
}
if (Y_AXIS.ticmode & TICS_ON_AXIS
&& !X_AXIS.log
&& inrange (0.0, X_AXIS.min, X_AXIS.max)
) {
map3d_xyz(0.0, place, base_z, &v1);
}
/* NB: secondary axis must be linked to primary */
if (this_axis->index == SECOND_Y_AXIS
&& this_axis->linked_to_primary
&& this_axis->link_udf->at != NULL) {
place = eval_link_function(&axis_array[FIRST_Y_AXIS], place);
}
/* Draw left tic mark */
if ((this_axis->index == FIRST_Y_AXIS)
|| (this_axis->index == SECOND_Y_AXIS && (this_axis->ticmode & TICS_MIRROR))) {
v2.x = v1.x + tic_unitx * scale * t->h_tic;
v2.y = v1.y + tic_unity * scale * t->h_tic;
v2.z = v1.z + tic_unitz * scale * t->h_tic;
v2.real_z = v1.real_z;
draw3d_line(&v1, &v2, &border_lp);
}
/* Draw right tic mark */
if ((this_axis->index == SECOND_Y_AXIS)
|| (this_axis->index == FIRST_Y_AXIS && (this_axis->ticmode & TICS_MIRROR))) {
map3d_xyz(other_end, place, base_z, &v3);
v4.x = v3.x - tic_unitx * scale * t->h_tic;
v4.y = v3.y - tic_unity * scale * t->h_tic;
v4.z = v3.z - tic_unitz * scale * t->h_tic;
v4.real_z = v3.real_z;
draw3d_line(&v3, &v4, &border_lp);
}
/* Draw tic label */
if (text) {
int just;
unsigned int x2, y2;
int angle;
int offsetx, offsety;
/* Skip label if we've already written a user-specified one here */
# define MINIMUM_SEPARATION 0.001
while (userlabels) {
if (fabs((place - userlabels->position) / (Y_AXIS.max - Y_AXIS.min))
<= MINIMUM_SEPARATION) {
text = NULL;
break;
}
userlabels = userlabels->next;
}
# undef MINIMUM_SEPARATION
/* get offset */
map3d_position_r(&(this_axis->ticdef.offset), &offsetx, &offsety, "ytics");
/* allow manual justification of tick labels, but only for "set view map" */
if (splot_map && this_axis->manual_justify)
just = this_axis->tic_pos;
else if (tic_unitx * xscaler < -0.9)
just = (this_axis->index == FIRST_Y_AXIS) ? LEFT : RIGHT;
else if (tic_unitx * xscaler < 0.9)
just = CENTRE;
else
just = (this_axis->index == FIRST_Y_AXIS) ? RIGHT : LEFT;
if (this_axis->index == SECOND_Y_AXIS) {
v4.x = v3.x + tic_unitx * t->h_char * 1;
v4.y = v3.y + tic_unity * t->v_char * 1;
if (!this_axis->tic_in) {
v4.x += tic_unitx * t->h_tic * this_axis->ticscale;
v4.y += tic_unity * t->v_tic * this_axis->ticscale;
}
TERMCOORD(&v4, x2, y2);
} else {
v2.x = v1.x - tic_unitx * t->h_char * 1;
v2.y = v1.y - tic_unity * t->v_char * 1;
if (!this_axis->tic_in) {
v2.x -= tic_unitx * t->h_tic * this_axis->ticscale;
v2.y -= tic_unity * t->v_tic * this_axis->ticscale;
}
TERMCOORD(&v2, x2, y2);
}
/* User-specified different color for the tics text */
if (this_axis->ticdef.textcolor.type != TC_DEFAULT)
apply_pm3dcolor(&(this_axis->ticdef.textcolor));
angle = this_axis->tic_rotate;
if (!(splot_map && angle && term->text_angle(angle)))
angle = 0;
ignore_enhanced(!this_axis->ticdef.enhanced);
write_multiline(x2+offsetx, y2+offsety, text, just, JUST_TOP,
angle, this_axis->ticdef.font);
ignore_enhanced(FALSE);
term->text_angle(0);
term_apply_lp_properties(&border_lp);
}
}
static void
ztick_callback(
struct axis *this_axis,
double place,
char *text,
int ticlevel,
struct lp_style_type grid,
struct ticmark *userlabels)
{
struct termentry *t = term;
int len = tic_scale(ticlevel, this_axis)
* (this_axis->tic_in ? 1 : -1) * (term->h_tic);
vertex v1, v2, v3;
if (this_axis->ticmode & TICS_ON_AXIS)
map3d_xyz(0., 0., place, &v1);
else
map3d_xyz(zaxis_x, zaxis_y, place, &v1);
/* Needed both for grid and for azimuth ztics */
map3d_xyz(right_x, right_y, place, &v3);
if (grid.l_type > LT_NODRAW) {
(t->layer)(TERM_LAYER_BEGIN_GRID);
map3d_xyz(back_x, back_y, place, &v2);
draw3d_line(&v1, &v2, &grid);
draw3d_line(&v2, &v3, &grid);
(t->layer)(TERM_LAYER_END_GRID);
}
if (azimuth != 0) {
v2.x = v1.x + (v3.x - v1.x) * len / xyscaler;
v2.y = v1.y + (v3.y - v1.y) * len / xyscaler;
v2.z = v1.z + (v3.z - v1.z) * len / xyscaler;
} else {
v2.x = v1.x + len / (double)xscaler;
v2.y = v1.y;
v2.z = v1.z;
}
v2.real_z = v1.real_z;
draw3d_line(&v1, &v2, &border_lp);
if (text) {
unsigned int x1, y1;
int offsetx, offsety;
/* Skip label if we've already written a user-specified one here */
# define MINIMUM_SEPARATION 0.001
while (userlabels) {
if (fabs((place - userlabels->position) / (Z_AXIS.max - Z_AXIS.min))
<= MINIMUM_SEPARATION) {
text = NULL;
break;
}
userlabels = userlabels->next;
}
# undef MINIMUM_SEPARATION
/* get offset */
map3d_position_r(&(this_axis->ticdef.offset), &offsetx, &offsety, "ztics");
TERMCOORD(&v1, x1, y1);
x1 -= (term->h_tic) * 2;
if (!this_axis->tic_in)
x1 -= (term->h_tic) * this_axis->ticscale;
/* User-specified different color for the tics text */
if (this_axis->ticdef.textcolor.type == TC_Z)
this_axis->ticdef.textcolor.value = place;
if (this_axis->ticdef.textcolor.type != TC_DEFAULT)
apply_pm3dcolor(&(this_axis->ticdef.textcolor));
ignore_enhanced(!this_axis->ticdef.enhanced);
write_multiline(x1+offsetx, y1+offsety, text, RIGHT, JUST_CENTRE,
0, this_axis->ticdef.font);
ignore_enhanced(FALSE);
term_apply_lp_properties(&border_lp);
}
if (Z_AXIS.ticmode & TICS_MIRROR) {
if (azimuth != 0) {
v2.x = v3.x + (v1.x - v3.x) * len / xyscaler;
v2.y = v3.y + (v1.y - v3.y) * len / xyscaler;
v2.z = v3.z + (v1.z - v3.z) * len / xyscaler;
draw3d_line(&v3, &v2, &border_lp);
} else {
map3d_xyz(right_x, right_y, place, &v1);
v2.x = v1.x - len / (double)xscaler;
v2.y = v1.y;
v2.z = v1.z;
v2.real_z = v1.real_z;
draw3d_line(&v1, &v2, &border_lp);
}
}
}
static int
map3d_getposition(
struct position *pos,
const char *what,
double *xpos, double *ypos, double *zpos)
{
TBOOLEAN screen_coords = FALSE;
TBOOLEAN char_coords = FALSE;
TBOOLEAN plot_coords = FALSE;
double xx, yy;
switch (pos->scalex) {
case first_axes:
case second_axes:
*xpos = axis_log_value_checked(FIRST_X_AXIS, *xpos, what);
plot_coords = TRUE;
break;
case graph:
*xpos = X_AXIS.min + *xpos * (X_AXIS.max - X_AXIS.min);
plot_coords = TRUE;
break;
case screen:
*xpos = *xpos * (term->xmax -1) + 0.5;
screen_coords = TRUE;
break;
case character:
*xpos = *xpos * term->h_char + 0.5;
char_coords = TRUE;
break;
case polar_axes:
(void) polar_to_xy(*xpos, *ypos, &xx, &yy, FALSE);
*xpos = axis_log_value_checked(FIRST_X_AXIS, xx, what);
*ypos = axis_log_value_checked(FIRST_Y_AXIS, yy, what);
plot_coords = TRUE;
pos->scaley = polar_axes; /* Just to make sure */
break;
}
switch (pos->scaley) {
case first_axes:
case second_axes:
*ypos = axis_log_value_checked(FIRST_Y_AXIS, *ypos, what);
plot_coords = TRUE;
break;
case graph:
if (splot_map)
*ypos = Y_AXIS.max - *ypos * (Y_AXIS.max - Y_AXIS.min);
else
*ypos = Y_AXIS.min + *ypos * (Y_AXIS.max - Y_AXIS.min);
plot_coords = TRUE;
break;
case screen:
*ypos = *ypos * (term->ymax -1) + 0.5;
screen_coords = TRUE;
break;
case character:
*ypos = *ypos * term->v_char + 0.5;
char_coords = TRUE;
break;
case polar_axes:
break;
}
switch (pos->scalez) {
case first_axes:
case second_axes:
case polar_axes:
if (splot_map)
*zpos = 1; /* Avoid failure if z=0 with logscale z */
else
*zpos = axis_log_value_checked(FIRST_Z_AXIS, *zpos, what);
plot_coords = TRUE;
break;
case graph:
*zpos = Z_AXIS.min + *zpos * (Z_AXIS.max - Z_AXIS.min);
plot_coords = TRUE;
break;
case screen:
screen_coords = TRUE;
break;
case character:
char_coords = TRUE;
break;
}
if (plot_coords && (screen_coords || char_coords))
int_error(NO_CARET,"Cannot mix screen or character coords with plot coords");
return (screen_coords || char_coords);
}
/*
* map3d_position() wrapper for map3d_position_double
*/
void
map3d_position(
struct position *pos,
int *x, int *y,
const char *what)
{
double xx, yy;
map3d_position_double(pos, &xx, &yy, what);
*x = xx;
*y = yy;
}
void
map3d_position_double(
struct position *pos,
double *x, double *y,
const char *what)
{
double xpos = pos->x;
double ypos = pos->y;
double zpos = pos->z;
if (map3d_getposition(pos, what, &xpos, &ypos, &zpos) == 0) {
map3d_xy_double(xpos, ypos, zpos, x, y);
} else {
/* Screen or character coordinates */
*x = xpos;
*y = ypos;
}
}
void
map3d_position_r(
struct position *pos,
int *x, int *y,
const char *what)
{
double xpos = pos->x;
double ypos = pos->y;
double zpos = (splot_map) ? Z_AXIS.min : pos->z;
/* startpoint in graph coordinates */
if (map3d_getposition(pos, what, &xpos, &ypos, &zpos) == 0) {
int xoriginlocal, yoriginlocal;
double xx, yy;
map3d_xy_double(xpos, ypos, zpos, &xx, &yy);
*x = xx;
*y = yy;
if (pos->scalex == graph)
xpos = X_AXIS.min;
else
xpos = 0;
if (pos->scaley == graph)
ypos = (splot_map) ? Y_AXIS.max : Y_AXIS.min;
else
ypos = 0;
if (pos->scalez == graph)
zpos = Z_AXIS.min;
else if (splot_map)
zpos = Z_AXIS.min;
else
zpos = 0;
map3d_xy(xpos, ypos, zpos, &xoriginlocal, &yoriginlocal);
*x -= xoriginlocal;
*y -= yoriginlocal;
} else {
/* endpoint `screen' or 'character' coordinates */
*x = xpos;
*y = ypos;
}
return;
}
/*
* these code blocks were moved to functions, to make the code simpler
*/
static void
key_text(int xl, int yl, char *text)
{
legend_key *key = &keyT;
(term->layer)(TERM_LAYER_BEGIN_KEYSAMPLE);
if (key->just == GPKEY_LEFT && key->region != GPKEY_USER_PLACEMENT) {
write_multiline(xl + key_text_left, yl, text, LEFT, JUST_TOP, 0, key->font);
} else {
if ((*term->justify_text) (RIGHT)) {
write_multiline(xl + key_text_right, yl, text, RIGHT, JUST_TOP, 0, key->font);
} else {
int x = xl + key_text_right - (term->h_char) * estimate_strlen(text);
write_multiline(x, yl, text, LEFT, JUST_TOP, 0, key->font);
}
}
(term->layer)(TERM_LAYER_END_KEYSAMPLE);
}
static void
key_sample_line(int xl, int yl)
{
BoundingBox *clip_save = clip_area;
/* Clip against canvas */
if (term->flags & TERM_CAN_CLIP)
clip_area = NULL;
else
clip_area = &canvas;
(term->layer)(TERM_LAYER_BEGIN_KEYSAMPLE);
draw_clip_line(xl + key_sample_left, yl, xl + key_sample_right, yl);
(term->layer)(TERM_LAYER_END_KEYSAMPLE);
clip_area = clip_save;
}
static void
key_sample_point(int xl, int yl, int pointtype)
{
BoundingBox *clip_save = clip_area;
/* Clip against canvas */
if (term->flags & TERM_CAN_CLIP)
clip_area = NULL;
else
clip_area = &canvas;
(term->layer)(TERM_LAYER_BEGIN_KEYSAMPLE);
if (!clip_point(xl + key_point_offset, yl))
(*term->point) (xl + key_point_offset, yl, pointtype);
(term->layer)(TERM_LAYER_END_KEYSAMPLE);
clip_area = clip_save;
}
static void
key_sample_fill(int xl, int yl, struct fill_style_type *fs)
{
int style = style_from_fill(fs);
unsigned int x = xl + key_sample_left;
unsigned int y = yl - key_entry_height/4;
unsigned int w = key_sample_right - key_sample_left;
unsigned int h = key_entry_height/2;
if (!(term->fillbox))
return;
(term->layer)(TERM_LAYER_BEGIN_KEYSAMPLE);
apply_pm3dcolor(&fs->border_color);
if (w > 0)
(term->fillbox)(style,x,y,w,h);
(term->layer)(TERM_LAYER_END_KEYSAMPLE);
}
/*
* returns minimal and maximal values of the cb-range (or z-range if taking the
* color from the z value) of the given surface
*/
static void
get_surface_cbminmax(struct surface_points *plot, double *cbmin, double *cbmax)
{
int i, curve = 0;
TBOOLEAN color_from_column = plot->pm3d_color_from_column; /* just a shortcut */
coordval cb;
struct iso_curve *icrvs = plot->iso_crvs;
struct coordinate GPHUGE *points;
*cbmin = VERYLARGE;
*cbmax = -VERYLARGE;
while (icrvs && curve < plot->num_iso_read) {
/* fprintf(stderr,"**** NEW ISOCURVE - nb of pts: %i ****\n", icrvs->p_count); */
for (i = 0, points = icrvs->points; i < icrvs->p_count; i++) {
/* 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); */
if (points[i].type == INRANGE) {
/* ?? if (!clip_point(x, y)) ... */
cb = color_from_column ? points[i].CRD_COLOR : points[i].z;
if (cb < *cbmin) *cbmin = cb;
if (cb > *cbmax) *cbmax = cb;
}
} /* points on one scan */
icrvs = icrvs->next;
curve++;
} /* surface */
}
/*
* Draw a gradient color line for a key (legend).
*/
static void
key_sample_line_pm3d(struct surface_points *plot, int xl, int yl)
{
int steps = GPMIN(24, abs(key_sample_right - key_sample_left));
/* don't multiply by key->swidth --- could be >> palette.maxcolors */
int x_to = xl + key_sample_right;
double step = ((double)(key_sample_right - key_sample_left)) / steps;
int i = 1, x1 = xl + key_sample_left, x2;
double cbmin, cbmax;
double gray, gray_from, gray_to, gray_step;
int colortype = plot->lp_properties.pm3d_color.type;
/* If plot uses a constant color, set it here and then let simpler routine take over */
if ((colortype == TC_RGB && plot->lp_properties.pm3d_color.value >= 0.0)
|| (colortype == TC_LT)
|| (colortype == TC_LINESTYLE)) {
lp_style_type lptmp = plot->lp_properties;
if (plot->lp_properties.l_type == LT_COLORFROMCOLUMN)
lp_use_properties(&lptmp, (int)(plot->iso_crvs->points[0].CRD_COLOR));
apply_pm3dcolor(&lptmp.pm3d_color);
key_sample_line(xl,yl);
return;
}
/* color gradient only over the cb-values of the surface, if smaller than the
* cb-axis range (the latter are gray values [0:1]) */
get_surface_cbminmax(plot, &cbmin, &cbmax);
if (cbmin > cbmax) return; /* splot 1/0, for example */
cbmin = GPMAX(cbmin, CB_AXIS.min);
cbmax = GPMIN(cbmax, CB_AXIS.max);
gray_from = cb2gray(cbmin);
gray_to = cb2gray(cbmax);
gray_step = (gray_to - gray_from)/steps;
clip_move(x1, yl);
x2 = x1;
while (i <= steps) {
/* if (i>1) set_color( i==steps ? 1 : (i-0.5)/steps ); ... range [0:1] */
gray = (i==steps) ? gray_to : gray_from+i*gray_step;
set_color(gray);
clip_move(x2, yl);
x2 = (i==steps) ? x_to : x1 + (int)(i*step+0.5);
clip_vector(x2, yl);
i++;
}
}
/*
* Draw a sequence of points with gradient color a key (legend).
*/
static void
key_sample_point_pm3d(
struct surface_points *plot,
int xl, int yl,
int pointtype)
{
BoundingBox *clip_save = clip_area;
int x_to = xl + key_sample_right;
int i = 0, x1 = xl + key_sample_left, x2;
double cbmin, cbmax;
double gray, gray_from, gray_to, gray_step;
int colortype = plot->lp_properties.pm3d_color.type;
/* rule for number of steps: 3*char_width*pointsize or char_width for dots,
* but at least 3 points */
double step = term->h_char * (pointtype == -1 ? 1 : 3*(1+(pointsize-1)/2));
int steps = (int)(((double)(key_sample_right - key_sample_left)) / step + 0.5);
if (steps < 2) steps = 2;
step = ((double)(key_sample_right - key_sample_left)) / steps;
/* If plot uses a constant color, set it here and then let simpler routine take over */
if ((colortype == TC_RGB && plot->lp_properties.pm3d_color.value >= 0.0)
|| (colortype == TC_LT)
|| (colortype == TC_LINESTYLE)) {
lp_style_type lptmp = plot->lp_properties;
if (plot->lp_properties.l_type == LT_COLORFROMCOLUMN)
lp_use_properties(&lptmp, (int)(plot->iso_crvs->points[0].CRD_COLOR));
apply_pm3dcolor(&lptmp.pm3d_color);
key_sample_point(xl,yl,pointtype);
return;
}
/* color gradient only over the cb-values of the surface, if smaller than the
* cb-axis range (the latter are gray values [0:1]) */
get_surface_cbminmax(plot, &cbmin, &cbmax);
if (cbmin > cbmax) return; /* splot 1/0, for example */
cbmin = GPMAX(cbmin, CB_AXIS.min);
cbmax = GPMIN(cbmax, CB_AXIS.max);
gray_from = cb2gray(cbmin);
gray_to = cb2gray(cbmax);
gray_step = (gray_to - gray_from)/steps;
/* Clip to canvas */
if (term->flags & TERM_CAN_CLIP)
clip_area = NULL;
else
clip_area = &canvas;
while (i <= steps) {
/* if (i>0) set_color( i==steps ? gray_to : (i-0.5)/steps ); ... range [0:1] */
gray = (i==steps) ? gray_to : gray_from+i*gray_step;
set_color(gray);
x2 = i==0 ? x1 : (i==steps ? x_to : x1 + (int)(i*step+0.5));
/* x2 += key_point_offset; ... that's if there is only 1 point */
if (!clip_point(x2, yl))
(*term->point) (x2, yl, pointtype);
i++;
}
clip_area = clip_save;
}
/* plot_vectors:
* Plot the curves in VECTORS style
*/
static void
plot3d_vectors(struct surface_points *plot)
{
int i;
double x1, y1, x2, y2;
arrow_style_type ap;
struct coordinate GPHUGE *tails = plot->iso_crvs->points;
struct coordinate GPHUGE *heads = plot->iso_crvs->next->points;
/* Only necessary once, unless variable arrow style */
ap = plot->arrow_properties;
term_apply_lp_properties(&ap.lp_properties);
apply_head_properties(&ap);
for (i = 0; i < plot->iso_crvs->p_count; i++) {
if (heads[i].type == UNDEFINED || tails[i].type == UNDEFINED)
continue;
/* variable arrow style read from extra data column */
if (plot->arrow_properties.tag == AS_VARIABLE) {
int as= heads[i].CRD_COLOR;
arrow_use_properties(&ap, as);
term_apply_lp_properties(&ap.lp_properties);
apply_head_properties(&ap);
} else {
check3d_for_variable_color(plot, &heads[i]);
}
/* The normal case: both ends in range */
if (heads[i].type == INRANGE && tails[i].type == INRANGE) {
map3d_xy_double(tails[i].x, tails[i].y, tails[i].z, &x1, &y1);
map3d_xy_double(heads[i].x, heads[i].y, heads[i].z, &x2, &y2);
draw_clip_arrow((int)x1, (int)y1, (int)x2, (int)y2, ap.head);
/* "set clip two" - both ends out of range */
} else if (heads[i].type != INRANGE && tails[i].type != INRANGE) {
double lx[2], ly[2], lz[2];
if (!clip_lines2)
continue;
two_edge3d_intersect(&tails[i], &heads[i], lx, ly, lz);
map3d_xy_double(lx[0], ly[0], lz[0], &x1, &y1);
map3d_xy_double(lx[1], ly[1], lz[1], &x2, &y2);
draw_clip_arrow((int)x1, (int)y1, (int)x2, (int)y2, ap.head);
/* "set clip one" - one end out of range */
} else if (clip_lines1) {
double clip_x, clip_y, clip_z;
edge3d_intersect(&heads[i], &tails[i], &clip_x, &clip_y, &clip_z);
if (tails[i].type == INRANGE) {
map3d_xy_double(tails[i].x, tails[i].y, tails[i].z, &x1, &y1);
map3d_xy_double(clip_x, clip_y, clip_z, &x2, &y2);
} else {
map3d_xy_double(clip_x, clip_y, clip_z, &x1, &y1);
map3d_xy_double(heads[i].x, heads[i].y, heads[i].z, &x2, &y2);
}
draw_clip_arrow((int)x1, (int)y1, (int)x2, (int)y2, ap.head);
}
}
}
/*
* splot with zerrorfill
* This 3D style is similar to a 2D filledcurves plot between two lines.
* Put together a list of the component quadrangles using the data structures
* normally used by pm3d routines pm3d_plot(), pm3d_depth_queue_flush().
* The component quadrangles from all plots are sorted and flushed together.
*/
static void
plot3d_zerrorfill(struct surface_points *plot)
{
struct iso_curve *curve = plot->iso_crvs;
int i1, i2; /* index leading and trailing coord of current quadrangle */
int count = 0;
gpdPoint corner[4];
/* Find leading edge of first quadrangle */
for (i1=0; i1 < curve->p_count; i1++) {
if (curve->points[i1].type == INRANGE)
break;
}
for (i2=i1+1; i2 < curve->p_count; i2++) {
if (curve->points[i2].type != INRANGE)
continue;
count++; /* Found one */
corner[0].x = corner[1].x = curve->points[i1].x;
corner[0].y = corner[1].y = curve->points[i1].y;
corner[0].z = curve->points[i1].CRD_ZLOW;
corner[1].z = curve->points[i1].CRD_ZHIGH;
corner[2].x = corner[3].x = curve->points[i2].x;
corner[2].y = corner[3].y = curve->points[i2].y;
corner[3].z = curve->points[i2].CRD_ZLOW;
corner[2].z = curve->points[i2].CRD_ZHIGH;
pm3d_add_quadrangle(plot, corner);
i1 = i2;
}
if (count == 0)
int_error(NO_CARET, "all points out of range");
/* Default is to write out each zerror plot as we come to it */
/* (most recent plot occludes all previous plots). To get proper */
/* sorting, use "set pm3d depthorder". */
if (pm3d.direction != PM3D_DEPTH)
pm3d_depth_queue_flush();
}
static void
check3d_for_variable_color(struct surface_points *plot, struct coordinate *point)
{
int colortype = plot->lp_properties.pm3d_color.type;
TBOOLEAN rgb_from_column = plot->pm3d_color_from_column
&& plot->lp_properties.pm3d_color.type == TC_RGB
&& plot->lp_properties.pm3d_color.value < 0.0;
switch( colortype ) {
case TC_RGB:
if (rgb_from_column)
set_rgbcolor_var( (unsigned int)point->CRD_COLOR );
break;
case TC_Z:
case TC_DEFAULT: /* pm3d mode assumes this is default */
if (can_pm3d) {
if (plot->pm3d_color_from_column)
set_color( cb2gray(point->CRD_COLOR) );
else
set_color( cb2gray( z2cb(point->z) ) );
}
break;
case TC_LINESTYLE: /* color from linestyle in data column */
plot->lp_properties.pm3d_color.lt = (int)(point->CRD_COLOR);
apply_pm3dcolor(&(plot->lp_properties.pm3d_color));
break;
default:
/* The other cases were taken care of already */
break;
}
}
void
do_3dkey_layout(legend_key *key, int *xinkey, int *yinkey)
{
struct termentry *t = term;
int key_height, key_width;
/* NOTE: All of these had better not change after being calculated here! */
if (key->reverse) {
key_sample_left = -key_sample_width;
key_sample_right = 0;
key_text_left = t->h_char;
key_text_right = t->h_char * (max_ptitl_len + 1);
key_size_right = t->h_char * (max_ptitl_len + 2 + key->width_fix);
key_size_left = t->h_char + key_sample_width;
} else {
key_sample_left = 0;
key_sample_right = key_sample_width;
key_text_left = -(int) (t->h_char * (max_ptitl_len + 1));
key_text_right = -(int) t->h_char;
key_size_left = t->h_char * (max_ptitl_len + 2 + key->width_fix);
key_size_right = t->h_char + key_sample_width;
}
key_point_offset = (key_sample_left + key_sample_right) / 2;
key_title_height = ktitle_lines * t->v_char;
key_title_extra = 0;
if ((key->title.text) && (t->flags & TERM_ENHANCED_TEXT)
&& (strchr(key->title.text,'^') || strchr(key->title.text,'_')))
key_title_extra = t->v_char;
key_width = key_col_wth * (key_cols - 1) + key_size_right + key_size_left;
key_height = key_title_height + key_title_extra
+ key_entry_height * key_rows + key->height_fix * t->v_char;
/* Now that we know the size of the key, we can position it as requested */
if (key->region == GPKEY_USER_PLACEMENT) {
int corner_x, corner_y;
map3d_position(&key->user_pos, &corner_x, &corner_y, "key");
if (key->hpos == CENTRE)
key->bounds.xleft = corner_x - key_width / 2;
else if (key->hpos == RIGHT)
key->bounds.xleft = corner_x - key_width;
else
key->bounds.xleft = corner_x;
key->bounds.xright = key->bounds.xleft + key_width;
key->bounds.ytop = corner_y;
key->bounds.ybot = corner_y - key_height;
*xinkey = key->bounds.xleft + key_size_left;
*yinkey = key->bounds.ytop - key_title_height - key_title_extra;
} else {
BoundingBox *bounds;
if (key->fixed && !splot_map)
bounds = &page_bounds;
else
bounds = &plot_bounds;
if (key->region != GPKEY_AUTO_INTERIOR_LRTBC && key->margin == GPKEY_BMARGIN) {
if (ptitl_cnt > 0) {
/* we divide into columns, then centre in column by considering
* ratio of key_left_size to key_right_size
* key_size_left / (key_size_left+key_size_right)
* * (bounds->xright-bounds->xleft)/key_cols
* do one integer division to maximise accuracy (hope we dont overflow!)
*/
*xinkey = bounds->xleft
+ ((bounds->xright - bounds->xleft) * key_size_left)
/ (key_cols * (key_size_left + key_size_right));
key->bounds.xleft = *xinkey - key_size_left;
key->bounds.xright = key->bounds.xleft + key_width;
key->bounds.ytop = bounds->ybot;
key->bounds.ybot = bounds->ybot - key_height;
*yinkey = key->bounds.ytop - key_title_height - key_title_extra;
}
} else {
if (key->vpos == JUST_TOP) {
key->bounds.ytop = bounds->ytop - t->v_tic;
key->bounds.ybot = key->bounds.ytop - key_height;
*yinkey = key->bounds.ytop - key_title_height - key_title_extra;
} else {
key->bounds.ybot = bounds->ybot + t->v_tic;
key->bounds.ytop = key->bounds.ybot + key_height;
*yinkey = key->bounds.ytop - key_title_height - key_title_extra;
}
if (key->region != GPKEY_AUTO_INTERIOR_LRTBC && key->margin == GPKEY_RMARGIN) {
/* keys outside plot border (right) */
key->bounds.xleft = bounds->xright + t->h_tic;
key->bounds.xright = key->bounds.xleft + key_width;
*xinkey = key->bounds.xleft + key_size_left;
} else if (key->region != GPKEY_AUTO_INTERIOR_LRTBC && key->margin == GPKEY_LMARGIN) {
/* keys outside plot border (left) */
key->bounds.xright = bounds->xleft - t->h_tic;
key->bounds.xleft = key->bounds.xright - key_width;
*xinkey = key->bounds.xleft + key_size_left;
} else if (key->hpos == LEFT) {
key->bounds.xleft = bounds->xleft + t->h_tic;
key->bounds.xright = key->bounds.xleft + key_width;
*xinkey = key->bounds.xleft + key_size_left;
} else {
key->bounds.xright = bounds->xright - t->h_tic;
key->bounds.xleft = key->bounds.xright - key_width;
*xinkey = key->bounds.xleft + key_size_left;
}
}
yl_ref = *yinkey - key_title_height - key_title_extra;
}
/* Center the key entries vertically, allowing for requested extra space */
*yinkey -= (key->height_fix * t->v_char) / 2;
}