--- /dev/null
+#ifndef lint
+static char *RCSid() { return RCSid("gadgets.c,v 1.1.3.1 2000/05/03 21:47:15 hbb Exp"); }
+#endif
+
+/* GNUPLOT - gadgets.c */
+
+/*[
+ * Copyright 2000, 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.
+]*/
+
+#include "gadgets.h"
+#include "command.h"
+#include "graph3d.h" /* for map3d_position_r() */
+#include "graphics.h"
+#include "plot3d.h" /* For is_plot_with_palette() */
+
+#include "pm3d.h"
+
+/* This file contains mainly a collection of global variables that
+ * used to be in 'set.c', where they didn't really belong. They
+ * describe the status of several parts of the gnuplot plotting engine
+ * that are used by both 2D and 3D plots, and thus belong neither to
+ * graphics.c nor graph3d.c, alone. This is not a very clean solution,
+ * but better than mixing internal status and the user interface as we
+ * used to have it, in set.c and setshow.h */
+
+legend_key keyT = DEFAULT_KEY_PROPS;
+
+/* Description of the color box associated with CB_AXIS */
+color_box_struct color_box; /* initialized in init_color() */
+color_box_struct default_color_box = {SMCOLOR_BOX_DEFAULT, 'v', 1, LT_BLACK, LAYER_FRONT,
+ {screen, screen, screen, 0.90, 0.2, 0.0},
+ {screen, screen, screen, 0.05, 0.6, 0.0}};
+
+/* The graph box, in terminal coordinates, as calculated by boundary()
+ * or boundary3d(): */
+BoundingBox plot_bounds;
+
+/* The bounding box for the entire drawable area of current terminal */
+BoundingBox canvas;
+
+/* The bounding box against which clipping is to be done */
+BoundingBox *clip_area = &plot_bounds;
+
+/* 'set size', 'set origin' setttings */
+float xsize = 1.0; /* scale factor for size */
+float ysize = 1.0; /* scale factor for size */
+float zsize = 1.0; /* scale factor for size */
+float xoffset = 0.0; /* x origin */
+float yoffset = 0.0; /* y origin */
+float aspect_ratio = 0.0; /* don't attempt to force it */
+int aspect_ratio_3D = 0; /* 2 will put x and y on same scale, 3 for z also */
+
+/* EAM Augest 2006 -
+ redefine margin as t_position so that absolute placement is possible */
+/* space between left edge and plot_bounds.xleft in chars (-1: computed) */
+t_position lmargin = DEFAULT_MARGIN_POSITION;
+/* space between bottom and plot_bounds.ybot in chars (-1: computed) */
+t_position bmargin = DEFAULT_MARGIN_POSITION;
+/* space between right egde and plot_bounds.xright in chars (-1: computed) */
+t_position rmargin = DEFAULT_MARGIN_POSITION;
+/* space between top egde and plot_bounds.ytop in chars (-1: computed) */
+t_position tmargin = DEFAULT_MARGIN_POSITION;
+
+/* File descriptor for output during 'set table' mode */
+FILE *table_outfile = NULL;
+TBOOLEAN table_mode = FALSE;
+
+/* Pointer to the start of the linked list of 'set label' definitions */
+struct text_label *first_label = NULL;
+
+/* Pointer to first 'set linestyle' definition in linked list */
+struct linestyle_def *first_linestyle = NULL;
+
+/* Pointer to first 'set style arrow' definition in linked list */
+struct arrowstyle_def *first_arrowstyle = NULL;
+
+/* set arrow */
+struct arrow_def *first_arrow = NULL;
+
+#ifdef EAM_OBJECTS
+/* Pointer to first object instance in linked list */
+struct object *first_object = NULL;
+#endif
+
+/* 'set title' status */
+text_label title = EMPTY_LABELSTRUCT;
+
+/* 'set timelabel' status */
+text_label timelabel = EMPTY_LABELSTRUCT;
+int timelabel_rotate = FALSE;
+int timelabel_bottom = TRUE;
+
+/* flag for polar mode */
+TBOOLEAN polar = FALSE;
+
+/* zero threshold, may _not_ be 0! */
+double zero = ZERO;
+
+/* Status of 'set pointsize' command */
+double pointsize = 1.0;
+
+/* set border */
+int draw_border = 31;
+int border_layer = 1;
+# define DEFAULT_BORDER_LP { 0, -2, 0, 1.0, 1.0, 0 }
+struct lp_style_type border_lp = DEFAULT_BORDER_LP;
+const struct lp_style_type default_border_lp = DEFAULT_BORDER_LP;
+
+/* set clip */
+TBOOLEAN clip_lines1 = TRUE;
+TBOOLEAN clip_lines2 = FALSE;
+/* FIXME HBB 20000521: clip_points is only used by 2D plots. This may
+ * well constitute a yet unknown bug... */
+TBOOLEAN clip_points = FALSE;
+
+/* set samples */
+int samples_1 = SAMPLES;
+int samples_2 = SAMPLES;
+
+/* set angles */
+double ang2rad = 1.0; /* 1 or pi/180, tracking angles_format */
+
+enum PLOT_STYLE data_style = POINTSTYLE;
+enum PLOT_STYLE func_style = LINES;
+
+TBOOLEAN parametric = FALSE;
+
+/* If last plot was a 3d one. */
+TBOOLEAN is_3d_plot = FALSE;
+
+#ifdef WITH_IMAGE
+/* If last plot was one using color bus, e.g., image, map, pm3d. */
+TBOOLEAN is_cb_plot = FALSE;
+#endif
+
+fill_style_type default_fillstyle = { FS_EMPTY, 100, 0, LT_UNDEFINED } ;
+
+#ifdef EAM_OBJECTS
+/* Default rectangle style - background fill, black border */
+struct object default_rectangle = DEFAULT_RECTANGLE_STYLE;
+#endif
+
+/* filledcurves style options */
+filledcurves_opts filledcurves_opts_data = EMPTY_FILLEDCURVES_OPTS;
+filledcurves_opts filledcurves_opts_func = EMPTY_FILLEDCURVES_OPTS;
+
+/* Prefer line styles over plain line types */
+TBOOLEAN prefer_line_styles = FALSE;
+
+#ifdef EAM_HISTOGRAMS
+histogram_style histogram_opts = DEFAULT_HISTOGRAM_STYLE;
+#endif
+
+/*****************************************************************/
+/* Routines that deal with global objects defined in this module */
+/*****************************************************************/
+
+/* Clipping to the bounding box: */
+
+/* Test a single point to be within the BoundingBox.
+ * Sets the returned integers 4 l.s.b. as follows:
+ * bit 0 if to the left of xleft.
+ * bit 1 if to the right of xright.
+ * bit 2 if below of ybot.
+ * bit 3 if above of ytop.
+ * 0 is returned if inside.
+ */
+int
+clip_point(unsigned int x, unsigned int y)
+{
+ int ret_val = 0;
+
+ if (!clip_area)
+ return 0;
+ if ((int)x < clip_area->xleft)
+ ret_val |= 0x01;
+ if ((int)x > clip_area->xright)
+ ret_val |= 0x02;
+ if ((int)y < clip_area->ybot)
+ ret_val |= 0x04;
+ if ((int)y > clip_area->ytop)
+ ret_val |= 0x08;
+
+ return ret_val;
+}
+
+/* Clip the given line to drawing coords defined by BoundingBox.
+ * This routine uses the cohen & sutherland bit mapping for fast clipping -
+ * see "Principles of Interactive Computer Graphics" Newman & Sproull page 65.
+ */
+void
+draw_clip_line(int x1, int y1, int x2, int y2)
+{
+ struct termentry *t = term;
+
+#if defined(ATARI) || defined(MTOS)
+ /* HBB 20000522: why would this test be particular to ATARIs? And
+ * what was the bug this is supposed to fix? */
+ if (x1 < 0 || x2 < 0 || y1 < 0 || y2 < 0)
+ return; /* temp bug fix */
+#endif
+
+ /* HBB 20000522: I've made this routine use the clippling in
+ * clip_line(), in a movement to reduce code duplication. There
+ * was one very small difference between these two routines. See
+ * clip_line() for a comment about it, at the relevant place. */
+ if (!clip_line(&x1, &y1, &x2, &y2))
+ /* clip_line() returns zero --> segment completely outside
+ * bounding box */
+ return;
+
+ /* HBB 20000617: there was a design error, here. By nature, this
+ * is a 2D routine. It should never have to check for hidden3d
+ * related variables, or call the hidden3d routine
+ * draw_line_hidden, like this. I've thrown this out. */
+
+ /* FIXME HBB 20000522: I strongly doubt this can work as
+ * advertised. It's supposed to allow for continuous contour
+ * curves, but as soon as the contour curve moves outside the
+ * boundary, you get overpainting instead, as the contour curve
+ * walks along the border. Or worse artefacts. */
+#if 0 /* UNUSED */
+ if (!suppressMove)
+#endif
+ (*t->move) (x1, y1);
+ (*t->vector) (x2, y2);
+}
+
+void
+draw_clip_arrow( int sx, int sy, int ex, int ey, int head)
+{
+ struct termentry *t = term;
+
+ /* Don't draw head if the arrow itself is clipped */
+ if (clip_point(sx,sy))
+ head &= ~BACKHEAD;
+ if (clip_point(ex,ey))
+ head &= ~END_HEAD;
+ clip_line(&sx, &sy, &ex, &ey);
+
+ /* Call terminal routine to draw the clipped arrow */
+ (*t->arrow)((unsigned int)sx, (unsigned int)sy,
+ (unsigned int)ex, (unsigned int)ey, head);
+}
+
+
+/* And text clipping routine. */
+void
+clip_put_text(unsigned int x, unsigned y, char *str)
+{
+ struct termentry *t = term;
+
+ if (clip_point(x, y))
+ return;
+
+ (*t->put_text) (x, y, str);
+}
+
+
+/* Clip the given line to drawing coords defined by BoundingBox.
+ * This routine uses the cohen & sutherland bit mapping for fast clipping -
+ * see "Principles of Interactive Computer Graphics" Newman & Sproull page 65.
+ */
+/* FIXME HBB 20000521: the parameters of this routine should become
+ * unsigned ints. */
+int
+clip_line(int *x1, int *y1, int *x2, int *y2)
+{
+ int x, y, dx, dy, x_intr[4], y_intr[4], count, pos1, pos2;
+ int x_max, x_min, y_max, y_min;
+ pos1 = clip_point(*x1, *y1);
+ pos2 = clip_point(*x2, *y2);
+ if (!pos1 && !pos2)
+ return 1; /* segment is totally in */
+ if (pos1 & pos2)
+ return 0; /* segment is totally out. */
+ /* Here part of the segment MAY be inside. test the intersection
+ * of this segment with the 4 boundaries for hopefully 2 intersections
+ * in. If none are found segment is totaly out.
+ * Under rare circumstances there may be up to 4 intersections (e.g.
+ * when the line passes directly through at least one corner). In
+ * this case it is sufficient to take any 2 intersections (e.g. the
+ * first two found).
+ */
+ count = 0;
+ dx = *x2 - *x1;
+ dy = *y2 - *y1;
+ /* Find intersections with the x parallel bbox lines: */
+ if (dy != 0) {
+ x = (clip_area->ybot - *y2) * dx / dy + *x2; /* Test for clip_area->ybot boundary. */
+ if (x >= clip_area->xleft && x <= clip_area->xright) {
+ x_intr[count] = x;
+ y_intr[count++] = clip_area->ybot;
+ }
+ x = (clip_area->ytop - *y2) * dx / dy + *x2; /* Test for clip_area->ytop boundary. */
+ if (x >= clip_area->xleft && x <= clip_area->xright) {
+ x_intr[count] = x;
+ y_intr[count++] = clip_area->ytop;
+ }
+ }
+ /* Find intersections with the y parallel bbox lines: */
+ if (dx != 0) {
+ y = (clip_area->xleft - *x2) * dy / dx + *y2; /* Test for clip_area->xleft boundary. */
+ if (y >= clip_area->ybot && y <= clip_area->ytop) {
+ x_intr[count] = clip_area->xleft;
+ y_intr[count++] = y;
+ }
+ y = (clip_area->xright - *x2) * dy / dx + *y2; /* Test for clip_area->xright boundary. */
+ if (y >= clip_area->ybot && y <= clip_area->ytop) {
+ x_intr[count] = clip_area->xright;
+ y_intr[count++] = y;
+ }
+ }
+ if (count < 2)
+ return 0;
+
+ if (*x1 < *x2) {
+ x_min = *x1;
+ x_max = *x2;
+ } else {
+ x_min = *x2;
+ x_max = *x1;
+ }
+ if (*y1 < *y2) {
+ y_min = *y1;
+ y_max = *y2;
+ } else {
+ y_min = *y2;
+ y_max = *y1;
+ }
+
+ if (pos1 && pos2) { /* Both were out - update both */
+ *x1 = x_intr[0];
+ *y1 = y_intr[0];
+ *x2 = x_intr[1];
+ *y2 = y_intr[1];
+ } else if (pos1) { /* Only x1/y1 was out - update only it */
+ /* This is about the only real difference between this and
+ * draw_clip_line(): it compares for '>0', here */
+ if (dx * (*x2 - x_intr[0]) + dy * (*y2 - y_intr[0]) >= 0) {
+ *x1 = x_intr[0];
+ *y1 = y_intr[0];
+ } else {
+ *x1 = x_intr[1];
+ *y1 = y_intr[1];
+ }
+ } else { /* Only x2/y2 was out - update only it */
+ /* Same difference here, again */
+ if (dx * (x_intr[0] - *x1) + dy * (y_intr[0] - *y1) >= 0) {
+ *x2 = x_intr[0];
+ *y2 = y_intr[0];
+ } else {
+ *x2 = x_intr[1];
+ *y2 = y_intr[1];
+ }
+ }
+
+ if (*x1 < x_min || *x1 > x_max || *x2 < x_min || *x2 > x_max || *y1 < y_min || *y1 > y_max || *y2 < y_min || *y2 > y_max)
+ return 0;
+
+ return 1;
+}
+
+
+/* Two routines to emulate move/vector sequence using line drawing routine. */
+static unsigned int move_pos_x, move_pos_y;
+
+void
+clip_move(unsigned int x, unsigned int y)
+{
+ move_pos_x = x;
+ move_pos_y = y;
+}
+
+void
+clip_vector(unsigned int x, unsigned int y)
+{
+ draw_clip_line(move_pos_x, move_pos_y, x, y);
+ move_pos_x = x;
+ move_pos_y = y;
+}
+
+/* Common routines for setting text or line color from t_colorspec */
+
+void
+apply_pm3dcolor(struct t_colorspec *tc, const struct termentry *t)
+{
+
+ /* Replace colorspec with that of the requested line style */
+ struct lp_style_type style;
+ if (tc->type == TC_LINESTYLE) {
+ lp_use_properties(&style, tc->lt, 0);
+ (*t->linetype)(style.l_type);
+ tc = &style.pm3d_color;
+ }
+
+ if (tc->type == TC_DEFAULT) {
+ (*t->linetype)(LT_BLACK);
+ return;
+ }
+ if (tc->type == TC_LT) {
+ if (t->set_color)
+ t->set_color(tc);
+ else
+ (*t->linetype)(tc->lt);
+ return;
+ }
+ if (tc->type == TC_RGB && t->set_color) {
+ t->set_color(tc);
+ return;
+ }
+ if (!is_plot_with_palette() || !t->set_color) {
+ (*t->linetype)(LT_BLACK);
+ return;
+ }
+ switch (tc->type) {
+ case TC_Z: set_color(cb2gray(z2cb(tc->value))); break;
+ case TC_CB: set_color(cb2gray(tc->value)); break;
+ case TC_FRAC: set_color(sm_palette.positive == SMPAL_POSITIVE ?
+ tc->value : 1-tc->value);
+ break;
+ }
+ if (tc->type == TC_LT) {
+ (*t->linetype)(tc->lt);
+ return;
+ }
+}
+
+void
+reset_textcolor(const struct t_colorspec *tc, const struct termentry *t)
+{
+ if (tc->type != TC_DEFAULT)
+ (*t->linetype)(LT_BLACK);
+}
+
+
+void
+default_arrow_style(struct arrow_style_type *arrow)
+{
+ static const struct lp_style_type tmp_lp_style = DEFAULT_LP_STYLE_TYPE;
+
+ arrow->layer = 0;
+ arrow->lp_properties = tmp_lp_style;
+ arrow->head = 1;
+ arrow->head_length = 0.0;
+ arrow->head_lengthunit = first_axes;
+ arrow->head_angle = 15.0;
+ arrow->head_backangle = 90.0;
+ arrow->head_filled = 0;
+}
+
+#ifdef EAM_DATASTRINGS
+void
+free_labels(struct text_label *label)
+{
+struct text_label *temp;
+char *master_font = label->font;
+
+ /* Labels generated by 'plot with labels' all use the same font */
+ if (master_font)
+ free(master_font);
+
+ while (label) {
+ if (label->text)
+ free(label->text);
+ if (label->font && label->font != master_font)
+ free(label->font);
+ temp=label->next;
+ free(label);
+ label = temp;
+ }
+
+}
+#endif
+
+void
+get_offsets(
+ struct text_label *this_label,
+ struct termentry *t,
+ int *htic, int *vtic)
+{
+ if (this_label->lp_properties.pointflag) {
+ *htic = (pointsize * t->h_tic * 0.5);
+ *vtic = (pointsize * t->v_tic * 0.5);
+ } else {
+ *htic = 0;
+ *vtic = 0;
+ }
+ if (is_3d_plot) {
+ int htic2, vtic2;
+ map3d_position_r(&(this_label->offset), &htic2, &vtic2, "get_offsets");
+ *htic += htic2;
+ *vtic += vtic2;
+ } else {
+ double htic2, vtic2;
+ map_position_r(&(this_label->offset), &htic2, &vtic2, "get_offsets");
+ *htic += (int)htic2;
+ *vtic += (int)vtic2;
+ }
+}
+
+
+/*
+ * Write one label, with all the trimmings.
+ * This routine is used for both 2D and 3D plots.
+ */
+void
+write_label(unsigned int x, unsigned int y, struct text_label *this_label)
+{
+ int htic, vtic;
+ int justify = JUST_TOP; /* This was the 2D default; 3D had CENTRE */
+
+ apply_pm3dcolor(&(this_label->textcolor),term);
+ ignore_enhanced(this_label->noenhanced);
+
+ get_offsets(this_label, term, &htic, &vtic);
+ if (this_label->rotate && (*term->text_angle) (this_label->rotate)) {
+ write_multiline(x + htic, y + vtic, this_label->text,
+ this_label->pos, justify, this_label->rotate,
+ this_label->font);
+ (*term->text_angle) (0);
+ } else {
+ write_multiline(x + htic, y + vtic, this_label->text,
+ this_label->pos, justify, 0, this_label->font);
+ }
+ /* write_multiline() clips text to on_page; do the same for any point */
+ if (this_label->lp_properties.pointflag && on_page(x,y)) {
+ term_apply_lp_properties(&this_label->lp_properties);
+ (*term->point) (x, y, this_label->lp_properties.p_type);
+ /* the default label color is that of border */
+ term_apply_lp_properties(&border_lp);
+ }
+
+ ignore_enhanced(FALSE);
+}