File: [local] / OpenXM_contrib / gnuplot / Attic / graph3d.c (download)
Revision 1.1.1.3 (vendor branch), Mon Sep 15 07:09:25 2003 UTC (20 years, 11 months ago) by ohara
Branch: GNUPLOT
CVS Tags: VERSION_3_7_3, RELEASE_1_2_3, RELEASE_1_2_2_KNOPPIX_b, RELEASE_1_2_2_KNOPPIX Changes since 1.1.1.2: +32 -28
lines
Import gnuplot 3.7.3
|
#ifndef lint
static char *RCSid = "$Id: graph3d.c,v 1.13.2.9 2002/01/31 21:24:11 lhecking Exp $";
#endif
/* GNUPLOT - graph3d.c */
/*[
* Copyright 1986 - 1993, 1998 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 "plot.h"
#include "setshow.h"
static int p_height;
static int p_width; /* pointsize * t->h_tic */
static int key_entry_height; /* bigger of t->v_size, pointsize*t->v_tick */
int suppressMove = 0; /* to prevent moveto while drawing contours */
/*
* hidden_line_type_above, hidden_line_type_below - controls type of lines
* for above and below parts of the surface.
* hidden_no_update - if TRUE lines will be hidden line removed but they
* are not assumed to be part of the surface (i.e. grid) and therefore
* do not influence the hidings.
* hidden_active - TRUE if hidden lines are to be removed.
*/
int hidden_active = FALSE;
int hidden_no_update; /* HBB 980324: made this visible despite LITE */
/* LITE defines a restricted memory version for MS-DOS, which doesn't
* use the routines in hidden3d.c
*/
#ifndef LITE
int hidden_line_type_above, hidden_line_type_below;
#endif /* LITE */
static double LogScale __PROTO((double coord, TBOOLEAN is_log,
double log_base_log, char *what, char *axis));
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_dots __PROTO((struct surface_points * plot));
static void cntr3d_impulses __PROTO((struct gnuplot_contours * cntr,
struct surface_points * plot));
static void cntr3d_lines __PROTO((struct gnuplot_contours * cntr));
static void cntr3d_points __PROTO((struct gnuplot_contours * cntr,
struct surface_points * plot));
static void cntr3d_dots __PROTO((struct gnuplot_contours * cntr));
static void check_corner_height __PROTO((struct coordinate GPHUGE * point,
double height[2][2], double depth[2][2]));
static void draw_bottom_grid __PROTO((struct surface_points * plot,
int plot_count));
/* HBB 20010118: these should be static, but can't --- HP-UX assembler bug */
void xtick_callback __PROTO((int axis, double place, char *text,
struct lp_style_type grid));
void ytick_callback __PROTO((int axis, double place, char *text,
struct lp_style_type grid));
void ztick_callback __PROTO((int axis, double place, char *text,
struct lp_style_type grid));
static void setlinestyle __PROTO((struct lp_style_type style));
static void boundary3d __PROTO((int scaling, struct surface_points * plots,
int count));
#if 0 /* not used */
static double dbl_raise __PROTO((double x, int y));
#endif
static void map_position __PROTO((struct position * pos, unsigned int *x,
unsigned int *y, char *what));
/* 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_text __PROTO((int xl, int yl, char *text));
#if defined(sun386) || defined(AMIGA_SC_6_1)
static double CheckLog __PROTO((TBOOLEAN is_log, double base_log, double x));
#endif
/*
* The Amiga SAS/C 6.2 compiler moans about macro envocations causing
* multiple calls to functions. I converted these macros to inline
* functions coping with the problem without loosing speed.
* (MGR, 1993)
*/
#ifdef AMIGA_SC_6_1
GP_INLINE static TBOOLEAN i_inrange(int z, int min, int max)
{
return ((min < max) ? ((z >= min) && (z <= max)) : ((z >= max) && (z <= min)));
}
GP_INLINE static double f_max(double a, double b)
{
return (max(a, b));
}
GP_INLINE static double f_min(double a, double b)
{
return (min(a, b));
}
#else
# 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))
#endif
#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.
*/
/* in order to allow graphic.c to use clip_draw_line, we must
* share xleft, xright, ybot, ytop with graphics.c
*/
extern int xleft, xright, ybot, ytop;
int xmiddle, ymiddle, xscaler, yscaler;
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 int ktitle_lines = 0;
/* Boundary and scale factors, in user coordinates */
/* x_min3d, x_max3d, y_min3d, y_max3d, z_min3d, z_max3d are local to this
* file and are not the same as variables of the same names in other files
*/
/*static double x_min3d, x_max3d, y_min3d, y_max3d, z_min3d, z_max3d; */
/* sizes are now set in min_array[], max_array[] from plot.c */
extern double min_array[], max_array[];
extern int auto_array[], log_array[];
extern double base_array[], log_base_array[];
/* for convenience while converting to use these arrays */
#define x_min3d min_array[FIRST_X_AXIS]
#define x_max3d max_array[FIRST_X_AXIS]
#define y_min3d min_array[FIRST_Y_AXIS]
#define y_max3d max_array[FIRST_Y_AXIS]
#define z_min3d min_array[FIRST_Z_AXIS]
#define z_max3d max_array[FIRST_Z_AXIS]
/* There are several z's to take into account - I hope I get these
* right !
*
* 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 ceiling_z, floor_z, base_z;
/* and some bodges while making the change */
#define min3d_z min_array[FIRST_Z_AXIS]
#define max3d_z max_array[FIRST_Z_AXIS]
double xscale3d, yscale3d, zscale3d;
typedef double transform_matrix[4][4];
transform_matrix trans_mat;
static double xaxis_y, yaxis_x, zaxis_x, zaxis_y;
/* the co-ordinates of the back corner */
static double back_x, back_y;
/* the penalty for convenience of using tic_gen to make callbacks
* to tick routines is that we cant pass parameters very easily.
* We communicate with the tick_callbacks using static variables
*/
/* unit vector (terminal coords) */
static double tic_unitx, tic_unity;
/* (DFK) Watch for cancellation error near zero on axes labels */
#define CheckZero(x,tic) (fabs(x) < ((tic) * SIGNIF) ? 0.0 : (x))
#define NearlyEqual(x,y,tic) (fabs((x)-(y)) < ((tic) * SIGNIF))
/* And the functions to map from user to terminal coordinates */
#define map_x(x) (int)(x+0.5) /* maps floating point x to screen */
#define map_y(y) (int)(y+0.5) /* same for y */
/* And the functions to map from user 3D space into normalized -1..1 */
#define map_x3d(x) ((x-x_min3d)*xscale3d-1.0)
#define map_y3d(y) ((y-y_min3d)*yscale3d-1.0)
#define map_z3d(z) ((z-floor_z)*zscale3d-1.0)
/* Initialize the line style using the current device and set hidden styles
* to it as well if hidden line removal is enabled */
static void setlinestyle(style)
struct lp_style_type style;
{
term_apply_lp_properties(&style);
#ifndef LITE
if (hidden3d) {
hidden_line_type_above = style.l_type;
hidden_line_type_below = style.l_type;
}
#endif /* LITE */
}
/* (DFK) For some reason, the Sun386i compiler screws up with the CheckLog
* macro, so I write it as a function on that machine.
*
* Amiga SAS/C 6.2 thinks it will do too much work calling functions in
* macro arguments twice, thus I inline theese functions. (MGR, 1993)
*/
#if defined(sun386) || defined(AMIGA_SC_6_1)
GP_INLINE static double CheckLog(is_log, base_log, x)
TBOOLEAN is_log;
double base_log;
double x;
{
if (is_log)
return (pow(base_log, x));
else
return (x);
}
#else
/* (DFK) Use 10^x if logscale is in effect, else x */
# define CheckLog(is_log, base_log, x) ((is_log) ? pow(base_log, (x)) : (x))
#endif /* sun386 || SAS/C */
static double LogScale(coord, is_log, log_base_log, what, axis)
double coord; /* the value */
TBOOLEAN is_log; /* is this axis in logscale? */
double log_base_log; /* if so, the log of its base */
char *what; /* what is the coord for? */
char *axis; /* which axis is this for ("x" or "y")? */
{
if (is_log) {
if (coord <= 0.0) {
char errbuf[100]; /* place to write error message */
(void) sprintf(errbuf, "%s has %s coord of %g; must be above 0 for log scale!",
what, axis, coord);
graph_error(errbuf);
} else
return (log(coord) / log_base_log);
}
return (coord);
}
/* And the functions to map from user 3D space to terminal coordinates */
void map3d_xy(x, y, z, xt, yt)
double x, y, z;
unsigned int *xt, *yt;
{
int i, j;
double v[4], res[4], /* Homogeneous coords. vectors. */
w = trans_mat[3][3];
v[0] = map_x3d(x); /* Normalize object space to -1..1 */
v[1] = map_y3d(y);
v[2] = map_z3d(z);
v[3] = 1.0;
for (i = 0; i < 2; i++) { /* Dont use the third axes (z). */
res[i] = trans_mat[3][i]; /* Initiate it with the weight factor */
for (j = 0; j < 3; j++)
res[i] += v[j] * trans_mat[j][i];
}
for (i = 0; i < 3; i++)
w += v[i] * trans_mat[i][3];
if (w == 0)
w = 1e-5;
*xt = (unsigned int) ((res[0] * xscaler / w) + xmiddle);
*yt = (unsigned int) ((res[1] * yscaler / w) + ymiddle);
}
/* And the functions to map from user 3D space to terminal z coordinate */
int map3d_z(x, y, z)
double x, y, z;
{
int i, zt;
double v[4], res, /* Homogeneous coords. vectors. */
w = trans_mat[3][3];
v[0] = map_x3d(x); /* Normalize object space to -1..1 */
v[1] = map_y3d(y);
v[2] = map_z3d(z);
v[3] = 1.0;
res = trans_mat[3][2]; /* Initiate it with the weight factor. */
for (i = 0; i < 3; i++)
res += v[i] * trans_mat[i][2];
if (w == 0)
w = 1e-5;
for (i = 0; i < 3; i++)
w += v[i] * trans_mat[i][3];
zt = ((int) (res * 16384 / w));
return zt;
}
/* borders of plotting area */
/* computed once on every call to do_plot */
static void boundary3d(scaling, plots, count)
TBOOLEAN scaling; /* TRUE if terminal is doing the scaling */
struct surface_points *plots;
int count;
{
register struct termentry *t = term;
int ytlen, i;
titlelin = 0;
p_height = pointsize * t->v_tic;
p_width = pointsize * t->h_tic;
if (key_swidth >= 0)
key_sample_width = key_swidth * (t->h_char) + pointsize * (t->h_tic);
else
key_sample_width = 0;
key_entry_height = pointsize * (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);
if ((ytlen = label_width(key_title, &ktitle_lines)) > max_ptitl_len)
max_ptitl_len = ytlen;
key_col_wth = (max_ptitl_len + 4) * (t->h_char) + key_sample_width;
/* luecken@udel.edu modifications
sizes the plot to take up more of available resolution */
if (lmargin >= 0)
xleft = (t->h_char) * lmargin;
else
xleft = (t->h_char) * 2 + (t->h_tic);
xright = (scaling ? 1 : xsize) * (t->xmax) - (t->h_char) * 2 - (t->h_tic);
key_rows = ptitl_cnt;
key_cols = 1;
if (key == -1 && key_vpos == TUNDER) {
if (ptitl_cnt > 0) {
/* calculate max no cols, limited by label-length */
key_cols = (int) (xright - 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 / key_cols) + ((ptitl_cnt % key_cols) > 0);
/* now calculate actual no cols depending on no rows */
key_cols = (int) (ptitl_cnt / key_rows) + ((ptitl_cnt % key_rows) > 0);
key_col_wth = (int) (xright - xleft) / key_cols;
/* key_rows += ktitle_lines; - messes up key - div */
} else {
key_rows = key_cols = key_col_wth = 0;
}
}
/* this should also consider the view and number of lines in
* xformat || yformat || xlabel || ylabel */
/* an absolute 1, with no terminal-dependent scaling ? */
ybot = (t->v_char) * 2.5 + 1;
if (key_rows && key == -1 && key_vpos == TUNDER) /* HBB 20000328 */
ybot += key_rows * key_entry_height + ktitle_lines * t->v_char;
if (strlen(title.text)) {
titlelin++;
for (i = 0; i < strlen(title.text); i++) {
if (title.text[i] == '\\')
titlelin++;
}
}
ytop = (scaling ? 1 : ysize) * (t->ymax) - (t->v_char) * (titlelin + 1.5) - 1;
if (key == -1 && key_vpos != TUNDER) {
/* calculate max no rows, limited be ytop-ybot */
i = (int) (ytop - ybot) / (t->v_char) - 1 - ktitle_lines;
if (ptitl_cnt > i) {
key_cols = (int) (ptitl_cnt / i) + ((ptitl_cnt % i) > 0);
/* now calculate actual no rows depending on no cols */
key_rows = (int) (ptitl_cnt / key_cols) + ((ptitl_cnt % key_cols) > 0);
}
key_rows += ktitle_lines;
}
if (key_hpos == TOUT) {
xright -= key_col_wth * (key_cols - 1) + key_col_wth - 2 * (t->h_char);
}
xleft += t->xmax * xoffset;
xright += t->xmax * xoffset;
ytop += t->ymax * yoffset;
ybot += t->ymax * yoffset;
xmiddle = (xright + xleft) / 2;
ymiddle = (ytop + ybot) / 2;
/* HBB 980308: sigh... another 16bit glitch: on term's with more than
* 8000 pixels in either direction, these calculations produce garbage
* results if done in (16bit) ints */
xscaler = ((xright - xleft) * 4L) / 7L; /* HBB: Magic number alert! */
yscaler = ((ytop - ybot) * 4L) / 7L;
}
#if 0
/* not used ; anyway, should be done using bitshifts and squares,
* rather than iteratively
*/
static double dbl_raise(x, y)
double x;
int y;
{
register int i = ABS(y);
double val = 1.0;
while (--i >= 0)
val *= x;
if (y < 0)
return (1.0 / val);
return (val);
}
#endif
/* 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(plots, pcount)
struct surface_points *plots;
int pcount; /* count of plots in linked list */
{
struct termentry *t = term;
int surface;
struct surface_points *this_plot = NULL;
unsigned int xl, yl;
int linetypeOffset = 0;
/* double ztemp, temp; unused */
struct text_label *this_label;
struct arrow_def *this_arrow;
TBOOLEAN scaling;
transform_matrix mat;
int key_count;
char *s, *e;
/* Initiate transformation matrix using the global view variables. */
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 extrema need to be set even when a surface is not being
* drawn. Without this, gnuplot used to assume that the X and
* Y axis started at zero. -RKC
*/
if (polar)
graph_error("Cannot splot in polar coordinate system.");
/* done in plot3d.c
* if (z_min3d == VERYLARGE || z_max3d == -VERYLARGE ||
* x_min3d == VERYLARGE || x_max3d == -VERYLARGE ||
* y_min3d == VERYLARGE || y_max3d == -VERYLARGE)
* graph_error("all points undefined!");
*/
/* If we are to draw the bottom grid make sure zmin is updated properly. */
if (xtics || ytics || work_grid.l_type) {
base_z = z_min3d - (z_max3d - z_min3d) * ticslevel;
if (ticslevel >= 0)
floor_z = base_z;
else
floor_z = z_min3d;
if (ticslevel < -1)
ceiling_z = base_z;
else
ceiling_z = z_max3d;
} else {
floor_z = base_z = z_min3d;
ceiling_z = z_max3d;
}
/* see comment accompanying similar tests of x_min/x_max and y_min/y_max
* in graphics.c:do_plot(), for history/rationale of these tests */
if (x_min3d == x_max3d)
graph_error("x_min3d should not equal x_max3d!");
if (y_min3d == y_max3d)
graph_error("y_min3d should not equal y_max3d!");
if (z_min3d == z_max3d)
graph_error("z_min3d should not equal z_max3d!");
#ifndef LITE
if (hidden3d) {
struct surface_points *plot;
int p = 0;
/* Verify data is hidden line removable - grid based. */
for (plot = plots; ++p <= pcount; plot = plot->next_sp) {
if (plot->plot_type == DATA3D && !plot->has_grid_topology) {
fprintf(stderr, "Notice: Cannot remove hidden lines from non grid data\n");
return;
}
}
}
#endif /* not LITE */
term_start_plot();
screen_ok = FALSE;
scaling = (*t->scale) (xsize, ysize);
/* now compute boundary for plot (xleft, xright, ytop, ybot) */
boundary3d(scaling, plots, pcount);
/* SCALE FACTORS */
zscale3d = 2.0 / (ceiling_z - floor_z) * surface_zscale;
yscale3d = 2.0 / (y_max3d - y_min3d);
xscale3d = 2.0 / (x_max3d - x_min3d);
term_apply_lp_properties(&border_lp); /* border linetype */
/* PLACE TITLE */
if (*title.text != 0) {
write_multiline((unsigned int) ((xleft + xright) / 2 + title.xoffset * t->h_char),
(unsigned int) (ytop + (titlelin + title.yoffset) * (t->h_char)),
title.text, CENTRE, JUST_TOP, 0, title.font);
}
/* PLACE TIMEDATE */
if (*timelabel.text) {
char str[MAX_LINE_LEN+1];
time_t now;
unsigned int x = t->v_char + timelabel.xoffset * t->h_char;
unsigned int y = timelabel_bottom ?
yoffset * ymax + (timelabel.yoffset + 1) * t->v_char :
ytop + (timelabel.yoffset - 1) * t->v_char;
time(&now);
strftime(str, MAX_LINE_LEN, timelabel.text, localtime(&now));
if (timelabel_rotate && (*t->text_angle) (1)) {
if (timelabel_bottom)
write_multiline(x, y, str, LEFT, JUST_TOP, 1, timelabel.font);
else
write_multiline(x, y, str, RIGHT, JUST_TOP, 1, timelabel.font);
(*t->text_angle) (0);
} else {
if (timelabel_bottom)
write_multiline(x, y, str, LEFT, JUST_BOT, 0, timelabel.font);
else
write_multiline(x, y, str, LEFT, JUST_TOP, 0, timelabel.font);
}
}
/* PLACE LABELS */
for (this_label = first_label; this_label != NULL;
this_label = this_label->next) {
unsigned int x, y;
map_position(&this_label->place, &x, &y, "label");
if (this_label->rotate && (*t->text_angle) (1)) {
write_multiline(x, y, this_label->text, this_label->pos, CENTRE, 1, this_label->font);
(*t->text_angle) (0);
} else {
write_multiline(x, y, this_label->text, this_label->pos, CENTRE, 0, this_label->font);
}
}
/* PLACE ARROWS */
for (this_arrow = first_arrow; this_arrow != NULL;
this_arrow = this_arrow->next) {
unsigned int sx, sy, ex, ey;
map_position(&this_arrow->start, &sx, &sy, "arrow");
map_position(&this_arrow->end, &ex, &ey, "arrow");
term_apply_lp_properties(&(this_arrow->lp_properties));
(*t->arrow) (sx, sy, ex, ey, this_arrow->head);
}
#ifndef LITE
if (hidden3d && draw_surface) {
init_hidden_line_removal();
reset_hidden_line_removal();
hidden_active = TRUE;
}
#endif /* not LITE */
/* WORK OUT KEY SETTINGS AND DO KEY TITLE / BOX */
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;
if (key == -1) {
if (key_vpos == TUNDER) {
#if 0
yl = yoffset * t->ymax + (key_rows) * key_entry_height + (ktitle_lines + 2) * t->v_char;
xl = max_ptitl_len * 1000 / (key_sample_width / (t->h_char) + max_ptitl_len + 2);
xl *= (xright - xleft) / key_cols;
xl /= 1000;
xl += xleft;
#else
/* HBB 19990608: why calculate these again? boundary3d has already
* done it... */
if (ptitl_cnt > 0) {
/* maximise no cols, limited by label-length */
key_cols = (int) (xright - xleft) / key_col_wth;
key_rows = (int) (ptitl_cnt + key_cols - 1) / key_cols;
/* now calculate actual no cols depending on no rows */
key_cols = (int) (ptitl_cnt + key_rows - 1) / key_rows;
key_col_wth = (int) (xright - xleft) / key_cols;
/* 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) * (xright-xleft)/key_cols
* do one integer division to maximise accuracy (hope we dont
* overflow !)
*/
xl = xleft + ((xright - xleft) * key_size_left) / (key_cols * (key_size_left + key_size_right));
yl = yoffset * t->ymax + (key_rows) * key_entry_height + (ktitle_lines + 2) * t->v_char;
}
#endif
} else {
if (key_vpos == TTOP) {
yl = ytop - (t->v_tic) - t->v_char;
} else {
yl = ybot + (t->v_tic) + key_entry_height * key_rows + ktitle_lines * t->v_char;
}
if (key_hpos == TOUT) {
/* keys outside plot border (right) */
xl = xright + (t->h_tic) + key_size_left;
} else if (key_hpos == TLEFT) {
xl = xleft + (t->h_tic) + key_size_left;
} else {
xl = xright - key_size_right - key_col_wth * (key_cols - 1);
}
}
yl_ref = yl - ktitle_lines * (t->v_char);
}
if (key == 1) {
map_position(&key_user_pos, &xl, &yl, "key");
}
if (key && key_box.l_type > -3) {
int yt = yl;
int yb = yl - key_entry_height * (key_rows - ktitle_lines) - ktitle_lines * t->v_char;
int key_xr = xl + key_col_wth * (key_cols - 1) + key_size_right;
/* key_rows seems to contain title at this point ??? */
term_apply_lp_properties(&key_box);
(*t->move) (xl - key_size_left, yb);
(*t->vector) (xl - key_size_left, yt);
(*t->vector) (key_xr, yt);
(*t->vector) (key_xr, yb);
(*t->vector) (xl - key_size_left, yb);
/* draw a horizontal line between key title and first entry JFi */
(*t->move) (xl - key_size_left, yt - (ktitle_lines) * t->v_char);
(*t->vector) (xl + key_size_right, yt - (ktitle_lines) * t->v_char);
}
/* DRAW SURFACES AND CONTOURS */
#ifndef LITE
if (hidden3d && draw_surface)
plot3d_hidden(plots, pcount);
#endif /* not LITE */
/* KEY TITLE */
if (key != 0 && strlen(key_title)) {
char *ss = gp_alloc(strlen(key_title) + 2, "tmp string ss");
strcpy(ss, key_title);
strcat(ss, "\n");
s = ss;
yl -= t->v_char / 2;
while ((e = (char *) strchr(s, '\n')) != NULL) {
*e = '\0';
if (key_just == JLEFT) {
(*t->justify_text) (LEFT);
(*t->put_text) (xl + key_text_left, yl, s);
} else {
if ((*t->justify_text) (RIGHT)) {
(*t->put_text) (xl + key_text_right,
yl, s);
} else {
int x = xl + key_text_right - (t->h_char) * strlen(s);
if (inrange(x, xleft, xright))
(*t->put_text) (x, yl, s);
}
}
s = ++e;
yl -= t->v_char;
}
yl += t->v_char / 2;
free(ss);
}
key_count = 0;
yl_ref = yl -= key_entry_height / 2; /* centralise the keys */
#define NEXT_KEY_LINE() \
if ( ++key_count >= key_rows ) { \
yl = yl_ref; xl += key_col_wth; key_count = 0; \
} else \
yl -= key_entry_height
this_plot = plots;
for (surface = 0;
surface < pcount;
this_plot = this_plot->next_sp, surface++) {
#ifndef LITE
if (hidden3d)
hidden_no_update = FALSE;
#endif /* not LITE */
if (draw_surface) {
int lkey = (key != 0 && this_plot->title && this_plot->title[0]);
term_apply_lp_properties(&(this_plot->lp_properties));
#ifndef LITE
if (hidden3d) {
hidden_line_type_above = this_plot->lp_properties.l_type;
hidden_line_type_below = this_plot->lp_properties.l_type + 1;
}
#endif /* not LITE */
if (lkey) {
key_text(xl, yl, this_plot->title);
}
switch (this_plot->plot_style) {
case BOXES: /* can't do boxes in 3d yet so use impulses */
case IMPULSES:{
if (lkey) {
key_sample_line(xl, yl);
}
if (!(hidden3d && draw_surface))
plot3d_impulses(this_plot);
break;
}
case STEPS: /* HBB: I think these should be here */
case FSTEPS:
case HISTEPS:
case LINES:{
if (lkey) {
key_sample_line(xl, yl);
}
if (!(hidden3d && draw_surface))
plot3d_lines(this_plot);
break;
}
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: dito */
case FINANCEBARS:
case VECTOR:
case POINTSTYLE:
if (lkey) {
key_sample_point(xl, yl, this_plot->lp_properties.p_type);
}
if (!(hidden3d && draw_surface))
plot3d_points(this_plot);
break;
case LINESPOINTS:
/* put lines */
if (lkey)
key_sample_line(xl, yl);
if (!(hidden3d && draw_surface))
plot3d_lines(this_plot);
/* put points */
if (lkey)
key_sample_point(xl, yl, this_plot->lp_properties.p_type);
if (!(hidden3d && draw_surface))
plot3d_points(this_plot);
break;
case DOTS:
if (lkey) {
if (key == 1) {
if (!clip_point(xl + key_point_offset, yl))
(*t->point) (xl + key_point_offset, yl, -1);
} else {
(*t->point) (xl + key_point_offset, yl, -1);
/* (*t->point)(xl+2*(t->h_char),yl, -1); */
}
}
if (!(hidden3d && draw_surface))
plot3d_dots(this_plot);
break;
} /* switch(plot-style) */
/* move key on a line */
if (lkey) {
NEXT_KEY_LINE();
}
} /* draw_surface */
#ifndef LITE
if (hidden3d) {
hidden_no_update = TRUE;
hidden_line_type_above = this_plot->lp_properties.l_type + (hidden3d ? 2 : 1);
hidden_line_type_below = this_plot->lp_properties.l_type + (hidden3d ? 2 : 1);
}
#endif /* not LITE */
if (draw_contour && this_plot->contours != NULL) {
struct gnuplot_contours *cntrs = this_plot->contours;
term_apply_lp_properties(&(this_plot->lp_properties));
(*t->linetype) (this_plot->lp_properties.l_type + (hidden3d ? 2 : 1));
if (key != 0 && this_plot->title && this_plot->title[0]
&& !draw_surface && !label_contours) {
/* unlabelled contours but no surface : put key entry in now */
key_text(xl, yl, this_plot->title);
switch (this_plot->plot_style) {
case IMPULSES:
case LINES:
case BOXES: /* HBB: I think these should be here... */
case STEPS:
case FSTEPS:
case HISTEPS:
key_sample_line(xl, yl);
break;
case YERRORBARS: /* ignored; treat like points */
case XERRORBARS: /* ignored; treat like points */
case XYERRORBARS: /* ignored; treat like points */
case BOXERROR: /* HBB: ignore these as well */
case BOXXYERROR:
case CANDLESTICKS: /* HBB: dito */
case FINANCEBARS:
case VECTOR:
case POINTSTYLE:
key_sample_point(xl, yl, this_plot->lp_properties.p_type);
break;
case LINESPOINTS:
key_sample_line(xl, yl);
break;
case DOTS:
key_sample_point(xl, yl, -1);
break;
}
NEXT_KEY_LINE();
}
linetypeOffset = this_plot->lp_properties.l_type + (hidden3d ? 2 : 1);
while (cntrs) {
if (label_contours && cntrs->isNewLevel) {
(*t->linetype) (linetypeOffset++);
if (key) {
#ifndef LITE
if (hidden3d)
hidden_line_type_below = hidden_line_type_above = linetypeOffset - 1;
#endif /* not LITE */
key_text(xl, yl, cntrs->label);
switch (this_plot->plot_style) {
case IMPULSES:
case LINES:
case LINESPOINTS:
case BOXES: /* HBB: these should be treated as well... */
case STEPS:
case FSTEPS:
case HISTEPS:
key_sample_line(xl, yl);
break;
case YERRORBARS: /* ignored; treat like points */
case XERRORBARS: /* ignored; treat like points */
case XYERRORBARS: /* ignored; treat like points */
case BOXERROR: /* HBB: treat these likewise */
case BOXXYERROR:
case CANDLESTICKS: /* HBB: dito */
case FINANCEBARS:
case VECTOR:
case POINTSTYLE:
key_sample_point(xl, yl, this_plot->lp_properties.p_type);
break;
case DOTS:
key_sample_point(xl, yl, -1);
break;
} /* switch */
NEXT_KEY_LINE();
} /* key */
} /* label_contours */
/* now draw the contour */
switch (this_plot->plot_style) {
case IMPULSES:
case BOXES: /* HBB: this should also be treated somehow */
cntr3d_impulses(cntrs, this_plot);
break;
case LINES:
case STEPS: /* HBB: these should also be handled, I think */
case FSTEPS:
case HISTEPS:
cntr3d_lines(cntrs);
break;
case YERRORBARS: /* ignored; treat like points */
case XERRORBARS: /* ignored; treat like points */
case XYERRORBARS: /* ignored; treat like points */
case BOXERROR: /* HBB: ignore these too... */
case BOXXYERROR:
case CANDLESTICKS: /* HBB: dito */
case FINANCEBARS:
case VECTOR:
case POINTSTYLE:
cntr3d_points(cntrs, this_plot);
break;
case LINESPOINTS:
cntr3d_lines(cntrs);
cntr3d_points(cntrs, this_plot);
break;
case DOTS:
cntr3d_dots(cntrs);
break;
} /*switch */
cntrs = cntrs->next;
} /* loop over contours */
} /* draw contours */
} /* loop over surfaces */
draw_bottom_grid(plots, pcount);
term_end_plot();
#ifndef LITE
if (hidden3d && draw_surface) {
term_hidden_line_removal();
hidden_active = FALSE;
}
#endif /* not LITE */
}
/* plot3d_impulses:
* Plot the surfaces in IMPULSES style
*/
static void plot3d_impulses(plot)
struct surface_points *plot;
{
int i; /* point index */
unsigned int x, y, x0, y0; /* point in terminal coordinates */
struct iso_curve *icrvs = plot->iso_crvs;
while (icrvs) {
struct coordinate GPHUGE *points = icrvs->points;
for (i = 0; i < icrvs->p_count; i++) {
switch (points[i].type) {
case INRANGE:
{
map3d_xy(points[i].x, points[i].y, points[i].z, &x, &y);
if (inrange(0.0, min3d_z, max3d_z)) {
map3d_xy(points[i].x, points[i].y, 0.0, &x0, &y0);
} else if (inrange(min3d_z, 0.0, points[i].z)) {
map3d_xy(points[i].x, points[i].y, min3d_z, &x0, &y0);
} else {
map3d_xy(points[i].x, points[i].y, max3d_z, &x0, &y0);
}
clip_move(x0, y0);
clip_vector(x, y);
break;
}
case OUTRANGE:
{
if (!inrange(points[i].x, x_min3d, x_max3d) ||
!inrange(points[i].y, y_min3d, y_max3d))
break;
if (inrange(0.0, min3d_z, max3d_z)) {
/* zero point is INRANGE */
map3d_xy(points[i].x, points[i].y, 0.0, &x0, &y0);
/* must cross z = min3d_z or max3d_z limits */
if (inrange(min3d_z, 0.0, points[i].z) &&
min3d_z != 0.0 && min3d_z != points[i].z) {
map3d_xy(points[i].x, points[i].y, min3d_z, &x, &y);
} else {
map3d_xy(points[i].x, points[i].y, max3d_z, &x, &y);
}
} else {
/* zero point is also OUTRANGE */
if (inrange(min3d_z, 0.0, points[i].z) &&
inrange(max3d_z, 0.0, points[i].z)) {
/* crosses z = min3d_z or max3d_z limits */
map3d_xy(points[i].x, points[i].y, max3d_z, &x, &y);
map3d_xy(points[i].x, points[i].y, min3d_z, &x0, &y0);
} else {
/* doesn't cross z = min3d_z or max3d_z limits */
break;
}
}
clip_move(x0, y0);
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(plot)
struct surface_points *plot;
{
int i;
unsigned int x, y, x0, y0; /* point in terminal coordinates */
double clip_x, clip_y, clip_z;
struct iso_curve *icrvs = plot->iso_crvs;
struct coordinate GPHUGE *points;
enum coord_type prev = UNDEFINED;
double lx[2], ly[2], lz[2]; /* two edge points */
#ifndef LITE
/* These are handled elsewhere. */
if (plot->has_grid_topology && hidden3d)
return;
#endif /* not LITE */
while (icrvs) {
prev = UNDEFINED; /* type of previous plot */
for (i = 0, points = icrvs->points; i < icrvs->p_count; i++) {
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, &clip_x, &clip_y, &clip_z);
map3d_xy(clip_x, clip_y, clip_z, &x0, &y0);
clip_move(x0, y0);
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, &clip_x, &clip_y, &clip_z);
map3d_xy(clip_x, clip_y, clip_z, &x0, &y0);
clip_vector(x0, y0);
}
} else if (prev == OUTRANGE) {
/* from outrange to outrange */
if (clip_lines2) {
/*
* Calculate the two 3D intersection points
* if present
*/
if (two_edge3d_intersect(points, i, lx, ly, lz)) {
map3d_xy(lx[0], ly[0], lz[0], &x, &y);
map3d_xy(lx[1], ly[1], lz[1], &x0, &y0);
clip_move(x, y);
clip_vector(x0, y0);
}
}
}
break;
}
case UNDEFINED:{
break;
default:
graph_error("Unknown point type in plot3d_lines");
}
}
prev = points[i].type;
}
icrvs = icrvs->next;
}
}
/* plot3d_points:
* Plot the surfaces in POINTSTYLE style
*/
static void plot3d_points(plot)
struct surface_points *plot;
{
int i;
unsigned int x, y;
struct termentry *t = term;
struct iso_curve *icrvs = plot->iso_crvs;
while (icrvs) {
struct coordinate GPHUGE *points = icrvs->points;
for (i = 0; i < icrvs->p_count; i++) {
if (points[i].type == INRANGE) {
map3d_xy(points[i].x, points[i].y, points[i].z, &x, &y);
if (!clip_point(x, y))
(*t->point) (x, y, plot->lp_properties.p_type);
}
}
icrvs = icrvs->next;
}
}
/* plot3d_dots:
* Plot the surfaces in DOTS style
*/
static void plot3d_dots(plot)
struct surface_points *plot;
{
int i;
struct termentry *t = term;
struct iso_curve *icrvs = plot->iso_crvs;
while (icrvs) {
struct coordinate GPHUGE *points = icrvs->points;
for (i = 0; i < icrvs->p_count; i++) {
if (points[i].type == INRANGE) {
unsigned int x, y;
map3d_xy(points[i].x, points[i].y, points[i].z, &x, &y);
if (!clip_point(x, y))
(*t->point) (x, y, -1);
}
}
icrvs = icrvs->next;
}
}
/* cntr3d_impulses:
* Plot a surface contour in IMPULSES style
*/
static void cntr3d_impulses(cntr, plot)
struct gnuplot_contours *cntr;
struct surface_points *plot;
{
int i; /* point index */
unsigned int x, y, x0, y0; /* point in terminal coordinates */
if (draw_contour & CONTOUR_SRF) {
for (i = 0; i < cntr->num_pts; i++) {
map3d_xy(cntr->coords[i].x, cntr->coords[i].y, cntr->coords[i].z,
&x, &y);
map3d_xy(cntr->coords[i].x, cntr->coords[i].y, base_z,
&x0, &y0);
clip_move(x0, y0);
clip_vector(x, y);
}
} else {
/* Must be on base grid, so do points. */
cntr3d_points(cntr, plot);
}
}
/* cntr3d_lines:
* Plot a surface contour in LINES style
*/
static void cntr3d_lines(cntr)
struct gnuplot_contours *cntr;
{
int i; /* point index */
unsigned int x, y; /* point in terminal coordinates */
if (draw_contour & CONTOUR_SRF) {
for (i = 0; i < cntr->num_pts; i++) {
map3d_xy(cntr->coords[i].x, cntr->coords[i].y, cntr->coords[i].z,
&x, &y);
if (i > 0) {
clip_vector(x, y);
if (i == 1)
suppressMove = TRUE;
} else {
clip_move(x, y);
}
}
}
/* beginning a new contour level, so moveto() required */
suppressMove = FALSE;
if (draw_contour & CONTOUR_BASE) {
for (i = 0; i < cntr->num_pts; i++) {
map3d_xy(cntr->coords[i].x, cntr->coords[i].y, base_z,
&x, &y);
if (i > 0) {
clip_vector(x, y);
if (i == 1)
suppressMove = TRUE;
} else {
clip_move(x, y);
}
}
}
/* beginning a new contour level, so moveto() required */
suppressMove = FALSE;
}
/* cntr3d_points:
* Plot a surface contour in POINTSTYLE style
*/
static void cntr3d_points(cntr, plot)
struct gnuplot_contours *cntr;
struct surface_points *plot;
{
int i;
unsigned int x, y;
struct termentry *t = term;
if (draw_contour & CONTOUR_SRF) {
for (i = 0; i < cntr->num_pts; i++) {
map3d_xy(cntr->coords[i].x, cntr->coords[i].y, cntr->coords[i].z, &x, &y);
if (!clip_point(x, y))
(*t->point) (x, y, plot->lp_properties.p_type);
}
}
if (draw_contour & CONTOUR_BASE) {
for (i = 0; i < cntr->num_pts; i++) {
map3d_xy(cntr->coords[i].x, cntr->coords[i].y, base_z,
&x, &y);
if (!clip_point(x, y))
(*t->point) (x, y, plot->lp_properties.p_type);
}
}
}
/* cntr3d_dots:
* Plot a surface contour in DOTS style
*/
static void cntr3d_dots(cntr)
struct gnuplot_contours *cntr;
{
int i;
unsigned int x, y;
struct termentry *t = term;
if (draw_contour & CONTOUR_SRF) {
for (i = 0; i < cntr->num_pts; i++) {
map3d_xy(cntr->coords[i].x, cntr->coords[i].y, cntr->coords[i].z, &x, &y);
if (!clip_point(x, y))
(*t->point) (x, y, -1);
}
}
if (draw_contour & CONTOUR_BASE) {
for (i = 0; i < cntr->num_pts; i++) {
map3d_xy(cntr->coords[i].x, cntr->coords[i].y, base_z,
&x, &y);
if (!clip_point(x, y))
(*t->point) (x, y, -1);
}
}
}
/* map xmin | xmax to 0 | 1 and same for y
* 0.1 avoids any rounding errors
*/
#define MAP_HEIGHT_X(x) ( (int) (((x)-x_min3d)/(x_max3d-x_min3d)+0.1) )
#define MAP_HEIGHT_Y(y) ( (int) (((y)-y_min3d)/(y_max3d-y_min3d)+0.1) )
/* 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(p, height, depth)
struct coordinate GPHUGE *p;
double height[2][2];
double depth[2][2];
{
if (p->type != INRANGE)
return;
if ((fabs(p->x - x_min3d) < zero || fabs(p->x - x_max3d) < zero) &&
(fabs(p->y - y_min3d) < zero || fabs(p->y - y_max3d) < 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;
}
}
/* Draw the bottom grid that hold the tic marks for 3d surface. */
static void draw_bottom_grid(plot, plot_num)
struct surface_points *plot;
int plot_num;
{
unsigned int x, y; /* point in terminal coordinates */
struct termentry *t = term;
char ss[MAX_LINE_LEN+1];
/* work out where the axes and tics are drawn */
{
int quadrant = surface_rot_z / 90;
if ((quadrant + 1) & 2) {
zaxis_x = x_max3d;
xaxis_y = y_max3d;
} else {
zaxis_x = x_min3d;
xaxis_y = y_min3d;
}
if (quadrant & 2) {
zaxis_y = y_max3d;
yaxis_x = x_min3d;
} else {
zaxis_y = y_min3d;
yaxis_x = x_max3d;
}
if (surface_rot_x > 90) {
/* labels on the back axes */
back_x = yaxis_x = x_min3d + x_max3d - yaxis_x;
back_y = xaxis_y = y_min3d + y_max3d - xaxis_y;
} else {
back_x = x_min3d + x_max3d - yaxis_x;
back_y = y_min3d + y_max3d - xaxis_y;
}
}
if (draw_border) {
unsigned int bl_x, bl_y; /* bottom left */
unsigned int bb_x, bb_y; /* bottom back */
unsigned int br_x, br_y; /* bottom right */
unsigned int bf_x, bf_y; /* bottom front */
#ifndef LITE
int save_update = hidden_no_update;
hidden_no_update = TRUE;
#endif /* LITE */
/* Here is the one and only call to this function. */
setlinestyle(border_lp);
map3d_xy(zaxis_x, zaxis_y, base_z, &bl_x, &bl_y);
map3d_xy(back_x, back_y, base_z, &bb_x, &bb_y);
map3d_xy(x_min3d + x_max3d - zaxis_x, y_min3d + y_max3d - zaxis_y, base_z, &br_x, &br_y);
map3d_xy(x_min3d + x_max3d - back_x, y_min3d + y_max3d - back_y, base_z, &bf_x, &bf_y);
/* border around base */
{
int save = hidden_active;
hidden_active = FALSE; /* this is in front */
if (draw_border & 4)
draw_clip_line(br_x, br_y, bf_x, bf_y);
if (draw_border & 1)
draw_clip_line(bl_x, bl_y, bf_x, bf_y);
hidden_active = save;
}
if (draw_border & 2)
draw_clip_line(bl_x, bl_y, bb_x, bb_y);
if (draw_border & 8)
draw_clip_line(br_x, br_y, bb_x, bb_y);
if (draw_surface || (draw_contour & CONTOUR_SRF)) {
int save = hidden_active;
/* map the 8 corners to screen */
unsigned int fl_x, fl_y; /* floor left */
unsigned int fb_x, fb_y; /* floor back */
unsigned int fr_x, fr_y; /* floor right */
unsigned int ff_x, ff_y; /* floor front */
unsigned int tl_x, tl_y; /* top left */
unsigned int tb_x, tb_y; /* top back */
unsigned int tr_x, tr_y; /* top right */
unsigned int tf_x, tf_y; /* top front */
map3d_xy(zaxis_x, zaxis_y, floor_z, &fl_x, &fl_y);
map3d_xy(back_x, back_y, floor_z, &fb_x, &fb_y);
map3d_xy(x_min3d + x_max3d - zaxis_x, y_min3d + y_max3d - zaxis_y, floor_z, &fr_x, &fr_y);
map3d_xy(x_min3d + x_max3d - back_x, y_min3d + y_max3d - back_y, floor_z, &ff_x, &ff_y);
map3d_xy(zaxis_x, zaxis_y, ceiling_z, &tl_x, &tl_y);
map3d_xy(back_x, back_y, ceiling_z, &tb_x, &tb_y);
map3d_xy(x_min3d + x_max3d - zaxis_x, y_min3d + y_max3d - zaxis_y, ceiling_z, &tr_x, &tr_y);
map3d_xy(x_min3d + x_max3d - back_x, y_min3d + y_max3d - back_y, ceiling_z, &tf_x, &tf_y);
/* vertical lines, to surface or to very top */
if ((draw_border & 0xf0) == 0xf0) {
/* all four verticals drawn - save some time */
draw_clip_line(fl_x, fl_y, tl_x, tl_y);
draw_clip_line(fb_x, fb_y, tb_x, tb_y);
draw_clip_line(fr_x, fr_y, tr_x, tr_y);
hidden_active = FALSE; /* this is in front */
draw_clip_line(ff_x, ff_y, tf_x, tf_y);
hidden_active = save;
} else {
/* search surfaces for heights at corners */
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);
/* HBB: why isn't back_j 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;
for (; --plot_num >= 0; plot = plot->next_sp) {
struct iso_curve *curve = plot->iso_crvs;
int count = curve->p_count;
int iso;
if (plot->plot_type == DATA3D) {
if (!plot->has_grid_topology)
continue;
iso = plot->num_iso_read;
} else
iso = iso_samples_2;
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,bx,by,tx,ty) \
if (draw_border&mask) \
draw_clip_line(bx,by,tx,ty);\
else if (height[i][j] != depth[i][j]) \
{ unsigned int a0,b0, a1, b1; \
map3d_xy(x,y,depth[i][j],&a0,&b0); \
map3d_xy(x,y,height[i][j],&a1,&b1); \
draw_clip_line(a0,b0,a1,b1); \
}
VERTICAL(16,zaxis_x,zaxis_y,zaxis_i,zaxis_j,fl_x,fl_y,tl_x,tl_y);
VERTICAL(32,back_x,back_y,back_i,back_j,fb_x,fb_y,tb_x,tb_y);
VERTICAL(64,x_min3d+x_max3d-zaxis_x,y_min3d+y_max3d-zaxis_y,1-zaxis_i,1-zaxis_j,fr_x,fr_y,tr_x,tr_y);
hidden_active = FALSE;
VERTICAL(128,x_min3d+x_max3d-back_x,y_min3d+y_max3d-back_y,1-back_i,1-back_j,ff_x,ff_y,tf_x,tf_y);
hidden_active = save;
}
/* now border lines on top */
if (draw_border & 256)
draw_clip_line(tl_x, tl_y, tb_x, tb_y);
if (draw_border & 512)
draw_clip_line(tr_x, tr_y, tb_x, tb_y);
/* these lines are in front of surface (?) */
hidden_active = FALSE;
if (draw_border & 1024)
draw_clip_line(tl_x, tl_y, tf_x, tf_y);
if (draw_border & 2048)
draw_clip_line(tr_x, tr_y, tf_x, tf_y);
hidden_active = save;
}
#ifndef LITE
hidden_no_update = save_update;
#endif /* LITE */
}
if (xtics || *xlabel.text) {
unsigned int x0, y0, x1, y1;
double mid_x = (x_max3d + x_min3d) / 2;
double len;
map3d_xy(mid_x, xaxis_y, base_z, &x0, &y0);
map3d_xy(mid_x, y_min3d + y_max3d - xaxis_y, base_z, &x1, &y1);
{ /* take care over unsigned quantities */
int dx = x1 - x0;
int dy = y1 - y0;
/* HBB 980309: 16bit strikes back: */
len = sqrt(((double) dx) * dx + ((double) dy) * dy);
if (len != 0) {
tic_unitx = dx / len;
tic_unity = dy / len;
} else {
tic_unitx = tic_unity = 0;
}
}
if (xtics) {
gen_tics(FIRST_X_AXIS, &xticdef,
work_grid.l_type & (GRID_X | GRID_MX),
mxtics, mxtfreq, xtick_callback);
}
if (*xlabel.text) {
/* label at xaxis_y + 1/4 of (xaxis_y-other_y) */
double step = (2 * xaxis_y - y_max3d - y_min3d) / 4;
map3d_xy(mid_x, xaxis_y + step, base_z, &x1, &y1);
x1 += xlabel.xoffset * t->h_char;
y1 += xlabel.yoffset * t->v_char;
if (!tic_in) {
x1 -= tic_unitx * ticscale * (t->h_tic);
y1 -= tic_unity * ticscale * (t->v_tic);
}
/* write_multiline mods it */
write_multiline(x1, y1, xlabel.text, CENTRE, JUST_TOP, 0, xlabel.font);
}
}
if (ytics || *ylabel.text) {
unsigned int x0, y0, x1, y1;
double mid_y = (y_max3d + y_min3d) / 2;
double len;
map3d_xy(yaxis_x, mid_y, base_z, &x0, &y0);
map3d_xy(x_min3d + x_max3d - yaxis_x, mid_y, base_z, &x1, &y1);
{ /* take care over unsigned quantities */
int dx = x1 - x0;
int dy = y1 - y0;
/* HBB 980309: 16 Bits strike again: */
len = sqrt(((double) dx) * dx + ((double) dy) * dy);
if (len != 0) {
tic_unitx = dx / len;
tic_unity = dy / len;
} else {
tic_unitx = tic_unity = 0;
}
}
if (ytics) {
gen_tics(FIRST_Y_AXIS, &yticdef,
work_grid.l_type & (GRID_Y | GRID_MY),
mytics, mytfreq, ytick_callback);
}
if (*ylabel.text) {
double step = (x_max3d + x_min3d - 2 * yaxis_x) / 4;
map3d_xy(yaxis_x - step, mid_y, base_z, &x1, &y1);
x1 += ylabel.xoffset * t->h_char;
y1 += ylabel.yoffset * t->v_char;
if (!tic_in) {
x1 -= tic_unitx * ticscale * (t->h_tic);
y1 -= tic_unity * ticscale * (t->v_tic);
}
/* write_multiline mods it */
safe_strncpy(ss, ylabel.text, sizeof(ss));
write_multiline(x1, y1, ss, CENTRE, JUST_TOP, 0, ylabel.font);
}
}
/* do z tics */
if (ztics && (draw_surface || (draw_contour & CONTOUR_SRF))) {
gen_tics(FIRST_Z_AXIS, &zticdef, work_grid.l_type & (GRID_Z | GRID_MZ),
mztics, mztfreq, ztick_callback);
}
if ((yzeroaxis.l_type >= -2) && !is_log_x && inrange(0, x_min3d, x_max3d)) {
unsigned int x, y, x1, y1;
term_apply_lp_properties(&yzeroaxis);
map3d_xy(0.0, y_min3d, base_z, &x, &y); /* line through x=0 */
map3d_xy(0.0, y_max3d, base_z, &x1, &y1);
draw_clip_line(x, y, x1, y1);
}
if ((xzeroaxis.l_type >= -2) && !is_log_y && inrange(0, y_min3d, y_max3d)) {
unsigned int x, y, x1, y1;
term_apply_lp_properties(&xzeroaxis);
map3d_xy(x_min3d, 0.0, base_z, &x, &y); /* line through y=0 */
map3d_xy(x_max3d, 0.0, base_z, &x1, &y1);
draw_clip_line(x, y, x1, y1);
}
/* PLACE ZLABEL - along the middle grid Z axis - eh ? */
if (*zlabel.text && (draw_surface || (draw_contour & CONTOUR_SRF))) {
map3d_xy(zaxis_x, zaxis_y, z_max3d + (z_max3d - base_z) / 4, &x, &y);
x += zlabel.xoffset * t->h_char;
y += zlabel.yoffset * t->v_char;
write_multiline(x, y, zlabel.text, CENTRE, CENTRE, 0, zlabel.font);
}
}
/* HBB 20010118: all the *_callback() functions made non-static. This
* is necessary to work around a bug in HP's assembler shipped with
* HP-UX 10 and higher, if GCC tries to use it */
void xtick_callback(axis, place, text, grid)
int axis;
double place;
char *text;
struct lp_style_type grid; /* linetype or -2 for none */
{
unsigned int x, y, x1, y1;
double scale = (text ? ticscale : miniticscale);
int dirn = tic_in ? 1 : -1;
register struct termentry *t = term;
map3d_xy(place, xaxis_y, base_z, &x, &y);
if (grid.l_type > -2) {
term_apply_lp_properties(&grid);
/* to save mapping twice, map non-axis y */
map3d_xy(place, y_min3d + y_max3d - xaxis_y, base_z, &x1, &y1);
draw_clip_line(x, y, x1, y1);
term_apply_lp_properties(&border_lp);
}
if (xtics & TICS_ON_AXIS) {
map3d_xy(place, (y_min3d + y_max3d) / 2, base_z, &x, &y);
}
x1 = x + tic_unitx * scale * (t->h_tic) * dirn;
y1 = y + tic_unity * scale * (t->v_tic) * dirn;
draw_clip_line(x, y, x1, y1);
if (text) {
int just;
if (tic_unitx < -0.9)
just = LEFT;
else if (tic_unitx < 0.9)
just = CENTRE;
else
just = RIGHT;
#if 1
/* HBB 970729: let's see if the 'tic labels collide with axes' problem
* may be fixed this way: */
x1 = x - tic_unitx * (t->h_char) * 1;
y1 = y - tic_unity * (t->v_char) * 1;
#else
x1 = x - tic_unitx * (t->h_tic) * 2;
y1 = y - tic_unity * (t->v_tic) * 2;
#endif
if (!tic_in) {
x1 -= tic_unitx * (t->h_tic) * ticscale;
y1 -= tic_unity * (t->v_tic) * ticscale;
}
clip_put_text_just(x1, y1, text, just);
}
if (xtics & TICS_MIRROR) {
map3d_xy(place, y_min3d + y_max3d - xaxis_y, base_z, &x, &y);
x1 = x - tic_unitx * scale * (t->h_tic) * dirn;
y1 = y - tic_unity * scale * (t->v_tic) * dirn;
draw_clip_line(x, y, x1, y1);
}
}
void ytick_callback(axis, place, text, grid)
int axis;
double place;
char *text;
struct lp_style_type grid;
{
unsigned int x, y, x1, y1;
double scale = (text ? ticscale : miniticscale);
int dirn = tic_in ? 1 : -1;
register struct termentry *t = term;
map3d_xy(yaxis_x, place, base_z, &x, &y);
if (grid.l_type > -2) {
term_apply_lp_properties(&grid);
map3d_xy(x_min3d + x_max3d - yaxis_x, place, base_z, &x1, &y1);
draw_clip_line(x, y, x1, y1);
term_apply_lp_properties(&border_lp);
}
if (ytics & TICS_ON_AXIS) {
map3d_xy((x_min3d + x_max3d) / 2, place, base_z, &x, &y);
}
x1 = x + tic_unitx * scale * dirn * (t->h_tic);
y1 = y + tic_unity * scale * dirn * (t->v_tic);
draw_clip_line(x, y, x1, y1);
if (text) {
int just;
if (tic_unitx < -0.9)
just = LEFT;
else if (tic_unitx < 0.9)
just = CENTRE;
else
just = RIGHT;
#if 1
/* HBB 970729: same as above in xtics_callback */
x1 = x - tic_unitx * (t->h_char) * 1;
y1 = y - tic_unity * (t->v_char) * 1;
#else
x1 = x - tic_unitx * (t->h_tic) * 2;
y1 = y - tic_unity * (t->v_tic) * 2;
#endif
if (!tic_in) {
x1 -= tic_unitx * (t->h_tic) * ticscale;
y1 -= tic_unity * (t->v_tic) * ticscale;
}
clip_put_text_just(x1, y1, text, just);
}
if (ytics & TICS_MIRROR) {
map3d_xy(x_min3d + x_max3d - yaxis_x, place, base_z, &x, &y);
x1 = x - tic_unitx * scale * (t->h_tic) * dirn;
y1 = y - tic_unity * scale * (t->v_tic) * dirn;
draw_clip_line(x, y, x1, y1);
}
}
void ztick_callback(axis, place, text, grid)
int axis;
double place;
char *text;
struct lp_style_type grid;
{
/* HBB: inserted some ()'s to shut up gcc -Wall, here and below */
int len = (text ? ticscale : miniticscale) * (tic_in ? 1 : -1) * (term->h_tic);
unsigned int x, y;
if (grid.l_type > -2) {
unsigned int x1, y1, x2, y2, x3, y3;
double other_x = x_min3d + x_max3d - zaxis_x;
double other_y = y_min3d + y_max3d - zaxis_y;
term_apply_lp_properties(&grid);
map3d_xy(zaxis_x, zaxis_y, place, &x1, &y1);
map3d_xy(back_x, back_y, place, &x2, &y2);
map3d_xy(other_x, other_y, place, &x3, &y3);
draw_clip_line(x1, y1, x2, y2);
draw_clip_line(x2, y2, x3, y3);
term_apply_lp_properties(&border_lp);
}
map3d_xy(zaxis_x, zaxis_y, place, &x, &y);
draw_clip_line(x, y, x + len, y);
if (text) {
int x1 = x - (term->h_tic) * 2;
if (!tic_in)
x1 -= (term->h_tic) * ticscale;
clip_put_text_just(x1, y, text, RIGHT);
}
if (ztics & TICS_MIRROR) {
double other_x = x_min3d + x_max3d - zaxis_x;
double other_y = y_min3d + y_max3d - zaxis_y;
map3d_xy(other_x, other_y, place, &x, &y);
draw_clip_line(x, y, x - len, y);
}
}
static void map_position(pos, x, y, what)
struct position *pos;
unsigned int *x, *y;
char *what;
{
double xpos = pos->x;
double ypos = pos->y;
double zpos = pos->z;
int screens = 0; /* need either 0 or 3 screen co-ordinates */
switch (pos->scalex) {
case first_axes:
case second_axes:
xpos = LogScale(xpos, is_log_x, log_base_log_x, what, "x");
break;
case graph:
xpos = min_array[FIRST_X_AXIS] +
xpos * (max_array[FIRST_X_AXIS] - min_array[FIRST_X_AXIS]);
break;
case screen:
++screens;
}
switch (pos->scaley) {
case first_axes:
case second_axes:
ypos = LogScale(ypos, is_log_y, log_base_log_y, what, "y");
break;
case graph:
ypos = min_array[FIRST_Y_AXIS] +
ypos * (max_array[FIRST_Y_AXIS] - min_array[FIRST_Y_AXIS]);
break;
case screen:
++screens;
}
switch (pos->scalez) {
case first_axes:
case second_axes:
zpos = LogScale(zpos, is_log_z, log_base_log_z, what, "z");
break;
case graph:
zpos = min_array[FIRST_Z_AXIS] +
zpos * (max_array[FIRST_Z_AXIS] - min_array[FIRST_Z_AXIS]);
break;
case screen:
++screens;
}
if (screens == 0) {
map3d_xy(xpos, ypos, zpos, x, y);
return;
}
if (screens != 3) {
graph_error("Cannot mix screen co-ordinates with other types");
} {
register struct termentry *t = term;
/* HBB 20000914: off-by-one bug. Maximum allowed output is
* t->?max - 1, not t->?max itself! */
*x = pos->x * (t->xmax -1) + 0.5;
*y = pos->y * (t->ymax -1) + 0.5;
}
return;
}
/*
* these code blocks were moved to functions, to make the code simpler
*/
static void key_text(xl, yl, text)
int xl, yl;
char *text;
{
if (key_just == JLEFT && key == -1) {
(*term->justify_text) (LEFT);
(*term->put_text) (xl + key_text_left, yl, text);
} else {
if ((*term->justify_text) (RIGHT)) {
if (key == 1)
clip_put_text(xl + key_text_right, yl, text);
else
(*term->put_text) (xl + key_text_right, yl, text);
} else {
int x = xl + key_text_right - (term->h_char) * strlen(text);
if (key == 1) {
if (i_inrange(x, xleft, xright))
clip_put_text(x, yl, text);
} else {
(*term->put_text) (x, yl, text);
}
}
}
}
static void key_sample_line(xl, yl)
int xl, yl;
{
if (key == -1) {
(*term->move) (xl + key_sample_left, yl);
(*term->vector) (xl + key_sample_right, yl);
} else {
/* HBB 981118: avoid crash if hidden3d sees a manually placed key:
* simply turn off hidden-lining while we're drawing the key line: */
int save_hidden_active = hidden_active;
hidden_active = FALSE;
clip_move(xl + key_sample_left, yl);
clip_vector(xl + key_sample_right, yl);
hidden_active = save_hidden_active;
}
}
static void key_sample_point(xl, yl, pointtype)
int xl, yl;
int pointtype;
{
/* HBB 20000412: fixed incorrect clipping: the point sample was
* clipped against the graph box, even if in 'below' or 'outside'
* position. But the result of that clipping was utterly ignored,
* because the 'else' part did exactly the same thing as the
* 'then' one. Some callers of this routine thus did their own
* clipping, which I removed, along with this change.
*
* Now, all 'automatically' placed cases will never be clipped,
* only user-specified ones. */
if ((key == -1) /* ==-1 means auto-placed key */
|| !clip_point(xl + key_point_offset, yl)) {
(*term->point) (xl + key_point_offset, yl, pointtype);
}
}