gsl-shell.git - gsl-shell

index : gsl-shell.git
gsl-shell
summary refs log tree commit diff
path: root/plot
diff options
context:
space:
mode:
Diffstat (limited to 'plot')
-rw-r--r--plot/graph.c 151
-rw-r--r--plot/misc.c 369
-rw-r--r--plot/plot.c 27
-rw-r--r--plot/plotter.c 2651
4 files changed, 3198 insertions, 0 deletions
diff --git a/plot/graph.c b/plot/graph.c
new file mode 100644
index 00000000..ddfc7f2b
--- /dev/null
+++ b/plot/graph.c
@@ -0,0 +1,151 @@
+
+
+struct MultigrapherStruct
+{
+ /* multigrapher parameters (not updated over a multigrapher's lifetime) */
+ plPlotter *plotter; /* GNU libplot Plotter handle */
+ const char *output_format; /* type of libplot device driver [unused] */
+ const char *bg_color; /* color of background, if non-NULL */
+ bool save_screen; /* erase display when opening plotter? */
+ /* graph parameters (constant over any single graph) */
+ Transform x_trans, y_trans; /* user->device coor transformations */
+ Axis x_axis, y_axis; /* information on each axis */
+ grid_type grid_spec; /* frame specification */
+ double blankout_fraction; /* 1.0 means blank whole box before drawing */
+ bool no_rotate_y_label; /* useful for pre-X11R6 X servers */
+ double tick_size; /* fractional tick size */
+ double subtick_size; /* fractional subtick size (for linear axes) */
+ double frame_line_width; /* fractional width of lines in the frame */
+ double half_line_width; /* approx. half of this, in libplot coors */
+ const char *frame_color; /* color for frame (and graph, if no -C) */
+ const char *title; /* graph title */
+ const char *title_font_name; /* font for graph title */
+ double title_font_size; /* fractional height of graph title */
+ int clip_mode; /* 0, 1, or 2 (cf. clipping in gnuplot) */
+ /* following elements are updated during plotting of points; they're the
+ chief repository for internal state */
+ bool first_point_of_polyline; /* true only at beginning of each polyline */
+ double oldpoint_x, oldpoint_y; /* last-plotted point */
+ int symbol; /* symbol being plotted at each point */
+ int linemode; /* linemode used for polyline */
+};
+
+typedef struct MultigrapherStruct Multigrapher;
+
+int
+graph_show (lua_State *L)
+{
+ /* command-line parameters (constant over multigrapher operation) */
+ const char *output_format = "meta";/* libplot output format */
+ const char *bg_color = NULL; /* color of background, if non-NULL */
+ const char *bitmap_size = NULL;
+ const char *emulate_color = NULL;
+ const char *max_line_length = NULL;
+ const char *meta_portable = NULL;
+ const char *page_size = NULL;
+ const char *rotation_angle = NULL;
+ bool save_screen = false; /* save screen, i.e. no erase before plot? */
+
+ int log_axis = 0;
+ int round_to_next_tick = 0;
+ double min_x = 0.0, max_x = 0.0, spacing_x = 0.0;
+ double min_y = 0.0, max_y = 0.0, spacing_y = 0.0;
+ bool spec_min_x = false, spec_min_y = false;
+ bool spec_max_x = false, spec_max_y = false;
+ bool spec_spacing_x = false, spec_spacing_y = false;
+
+ grid_type grid_spec = AXES_AND_BOX; /* frame type for current graph */
+ bool no_rotate_y_label = false; /* used for pre-X11R6 servers */
+ const char *frame_color = "black"; /* color of frame (and graph, if no -C)*/
+ int clip_mode = 1; /* clipping mode (cf. gnuplot) */
+ /* following variables are portmanteau: x and y are included as bitfields*/
+ int log_axis = 0; /* log axes or linear axes? */
+ int round_to_next_tick = 0; /* round axis limits to nearest tick? */
+ int switch_axis_end = 0; /* axis at top/right instead of bottom/left? */
+ int omit_ticks = 0; /* omit ticks and tick labels from an axis? */
+
+ /* graph dimensions, expressed as fractions of the width of the libplot
+ graphics display [by convention square]; <0.0 means use libplot default */
+ double frame_line_width = -0.001; /* width of lines in the graph frame */
+
+ /* dimensions of graphing area, expressed as fractions of the width of
+ the libplot graphics display [by convention square] */
+ double margin_below = .2; /* margin below the plot */
+ double margin_left = .2; /* margin left of the plot */
+ double plot_height = .6; /* height of the plot */
+ double plot_width = .6; /* width of the plot */
+
+ /* dimensions, expressed as fractions of the size of the plotting area */
+ double tick_size = .02; /* size of tick marks (< 0.0 allowed) */
+ double font_size = 0.0525; /* fontsize */
+ double title_font_size = 0.07; /* title fontsize */
+ double blankout_fraction = 1.3; /* this fraction of size of plotting box
+ is erased before the plot is drawn */
+
+ /* text-related */
+ const char *font_name = NULL; /* font name, NULL -> device default */
+ const char *title_font_name = NULL; /* title font name, NULL -> default */
+ const char *symbol_font_name = "ZapfDingbats"; /* symbol font name, NULL -> default */
+ const char *x_label = NULL; /* label for the x axis, NULL -> no label */
+ const char *y_label = NULL; /* label for the y axis, NULL -> no label */
+ const char *top_label = NULL; /* title above the plot, NULL -> no title */
+
+ bool transpose_axes = false;
+
+ MultiGrapher *multigrapher;
+
+ double scale = 1.0, trans_x = 0.0, trans_y = 0.0;
+
+ struct plot * p = plot_check (L, 1);
+
+ multigrapher = new_multigrapher (output_format, bg_color, bitmap_size,
+ emulate_color, max_line_length,
+ meta_portable, page_size, rotation_angle,
+ save_screen);
+
+ array_bounds (p->points, p->length,
+ transpose_axes, clip_mode,
+ &min_x, &min_y, &max_x, &max_y,
+ spec_min_x, spec_min_y, spec_max_x, spec_max_y);
+
+ begin_graph (multigrapher, scale, trans_x, trans_y);
+
+ set_graph_parameters (multigrapher,
+ frame_line_width,
+ frame_color,
+ top_label,
+ title_font_name, title_font_size, /*for title*/
+ tick_size, grid_spec,
+ min_x, max_x, spacing_x,
+ min_y, max_y, spacing_y,
+ spec_spacing_x, spec_spacing_y,
+ plot_width, plot_height,
+ margin_below, margin_left,
+ font_name, font_size, /* for abscissa label */
+ x_label,
+ font_name, font_size, /* for ordinate label */
+ y_label,
+ no_rotate_y_label,
+ /* these args are portmanteaux */
+ log_axis, round_to_next_tick,
+ switch_axis_end, omit_ticks,
+ /* more args */
+ clip_mode,
+ blankout_fraction,
+ transpose_axes);
+
+ /* draw the graph frame (grid, ticks, etc.); draw a `canvas' (a
+ background opaque white rectangle) only if this isn't the
+ first graph */
+ draw_frame_of_graph (multigrapher, false);
+
+ /* plot the laboriously read-in array */
+ plot_point_array (multigrapher, p->points, p->length);
+
+ end_graph (multigrapher);
+
+ /* finish up by deleting our multigrapher */
+ delete_multigrapher (multigrapher);
+
+ return 0;
+}
diff --git a/plot/misc.c b/plot/misc.c
new file mode 100644
index 00000000..0a8c0fcd
--- /dev/null
+++ b/plot/misc.c
@@ -0,0 +1,369 @@
+/* This file is part of the GNU plotutils package. Copyright (C) 1989,
+ 1990, 1991, 1995, 1996, 1997, 1998, 1999, 2000, 2005, 2008, Free
+ Software Foundation, Inc.
+
+ The GNU plotutils package is free software. You may redistribute it
+ and/or modify it under the terms of the GNU General Public License as
+ published by the Free Software foundation; either version 2, or (at your
+ option) any later version.
+
+ The GNU plotutils package is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with the GNU plotutils package; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* This file contains miscellaneous subroutines for GNU graph. Currently,
+ it contains only array_bounds(), which is called if the user fails to
+ specify at least one of the bounds xmin,xmax,ymin,ymax.
+
+ array_bounds() returns the unspecified bounds via pointers. I.e., it
+ finishes the job of specifying a bounding box for the data points that
+ will be plotted. The box may later be expanded so that its bounds are
+ multiples of the tick spacing (see plotter.c).
+
+ array_bounds() is called in graph.c, just before a graph is begun. */
+
+#include "sys-defines.h"
+#include "extern.h"
+
+/* bit fields for return value from Cohen-Sutherland clipper */
+enum { ACCEPTED = 0x1, CLIPPED_FIRST = 0x2, CLIPPED_SECOND = 0x4 };
+
+/* for internal clipper use */
+enum { TOP = 0x1, BOTTOM = 0x2, RIGHT = 0x4, LEFT = 0x8 };
+
+/* forward references */
+static int clip_line (double *x0_p, double *y0_p, double *x1_p, double *y1_p, double x_min_clip, double x_max_clip, double y_min_clip, double y_max_clip, bool spec_min_x, bool spec_min_y, bool spec_max_x, bool spec_max_y);
+static int compute_relevant_points (double xx, double yy, double oldxx, double oldyy, int clip_mode, double user_min_x, double user_min_y, double user_max_x, double user_max_y, bool spec_min_x, bool spec_min_y, bool spec_max_x, bool spec_max_y, double xxr[2], double yyr[2]);
+static int compute_outcode (double x, double y, double x_min_clip, double x_max_clip, double y_min_clip, double y_max_clip, bool spec_min_x, bool spec_min_y, bool spec_max_x, bool spec_max_y);
+
+void
+array_bounds (const Point *p, int length,
+ bool transpose_axes, int clip_mode,
+ double *min_x, double *min_y, double *max_x, double *max_y,
+ bool spec_min_x, bool spec_min_y,
+ bool spec_max_x, bool spec_max_y)
+{
+ /* keep compilers happy */
+ double user_min_x = 0.0, user_min_y = 0.0;
+ double user_max_x = 0.0, user_max_y = 0.0;
+ double local_min_x = 0.0, local_min_y = 0.0;
+ double local_max_x = 0.0, local_max_y = 0.0;
+ double xx, yy, oldxx, oldyy;
+ bool point_seen = false;
+ int i;
+
+ if (length == 0)
+ /* adopt a convention */
+ {
+ if (!spec_min_x)
+ *min_x = 0.0;
+ if (!spec_min_y)
+ *min_y = 0.0;
+ if (!spec_max_x)
+ *max_x = *min_x;
+ if (!spec_max_y)
+ *max_y = *min_y;
+ return;
+ }
+
+ if (spec_min_x)
+ user_min_x = *min_x;
+ else /* won't use user_min_x */
+ local_min_x = DBL_MAX;
+ if (spec_max_x)
+ user_max_x = *max_x;
+ else /* won't use user_max_x */
+ local_max_x = -(DBL_MAX);
+
+ /* special case: user specified both bounds, but min > max (reversed axis) */
+ if (spec_min_x && spec_max_x && user_min_x > user_max_x)
+ {
+ double tmp;
+
+ tmp = user_min_x;
+ user_min_x = user_max_x;
+ user_max_x = tmp;
+ }
+
+ if (spec_min_y)
+ user_min_y = *min_y;
+ else
+ local_min_y = DBL_MAX; /* won't use user_min_y */
+ if (spec_max_y)
+ user_max_y = *max_y;
+ else /* won't use user_max_y */
+ local_max_y = -(DBL_MAX);
+
+ /* special case: user specified both bounds, but min > max (reversed axis) */
+ if (spec_min_y && spec_max_y && user_min_y > user_max_y)
+ {
+ double tmp;
+
+ tmp = user_min_y;
+ user_min_y = user_max_y;
+ user_max_y = tmp;
+ }
+
+ /* loop through points in array; examine each line segment */
+
+ oldxx = oldyy = 0.0; /* previous point */
+ for (i = 0; i < length; i++)
+ {
+ double xxr[2], yyr[2]; /* storage for `relevant points' */
+ int n, j;
+ int effective_clip_mode;
+
+ /* get new point */
+ xx = (transpose_axes ? p[i].y : p[i].x);
+ yy = (transpose_axes ? p[i].x : p[i].y);
+
+ /* determine clipping mode (see compute_relevant_points() below) */
+ if (i == 0 || p[i].pendown == false
+ || (p[i].linemode <= 0 && p[i].fill_fraction < 0.0))
+ /* no polyline or filling, each point is isolated */
+ effective_clip_mode = 0;
+ else if (p[i].fill_fraction >= 0.0)
+ effective_clip_mode = 2;
+ else
+ effective_clip_mode = clip_mode;
+
+ n = compute_relevant_points (xx, yy, oldxx, oldyy,
+ effective_clip_mode,
+ user_min_x, user_min_y,
+ user_max_x, user_max_y,
+ spec_min_x, spec_min_y,
+ spec_max_x, spec_max_y,
+ xxr, yyr);
+ /* loop through relevant points, updating bounding box */
+ for (j = 0; j < n; j++)
+ {
+ point_seen = true;
+ if (!spec_min_x)
+ local_min_x = DMIN(local_min_x, xxr[j]);
+ if (!spec_min_y)
+ local_min_y = DMIN(local_min_y, yyr[j]);
+ if (!spec_max_x)
+ local_max_x = DMAX(local_max_x, xxr[j]);
+ if (!spec_max_y)
+ local_max_y = DMAX(local_max_y, yyr[j]);
+ }
+ oldxx = xx;
+ oldyy = yy;
+ }
+
+ if (!point_seen)
+ /* a convention */
+ local_min_x = local_min_y = local_max_x = local_max_y = 0.0;
+
+ /* pass back bounds that user didn't specify */
+ if (!spec_min_x)
+ *min_x = local_min_x;
+ if (!spec_min_y)
+ *min_y = local_min_y;
+ if (!spec_max_x)
+ *max_x = local_max_x;
+ if (!spec_max_y)
+ *max_y = local_max_y;
+
+ return;
+}
+
+/* For a new data point (xx,yy), compute the `relevant points', i.e. the
+ ones that should be used in updating the bounding box. There may be 0,
+ 1, or 2 of them. The number of relevant points is returned, and the
+ relevant points themselves are returned via pointers.
+
+ The relevant points are computed from the line segment extending from
+ (oldxx,oldyy) to (xx,yy), via an algorithm parametrized by a
+ gnuplot-style clip mode (0, 1, or 2).
+
+ If clip mode=0 then the simplest algorithm is used: (xx,yy) is a
+ relevant point iff it satisfies the user-specified bound(s), and there
+ are no other relevant points, i.e., (oldxx,oldyy) is ignored.
+
+ If clip mode=1 then if the line segment from (oldxx, oldyy) from (xx,yy)
+ has at least one endpoint that satisfies the user-specified bounds, it
+ generates two relevant points: the endpoints of the line segment,
+ clipped to the bounds. If on the other hand neither endpoint of the
+ line segment from (oldxx,oldyy) to (xx,yy) satisfies the user-specified
+ bounds, no relevant points are generated even if the line segment
+ contains points that satisfy the bounds.
+
+ If clip mode=2 then the line segment, if it intersects the bounding box,
+ is clipped on both ends, and both resulting endpoints are relevant. */
+
+static int
+compute_relevant_points (double xx, double yy,
+ double oldxx, double oldyy,
+ int clip_mode,
+ double user_min_x, double user_min_y,
+ double user_max_x, double user_max_y,
+ bool spec_min_x, bool spec_min_y,
+ bool spec_max_x, bool spec_max_y,
+ double xxr[2], double yyr[2])
+{
+ int clipval;
+
+ switch (clip_mode)
+ {
+ case 0:
+ if ((!spec_min_x || xx >= user_min_x)
+ && (!spec_max_x || xx <= user_max_x)
+ && (!spec_min_y || yy >= user_min_y)
+ && (!spec_max_y || yy <= user_max_y))
+ {
+ xxr[0] = xx;
+ yyr[0] = yy;
+ return 1;
+ }
+ else
+ return 0;
+ break;
+ case 1:
+ default:
+ clipval = clip_line (&oldxx, &oldyy, &xx, &yy, user_min_x, user_max_x, user_min_y, user_max_y, spec_min_x, spec_min_y, spec_max_x, spec_max_y);
+ if ((clipval & ACCEPTED)
+ && !((clipval & CLIPPED_FIRST) && (clipval & CLIPPED_SECOND)))
+ {
+ xxr[0] = oldxx;
+ yyr[0] = oldyy;
+ xxr[1] = xx;
+ yyr[1] = yy;
+ return 2;
+ }
+ else
+ return 0;
+ break;
+ case 2:
+ clipval = clip_line (&oldxx, &oldyy, &xx, &yy, user_min_x, user_max_x, user_min_y, user_max_y, spec_min_x, spec_min_y, spec_max_x, spec_max_y);
+ if (clipval & ACCEPTED)
+ {
+ xxr[0] = oldxx;
+ yyr[0] = oldyy;
+ xxr[1] = xx;
+ yyr[1] = yy;
+ return 2;
+ }
+ else
+ return 0;
+ break;
+ }
+}
+
+/* clip_line() takes two points, the endpoints of a line segment in the
+ * device frame (expressed in terms of floating-point device coordinates),
+ * and destructively passes back two points: the endpoints of the line
+ * segment clipped by Cohen-Sutherland to the rectangular clipping area.
+ * The return value contains bitfields ACCEPTED, CLIPPED_FIRST, and
+ * CLIPPED_SECOND.
+ *
+ * This is a modified C-S clipper: the flags spec_{min,max}_{x,y} indicate
+ * whether or not clipping is to be performed on each edge.
+ */
+
+static int
+clip_line (double *x0_p, double *y0_p, double *x1_p, double *y1_p, double x_min_clip, double x_max_clip, double y_min_clip, double y_max_clip, bool spec_min_x, bool spec_min_y, bool spec_max_x, bool spec_max_y)
+{
+ double x0 = *x0_p;
+ double y0 = *y0_p;
+ double x1 = *x1_p;
+ double y1 = *y1_p;
+ int outcode0, outcode1;
+ bool accepted;
+ int clipval = 0;
+
+ outcode0 = compute_outcode (x0, y0, x_min_clip, x_max_clip, y_min_clip, y_max_clip, spec_min_x, spec_min_y, spec_max_x, spec_max_y);
+ outcode1 = compute_outcode (x1, y1, x_min_clip, x_max_clip, y_min_clip, y_max_clip, spec_min_x, spec_min_y, spec_max_x, spec_max_y);
+
+ for ( ; ; )
+ {
+ if (!(outcode0 | outcode1)) /* accept */
+ {
+ accepted = true;
+ break;
+ }
+ else if (outcode0 & outcode1) /* reject */
+ {
+ accepted = false;
+ break;
+ }
+ else
+ {
+ /* at least one endpoint is outside; choose one that is */
+ int outcode_out = (outcode0 ? outcode0 : outcode1);
+ double x, y; /* intersection with clip edge */
+
+ if (outcode_out & RIGHT)
+ {
+ x = x_max_clip;
+ y = y0 + (y1 - y0) * (x_max_clip - x0) / (x1 - x0);
+ }
+ else if (outcode_out & LEFT)
+ {
+ x = x_min_clip;
+ y = y0 + (y1 - y0) * (x_min_clip - x0) / (x1 - x0);
+ }
+ else if (outcode_out & TOP)
+ {
+ x = x0 + (x1 - x0) * (y_max_clip - y0) / (y1 - y0);
+ y = y_max_clip;
+ }
+ else /* BOTTOM bit must be set */
+ {
+ x = x0 + (x1 - x0) * (y_min_clip - y0) / (y1 - y0);
+ y = y_min_clip;
+ }
+
+ if (outcode_out == outcode0)
+ {
+ x0 = x;
+ y0 = y;
+ outcode0 = compute_outcode (x0, y0, x_min_clip, x_max_clip, y_min_clip, y_max_clip, spec_min_x, spec_min_y, spec_max_x, spec_max_y);
+ }
+ else
+ {
+ x1 = x;
+ y1 = y;
+ outcode1 = compute_outcode (x1, y1, x_min_clip, x_max_clip, y_min_clip, y_max_clip, spec_min_x, spec_min_y, spec_max_x, spec_max_y);
+ }
+ }
+ }
+
+ if (accepted)
+ {
+ clipval |= ACCEPTED;
+ if ((x0 != *x0_p) || (y0 != *y0_p))
+ clipval |= CLIPPED_FIRST;
+ if ((x1 != *x1_p) || (y1 != *y1_p))
+ clipval |= CLIPPED_SECOND;
+ *x0_p = x0;
+ *y0_p = y0;
+ *x1_p = x1;
+ *y1_p = y1;
+ }
+
+ return clipval;
+}
+
+static int
+compute_outcode (double x, double y, double x_min_clip, double x_max_clip, double y_min_clip, double y_max_clip, bool spec_min_x, bool spec_min_y, bool spec_max_x, bool spec_max_y)
+{
+ int code = 0;
+
+ if (spec_max_x && x > x_max_clip)
+ code |= RIGHT;
+ else if (spec_min_x && x < x_min_clip)
+ code |= LEFT;
+ if (spec_max_y && y > y_max_clip)
+ code |= TOP;
+ else if (spec_min_y && y < y_min_clip)
+ code |= BOTTOM;
+
+ return code;
+}
diff --git a/plot/plot.c b/plot/plot.c
new file mode 100644
index 00000000..820ec3a4
--- /dev/null
+++ b/plot/plot.c
@@ -0,0 +1,27 @@
+
+typedef struct
+{
+ double x, y; /* location of the point in user coordinates */
+ bool have_x_errorbar, have_y_errorbar;
+ double xmin, xmax; /* meaningful only if have_x_errorbar field is set */
+ double ymin, ymax; /* meaningful only if have_y_errorbar field is set */
+ bool pendown; /* connect to previous point? (if false, polyline ends) */
+ /* following fields are polyline attributes: constant over a polyline */
+ int symbol; /* either a number indicating which standard marker
+ symbol is to be plotted at the point (<0 means none)
+ or an character to be plotted, depending on the value:
+ 0-31: a marker number, or 32-up: a character. */
+ double symbol_size; /* symbol size, as frac. of size of plotting area */
+ const char *symbol_font_name; /* font from which symbols >= 32 are taken */
+ int linemode; /* linemode of polyline (<0 means no polyline) */
+ double line_width; /* line width as fraction of size of the display */
+ double fill_fraction; /* in interval [0,1], <0 means polyline isn't filled */
+ bool use_color; /* color/monochrome interpretation of linemode */
+} Point;
+
+struct plot {
+ Point *points;
+ int length;
+ int size;
+};
+
diff --git a/plot/plotter.c b/plot/plotter.c
new file mode 100644
index 00000000..0a212100
--- /dev/null
+++ b/plot/plotter.c
@@ -0,0 +1,2651 @@
+/* This file is part of the GNU plotutils package. Copyright (C) 1989,
+ 1990, 1991, 1995, 1996, 1997, 1998, 1999, 2000, 2005, 2008, Free
+ Software Foundation, Inc.
+
+ The GNU plotutils package is free software. You may redistribute it
+ and/or modify it under the terms of the GNU General Public License as
+ published by the Free Software foundation; either version 2, or (at your
+ option) any later version.
+
+ The GNU plotutils package is distributed in the hope that it will be
+ useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with the GNU plotutils package; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin St., Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* This file contains the point plotter half of GNU graph. The point
+ plotter could easily be linked with other software. It translates a
+ sequence of points, regarded as defining a polyline or a sequence of
+ polylines, to a sequence of libplot calls. There is support for
+ multigraphing, i.e. producing a plot consisting of more than a single
+ graph. Each graph may be drawn from more than one file, i.e., input
+ stream, and each input stream may provide more than a single polyline.
+
+ A `point' is a structure. Each point structure contains the following
+ fields:
+
+ x and y coordinates of the point
+ a `have_x_errorbar' flag (true or false)
+ a `have_y_errorbar' flag (true or false)
+ xmin and xmax (meaningful only if have_x_errorbar is set)
+ ymin and ymax (meaningful only if have_y_errorbar is set)
+ a `pendown' flag
+
+ a symbol type (a small integer, interpreted as a marker type)
+ a symbol size (a fraction of the size of the plotting box)
+ a symbol font name (relevant only for symbol types >= 32)
+ a linemode (a small integer)
+ a linewidth (a fraction of the size of the display device)
+ a polyline fill-fraction (in the interval [0,1], <0 means no fill)
+ a use_color flag (true or false)
+
+ The point plotter constructs a polyline from each successive run of
+ points that have the pendown flag set. It assumes that the final seven
+ fields are assumed to be the same for each point in such a run, i.e., it
+ takes their values from the first point of the run. At the location of
+ each point on the polyline, the appropriate marker symbol, if any, will
+ be plotted. Symbol types greater than or equal to 32 are interpreted as
+ single characters to be plotted, rather than symbols.
+
+ Points without the pendown flag set cause the polyline to be broken, and
+ a new one to begin, before the symbol (if any) is plotted.
+
+ The plotter supports five basic linemodes: 1 through 5. The
+ interpretation of `linemode' depends on the polyline's use_color flag.
+
+ linemode If monochrome If color
+
+ 1 solid red
+ 2 dotted green
+ 3 dotdashed blue
+ 4 shortdashed magenta
+ 5 longdashed cyan
+
+ In the monochrome case, the pattern simply repeats: 6,7,8,9,10 are
+ equivalent to 1,2,3,4,5, etc. In the colored case, the sequence of
+ colors also repeats. But linemodes 1,2,3,4,5 are drawn solid, while
+ 6,7,8,9,10 are drawn dotted, 11,12,13,14,15 are drawn dotdashed, etc.
+ So there are 25 distinct colored linemodes, and 5 distinct monochrome
+ (black) ones.
+
+ The color of a symbol will be the same as the color of the polyline on
+ which it is plotted.
+
+ linemodes -1, -2, etc. have a special interpretation. They are
+ `disconnected' linemodes: no polyline will appear, but if color is
+ being used, the color of the plotted symbols (if any) will be
+ linemode-dependent. -1,-2,-3,-4,5 signify red,green,blue,magenta,cyan
+ (the same sequence as for 1,2,3,4,5); thereafter the sequence repeats.
+
+ linemode 0 is special (for backward compatibility). No line is drawn;
+ symbol #1 (a point) will be used. So using linemode 0 is the same as
+ using linemode -1, symbol 1.
+
+ The point plotter is invoked by calling the following, in order.
+
+ new_multigrapher() creates a new point plotter.
+ begin_graph()
+ set_graph_parameters() initializes global structures used by
+ the draw_frame_of_graph() and plot_point() routines. These include
+ the structures that specify the linear transformation from user
+ coordinates to the coordinates used by libplot, and structures
+ that specify the style of the plot frame.
+ draw_frame_of_graph() plots the graph frame. [Optional.]
+ plot_point() uses libplot routines to plot a single point, together
+ with (possibly) a line extending to it from the last point, and
+ a symbol. [Alternatively, plot_point_array() can be used, to plot
+ an array of points.]
+ end_graph()
+ ..
+ [The begin_graph()..end_graph() block can be repeated indefinitely
+ if desired, to create a multigraph. set_graph_parameters() allows
+ for repositioning of later graphs.]
+ ..
+ delete_multigrapher() deletes the point plotter.
+
+ There is also a function end_polyline_and_flush(), which is useful for
+ real-time display. */
+
+#include "sys-defines.h"
+#include "libcommon.h"
+#include "plot.h"
+#include "extern.h"
+
+/* we use floating point libplot coordinates in the range [0,PLOT_SIZE] */
+#define PLOT_SIZE 4096.0
+
+#define FUZZ 0.000001 /* bd. on floating pt. roundoff error */
+
+#define NEAR_EQUALITY(a, b, scale) (fabs((a) - (b)) < (FUZZ * fabs(scale)))
+
+typedef unsigned int outcode; /* for Cohen-Sutherland clipper */
+enum { TOP = 0x1, BOTTOM = 0x2, RIGHT = 0x4, LEFT = 0x8 };
+enum { ACCEPTED = 0x1, CLIPPED_FIRST = 0x2, CLIPPED_SECOND = 0x4 };
+
+#define TRIAL_NUMBER_OF_TICK_INTERVALS 5
+#define MAX_NUM_SUBTICKS 29 /* max num. of linearly spaced subticks */
+#define RELATIVE_SUBTICK_SIZE 0.4 /* subtick_size / tick_size */
+/* if a log axis spans >5.0 orders of magnitude, don't plot log subsubticks */
+#define MAX_DECADES_WITH_LOG_SUBSUBTICKS 5.0
+
+/* inter-tick spacing types, returned by scale1() and spacing_type() */
+#define S_ONE 0
+#define S_TWO 1
+#define S_FIVE 2
+#define S_TWO_FIVE 3 /* we don't use this one, but user may request it */
+#define S_UNKNOWN -2
+
+/* valid graph axis layout types; A_LOG2, anyone? */
+#define A_LINEAR 0
+#define A_LOG10 1
+
+/* The x_trans and y_trans elements of a Multigrapher specify the current
+ linear transformation from user coordinates to device coordinates. They
+ are used both in the plotting of a graph frame, and in the plotting of
+ data points within a graph. */
+
+typedef struct
+{
+ /* Input (user) coordinates, all floating point. These are the
+ coordinates used in the original data points (or their base-10 logs,
+ for an axis of log type). We'll map them to the unit interval
+ [0.0,1.0]. */
+ double input_min, input_max; /* min, max */
+ double input_range; /* max - min, precomputed for speed */
+ /* If we're reversing axes, we'll then map [0.0,1.0] to [1.0,0.0] */
+ bool reverse;
+ /* We'll map [0.0,1.0] to another (smaller) interval, linearly */
+ double squeezed_min, squeezed_max; /* min, max */
+ double squeezed_range; /* max - min */
+ /* Output [i.e., libplot] coordinates. The interval [0.0,1.0] will be
+ mapped to this range, and the squeezed interval to a sub-range. This
+ is so that the box within which points are plotted will be smaller
+ than the full area of the graphics display. */
+ double output_min, output_max; /* min */
+ double output_range; /* max - min */
+} Transform;
+
+/* Affine transformation macros */
+
+/* X Scale: convert from user x value to normalized x coordinate (floating
+ point, 0.0 to 1.0). */
+#define XS(x) (((x) - multigrapher->x_trans.input_min)/multigrapher->x_trans.input_range)
+/* X Reflect: map [0,1] to [1,0], if that's called for */
+#define XR(x) (multigrapher->x_trans.reverse ? 1.0 - (x) : (x))
+/* X Squeeze: map [0,1] range for normalized x coordinate into a smaller
+ interval, the x range for the plotting area within the graphics display */
+#define XSQ(x) (multigrapher->x_trans.squeezed_min + (x) * multigrapher->x_trans.squeezed_range)
+/* X Plot: convert from normalized x coordinate to floating point libplot
+ coordinate. */
+#define XP(x) (multigrapher->x_trans.output_min + (x) * multigrapher->x_trans.output_range)
+/* X Value: convert from user x value (floating point) to floating point
+ libplot coordinate. */
+#define XV(x) XP(XSQ(XR(XS(x))))
+/* X Normalize: the same, but do not perform reflection if any. (We use
+ this for plotting of axes and their labels.) */
+#define XN(y) XP(XSQ(XS(y)))
+
+/* Y Scale: convert from user y value to normalized y coordinate (floating
+ point, 0.0 to 1.0). */
+#define YS(y) (((y) - multigrapher->y_trans.input_min)/multigrapher->y_trans.input_range)
+/* Y Reflect: map [0,1] to [1,0], if that's called for */
+#define YR(y) (multigrapher->y_trans.reverse ? 1.0 - (y) : (y))
+/* Y Squeeze: map [0,1] range for normalized y coordinate into a smaller
+ interval, the y range for the plotting area within the graphics display */
+#define YSQ(y) (multigrapher->y_trans.squeezed_min + (y) * multigrapher->y_trans.squeezed_range)
+/* Y Plot: convert from normalized y coordinate to floating point libplot
+ coordinate. */
+#define YP(y) (multigrapher->y_trans.output_min + (y) * multigrapher->y_trans.output_range)
+/* Y Value: convert from user y value (floating point) to floating point
+ libplot coordinate. (We use this for plotting of points.) */
+#define YV(y) YP(YSQ(YR(YS(y))))
+/* Y Normalize: the same, but do not perform reflection if any. (We use
+ this for plotting of axes and their labels.) */
+#define YN(y) YP(YSQ(YS(y)))
+
+/* Size Scale: convert distances, or sizes, from normalized coors to
+ libplot coordinates. (Used for tick, symbol, and font sizes.) The min
+ should really be precomputed. */
+#define SS(x) \
+(DMIN(multigrapher->x_trans.output_range * multigrapher->x_trans.squeezed_range, \
+multigrapher->y_trans.output_range * multigrapher->y_trans.squeezed_range) * (x))
+
+/* The `x_axis' and `y_axis' elements of a Multigrapher, which are of type
+ `Axis', specify the layout of the two axes of a graph. They are used in
+ the drawing of a graph frame. All elements that are doubles are
+ expressed in user coordinates (unless the axis is logarithmic, in which
+ case logs are taken before this structure is filled in). */
+
+/* The `x_axis' and `y_axis' elements are filled in by calls to
+ prepare_axis() when set_graph_parameters() is called. The only
+ exceptions to this are the elements `max_width' and `non_user_ticks',
+ which are filled in by draw_frame_of_graph(), as the frame for a graph
+ is being drawn. */
+
+typedef struct
+{
+ const char *font_name; /* fontname for axis label and tick labels */
+ double font_size; /* font size for axis label and tick labels */
+ const char *label; /* axis label (a string) */
+ int type; /* axis layout type (A_LINEAR or A_LOG10) */
+ double tick_spacing; /* distance between ticks */
+ int min_tick_count, max_tick_count; /* tick location = count * spacing */
+ bool have_lin_subticks; /* does axis have linearly spaced subticks? */
+ double lin_subtick_spacing; /* distance between linearly spaced subticks */
+ int min_lin_subtick_count, max_lin_subtick_count;
+ bool have_normal_subsubticks; /* does axis have logarithmic subsubticks?*/
+ bool user_specified_subsubticks; /* axis has user-spec'd subsubticks? */
+ double subsubtick_spacing; /* spacing for user-specified ones */
+ double other_axis_loc; /* location of intersection w/ other axis */
+ double alt_other_axis_loc; /* alternative loc. (e.g. right end vs. left)*/
+ bool switch_axis_end; /* other axis at right/top, not left/bottom? */
+ bool omit_ticks; /* just plain omit them (and their labels) ? */
+ double max_label_width; /* max width of labels placed on axis, in
+ libplot coors (we update this during
+ plotting, for y axis only) */
+ int labelled_ticks; /* number of labelled ticks, subticks, and
+ subsubticks drawn on the axis
+ (we update this during plotting, so we
+ can advise the user to specify a tick
+ spacing by hand if labelled_ticks <= 2) */
+} Axis;
+
+
+/* The Multigrapher structure. A pointer to one of these is passed as the
+ first argument to each Multigrapher method (e.g., plot_point()). */
+
+struct MultigrapherStruct
+{
+ /* multigrapher parameters (not updated over a multigrapher's lifetime) */
+ plPlotter *plotter; /* GNU libplot Plotter handle */
+ const char *output_format; /* type of libplot device driver [unused] */
+ const char *bg_color; /* color of background, if non-NULL */
+ bool save_screen; /* erase display when opening plotter? */
+ /* graph parameters (constant over any single graph) */
+ Transform x_trans, y_trans; /* user->device coor transformations */
+ Axis x_axis, y_axis; /* information on each axis */
+ grid_type grid_spec; /* frame specification */
+ double blankout_fraction; /* 1.0 means blank whole box before drawing */
+ bool no_rotate_y_label; /* useful for pre-X11R6 X servers */
+ double tick_size; /* fractional tick size */
+ double subtick_size; /* fractional subtick size (for linear axes) */
+ double frame_line_width; /* fractional width of lines in the frame */
+ double half_line_width; /* approx. half of this, in libplot coors */
+ const char *frame_color; /* color for frame (and graph, if no -C) */
+ const char *title; /* graph title */
+ const char *title_font_name; /* font for graph title */
+ double title_font_size; /* fractional height of graph title */
+ int clip_mode; /* 0, 1, or 2 (cf. clipping in gnuplot) */
+ /* following elements are updated during plotting of points; they're the
+ chief repository for internal state */
+ bool first_point_of_polyline; /* true only at beginning of each polyline */
+ double oldpoint_x, oldpoint_y; /* last-plotted point */
+ int symbol; /* symbol being plotted at each point */
+ int linemode; /* linemode used for polyline */
+};
+
+/* forward references */
+static int clip_line (Multigrapher *multigrapher, double *x0_p, double *y0_p, double *x1_p, double *y1_p);
+static int spacing_type (double spacing);
+static outcode compute_outcode (Multigrapher *multigrapher, double x, double y, bool tolerant);
+static void plot_abscissa_log_subsubtick (Multigrapher *multigrapher, double xval);
+static void plot_errorbar (Multigrapher *multigrapher, const Point *p);
+static void plot_ordinate_log_subsubtick (Multigrapher *multigrapher, double xval);
+static void prepare_axis (Axis *axisp, Transform *trans, double min, double max, double spacing, const char *font_name, double font_size, const char *label, double subsubtick_spacing, bool user_specified_subsubticks, bool round_to_next_tick, bool log_axis, bool reverse_axis, bool switch_axis_end, bool omit_ticks);
+static void print_tick_label (char *labelbuf, const Axis *axis, const Transform *transform, double val);
+static void scale1 (double min, double max, double *tick_spacing, int *tick_spacing_type);
+static void set_line_style (Multigrapher *multigrapher, int style, bool use_color);
+static void transpose_portmanteau (int *val);
+
+
+/* print_tick_label() prints a label on an axis tick. The format depends
+ * on whether the axis is a log axis or a linear axis; also, the magnitude
+ * of the axis labels.
+ */
+
+static void
+print_tick_label (char *labelbuf, const Axis *axis, const Transform *transform, double val)
+{
+ int prec;
+ char *eloc, *ptr;
+ char labelbuf_tmp[64], incrbuf[64];
+ double spacing;
+ bool big_exponents;
+ double min, max;
+
+ /* two possibilities: large/small exponent magnitudes */
+
+ min = (axis->type == A_LOG10
+ ? pow (10.0, transform->input_min) : transform->input_min);
+ max = (axis->type == A_LOG10
+ ? pow (10.0, transform->input_max) : transform->input_max);
+
+ big_exponents = (((min != 0.0 && fabs (log10 (fabs (min))) >= 4.0)
+ || (max != 0.0 && fabs (log10 (fabs (max))) >= 4.0))
+ ? true : false);
+
+ if (big_exponents)
+ /* large exponents, rewrite as foo x 10^bar, using escape sequences */
+ {
+ char *src = labelbuf_tmp, *dst = labelbuf;
+ int exponent;
+ char floatbuf[64];
+ char *fptr = floatbuf;
+ double prefactor;
+
+ sprintf (labelbuf_tmp, "%e", val);
+ if ((eloc = strchr (labelbuf_tmp, (int)'e')) == NULL)
+ return;
+
+ if (axis->type == A_LOG10 && !axis->user_specified_subsubticks)
+ /* a hack: this must be a power of 10, so just print "10^bar" */
+ {
+ sscanf (++eloc, "%d", &exponent);
+ sprintf (dst, "10\\sp%d\\ep", exponent);
+ return;
+ }
+
+ /* special case: zero prints as `0', not 0.0x10^whatever */
+ if (val == 0.0)
+ {
+ *dst++ = '0';
+ *dst = '0円';
+ return;
+ }
+
+ while (src < eloc)
+ *fptr++ = *src++;
+ *fptr = '0円';
+ sscanf (floatbuf, "%lf", &prefactor); /* get foo */
+ sscanf (++src, "%d", &exponent); /* get bar */
+
+ spacing = (axis->type == A_LINEAR
+ ? axis->tick_spacing
+ : axis->subsubtick_spacing); /* user-specified, for log axis*/
+ sprintf (incrbuf, "%f",
+ spacing / pow (10.0, (double)exponent));
+ ptr = strchr (incrbuf, (int)'.');
+ prec = 0;
+ if (ptr != NULL)
+ {
+ int count = 0;
+
+ while (*(++ptr))
+ {
+ count++;
+ if (*ptr != '0')
+ prec = count;
+ }
+ }
+
+ /* \sp ... \ep is start_superscript ... end_superscript, and \r6 is
+ right-shift by 1/6 em. \mu is the `times' character. */
+ sprintf (dst, "%.*f\\r6\\mu10\\sp%d\\ep",
+ prec, prefactor, exponent);
+
+ return;
+ }
+
+ else /* small-size exponent magnitudes */
+ {
+ if (axis->type == A_LOG10 && !axis->user_specified_subsubticks)
+ /* a hack: this must be a (small) power of 10, so we'll just use
+ %g format (same as %f, no trailing zeroes) */
+ {
+ sprintf (labelbuf, "%.9g", val);
+ return;
+ }
+
+ /* always use no. of digits of precision present in increment */
+ spacing = (axis->type == A_LINEAR
+ ? axis->tick_spacing
+ : axis->subsubtick_spacing); /* user-specified, for log axis*/
+ sprintf (incrbuf, "%.9f", spacing);
+ ptr = strchr (incrbuf, (int)'.');
+ prec = 0;
+ if (ptr != NULL)
+ {
+ int count = 0;
+
+ while (*(++ptr))
+ {
+ count++;
+ if (*ptr != '0')
+ prec = count;
+ }
+ }
+ sprintf (labelbuf, "%.*f", prec, val);
+ return;
+ }
+}
+
+/* Algorithm SCALE1, for selecting an inter-tick spacing that will yield a
+ * good-looking linear-format axis. The spacing is always 1.0, 2.0, or 5.0
+ * times a power of ten.
+ *
+ * Reference: Lewart, C. R., "Algorithms SCALE1, SCALE2, and
+ * SCALE3 for Determination of Scales on Computer Generated
+ * Plots", Communications of the ACM, 10 (1973), 639-640.
+ * Also cited as ACM Algorithm 463.
+ *
+ * We call this routine even when the axis is logarithmic rather than
+ * linear. In that case the arguments are the logs of the actual
+ * arguments, so that it computes an optimal inter-tick factor rather than
+ * an optimal inter-tick spacing.
+ */
+
+/* ARGS: min,max = data min, max
+ tick_spacing = inter-tick spacing
+ tick_spacing_type = 0,1,2 i.e. S_ONE,S_TWO,S_FIVE */
+static void
+scale1 (double min, double max, double *tick_spacing, int *tick_spacing_type)
+{
+ int k;
+ double nal;
+ double a, b;
+
+ /* valid interval lengths */
+ static const double vint[] =
+ {
+ 1.0, 2.0, 5.0, 10.0
+ };
+
+ /* Corresponding breakpoints. The published algorithm uses geometric
+ means, i.e. sqrt(2), sqrt(10), sqrt(50), but using sqrt(10)=3.16...
+ will (if nticks=5, as we choose it to be) cause intervals of length
+ 1.5 to yield an inter-tick distance of 0.2 rather than 0.5. So we
+ could reduce it to 2.95. Similarly we could reduce sqrt(50) to 6.95
+ so that intervals of length 3.5 will yield an inter-tick distance of
+ 1.0 rather than 0.5. */
+ static const double sqr[] =
+ {
+ M_SQRT2, 3.16228, 7.07107
+ };
+
+ /* compute trial inter-tick interval length */
+ a = (max - min) / TRIAL_NUMBER_OF_TICK_INTERVALS;
+ a *= (max > min) ? 1.0 : -1.0; /* paranoia, max>min always */
+ if (a <= 0.0)
+ {
+ fprintf(stderr, "%s: error: the trial inter-tick spacing '%g' is bad\n",
+ progname, a);
+ exit (EXIT_FAILURE);
+ }
+ nal = floor(log10(a));
+ b = a * pow (10.0, -nal); /* 1.0 <= b < 10.0 */
+
+ /* round to closest permissible inter-tick interval length */
+ k = 0;
+ do
+ {
+ if (b < sqr[k])
+ break;
+ k++;
+ }
+ while (k < 3);
+
+ *tick_spacing = (max > min ? 1.0 : -1.0) * vint[k] * pow (10.0, nal);
+ /* for increment type, 0,1,2 means 1,2,5 times a power of 10 */
+ *tick_spacing_type = (k == 3 ? 0 : k);
+ return;
+}
+
+/* Determine whether an inter-tick spacing (in practice, one specified by
+ the user) is 1.0, 2.0, or 5.0 times a power of 10. */
+static int
+spacing_type (double incr)
+{
+ int i;
+ int i_tenpower = (int)(floor(log10(incr)));
+ double tenpower = 1.0;
+ bool neg_power = false;
+
+ if (i_tenpower < 0)
+ {
+ neg_power = true;
+ i_tenpower = -i_tenpower;
+ }
+
+ for (i = 0; i < i_tenpower; i++)
+ tenpower *= 10;
+ if (neg_power)
+ tenpower = 1.0 / tenpower;
+
+ if (NEAR_EQUALITY(incr, tenpower, tenpower))
+ return S_ONE;
+ else if (NEAR_EQUALITY(incr, 2 * tenpower, tenpower))
+ return S_TWO;
+ else if (NEAR_EQUALITY(incr, 2.5 * tenpower, tenpower))
+ return S_TWO_FIVE;
+ else if (NEAR_EQUALITY(incr, 5 * tenpower, tenpower))
+ return S_FIVE;
+ else
+ return S_UNKNOWN;
+}
+
+
+/* prepare_axis() fills in the Axis structure for an axis, and some of
+ * the linear transformation variables in the Transform structure also.
+ */
+
+/* ARGS: user_specified_subticks = linear ticks on a log axis?
+ round_to_next_tick = round limits to next tick mark?
+ log_axis = log axis?
+ reverse_axis = reverse min, max?
+ switch_axis_end = intersection w/ other axis in alt. pos.?
+ omit_ticks = suppress all ticks and tick labels? */
+static void
+prepare_axis (Axis *axisp, Transform *trans, double min, double max, double spacing, const char *font_name, double font_size, const char *label, double subsubtick_spacing, bool user_specified_subsubticks, bool round_to_next_tick, bool log_axis, bool reverse_axis, bool switch_axis_end, bool omit_ticks)
+{
+ double range;
+ int tick_spacing_type = 0;
+ double tick_spacing, lin_subtick_spacing;
+ int min_tick_count, max_tick_count;
+ int min_lin_subtick_count, max_lin_subtick_count;
+ bool have_lin_subticks;
+
+ if (min > max)
+ /* paranoia, max < min is swapped at top level */
+ {
+ fprintf(stderr, "%s: error: min > max for an axis, which is not allowed\n",
+ progname);
+ exit (EXIT_FAILURE);
+ }
+
+ if (min == max) /* expand in a clever way */
+ {
+ max = floor (max + 1.0);
+ min = ceil (min - 1.0);
+ }
+
+ if (log_axis) /* log axis, data are stored in logarithmic form */
+ /* compute a tick spacing; user can't specify it */
+ {
+ scale1 (min, max, &tick_spacing, &tick_spacing_type);
+ if (tick_spacing <= 1.0)
+ {
+ tick_spacing = 1.0;
+ tick_spacing_type = S_ONE;
+ }
+ }
+ else /* linear axis */
+ {
+ if (spacing == 0.0) /* i.e., not specified by user */
+ scale1 (min, max, &tick_spacing, &tick_spacing_type);
+ else /* luser is boss, don't use SCALE1 */
+ {
+ tick_spacing = spacing;
+ tick_spacing_type = spacing_type (spacing);
+ }
+ }
+
+ range = max - min; /* range is not negative */
+
+ if (round_to_next_tick) /* expand both limits to next tick */
+ {
+ if (user_specified_subsubticks)
+ /* Special Case. If user specified the `spacing' argument to -x or
+ -y on a logarithmic axis, our usual tick-generating and
+ tick-plotting algorithms are disabled. So we don't bother with
+ min_tick_count or several other fields of the axis struct;
+ instead we just compute a new (rounded) max, min, and range.
+ Since most data are stored as logs, this is complicated. */
+ {
+ double true_min = pow (10.0, min), true_max = pow (10.0, max);
+ double true_range = true_max - true_min;
+ int min_count, max_count;
+
+ min_count = (int)(floor ((true_min + FUZZ * true_range)
+ / subsubtick_spacing));
+ max_count = (int)(ceil ((true_max - FUZZ * true_range)
+ / subsubtick_spacing));
+ /* avoid core dump, do *not* reduce minimum to zero! */
+ if (min_count > 0)
+ min = log10 (min_count * subsubtick_spacing);
+ max = log10 (max_count * subsubtick_spacing);
+ range = max - min;
+ min_tick_count = max_tick_count = 0; /* keep gcc happy */
+ }
+ else /* normal `expand limits to next tick' case */
+ {
+ min_tick_count = (int)(floor((min + FUZZ * range)/ tick_spacing));
+ max_tick_count = (int)(ceil((max - FUZZ * range)/ tick_spacing));
+ /* max_tick_count > min_tick_count always */
+ /* tickval = tick_spacing * count,
+ for all count in [min_count,max_count]; must have >=2 ticks */
+ min = tick_spacing * min_tick_count;
+ max = tick_spacing * max_tick_count;
+ range = max - min;
+ }
+ }
+ else /* don't expand limits to next tick */
+ {
+ min_tick_count = (int)(ceil((min - FUZZ * range)/ tick_spacing));
+ max_tick_count = (int)(floor((max + FUZZ * range)/ tick_spacing));
+ /* max_tick_count <= min_tick_count is possible */
+ /* tickval = incr * count,
+ for all count in [min_count,max_count]; can have 0,1,2,3... ticks */
+ }
+
+ /* Allow 5 subticks per tick if S_FIVE or S_TWO_FIVE, 2 if S_TWO. Case
+ S_ONE is special; we try 10, 5, and 2 in succession */
+ switch (tick_spacing_type)
+ {
+ case S_FIVE:
+ case S_TWO_FIVE:
+ lin_subtick_spacing = tick_spacing / 5;
+ break;
+ case S_TWO:
+ lin_subtick_spacing = tick_spacing / 2;
+ break;
+ case S_ONE:
+ lin_subtick_spacing = tick_spacing / 10;
+ min_lin_subtick_count = (int)(ceil((min - FUZZ * range)/ lin_subtick_spacing));
+ max_lin_subtick_count = (int)(floor((max + FUZZ * range)/ lin_subtick_spacing));
+ if (max_lin_subtick_count - min_lin_subtick_count > MAX_NUM_SUBTICKS)
+ {
+ lin_subtick_spacing = tick_spacing / 5;
+ min_lin_subtick_count = (int)(ceil((min - FUZZ * range)/ lin_subtick_spacing));
+ max_lin_subtick_count = (int)(floor((max + FUZZ * range)/ lin_subtick_spacing));
+ if (max_lin_subtick_count - min_lin_subtick_count > MAX_NUM_SUBTICKS)
+ lin_subtick_spacing = tick_spacing / 2;
+ }
+ break;
+ default:
+ /* in default case, i.e. S_UNKNOWN, we won't plot linear subticks */
+ lin_subtick_spacing = tick_spacing; /* not actually needed, since not plotted */
+ break;
+ }
+
+ /* smallest possible inter-subtick factor for a log axis is 10.0 */
+ if (log_axis && lin_subtick_spacing <= 1.0)
+ lin_subtick_spacing = 1.0;
+
+ min_lin_subtick_count = (int)(ceil((min - FUZZ * range)/ lin_subtick_spacing));
+ max_lin_subtick_count = (int)(floor((max + FUZZ * range)/ lin_subtick_spacing));
+ have_lin_subticks
+ = ((tick_spacing_type != S_UNKNOWN /* S_UNKNOWN -> no subticks */
+ && (max_lin_subtick_count - min_lin_subtick_count) <= MAX_NUM_SUBTICKS)
+ ? true : false);
+
+ /* fill in parameters for axis-specific affine transformation */
+ trans->input_min = min;
+ trans->input_max = max;
+ trans->input_range = range; /* precomputed for speed */
+ trans->reverse = reverse_axis;
+
+ /* fill in axis-specific plot frame variables */
+ axisp->switch_axis_end = switch_axis_end;
+ axisp->omit_ticks = omit_ticks;
+ axisp->label = label;
+ axisp->font_name = font_name;
+ axisp->font_size = font_size;
+ axisp->max_label_width = 0.0;
+ axisp->type = log_axis ? A_LOG10 : A_LINEAR;
+ axisp->tick_spacing = tick_spacing;
+ axisp->min_tick_count = min_tick_count;
+ axisp->max_tick_count = max_tick_count;
+ axisp->have_lin_subticks = have_lin_subticks;
+ axisp->lin_subtick_spacing = lin_subtick_spacing;
+ axisp->min_lin_subtick_count = min_lin_subtick_count;
+ axisp->max_lin_subtick_count = max_lin_subtick_count;
+ axisp->user_specified_subsubticks = user_specified_subsubticks;
+ axisp->subsubtick_spacing = subsubtick_spacing;
+ axisp->labelled_ticks = 0; /* updated during drawing of frame */
+
+ if (log_axis) /* logarithmic axis */
+ /* do we have special logarithmic subsubticks, and should we label them? */
+ {
+ if (max - min <=
+ MAX_DECADES_WITH_LOG_SUBSUBTICKS + FUZZ)
+ /* not too many orders of magnitude, so plot normal log subsubticks */
+ axisp->have_normal_subsubticks = true;
+ else
+ /* too many orders of magnitude, don't plot log subsubticks */
+ axisp->have_normal_subsubticks = false;
+ }
+ else /* linear axes don't have log subsubticks */
+ axisp->have_normal_subsubticks = false;
+}
+
+/* The following routines [new_multigrapher(), begin_graph(),
+ * set_graph_parameters(), draw_frame_of_graph(), plot_point(),
+ * end_graph(), delete_multigrapher()] are the basic routines of the
+ * point-plotter . See descriptions at the head of this file. */
+
+/* Create a new Multigrapher. The arguments, after the first, are the
+ libplot Plotter parameters that the `graph' user can set on the command
+ line. */
+
+Multigrapher *
+new_multigrapher (const char *output_format, const char *bg_color, const char *bitmap_size, const char *emulate_color, const char *max_line_length, const char *meta_portable, const char *page_size, const char *rotation_angle, bool save_screen)
+{
+ plPlotterParams *plotter_params;
+ plPlotter *plotter;
+ Multigrapher *multigrapher;
+
+ multigrapher = (Multigrapher *)xmalloc (sizeof (Multigrapher));
+
+ /* set Plotter parameters */
+ plotter_params = pl_newplparams ();
+ pl_setplparam (plotter_params, "BG_COLOR", (void *)bg_color);
+ pl_setplparam (plotter_params, "BITMAPSIZE", (void *)bitmap_size);
+ pl_setplparam (plotter_params, "EMULATE_COLOR", (void *)emulate_color);
+ pl_setplparam (plotter_params, "MAX_LINE_LENGTH", (void *)max_line_length);
+ pl_setplparam (plotter_params, "META_PORTABLE", (void *)meta_portable);
+ pl_setplparam (plotter_params, "PAGESIZE", (void *)page_size);
+ pl_setplparam (plotter_params, "ROTATION", (void *)rotation_angle);
+
+ /* create Plotter and open it */
+ plotter = pl_newpl_r (output_format, NULL, stdout, stderr, plotter_params);
+ if (plotter == (plPlotter *)NULL)
+ return (Multigrapher *)NULL;
+ pl_deleteplparams (plotter_params);
+ multigrapher->plotter = plotter;
+ if (pl_openpl_r (plotter) < 0)
+ return (Multigrapher *)NULL;
+ multigrapher->bg_color = bg_color;
+
+ /* if called for, erase it; set up the user->device coor map */
+ if (!save_screen || bg_color)
+ pl_erase_r (plotter);
+ pl_fspace_r (plotter, 0.0, 0.0, (double)PLOT_SIZE, (double)PLOT_SIZE);
+
+ return multigrapher;
+}
+
+int
+delete_multigrapher (Multigrapher *multigrapher)
+{
+ int retval;
+
+ retval = pl_closepl_r (multigrapher->plotter);
+ if (retval >= 0)
+ retval = pl_deletepl_r (multigrapher->plotter);
+
+ free (multigrapher);
+ return retval;
+}
+
+
+void
+begin_graph (Multigrapher *multigrapher, double scale, double trans_x, double trans_y)
+{
+ pl_savestate_r (multigrapher->plotter);
+ pl_fconcat_r (multigrapher->plotter,
+ scale, 0.0, 0.0, scale,
+ trans_x * PLOT_SIZE, trans_y * PLOT_SIZE);
+}
+
+void
+end_graph (Multigrapher *multigrapher)
+{
+ pl_restorestate_r (multigrapher->plotter);
+}
+
+
+/* ARGS:
+ Multigrapher *multigrapher
+ double frame_line_width = fractional width of lines in the frame
+ const char *frame_color = color for frame (and plot if no -C option)
+ const char *title = graph title
+ const char *title_font_name = font for graph title (string)
+ double title_font_size = font size for graph title
+ double tick_size = fractional size of ticks
+ grid_type grid_spec = gridstyle (and tickstyle) spec
+ double x_min, x_max, x_spacing
+ double y_min, y_max, y_spacing
+ bool spec_x_spacing
+ bool spec_y_spacing
+ double width, height, up, right
+ const char *x_font_name
+ double x_font_size
+ const char *x_label
+ const char *y_font_name
+ double y_font_size
+ const char *y_label
+ bool no_rotate_y_label
+
+ -- portmanteaux args --
+ int log_axis = whether axes should be logarithmic
+ int round_to_next_tick = round limits to the next tick mark
+ int switch_axis_end = put axes at right/top, not left/bottom?
+ int omit_ticks = omit all ticks, tick labels from an axis?
+ -- other args --
+ int clip_mode = 0, 1, or 2
+ double blankout_fraction = 1.0 means blank out whole box before plot
+ bool transpose_axes */
+void
+set_graph_parameters (Multigrapher *multigrapher, double frame_line_width, const char *frame_color, const char *title, const char *title_font_name, double title_font_size, double tick_size, grid_type grid_spec, double x_min, double x_max, double x_spacing, double y_min, double y_max, double y_spacing, bool spec_x_spacing, bool spec_y_spacing, double width, double height, double up, double right, const char *x_font_name, double x_font_size, const char *x_label, const char *y_font_name, double y_font_size, const char *y_label, bool no_rotate_y_label, int log_axis, int round_to_next_tick, int switch_axis_end, int omit_ticks, int clip_mode, double blankout_fraction, bool transpose_axes)
+{
+ double x_subsubtick_spacing = 0.0, y_subsubtick_spacing = 0.0;
+ /* local portmanteau variables */
+ int reverse_axis = 0; /* min > max on an axis? */
+ int user_specified_subsubticks = 0; /* i.e. linear ticks on a log axis? */
+
+ if (log_axis & X_AXIS)
+ {
+ if (spec_x_spacing)
+ /* spacing is handled specially for log axes */
+ {
+ spec_x_spacing = false;
+ user_specified_subsubticks |= X_AXIS;
+ x_subsubtick_spacing = x_spacing;
+ }
+ }
+
+ if (log_axis & Y_AXIS)
+ {
+ if (spec_y_spacing)
+ {
+ /* spacing is handled specially for log axes */
+ spec_y_spacing = false;
+ user_specified_subsubticks |= Y_AXIS;
+ y_subsubtick_spacing = y_spacing;
+ }
+ }
+
+ /* check for reversed axes (min > max) */
+ if (x_max < x_min)
+ {
+ reverse_axis |= X_AXIS;
+ {
+ double temp;
+
+ temp = x_min;
+ x_min = x_max;
+ x_max = temp;
+ }
+ }
+ if (x_max == x_min)
+ {
+ fprintf (stderr,
+ "%s: identical upper and lower x limits are separated\n",
+ progname);
+ /* separate them */
+ x_max += 1.0;
+ x_min -= 1.0;
+ }
+ /* check for reversed axes (min > max) */
+ if (y_max < y_min)
+ {
+ reverse_axis |= Y_AXIS;
+ {
+ double temp;
+
+ temp = y_min;
+ y_min = y_max;
+ y_max = temp;
+ }
+ }
+ if (y_max == y_min)
+ {
+ fprintf (stderr,
+ "%s: identical upper and lower y limits are separated\n",
+ progname);
+ /* separate them */
+ y_max += 1.0;
+ y_min -= 1.0;
+ }
+
+ /* At this point, min < max for each axis, if the user specified the two
+ limits on an axis; reverse_axis portmanteau variable keeps track of
+ whether either axis was discovered to be reversed. */
+
+ /* silently accept negative spacing as equivalent as positive */
+ if (spec_x_spacing)
+ {
+ if (x_spacing == 0.0)
+ {
+ fprintf (stderr,
+ "%s: error: the spacing between ticks on an axis is zero\n",
+ progname);
+ exit (EXIT_FAILURE);
+ }
+ x_spacing = fabs (x_spacing);
+ }
+ if (spec_y_spacing)
+ {
+ if (y_spacing == 0.0)
+ {
+ fprintf (stderr,
+ "%s: error: the spacing between ticks on an axis is zero\n",
+ progname);
+ exit (EXIT_FAILURE);
+ }
+ y_spacing = fabs (y_spacing);
+ }
+
+ /* now transpose the two axes (i.e. their portmanteau variables, labels,
+ limits etc.) if transpose_axes was set */
+ if (transpose_axes)
+ {
+ const char *temp_string;
+ double temp_double;
+
+ transpose_portmanteau (&log_axis);
+ transpose_portmanteau (&round_to_next_tick);
+ transpose_portmanteau (&switch_axis_end);
+ transpose_portmanteau (&omit_ticks);
+
+ transpose_portmanteau (&reverse_axis);
+ transpose_portmanteau (&user_specified_subsubticks);
+
+ temp_string = x_label;
+ x_label = y_label;
+ y_label = temp_string;
+
+ temp_double = x_min;
+ x_min = y_min;
+ y_min = temp_double;
+
+ temp_double = x_max;
+ x_max = y_max;
+ y_max = temp_double;
+
+ temp_double = x_spacing;
+ x_spacing = y_spacing;
+ y_spacing = temp_double;
+
+ temp_double = x_subsubtick_spacing;
+ x_subsubtick_spacing = y_subsubtick_spacing;
+ y_subsubtick_spacing = temp_double;
+ }
+
+ /* fill in the Multigrapher struct */
+
+ multigrapher->frame_line_width = frame_line_width;
+ multigrapher->frame_color = frame_color;
+ multigrapher->no_rotate_y_label = no_rotate_y_label;
+ multigrapher->blankout_fraction = blankout_fraction;
+
+ if (title != NULL)
+ multigrapher->title = xstrdup (title);
+ else
+ multigrapher->title = NULL;
+ if (title_font_name != NULL)
+ multigrapher->title_font_name = xstrdup (title_font_name);
+ else
+ multigrapher->title_font_name = NULL;
+ multigrapher->title_font_size = title_font_size;
+ multigrapher->tick_size = tick_size;
+ multigrapher->subtick_size = RELATIVE_SUBTICK_SIZE * tick_size;
+ multigrapher->grid_spec = grid_spec;
+ multigrapher->clip_mode = clip_mode;
+
+ /* fill in the Transform and Axis elements for each coordinate */
+ prepare_axis (&multigrapher->x_axis, &multigrapher->x_trans,
+ x_min, x_max, x_spacing,
+ x_font_name, x_font_size, x_label,
+ x_subsubtick_spacing,
+ (bool)(user_specified_subsubticks & X_AXIS),
+ (bool)(round_to_next_tick & X_AXIS),
+ (bool)(log_axis & X_AXIS),
+ (bool)(reverse_axis & X_AXIS),
+ (bool)(switch_axis_end & X_AXIS),
+ (bool)(omit_ticks & X_AXIS));
+ prepare_axis (&multigrapher->y_axis, &multigrapher->y_trans,
+ y_min, y_max, y_spacing,
+ y_font_name, y_font_size, y_label,
+ y_subsubtick_spacing,
+ (bool)(user_specified_subsubticks & Y_AXIS),
+ (bool)(round_to_next_tick & Y_AXIS),
+ (bool)(log_axis & Y_AXIS),
+ (bool)(reverse_axis & Y_AXIS),
+ (bool)(switch_axis_end & Y_AXIS),
+ (bool)(omit_ticks & Y_AXIS));
+
+ /* fill in additional parameters in the two Transform structures */
+ multigrapher->x_trans.squeezed_min = right;
+ multigrapher->x_trans.squeezed_max = right + width;
+ multigrapher->x_trans.squeezed_range = width;
+ multigrapher->y_trans.squeezed_min = up;
+ multigrapher->y_trans.squeezed_max = up + height;
+ multigrapher->y_trans.squeezed_range = height;
+
+ /* specify interval range for each coordinate, in libplot units */
+ multigrapher->x_trans.output_min = 0.0;
+ multigrapher->x_trans.output_max = (double)PLOT_SIZE;
+ multigrapher->x_trans.output_range = multigrapher->x_trans.output_max - multigrapher->x_trans.output_min;
+ multigrapher->x_trans.output_min = 0.0;
+ multigrapher->y_trans.output_max = (double)PLOT_SIZE;
+ multigrapher->y_trans.output_range = multigrapher->y_trans.output_max - multigrapher->y_trans.output_min;
+
+ /* fill in fields in Axis structs dealing with location of other axis */
+ if (multigrapher->grid_spec != AXES_AT_ORIGIN)
+ /* Normal case */
+ {
+ /* axes are at left/bottom */
+ multigrapher->x_axis.other_axis_loc = multigrapher->x_trans.input_min;
+ multigrapher->y_axis.other_axis_loc = multigrapher->y_trans.input_min;
+ /* secondary axes (used only if --switch-axis-end is specified) */
+ multigrapher->x_axis.alt_other_axis_loc = multigrapher->x_trans.input_max;
+ multigrapher->y_axis.alt_other_axis_loc = multigrapher->y_trans.input_max;
+ }
+ else
+ /* Special case: grid type #4, AXES_AT_ORIGIN */
+ {
+ /* In this case (grid type #4), we don't allow the user to move the
+ axis position by using the --switch-axis-end option. Each axis is
+ at the value 0 (the origin) if the value 0 is between the limits
+ of the opposing axis. Otherwise, the position is at the end
+ closer to the value of 0. */
+ multigrapher->x_axis.other_axis_loc
+ = (multigrapher->x_trans.input_min * multigrapher->x_trans.input_max <= 0.0) ? 0.0 :
+ (multigrapher->x_trans.input_min > 0.0 ? multigrapher->x_trans.input_min : multigrapher->x_trans.input_max);
+ multigrapher->y_axis.other_axis_loc
+ = (multigrapher->y_trans.input_min * multigrapher->y_trans.input_max <= 0.0) ? 0.0 :
+ (multigrapher->y_trans.input_min > 0.0 ? multigrapher->y_trans.input_min : multigrapher->y_trans.input_max);
+ /* secondary axes are the same */
+ multigrapher->x_axis.alt_other_axis_loc = multigrapher->x_axis.other_axis_loc;
+ multigrapher->y_axis.alt_other_axis_loc = multigrapher->y_axis.other_axis_loc;
+ multigrapher->x_axis.switch_axis_end = (((multigrapher->x_trans.input_max - multigrapher->x_axis.other_axis_loc)
+ < (multigrapher->x_axis.other_axis_loc - multigrapher->x_trans.input_min))
+ ? true : false);
+ multigrapher->y_axis.switch_axis_end = (((multigrapher->y_trans.input_max - multigrapher->y_axis.other_axis_loc)
+ < (multigrapher->y_axis.other_axis_loc - multigrapher->y_trans.input_min))
+ ? true : false);
+ }
+
+ /* The following is a version of (multigrapher->frame_line_width)/2
+ (expressed in terms of libplot coordinates) which the plotter uses as
+ an offset, to get highly accurate positioning of ticks and labels. */
+ if (frame_line_width < 0.0
+ || pl_havecap_r (multigrapher->plotter, "WIDE_LINES") == 0)
+ multigrapher->half_line_width = 0.0;/* N.B. <0.0 -> default width, pres. small */
+ else
+ multigrapher->half_line_width = 0.5 * frame_line_width * multigrapher->x_trans.output_range;
+
+ /* initialize the plotter state variables */
+ multigrapher->first_point_of_polyline = true;
+ multigrapher->oldpoint_x = 0.0;
+ multigrapher->oldpoint_y = 0.0;
+}
+
+
+/* draw_frame_of_graph() plots the graph frame (grid plus axis labels and
+ title), using the multigrapher's variables (the multigrapher->x_axis,
+ multigrapher->y_axis, multigrapher->x_trans, multigrapher->y_trans
+ structures). It comprises several almost-independent tasks:
+
+ 0. draw opaque white rectangle (``canvas''), if requested
+ 1. draw title, if any, above drawing box
+ 2. draw axes, and a full drawing box if requested
+ 3. draw ticks, grid lines, and tick labels on abscissa;
+ also subticks, if abscissa is a linear axis
+ 4. draw ticks, grid lines, and tick labels on ordinate,
+ also subticks, if ordinate is a linear axis
+ 5. draw sub-tick marks, sub-grid lines, and sub-labels,
+ if abscissa is a logarithmic axis
+ 6. draw sub-tick marks, sub-grid lines, and sub-labels,
+ if ordinate is a logarithmic axis
+ 7. draw axis label on abscissa
+ 8. draw axis label on ordinate (normally rotated 90 degrees)
+
+ A savestate()--restorestate() pair is wrapped around these nine tasks.
+ They are not quite independent because in (4) and (6) we keep track of
+ the maximum width of the tick labels on the ordinate, to position the
+ rotated ordinate label properly in (8).
+
+ At the conclusion of the eight tasks, we warn the user if (i) he/she
+ didn't specify a tick spacing for a logarithmic axis by hand, and (ii)
+ our default algorithm for drawing ticks on a logarithmic axis has drawn
+ too few ticks (i.e. <= 2 ticks) on the axis.
+
+ Task #0 (drawing a white canvas) isn't always performed; only if the
+ argument draw_canvas is set. It isn't set when we call this function
+ for the first time; see graph.c. */
+
+void
+draw_frame_of_graph (Multigrapher *multigrapher, bool draw_canvas)
+{
+ static bool tick_warning_printed = false; /* when too few labelled ticks */
+
+ /* wrap savestate()--restorestate() around all 9 tasks */
+ pl_savestate_r (multigrapher->plotter);
+
+ /* set color for graph frame */
+ if (multigrapher->frame_color)
+ pl_pencolorname_r (multigrapher->plotter, multigrapher->frame_color);
+
+ /* set line width as a fraction of size of display, <0.0 means default */
+ pl_flinewidth_r (multigrapher->plotter,
+ multigrapher->frame_line_width * (double)PLOT_SIZE);
+
+ /* axes (or box) will be drawn in solid line style */
+ pl_linemod_r (multigrapher->plotter, "solid");
+
+ /* turn off filling */
+ pl_filltype_r (multigrapher->plotter, 0);
+
+ /* 0. DRAW AN OPAQUE WHITE BOX */
+
+ if (draw_canvas)
+ {
+ pl_savestate_r (multigrapher->plotter);
+ /* use user-specified background color (if any) instead of white */
+ if (pl_havecap_r (multigrapher->plotter, "SETTABLE_BACKGROUND") != 0
+ && multigrapher->bg_color)
+ pl_colorname_r (multigrapher->plotter, multigrapher->bg_color);
+ else
+ pl_colorname_r (multigrapher->plotter, "white");
+
+ pl_filltype_r (multigrapher->plotter, 1); /* turn on filling */
+ pl_fbox_r (multigrapher->plotter,
+ XP(XSQ(0.5 - 0.5 * multigrapher->blankout_fraction)),
+ YP(YSQ(0.5 - 0.5 * multigrapher->blankout_fraction)),
+ XP(XSQ(0.5 + 0.5 * multigrapher->blankout_fraction)),
+ YP(YSQ(0.5 + 0.5 * multigrapher->blankout_fraction)));
+ pl_restorestate_r (multigrapher->plotter);
+ }
+
+ /* 1. DRAW THE TITLE, I.E. THE TOP LABEL */
+
+ if (multigrapher->grid_spec != NO_AXES
+ && !multigrapher->y_axis.switch_axis_end /* no title if x axis is at top of plot */
+ && multigrapher->title != NULL && *multigrapher->title != '0円')
+ {
+ double title_font_size;
+
+ /* switch to our font for drawing title */
+ pl_fontname_r (multigrapher->plotter, multigrapher->title_font_name);
+ title_font_size = pl_ffontsize_r (multigrapher->plotter,
+ SS(multigrapher->title_font_size));
+
+ pl_fmove_r (multigrapher->plotter,
+ XP(XSQ(0.5)),
+ YP(YSQ(1.0
+ + (((multigrapher->grid_spec == AXES_AND_BOX
+ || multigrapher->grid_spec == AXES)
+ && (multigrapher->tick_size <= 0.0) ? 1.0 : 0.5)
+ * fabs(multigrapher->tick_size))))
+ + 0.65 * title_font_size
+ + multigrapher->half_line_width);
+ /* title centered, bottom spec'd */
+ pl_alabel_r (multigrapher->plotter, 'c', 'b', multigrapher->title);
+ }
+
+ /* 2. DRAW AXES FOR THE PLOT */
+
+ switch (multigrapher->grid_spec)
+ {
+ case AXES_AND_BOX_AND_GRID:
+ case AXES_AND_BOX:
+ /* draw a box, not just a pair of axes */
+ pl_fbox_r (multigrapher->plotter,
+ XP(XSQ(0.0)), YP(YSQ(0.0)), XP(XSQ(1.0)), YP(YSQ(1.0)));
+ break;
+ case AXES:
+ {
+ double xstart, ystart, xmid, ymid, xend, yend;
+
+ xstart = (multigrapher->x_axis.switch_axis_end
+ ? XN(multigrapher->x_axis.other_axis_loc) - multigrapher->half_line_width
+ : XN(multigrapher->x_axis.alt_other_axis_loc) + multigrapher->half_line_width);
+ ystart = (multigrapher->y_axis.switch_axis_end
+ ? YN(multigrapher->y_axis.alt_other_axis_loc)
+ : YN(multigrapher->y_axis.other_axis_loc));
+ xmid = (multigrapher->x_axis.switch_axis_end
+ ? XN(multigrapher->x_axis.alt_other_axis_loc)
+ : XN(multigrapher->x_axis.other_axis_loc));
+ ymid = ystart;
+ xend = xmid;
+ yend = (multigrapher->y_axis.switch_axis_end
+ ? YN(multigrapher->y_axis.other_axis_loc) - multigrapher->half_line_width
+ : YN(multigrapher->y_axis.alt_other_axis_loc) + multigrapher->half_line_width);
+
+ pl_fmove_r (multigrapher->plotter, xstart, ystart);
+ pl_fcont_r (multigrapher->plotter, xmid, ymid);
+ pl_fcont_r (multigrapher->plotter, xend, yend);
+ }
+ break;
+ case AXES_AT_ORIGIN:
+ {
+ double xpos, ypos;
+
+ xpos = (multigrapher->x_axis.switch_axis_end
+ ? XN(multigrapher->x_axis.other_axis_loc)
+ : XN(multigrapher->x_axis.alt_other_axis_loc));
+ ypos = (multigrapher->y_axis.switch_axis_end
+ ? YN(multigrapher->y_axis.alt_other_axis_loc)
+ : YN(multigrapher->y_axis.other_axis_loc));
+
+ pl_fline_r (multigrapher->plotter,
+ xpos, YP(YSQ(0.0)) - multigrapher->half_line_width,
+ xpos, YP(YSQ(1.0)) + multigrapher->half_line_width);
+ pl_fline_r (multigrapher->plotter,
+ XP(XSQ(0.0)) - multigrapher->half_line_width, ypos,
+ XP(XSQ(1.0)) + multigrapher->half_line_width, ypos);
+ }
+ break;
+ case NO_AXES:
+ default:
+ break;
+ }
+
+ /* 3. PLOT TICK MARKS, GRID LINES, AND TICK LABELS ON ABSCISSA */
+
+ if (multigrapher->grid_spec != NO_AXES && !multigrapher->x_axis.omit_ticks
+ && !multigrapher->x_axis.user_specified_subsubticks)
+ {
+ int i;
+ double xval, xrange = multigrapher->x_trans.input_max - multigrapher->x_trans.input_min;
+ /* there is no way you could use longer labels on tick marks! */
+ char labelbuf[2048];
+
+ /* switch to our font for drawing x axis label and tick labels */
+ pl_fontname_r (multigrapher->plotter, multigrapher->x_axis.font_name);
+ pl_ffontsize_r (multigrapher->plotter, SS(multigrapher->x_axis.font_size));
+
+ for (i = multigrapher->x_axis.min_tick_count; i <= multigrapher->x_axis.max_tick_count; i++)
+ /* tick range can be empty */
+ {
+ xval = i * multigrapher->x_axis.tick_spacing;
+
+ /* discard tick locations outside plotting area */
+ if (xval < multigrapher->x_trans.input_min - FUZZ * xrange
+ || xval > multigrapher->x_trans.input_max + FUZZ * xrange)
+ continue;
+
+ /* Plot the abscissa tick labels. */
+ if (!multigrapher->y_axis.switch_axis_end
+ && !(multigrapher->grid_spec == AXES_AT_ORIGIN
+ /* don't plot label if it could run into an axis */
+ && NEAR_EQUALITY (xval, multigrapher->x_axis.other_axis_loc,
+ multigrapher->x_trans.input_range)
+ && (multigrapher->y_axis.other_axis_loc != multigrapher->y_trans.input_min)
+ && (multigrapher->y_axis.other_axis_loc != multigrapher->y_trans.input_max)))
+ /* print labels below bottom boundary */
+ {
+ pl_fmove_r (multigrapher->plotter,
+ XV (xval),
+ YN (multigrapher->y_axis.other_axis_loc)
+ - (SS ((multigrapher->tick_size >= 0.0 ? 0.75 : 1.75) * fabs(multigrapher->tick_size))
+ + multigrapher->half_line_width));
+ print_tick_label (labelbuf,
+ &multigrapher->x_axis, &multigrapher->x_trans,
+ (multigrapher->x_axis.type == A_LOG10) ? pow (10.0, xval) : xval);
+ pl_alabel_r (multigrapher->plotter, 'c', 't', labelbuf);
+ multigrapher->x_axis.labelled_ticks++;
+ }
+ else
+ /* print labels above top boundary */
+ if (multigrapher->y_axis.switch_axis_end
+ && !(multigrapher->grid_spec == AXES_AT_ORIGIN
+ /* don't plot label if it could run into an axis */
+ && NEAR_EQUALITY (xval, multigrapher->x_axis.other_axis_loc,
+ multigrapher->x_trans.input_range)
+ && (multigrapher->y_axis.other_axis_loc != multigrapher->y_trans.input_min)
+ && (multigrapher->y_axis.other_axis_loc != multigrapher->y_trans.input_max)))
+ {
+ pl_fmove_r (multigrapher->plotter,
+ XV (xval),
+ YN (multigrapher->y_axis.alt_other_axis_loc)
+ + (SS ((multigrapher->tick_size >= 0.0 ? 0.75 : 1.75) * fabs(multigrapher->tick_size))
+ + multigrapher->half_line_width));
+ print_tick_label (labelbuf,
+ &multigrapher->x_axis, &multigrapher->x_trans,
+ (multigrapher->x_axis.type == A_LOG10) ? pow (10.0, xval) : xval);
+ pl_alabel_r (multigrapher->plotter, 'c', 'b', labelbuf);
+ multigrapher->x_axis.labelled_ticks++;
+ }
+
+ /* Plot the abscissa tick marks, and vertical grid lines. */
+ switch (multigrapher->grid_spec)
+ {
+ case AXES_AND_BOX_AND_GRID:
+ pl_linemod_r (multigrapher->plotter, "dotted");
+ pl_fmove_r (multigrapher->plotter, XV(xval), YP(YSQ(0.0)));
+ pl_fcont_r (multigrapher->plotter, XV(xval), YP(YSQ(1.0)));
+ pl_linemod_r (multigrapher->plotter, "solid");
+ /* fall through */
+ case AXES_AND_BOX:
+ if (!multigrapher->y_axis.switch_axis_end)
+ {
+ pl_fmove_r (multigrapher->plotter,
+ XV (xval),
+ YN (multigrapher->y_axis.alt_other_axis_loc));
+ pl_fcont_r (multigrapher->plotter,
+ XV (xval),
+ YN (multigrapher->y_axis.alt_other_axis_loc)
+ - (SS (multigrapher->tick_size)
+ + (multigrapher->tick_size > 0.0 ? multigrapher->half_line_width
+ : -multigrapher->half_line_width)));
+ }
+ else
+ {
+ pl_fmove_r (multigrapher->plotter,
+ XV (xval),
+ YN (multigrapher->y_axis.other_axis_loc));
+ pl_fcont_r (multigrapher->plotter,
+ XV (xval),
+ YN (multigrapher->y_axis.other_axis_loc)
+ + (SS (multigrapher->tick_size)
+ + (multigrapher->tick_size > 0.0 ? multigrapher->half_line_width
+ : -multigrapher->half_line_width)));
+ }
+ /* fall through */
+ case AXES:
+ case AXES_AT_ORIGIN:
+ if (!multigrapher->y_axis.switch_axis_end)
+ {
+ pl_fmove_r (multigrapher->plotter,
+ XV (xval),
+ YN (multigrapher->y_axis.other_axis_loc));
+ pl_fcont_r (multigrapher->plotter,
+ XV (xval),
+ YN (multigrapher->y_axis.other_axis_loc)
+ + (SS (multigrapher->tick_size)
+ + (multigrapher->tick_size > 0.0 ? multigrapher->half_line_width
+ : -multigrapher->half_line_width)));
+ }
+ else
+ {
+ pl_fmove_r (multigrapher->plotter,
+ XV (xval),
+ YN (multigrapher->y_axis.alt_other_axis_loc));
+ pl_fcont_r (multigrapher->plotter,
+ XV (xval),
+ YN (multigrapher->y_axis.alt_other_axis_loc)
+ - (SS (multigrapher->tick_size)
+ + (multigrapher->tick_size > 0.0 ? multigrapher->half_line_width
+ : -multigrapher->half_line_width)));
+ }
+ break;
+ default: /* shouldn't happen */
+ break;
+ }
+ }
+
+ if (multigrapher->x_axis.have_lin_subticks)
+ {
+ double subtick_size; /* libplot coordinates */
+
+ /* linearly spaced subticks on log axes are as long as reg. ticks */
+ subtick_size = (multigrapher->x_axis.type == A_LOG10
+ ? SS(multigrapher->tick_size) : SS(multigrapher->subtick_size));
+
+ /* Plot the linearly spaced subtick marks on the abscissa */
+ for (i = multigrapher->x_axis.min_lin_subtick_count; i <= multigrapher->x_axis.max_lin_subtick_count; i++)
+ /* tick range can be empty */
+ {
+ xval = i * multigrapher->x_axis.lin_subtick_spacing;
+
+ /* discard subtick locations outside plotting area */
+ if (xval < multigrapher->x_trans.input_min - FUZZ * xrange
+ || xval > multigrapher->x_trans.input_max + FUZZ * xrange)
+ continue;
+
+ switch (multigrapher->grid_spec)
+ {
+ case AXES_AND_BOX_AND_GRID:
+ case AXES_AND_BOX:
+ /* draw on both sides */
+ if (!multigrapher->y_axis.switch_axis_end)
+ {
+ pl_fmove_r (multigrapher->plotter,
+ XV (xval),
+ YN (multigrapher->y_axis.alt_other_axis_loc));
+ pl_fcont_r (multigrapher->plotter,
+ XV (xval),
+ YN (multigrapher->y_axis.alt_other_axis_loc)
+ - (subtick_size
+ + (subtick_size > 0.0 ? multigrapher->half_line_width
+ : -multigrapher->half_line_width)));
+ }
+ else
+ {
+ pl_fmove_r (multigrapher->plotter,
+ XV (xval),
+ YN (multigrapher->y_axis.other_axis_loc));
+ pl_fcont_r (multigrapher->plotter,
+ XV (xval),
+ YN (multigrapher->y_axis.other_axis_loc)
+ + (subtick_size
+ + (subtick_size > 0.0 ? multigrapher->half_line_width
+ : -multigrapher->half_line_width)));
+ }
+ /* fall through */
+ case AXES:
+ case AXES_AT_ORIGIN:
+ if (!multigrapher->y_axis.switch_axis_end)
+ /* draw on only one side */
+ {
+ pl_fmove_r (multigrapher->plotter,
+ XV (xval),
+ YN (multigrapher->y_axis.other_axis_loc));
+ pl_fcont_r (multigrapher->plotter,
+ XV (xval),
+ YN (multigrapher->y_axis.other_axis_loc)
+ + (subtick_size
+ + (subtick_size > 0.0 ? multigrapher->half_line_width
+ : -multigrapher->half_line_width)));
+ }
+ else
+ {
+ pl_fmove_r (multigrapher->plotter,
+ XV (xval),
+ YN (multigrapher->y_axis.alt_other_axis_loc));
+ pl_fcont_r (multigrapher->plotter,
+ XV (xval),
+ YN (multigrapher->y_axis.alt_other_axis_loc)
+ - (subtick_size
+ + (subtick_size > 0.0 ? multigrapher->half_line_width
+ : -multigrapher->half_line_width)));
+ }
+ break;
+ default: /* shouldn't happen */
+ break;
+ }
+ }
+ }
+
+ /* plot a vertical dotted line at x = 0 */
+ if (multigrapher->grid_spec != AXES_AT_ORIGIN
+ && multigrapher->x_axis.type == A_LINEAR
+ && multigrapher->x_trans.input_min * multigrapher->x_trans.input_max < 0.0)
+ {
+ pl_linemod_r (multigrapher->plotter, "dotted");
+ pl_fline_r (multigrapher->plotter,
+ XV(0.0), YP(YSQ(0.0)), XV(0.0), YP(YSQ(1.0)));
+ pl_linemod_r (multigrapher->plotter, "solid");
+ }
+ }
+
+ /* 4. PLOT TICK MARKS, GRID LINES, AND TICK LABELS ON ORDINATE */
+
+ if (multigrapher->grid_spec != NO_AXES && !multigrapher->y_axis.omit_ticks
+ && !multigrapher->y_axis.user_specified_subsubticks)
+ {
+ int i;
+ double yval, yrange = multigrapher->y_trans.input_max - multigrapher->y_trans.input_min;
+ /* there is no way you could use longer labels on tick marks! */
+ char labelbuf[2048];
+
+ /* switch to our font for drawing y axis label and tick labels */
+ pl_fontname_r (multigrapher->plotter, multigrapher->y_axis.font_name);
+ pl_ffontsize_r (multigrapher->plotter, SS(multigrapher->y_axis.font_size));
+
+ for (i = multigrapher->y_axis.min_tick_count; i <= multigrapher->y_axis.max_tick_count; i++)
+ /* range can be empty */
+ {
+ yval = i * multigrapher->y_axis.tick_spacing;
+
+ /* discard tick locations outside plotting area */
+ if (yval < multigrapher->y_trans.input_min - FUZZ * yrange
+ || yval > multigrapher->y_trans.input_max + FUZZ * yrange)
+ continue;
+
+ /* Plot the ordinate tick labels. */
+ if (!multigrapher->x_axis.switch_axis_end
+ && !(multigrapher->grid_spec == AXES_AT_ORIGIN
+ /* don't plot label if it could run into an axis */
+ && NEAR_EQUALITY (yval, multigrapher->y_axis.other_axis_loc,
+ multigrapher->y_trans.input_range)
+ && (multigrapher->x_axis.other_axis_loc != multigrapher->x_trans.input_min)
+ && (multigrapher->x_axis.other_axis_loc != multigrapher->x_trans.input_max)))
+ /* print labels to left of left boundary */
+ {
+ double new_width;
+
+ pl_fmove_r (multigrapher->plotter,
+ XN (multigrapher->x_axis.other_axis_loc)
+ - (SS((multigrapher->tick_size >= 0.0 ? 0.75 : 1.75)
+ * fabs(multigrapher->tick_size))
+ + multigrapher->half_line_width),
+ YV (yval));
+ print_tick_label (labelbuf,
+ &multigrapher->y_axis, &multigrapher->y_trans,
+ (multigrapher->y_axis.type == A_LOG10) ? pow (10.0, yval) : yval);
+ new_width = pl_flabelwidth_r (multigrapher->plotter, labelbuf);
+ pl_alabel_r (multigrapher->plotter, 'r', 'c', labelbuf);
+ multigrapher->y_axis.max_label_width = DMAX(multigrapher->y_axis.max_label_width, new_width);
+ multigrapher->y_axis.labelled_ticks++;
+ }
+ else
+ /* print labels to right of right boundary */
+ if (multigrapher->x_axis.switch_axis_end
+ && !(multigrapher->grid_spec == AXES_AT_ORIGIN
+ /* don't plot label if it could run into an axis */
+ && NEAR_EQUALITY (yval, multigrapher->y_axis.other_axis_loc,
+ multigrapher->y_trans.input_range)
+ && (multigrapher->x_axis.other_axis_loc != multigrapher->x_trans.input_min)
+ && (multigrapher->x_axis.other_axis_loc != multigrapher->x_trans.input_max)))
+ {
+ double new_width;
+
+ pl_fmove_r (multigrapher->plotter,
+ XN (multigrapher->x_axis.alt_other_axis_loc)
+ + (SS((multigrapher->tick_size >= 0.0 ? 0.75 : 1.75)
+ * fabs(multigrapher->tick_size))
+ + multigrapher->half_line_width),
+ YV (yval));
+ print_tick_label (labelbuf,
+ &multigrapher->y_axis, &multigrapher->y_trans,
+ (multigrapher->y_axis.type == A_LOG10) ? pow (10.0, yval) : yval);
+ new_width = pl_flabelwidth_r (multigrapher->plotter, labelbuf);
+ pl_alabel_r (multigrapher->plotter, 'l', 'c', labelbuf);
+ multigrapher->y_axis.max_label_width = DMAX(multigrapher->y_axis.max_label_width, new_width);
+ multigrapher->y_axis.labelled_ticks++;
+ }
+
+ /* Plot the tick marks on the y-axis, and horizontal grid lines. */
+ switch (multigrapher->grid_spec)
+ {
+ case AXES_AND_BOX_AND_GRID:
+ pl_linemod_r (multigrapher->plotter, "dotted");
+ pl_fmove_r (multigrapher->plotter, XP(XSQ(0.0)), YV (yval));
+ pl_fcont_r (multigrapher->plotter, XP(XSQ(1.0)), YV (yval));
+ pl_linemod_r (multigrapher->plotter, "solid");
+ /* fall through */
+ case AXES_AND_BOX:
+ if (!multigrapher->x_axis.switch_axis_end)
+ {
+ pl_fmove_r (multigrapher->plotter,
+ XN (multigrapher->x_axis.alt_other_axis_loc),
+ YV (yval));
+ pl_fcont_r (multigrapher->plotter,
+ XN (multigrapher->x_axis.alt_other_axis_loc)
+ - (SS (multigrapher->tick_size)
+ + (multigrapher->tick_size > 0.0 ? multigrapher->half_line_width
+ : -multigrapher->half_line_width)),
+ YV (yval));
+ }
+ else
+ {
+ pl_fmove_r (multigrapher->plotter,
+ XN (multigrapher->x_axis.other_axis_loc),
+ YV (yval));
+ pl_fcont_r (multigrapher->plotter,
+ XN (multigrapher->x_axis.other_axis_loc)
+ + (SS (multigrapher->tick_size)
+ + (multigrapher->tick_size > 0.0 ? multigrapher->half_line_width
+ : -multigrapher->half_line_width)),
+ YV (yval));
+ }
+ /* fall through */
+ case AXES:
+ case AXES_AT_ORIGIN:
+ if (!multigrapher->x_axis.switch_axis_end)
+ {
+ pl_fmove_r (multigrapher->plotter,
+ XN (multigrapher->x_axis.other_axis_loc),
+ YV (yval));
+ pl_fcont_r (multigrapher->plotter,
+ XN (multigrapher->x_axis.other_axis_loc)
+ + (SS (multigrapher->tick_size)
+ + (multigrapher->tick_size > 0.0 ? multigrapher->half_line_width
+ : -multigrapher->half_line_width)),
+ YV (yval));
+ }
+ else
+ {
+ pl_fmove_r (multigrapher->plotter,
+ XN (multigrapher->x_axis.alt_other_axis_loc),
+ YV (yval));
+ pl_fcont_r (multigrapher->plotter,
+ XN (multigrapher->x_axis.alt_other_axis_loc)
+ - (SS (multigrapher->tick_size)
+ + (multigrapher->tick_size > 0.0 ? multigrapher->half_line_width
+ : -multigrapher->half_line_width)),
+ YV (yval));
+ }
+ break;
+ default: /* shouldn't happen */
+ break;
+ }
+ }
+
+ if (multigrapher->y_axis.have_lin_subticks)
+ {
+ double subtick_size; /* libplot coordinates */
+
+ /* linearly spaced subticks on a log axis are as long as regular ticks */
+ subtick_size = (multigrapher->y_axis.type == A_LOG10
+ ? SS(multigrapher->tick_size) : SS(multigrapher->subtick_size));
+
+ /* Plot the linearly spaced subtick marks on the ordinate */
+ for (i = multigrapher->y_axis.min_lin_subtick_count; i <= multigrapher->y_axis.max_lin_subtick_count; i++)
+ /* range can be empty */
+ {
+ yval = i * multigrapher->y_axis.lin_subtick_spacing;
+
+ /* discard subtick locations outside plotting area */
+ if (yval < multigrapher->y_trans.input_min - FUZZ * yrange
+ || yval > multigrapher->y_trans.input_max + FUZZ * yrange)
+ continue;
+
+ /* Plot the tick marks on the y-axis, and horizontal grid lines. */
+ switch (multigrapher->grid_spec)
+ {
+ case AXES_AND_BOX_AND_GRID:
+ case AXES_AND_BOX:
+ if (!multigrapher->x_axis.switch_axis_end)
+ {
+ pl_fmove_r (multigrapher->plotter,
+ XN (multigrapher->x_axis.alt_other_axis_loc),
+ YV (yval));
+ pl_fcont_r (multigrapher->plotter,
+ XN (multigrapher->x_axis.alt_other_axis_loc)
+ - (subtick_size
+ + (subtick_size > 0.0 ? multigrapher->half_line_width
+ : -multigrapher->half_line_width)),
+ YV (yval));
+ }
+ else
+ {
+ pl_fmove_r (multigrapher->plotter,
+ XN (multigrapher->x_axis.other_axis_loc),
+ YV (yval));
+ pl_fcont_r (multigrapher->plotter,
+ XN (multigrapher->x_axis.other_axis_loc)
+ + (subtick_size
+ + (subtick_size > 0.0 ? multigrapher->half_line_width
+ : -multigrapher->half_line_width)),
+ YV (yval));
+ }
+ /* fall through */
+ case AXES:
+ case AXES_AT_ORIGIN:
+ if (!multigrapher->x_axis.switch_axis_end)
+ {
+ pl_fmove_r (multigrapher->plotter,
+ XN (multigrapher->x_axis.other_axis_loc),
+ YV (yval));
+ pl_fcont_r (multigrapher->plotter,
+ XN (multigrapher->x_axis.other_axis_loc)
+ + (subtick_size
+ + (subtick_size > 0.0 ? multigrapher->half_line_width
+ : -multigrapher->half_line_width)),
+ YV (yval));
+ }
+ else
+ {
+ pl_fmove_r (multigrapher->plotter,
+ XN (multigrapher->x_axis.alt_other_axis_loc),
+ YV (yval));
+ pl_fcont_r (multigrapher->plotter,
+ XN (multigrapher->x_axis.alt_other_axis_loc)
+ - (subtick_size
+ + (subtick_size > 0.0 ? multigrapher->half_line_width
+ : -multigrapher->half_line_width)),
+ YV (yval));
+ }
+ break;
+ default: /* shouldn't happen */
+ break;
+ }
+ }
+ }
+
+ /* plot a horizontal dotted line at y = 0 */
+ if (multigrapher->grid_spec != AXES_AT_ORIGIN
+ && multigrapher->y_axis.type == A_LINEAR
+ && multigrapher->y_trans.input_min * multigrapher->y_trans.input_max < 0.0)
+ {
+ pl_linemod_r (multigrapher->plotter, "dotted");
+ pl_fline_r (multigrapher->plotter,
+ XP(XSQ(0.0)), YV(0.0), XP(XSQ(1.0)), YV(0.0));
+ pl_linemod_r (multigrapher->plotter, "solid");
+ }
+ }
+
+ /* 5. DRAW LOGARITHMIC SUBSUBTICKS AND THEIR LABELS ON ABSCISSA */
+
+ /* first, draw normal logarithmic subsubticks if any */
+ if (multigrapher->grid_spec != NO_AXES && multigrapher->x_axis.have_normal_subsubticks
+ && !multigrapher->x_axis.user_specified_subsubticks && !multigrapher->x_axis.omit_ticks)
+ {
+ int i, m, imin, imax;
+ double xval, xrange = multigrapher->x_trans.input_max - multigrapher->x_trans.input_min;
+
+ /* compute an integer range (of powers of 10) large enough to include
+ the entire desired axis */
+ imin = (int)(floor (multigrapher->x_trans.input_min - FUZZ * xrange));
+ imax = (int)(ceil (multigrapher->x_trans.input_max + FUZZ * xrange));
+
+ for (i = imin; i < imax; i++)
+ {
+ for (m = 1; m <= 9 ; m++)
+ {
+ xval = i + log10 ((double)m);
+
+ /* Plot subsubtick and label, if desired. */
+ /* N.B. if tick is outside axis range, nothing will be printed */
+ plot_abscissa_log_subsubtick (multigrapher, xval);
+ }
+ }
+ }
+
+ /* second, draw user-specified logarithmic subsubticks instead, if any */
+ if (multigrapher->grid_spec != NO_AXES && multigrapher->x_axis.user_specified_subsubticks
+ && !multigrapher->x_axis.omit_ticks)
+ {
+ int i, imin, imax;
+ double xval, xrange = multigrapher->x_trans.input_max - multigrapher->x_trans.input_min;
+
+ /* compute an integer range large enough to include the entire
+ desired axis */
+ imin = (int)(floor (pow (10.0, multigrapher->x_trans.input_min - FUZZ * xrange)
+ / multigrapher->x_axis.subsubtick_spacing));
+ imax = (int)(ceil (pow (10.0, multigrapher->x_trans.input_max + FUZZ * xrange)
+ / multigrapher->x_axis.subsubtick_spacing));
+
+ /* draw user-specified subsubticks */
+ for (i = imin; i <= imax; i++)
+ {
+ xval = log10 (i * multigrapher->x_axis.subsubtick_spacing);
+
+ /* Plot subsubtick and label, if desired. */
+ /* N.B. if tick is outside axis range, nothing will be printed */
+ plot_abscissa_log_subsubtick (multigrapher, xval);
+ }
+ }
+
+ /* 6. DRAW LOGARITHMIC SUBSUBTICKS AND THEIR LABELS ON ORDINATE */
+
+ /* first, draw normal logarithmic subsubticks if any */
+ if (multigrapher->grid_spec != NO_AXES && multigrapher->y_axis.have_normal_subsubticks
+ && !multigrapher->y_axis.user_specified_subsubticks && !multigrapher->y_axis.omit_ticks)
+ {
+ int i, m, imin, imax;
+ double yval, yrange = multigrapher->y_trans.input_max - multigrapher->y_trans.input_min;
+
+ /* compute an integer range (of powers of 10) large enough to include
+ the entire desired axis */
+ imin = (int)(floor (multigrapher->y_trans.input_min - FUZZ * yrange));
+ imax = (int)(ceil (multigrapher->y_trans.input_max + FUZZ * yrange));
+
+ /* draw normal subticks */
+ for (i = imin; i < imax; i++)
+ {
+ for (m = 1; m <= 9; m++)
+ {
+ yval = i + log10 ((double)m);
+
+ /* Plot subsubtick and label, if desired. */
+ /* N.B. if tick is outside axis range, nothing will be printed */
+ plot_ordinate_log_subsubtick (multigrapher, yval);
+ }
+ }
+ }
+
+ /* second, draw user-specified logarithmic subsubticks instead, if any */
+ if (multigrapher->grid_spec != NO_AXES && multigrapher->y_axis.user_specified_subsubticks
+ && !multigrapher->y_axis.omit_ticks)
+ {
+ int i, imin, imax;
+ double yval, yrange = multigrapher->y_trans.input_max - multigrapher->y_trans.input_min;
+
+ /* compute an integer range large enough to include the entire
+ desired axis */
+ imin = (int)(floor (pow (10.0, multigrapher->y_trans.input_min - FUZZ * yrange)
+ / multigrapher->y_axis.subsubtick_spacing));
+ imax = (int)(ceil (pow (10.0, multigrapher->y_trans.input_max + FUZZ * yrange)
+ / multigrapher->y_axis.subsubtick_spacing));
+
+ /* draw user-specified subsubticks */
+ for (i = imin; i <= imax; i++)
+ {
+ yval = log10 (i * multigrapher->y_axis.subsubtick_spacing);
+
+ /* Plot subsubtick and label, if desired. */
+ /* N.B. if tick is outside axis range, nothing will be printed */
+ plot_ordinate_log_subsubtick (multigrapher, yval);
+ }
+ }
+
+ /* 7. DRAW THE ABSCISSA LABEL */
+
+ if ((multigrapher->grid_spec != NO_AXES)
+ && multigrapher->x_axis.label != NULL && multigrapher->x_axis.label != '0円')
+ {
+ double x_axis_font_size;
+ double xloc;
+
+ /* switch to our font for drawing x axis label and tick labels */
+ pl_fontname_r (multigrapher->plotter, multigrapher->x_axis.font_name);
+ x_axis_font_size = pl_ffontsize_r (multigrapher->plotter,
+ SS(multigrapher->x_axis.font_size));
+
+ if (multigrapher->grid_spec != AXES_AT_ORIGIN)
+ /* center the label on the axis */
+ xloc = 0.5 * (multigrapher->x_trans.input_max + multigrapher->x_trans.input_min);
+ else
+ {
+ if ((multigrapher->y_axis.other_axis_loc == multigrapher->y_trans.input_min)
+ || (multigrapher->y_axis.other_axis_loc == multigrapher->y_trans.input_max))
+
+ xloc = 0.5 * (multigrapher->x_trans.input_max + multigrapher->x_trans.input_min);
+ else
+ /* center label in the larger of the two halves */
+ xloc =
+ multigrapher->x_trans.input_max-multigrapher->x_axis.other_axis_loc >= multigrapher->x_axis.other_axis_loc-multigrapher->x_trans.input_min ?
+ 0.5 * (multigrapher->x_trans.input_max + multigrapher->x_axis.other_axis_loc) :
+ 0.5 * (multigrapher->x_axis.other_axis_loc + multigrapher->x_trans.input_min);
+ }
+
+ if (!multigrapher->y_axis.switch_axis_end) /* axis on bottom, label below it */
+ {
+ pl_fmove_r (multigrapher->plotter,
+ XV (xloc),
+ YN (multigrapher->y_axis.other_axis_loc)
+ - (SS ((multigrapher->tick_size >= 0.0 ? 0.875 : 2.125)
+ * fabs(multigrapher->tick_size))
+ + (6 * x_axis_font_size)/5
+ + multigrapher->half_line_width));
+ pl_alabel_r (multigrapher->plotter,
+ 'c', 't', multigrapher->x_axis.label);
+ }
+ else /* axis on top, label above it */
+ {
+ pl_fmove_r (multigrapher->plotter,
+ XV (xloc),
+ YN (multigrapher->y_axis.alt_other_axis_loc)
+ + (SS ((multigrapher->tick_size >= 0.0 ? 0.875 : 2.125)
+ * fabs(multigrapher->tick_size))
+ + (6 * x_axis_font_size)/5
+ + multigrapher->half_line_width));
+ pl_alabel_r (multigrapher->plotter,
+ 'c', 'b', multigrapher->x_axis.label);
+ }
+ }
+
+ /* 8. DRAW THE ORDINATE LABEL */
+
+ if ((multigrapher->grid_spec != NO_AXES)
+ && (multigrapher->y_axis.label != NULL && *(multigrapher->y_axis.label) != '0円'))
+ {
+ double y_axis_font_size;
+ double yloc;
+
+ /* switch to our font for drawing y axis label and tick labels */
+ pl_fontname_r (multigrapher->plotter, multigrapher->y_axis.font_name);
+ y_axis_font_size = pl_ffontsize_r (multigrapher->plotter,
+ SS(multigrapher->y_axis.font_size));
+
+ if (multigrapher->grid_spec != AXES_AT_ORIGIN)
+ /* center the label on the axis */
+ yloc = 0.5 * (multigrapher->y_trans.input_min + multigrapher->y_trans.input_max);
+ else
+ {
+ if ((multigrapher->x_axis.other_axis_loc == multigrapher->x_trans.input_min)
+ || (multigrapher->x_axis.other_axis_loc == multigrapher->x_trans.input_max))
+ yloc = 0.5 * (multigrapher->y_trans.input_min + multigrapher->y_trans.input_max);
+ else
+ /* center label in the larger of the two halves */
+ yloc =
+ multigrapher->y_trans.input_max-multigrapher->y_axis.other_axis_loc >= multigrapher->y_axis.other_axis_loc-multigrapher->y_trans.input_min ?
+ 0.5 * (multigrapher->y_trans.input_max + multigrapher->y_axis.other_axis_loc) :
+ 0.5 * (multigrapher->y_axis.other_axis_loc + multigrapher->y_trans.input_min);
+ }
+
+/* a relic of temps perdus */
+#define libplot_has_font_metrics 1
+
+ if (!multigrapher->x_axis.switch_axis_end)
+ {
+ pl_fmove_r (multigrapher->plotter,
+ XN (multigrapher->x_axis.other_axis_loc)
+ - (libplot_has_font_metrics ?
+ (SS((multigrapher->tick_size >= 0.0 ? 0.75 : 1.75)
+ * fabs(multigrapher->tick_size))
+ + 1.15 * multigrapher->y_axis.max_label_width
+ + 0.5 * y_axis_font_size
+ + multigrapher->half_line_width)
+ : (SS((multigrapher->tick_size >= 0.0 ? 0.75 : 1.75)
+ * fabs(multigrapher->tick_size)) /* backup */
+ + 1.0 * y_axis_font_size
+ + multigrapher->half_line_width)),
+ YV(yloc));
+
+ if (libplot_has_font_metrics
+ && !multigrapher->no_rotate_y_label) /* can rotate label */
+ {
+ pl_textangle_r (multigrapher->plotter, 90);
+ pl_alabel_r (multigrapher->plotter,
+ 'c', 'x', multigrapher->y_axis.label);
+ pl_textangle_r (multigrapher->plotter, 0);
+ }
+ else
+ /* non-rotated axis label, right justified */
+ pl_alabel_r (multigrapher->plotter,
+ 'r', 'c', multigrapher->y_axis.label);
+ }
+ else
+ {
+ pl_fmove_r (multigrapher->plotter,
+ XN (multigrapher->x_axis.alt_other_axis_loc)
+ + (libplot_has_font_metrics ?
+ (SS((multigrapher->tick_size >= 0.0 ? 0.75 : 1.75)
+ * fabs(multigrapher->tick_size))
+ + 1.15 * multigrapher->y_axis.max_label_width
+ + 0.5 * y_axis_font_size
+ + multigrapher->half_line_width)
+ : (SS((multigrapher->tick_size >= 0.0 ? 0.75 : 1.75)
+ * fabs(multigrapher->tick_size)) /* backup */
+ + 1.0 * y_axis_font_size
+ + multigrapher->half_line_width)),
+ YV(yloc));
+
+ if (libplot_has_font_metrics
+ && !multigrapher->no_rotate_y_label) /* can rotate label */
+ {
+ pl_textangle_r (multigrapher->plotter, 90);
+ pl_alabel_r (multigrapher->plotter,
+ 'c', 't', multigrapher->y_axis.label);
+ pl_textangle_r (multigrapher->plotter, 0);
+ }
+ else
+ /* non-rotated axis label, left justified */
+ pl_alabel_r (multigrapher->plotter,
+ 'l', 'c', multigrapher->y_axis.label);
+ }
+ }
+
+ /* END OF TASKS */
+
+ /* flush frame to device */
+ pl_flushpl_r (multigrapher->plotter);
+
+ pl_restorestate_r (multigrapher->plotter);
+
+ if (multigrapher->grid_spec != NO_AXES)
+ {
+ if (!tick_warning_printed &&
+ ((!multigrapher->x_axis.omit_ticks && multigrapher->x_axis.labelled_ticks <= 2)
+ || (!multigrapher->y_axis.omit_ticks && multigrapher->y_axis.labelled_ticks <= 2)))
+ {
+ fprintf (stderr,
+ "%s: the tick spacing is adjusted, as there were too few labelled axis ticks\n",
+ progname);
+ tick_warning_printed = true;
+ }
+ }
+}
+
+
+
+/* plot_abscissa_log_subsubtick() and plot_ordinate_log_subsubtick() are
+ called to plot both normal log subticks and special (user-requested)
+ ones */
+
+/* ARGS: xval = log of location */
+static void
+plot_abscissa_log_subsubtick (Multigrapher *multigrapher, double xval)
+{
+ double xrange = multigrapher->x_trans.input_max - multigrapher->x_trans.input_min;
+ /* there is no way you could use longer labels on tick marks! */
+ char labelbuf[2048];
+ double tick_size = SS(multigrapher->tick_size); /* for positioning labels */
+ double subsubtick_size = SS(multigrapher->subtick_size);
+
+ /* switch to our font for drawing x axis label and tick labels */
+ pl_fontname_r (multigrapher->plotter, multigrapher->x_axis.font_name);
+ pl_ffontsize_r (multigrapher->plotter, SS(multigrapher->x_axis.font_size));
+
+ /* discard subsubtick locations outside plotting area */
+ if (xval < multigrapher->x_trans.input_min - FUZZ * xrange
+ || xval > multigrapher->x_trans.input_max + FUZZ * xrange)
+ return;
+
+ /* label subsubtick if it seems appropriate */
+ if (multigrapher->x_axis.user_specified_subsubticks)
+ {
+ print_tick_label (labelbuf,
+ &multigrapher->x_axis, &multigrapher->x_trans,
+ pow (10.0, xval));
+ if (!multigrapher->y_axis.switch_axis_end)
+ {
+ pl_fmove_r (multigrapher->plotter,
+ XV (xval),
+ YN (multigrapher->y_axis.other_axis_loc)
+ - ((tick_size >= 0 ? 0.75 : 1.75)
+ * fabs((double)tick_size)
+ + multigrapher->half_line_width));
+ pl_alabel_r (multigrapher->plotter, 'c', 't', labelbuf);
+ multigrapher->x_axis.labelled_ticks++;
+ }
+ else
+ {
+ pl_fmove_r (multigrapher->plotter,
+ XV (xval),
+ YN (multigrapher->y_axis.alt_other_axis_loc)
+ + ((tick_size >= 0 ? 0.75 : 1.75)
+ * fabs((double)tick_size)
+ + multigrapher->half_line_width));
+ pl_alabel_r (multigrapher->plotter, 'c', 'b', labelbuf);
+ multigrapher->x_axis.labelled_ticks++;
+ }
+ }
+
+ /* draw subsubtick */
+ switch (multigrapher->grid_spec)
+ {
+ case AXES_AND_BOX_AND_GRID:
+ pl_linemod_r (multigrapher->plotter, "dotted");
+ pl_fmove_r (multigrapher->plotter, XV (xval), YP(YSQ(0.0)));
+ pl_fcont_r (multigrapher->plotter, XV (xval), YP(YSQ(1.0)));
+ pl_linemod_r (multigrapher->plotter, "solid");
+ /* fall through */
+ case AXES_AND_BOX:
+ if (!multigrapher->y_axis.switch_axis_end)
+ {
+ pl_fmove_r (multigrapher->plotter,
+ XV (xval),
+ YN (multigrapher->y_axis.alt_other_axis_loc));
+ pl_fcont_r (multigrapher->plotter,
+ XV (xval),
+ YN (multigrapher->y_axis.alt_other_axis_loc)
+ - (subsubtick_size
+ + (subsubtick_size > 0.0
+ ? multigrapher->half_line_width
+ : -multigrapher->half_line_width)));
+ }
+ else
+ {
+ pl_fmove_r (multigrapher->plotter,
+ XV (xval),
+ YN (multigrapher->y_axis.other_axis_loc));
+ pl_fcont_r (multigrapher->plotter,
+ XV (xval),
+ YN (multigrapher->y_axis.other_axis_loc)
+ + (subsubtick_size
+ + (subsubtick_size > 0.0
+ ? multigrapher->half_line_width
+ : -multigrapher->half_line_width)));
+ }
+ /* fall through */
+ case AXES:
+ case AXES_AT_ORIGIN:
+ if (!multigrapher->y_axis.switch_axis_end)
+ {
+ pl_fmove_r (multigrapher->plotter,
+ XV (xval),
+ YN (multigrapher->y_axis.other_axis_loc));
+ pl_fcont_r (multigrapher->plotter,
+ XV (xval),
+ YN (multigrapher->y_axis.other_axis_loc)
+ + (subsubtick_size
+ + (subsubtick_size > 0.0
+ ? multigrapher->half_line_width
+ : -multigrapher->half_line_width)));
+ }
+ else
+ {
+ pl_fmove_r (multigrapher->plotter,
+ XV (xval),
+ YN (multigrapher->y_axis.alt_other_axis_loc));
+ pl_fcont_r (multigrapher->plotter,
+ XV (xval),
+ YN (multigrapher->y_axis.alt_other_axis_loc)
+ - (subsubtick_size
+ + (subsubtick_size > 0.0
+ ? multigrapher->half_line_width
+ : -multigrapher->half_line_width)));
+ }
+ break;
+ default: /* shouldn't happen */
+ break;
+ }
+}
+
+/* ARGS: yval = log of location */
+static void
+plot_ordinate_log_subsubtick (Multigrapher *multigrapher, double yval)
+{
+ double yrange = multigrapher->y_trans.input_max - multigrapher->y_trans.input_min;
+ /* there is no way you could use longer labels on tick marks! */
+ char labelbuf[2048];
+ double tick_size = SS(multigrapher->tick_size); /* for positioning labels */
+ double subsubtick_size = SS(multigrapher->subtick_size);
+
+ /* switch to our font for drawing y axis label and tick labels */
+ pl_fontname_r (multigrapher->plotter, multigrapher->y_axis.font_name);
+ pl_ffontsize_r (multigrapher->plotter, SS(multigrapher->y_axis.font_size));
+
+ /* discard subsubtick locations outside plotting area */
+ if (yval < multigrapher->y_trans.input_min - FUZZ * yrange
+ || yval > multigrapher->y_trans.input_max + FUZZ * yrange)
+ return;
+
+ /* label subsubtick if it seems appropriate */
+ if (multigrapher->y_axis.user_specified_subsubticks)
+ {
+ double new_width;
+
+ print_tick_label (labelbuf,
+ &multigrapher->y_axis, &multigrapher->y_trans,
+ pow (10.0, yval));
+ if (!multigrapher->x_axis.switch_axis_end)
+ {
+ pl_fmove_r (multigrapher->plotter,
+ XN(multigrapher->x_axis.other_axis_loc)
+ - ((tick_size >= 0 ? 0.75 : 1.75)
+ * fabs((double)tick_size)
+ + multigrapher->half_line_width),
+ YV (yval));
+ new_width = pl_flabelwidth_r (multigrapher->plotter, labelbuf);
+ pl_alabel_r (multigrapher->plotter, 'r', 'c', labelbuf);
+ multigrapher->y_axis.max_label_width = DMAX(multigrapher->y_axis.max_label_width, new_width);
+ multigrapher->y_axis.labelled_ticks++;
+ }
+ else
+ {
+ pl_fmove_r (multigrapher->plotter,
+ XN(multigrapher->x_axis.alt_other_axis_loc)
+ + ((tick_size >= 0 ? 0.75 : 1.75)
+ * fabs((double)tick_size)
+ + multigrapher->half_line_width),
+ YV (yval));
+ new_width = pl_flabelwidth_r (multigrapher->plotter, labelbuf);
+ pl_alabel_r (multigrapher->plotter, 'l', 'c', labelbuf);
+ multigrapher->y_axis.max_label_width = DMAX(multigrapher->y_axis.max_label_width, new_width);
+ multigrapher->y_axis.labelled_ticks++;
+ }
+ }
+
+ /* draw subsubtick */
+ switch (multigrapher->grid_spec)
+ {
+ case AXES_AND_BOX_AND_GRID:
+ pl_linemod_r (multigrapher->plotter, "dotted");
+ pl_fmove_r (multigrapher->plotter, XP(XSQ(0.0)), YV (yval));
+ pl_fcont_r (multigrapher->plotter, XP(XSQ(1.0)), YV (yval));
+ pl_linemod_r (multigrapher->plotter, "solid");
+ /* fall through */
+ case AXES_AND_BOX:
+ if (!multigrapher->x_axis.switch_axis_end)
+ {
+ pl_fmove_r (multigrapher->plotter,
+ XN (multigrapher->x_axis.alt_other_axis_loc),
+ YV (yval));
+ pl_fcont_r (multigrapher->plotter,
+ XN (multigrapher->x_axis.alt_other_axis_loc)
+ - (subsubtick_size
+ + (subsubtick_size > 0.0
+ ? multigrapher->half_line_width
+ : -multigrapher->half_line_width)),
+ YV (yval));
+ }
+ else
+ {
+ pl_fmove_r (multigrapher->plotter,
+ XN (multigrapher->x_axis.other_axis_loc),
+ YV (yval));
+ pl_fcont_r (multigrapher->plotter,
+ XN (multigrapher->x_axis.other_axis_loc)
+ + (subsubtick_size
+ + (subsubtick_size > 0.0
+ ? multigrapher->half_line_width
+ : -multigrapher->half_line_width)),
+ YV (yval));
+ }
+ /* fall through */
+ case AXES:
+ case AXES_AT_ORIGIN:
+ if (!multigrapher->x_axis.switch_axis_end)
+ {
+ pl_fmove_r (multigrapher->plotter,
+ XN (multigrapher->x_axis.other_axis_loc),
+ YV (yval));
+ pl_fcont_r (multigrapher->plotter,
+ XN (multigrapher->x_axis.other_axis_loc)
+ + (subsubtick_size
+ + (subsubtick_size > 0.0
+ ? multigrapher->half_line_width
+ : -multigrapher->half_line_width)),
+ YV (yval));
+ }
+ else
+ {
+ pl_fmove_r (multigrapher->plotter,
+ XN (multigrapher->x_axis.alt_other_axis_loc),
+ YV (yval));
+ pl_fcont_r (multigrapher->plotter,
+ XN (multigrapher->x_axis.alt_other_axis_loc)
+ - (subsubtick_size
+ + (multigrapher->tick_size > 0.0
+ ? multigrapher->half_line_width
+ : -multigrapher->half_line_width)),
+ YV (yval));
+ }
+ break;
+ default: /* shouldn't happen */
+ break;
+ }
+}
+
+
+/* set_line_style() maps from line modes to physical line modes. See
+ * explanation at head of file. */
+
+static void
+set_line_style (Multigrapher *multigrapher, int style, bool use_color)
+{
+ if (!use_color) /* monochrome */
+ {
+ if (style > 0)
+ /* don't issue pl_linemod_r() if style<=0, since no polyline will
+ be drawn */
+ {
+ int i;
+
+ i = (style - 1) % NO_OF_LINEMODES;
+ pl_linemod_r (multigrapher->plotter, linemodes[i]);
+ }
+
+ /* use same color as used for plot frame */
+ pl_colorname_r (multigrapher->plotter, multigrapher->frame_color);
+ }
+ else /* color */
+ {
+ int i, j;
+
+ if (style > 0) /* solid lines, various colors */
+ {
+ i = ((style - 1) / NO_OF_LINEMODES) % NO_OF_LINEMODES;
+ j = (style - 1) % NO_OF_LINEMODES;
+ pl_linemod_r (multigrapher->plotter, linemodes[i]);
+ }
+
+ else if (style == 0) /* use first color, as if -m 1 was spec'd */
+ /* (no line will be drawn) */
+ j = 0;
+
+ else /* neg. pl_linemode_r (no line will be drawn)*/
+ j = (-style - 1) % (NO_OF_LINEMODES - 1);
+
+ pl_colorname_r (multigrapher->plotter, colorstyle[j]);
+ }
+}
+
+
+/* plot_point_array() calls plot_point() on each point in an array of
+ * points.
+ */
+
+void
+plot_point_array (Multigrapher *multigrapher, const Point *p, int length)
+{
+ int index;
+
+ for (index = 0; index < length; index++)
+ plot_point (multigrapher, &(p[index]));
+}
+
+/* plot_point() plots a single point, including the appropriate symbol and
+ * errorbar(s) if any. It may call either pl_fcont_r() or pl_fmove_r(),
+ * depending on whether the pendown flag is set or not. Gnuplot-style
+ * clipping (clip mode = 0,1,2) is supported.
+ *
+ * plot_point() makes heavy use of the multigrapher->x_trans and
+ * multigrapher->y_trans structures, which specify the linear
+ * transformation from user coordinates to device coordinates. It also
+ * updates the multigrapher's internal state variables. */
+
+void
+plot_point (Multigrapher *multigrapher, const Point *point)
+{
+ double local_x0, local_y0, local_x1, local_y1;
+ int clipval;
+
+ /* If new polyline is beginning, take its line style, color/monochrome
+ attribute, and line width and fill fraction attributes from the first
+ point of the polyline. We assume all such attribute fields are the
+ same for all points in the polyline (our point reader arranges this
+ for us). */
+ if (!(point->pendown) || multigrapher->first_point_of_polyline)
+ {
+ int intfill;
+
+ set_line_style (multigrapher, point->linemode, point->use_color);
+
+ /* N.B. linewidth < 0.0 means use libplot default */
+ pl_flinewidth_r (multigrapher->plotter,
+ point->line_width * (double)PLOT_SIZE);
+
+ if (point->fill_fraction < 0.0)
+ intfill = 0; /* transparent */
+ else /* guaranteed to be <= 1.0 */
+ intfill = 1 + IROUND((1.0 - point->fill_fraction) * 0xfffe);
+ pl_filltype_r (multigrapher->plotter, intfill);
+ }
+
+ /* determine endpoints of new line segment (for the first point of a
+ polyline, use a zero-length line segment) */
+ if (multigrapher->first_point_of_polyline)
+ {
+ local_x0 = point->x;
+ local_y0 = point->y;
+ }
+ else
+ {
+ local_x0 = multigrapher->oldpoint_x;
+ local_y0 = multigrapher->oldpoint_y;
+ }
+ local_x1 = point->x;
+ local_y1 = point->y;
+
+ /* save current point for use as endpoint of next line segment */
+ multigrapher->oldpoint_x = point->x;
+ multigrapher->oldpoint_y = point->y;
+
+ /* apply Cohen-Sutherland clipper to new line segment */
+ clipval = clip_line (multigrapher,
+ &local_x0, &local_y0, &local_x1, &local_y1);
+
+ if (!(clipval & ACCEPTED)) /* rejected in toto */
+ {
+ pl_fmove_r (multigrapher->plotter,
+ XV (point->x), YV (point->y)); /* move with pen up */
+ multigrapher->first_point_of_polyline = false;
+ return;
+ }
+
+ /* not rejected, ideally move with pen down */
+ if (point->pendown && (point->linemode > 0))
+ {
+ switch (multigrapher->clip_mode) /* gnuplot style clipping (0,1, or 2) */
+ {
+ case 0:
+ if ((clipval & CLIPPED_FIRST) || (clipval & CLIPPED_SECOND))
+ /* clipped on at least one end, so move with pen up */
+ pl_fmove_r (multigrapher->plotter, XV (point->x), YV (point->y));
+ else
+ /* line segment within box, so move with pen down */
+ {
+ if (!multigrapher->first_point_of_polyline)
+ pl_fcont_r (multigrapher->plotter,
+ XV (point->x), YV (point->y));
+ else
+ pl_fmove_r (multigrapher->plotter,
+ XV (point->x), YV (point->y));
+ }
+ break;
+ case 1:
+ default:
+ if ((clipval & CLIPPED_FIRST) && (clipval & CLIPPED_SECOND))
+ /* both OOB, so move with pen up */
+ pl_fmove_r (multigrapher->plotter, XV (point->x), YV (point->y));
+ else
+ /* at most one point is OOB */
+ {
+ if (clipval & CLIPPED_FIRST) /*current pt. OOB, new pt. not OOB*/
+ {
+ if (!multigrapher->first_point_of_polyline)
+ {
+ /* move to clipped current point, draw line segment */
+ pl_fmove_r (multigrapher->plotter,
+ XV (local_x0), YV (local_y0));
+ pl_fcont_r (multigrapher->plotter,
+ XV (point->x), YV (point->y));
+ }
+ else
+ pl_fmove_r (multigrapher->plotter,
+ XV (point->x), YV (point->y));
+ }
+ else /* current point not OOB, new point OOB */
+ {
+ if (!multigrapher->first_point_of_polyline)
+ {
+ /* draw line segment to clipped new point */
+ pl_fcont_r (multigrapher->plotter,
+ XV (local_x1), YV (local_y1));
+ /* N.B. lib's notion of position now differs from ours */
+ }
+ else
+ pl_fmove_r (multigrapher->plotter,
+ XV (point->x), YV (point->y));
+ }
+ }
+ break;
+ case 2:
+ if ((clipval & CLIPPED_FIRST) || multigrapher->first_point_of_polyline)
+ /* move to clipped current point if necc. */
+ pl_fmove_r (multigrapher->plotter, XV (local_x0), YV (local_y0));
+
+ /* draw line segment to clipped new point */
+ pl_fcont_r (multigrapher->plotter, XV (local_x1), YV (local_y1));
+
+ if (clipval & CLIPPED_SECOND)
+ /* new point OOB, so move to new point, breaking polyline */
+ pl_fmove_r (multigrapher->plotter, XV (point->x), YV (point->y));
+ break;
+ }
+ }
+ else /* linemode=0 or pen up; so move with pen up */
+ pl_fmove_r (multigrapher->plotter, XV (point->x), YV (point->y));
+
+ multigrapher->first_point_of_polyline = false;
+
+ /* if target point is OOB, return without plotting symbol or errorbar */
+ if (clipval & CLIPPED_SECOND)
+ return;
+
+ /* plot symbol and errorbar, doing a pl_savestate_r()--pl_restorestate()
+ to keep from breaking the polyline under construction (if any) */
+ if (point->symbol >= 32) /* yow, a character */
+ {
+ /* will do a font change, so save & restore state */
+ pl_savestate_r (multigrapher->plotter);
+ plot_errorbar (multigrapher, point);
+ pl_fontname_r (multigrapher->plotter, point->symbol_font_name);
+ pl_fmarker_r (multigrapher->plotter, XV(point->x), YV(point->y),
+ point->symbol, SS(point->symbol_size));
+ pl_restorestate_r (multigrapher->plotter);
+ }
+
+ else if (point->symbol > 0) /* a marker symbol */
+ {
+ if (point->linemode > 0)
+ /* drawing a line, so (to keep from breaking it) save & restore state*/
+ {
+ pl_savestate_r (multigrapher->plotter);
+ plot_errorbar (multigrapher, point); /* may or may not have one */
+ pl_fmarker_r (multigrapher->plotter, XV(point->x), YV(point->y),
+ point->symbol, SS(point->symbol_size));
+ pl_restorestate_r (multigrapher->plotter);
+ }
+ else
+ /* not drawing a line, so just place the marker */
+ {
+ plot_errorbar (multigrapher, point);
+ pl_fmarker_r (multigrapher->plotter, XV(point->x), YV(point->y),
+ point->symbol, SS(point->symbol_size));
+ }
+ }
+
+ else if (point->symbol == 0 && point->linemode == 0)
+ /* backward compatibility: -m 0 (even with -S 0) plots a dot */
+ {
+ plot_errorbar (multigrapher, point);
+ pl_fmarker_r (multigrapher->plotter,
+ XV(point->x), YV(point->y), M_DOT, SS(point->symbol_size));
+ }
+
+ else /* no symbol, but may be an errorbar */
+ plot_errorbar (multigrapher, point);
+
+ return;
+}
+
+
+/* clip_line() takes two points, the endpoints of a line segment, and
+ * destructively passes back two points: the endpoints of the line segment
+ * clipped by Cohen-Sutherland to the rectangular plotting area. Return
+ * value contains bitfields ACCEPTED, CLIPPED_FIRST, and CLIPPED_SECOND.
+ */
+
+static int
+clip_line (Multigrapher *multigrapher, double *x0_p, double *y0_p, double *x1_p, double *y1_p)
+{
+ double x0 = *x0_p;
+ double y0 = *y0_p;
+ double x1 = *x1_p;
+ double y1 = *y1_p;
+ outcode outcode0 = compute_outcode (multigrapher, x0, y0, true);
+ outcode outcode1 = compute_outcode (multigrapher, x1, y1, true);
+ bool accepted;
+ int clipval = 0;
+
+ for ( ; ; )
+ {
+ if (!(outcode0 | outcode1)) /* accept */
+ {
+ accepted = true;
+ break;
+ }
+ else if (outcode0 & outcode1) /* reject */
+ {
+ accepted = false;
+ break;
+ }
+ else
+ {
+ /* at least one endpoint is outside; choose one that is */
+ outcode outcode_out = (outcode0 ? outcode0 : outcode1);
+ double x, y; /* intersection with clip edge */
+
+ if (outcode_out & RIGHT)
+ {
+ x = multigrapher->x_trans.input_max;
+ y = y0 + (y1 - y0) * (multigrapher->x_trans.input_max - x0) / (x1 - x0);
+ }
+ else if (outcode_out & LEFT)
+ {
+ x = multigrapher->x_trans.input_min;
+ y = y0 + (y1 - y0) * (multigrapher->x_trans.input_min - x0) / (x1 - x0);
+ }
+ else if (outcode_out & TOP)
+ {
+ x = x0 + (x1 - x0) * (multigrapher->y_trans.input_max - y0) / (y1 - y0);
+ y = multigrapher->y_trans.input_max;
+ }
+ else
+ {
+ x = x0 + (x1 - x0) * (multigrapher->y_trans.input_min - y0) / (y1 - y0);
+ y = multigrapher->y_trans.input_min;
+ }
+
+ if (outcode_out == outcode0)
+ {
+ x0 = x;
+ y0 = y;
+ outcode0 = compute_outcode (multigrapher, x0, y0, true);
+ }
+ else
+ {
+ x1 = x;
+ y1 = y;
+ outcode1 = compute_outcode (multigrapher, x1, y1, true);
+ }
+ }
+ }
+
+ if (accepted)
+ {
+ clipval |= ACCEPTED;
+ if ((x0 != *x0_p) || (y0 != *y0_p))
+ clipval |= CLIPPED_FIRST;
+ if ((x1 != *x1_p) || (y1 != *y1_p))
+ clipval |= CLIPPED_SECOND;
+ *x0_p = x0;
+ *y0_p = y0;
+ *x1_p = x1;
+ *y1_p = y1;
+ }
+
+ return clipval;
+}
+
+/* Compute usual Cohen-Sutherland outcode, containing bitfields LEFT,
+ RIGHT, BOTTOM, TOP. Nine possibilities:
+ {LEFT, interior, RIGHT} x {BOTTOM, interior, TOP}.
+ The `tolerant' flag specifies how we handle points on the boundary. */
+static outcode
+compute_outcode (Multigrapher *multigrapher, double x, double y, bool tolerant)
+{
+ outcode code = 0;
+ double xfuzz = FUZZ * multigrapher->x_trans.input_range;
+ double yfuzz = FUZZ * multigrapher->y_trans.input_range;
+ int sign = (tolerant == true ? 1 : -1);
+
+ if (x > multigrapher->x_trans.input_max + sign * xfuzz)
+ code |= RIGHT;
+ else if (x < multigrapher->x_trans.input_min - sign * xfuzz)
+ code |= LEFT;
+ if (y > multigrapher->y_trans.input_max + sign * yfuzz)
+ code |= TOP;
+ else if (y < multigrapher->y_trans.input_min - sign * yfuzz)
+ code |= BOTTOM;
+
+ return code;
+}
+
+static void
+transpose_portmanteau (int *val)
+{
+ bool xtrue, ytrue;
+ int newval;
+
+ xtrue = ((*val & X_AXIS) ? true : false);
+ ytrue = ((*val & Y_AXIS) ? true : false);
+
+ newval = (xtrue ? Y_AXIS : 0) | (ytrue ? X_AXIS : 0);
+ *val = newval;
+}
+
+static void
+plot_errorbar (Multigrapher *multigrapher, const Point *p)
+{
+ if (p->have_x_errorbar || p->have_y_errorbar)
+ /* save & restore state, since we invoke pl_linemod_r() */
+ {
+ pl_savestate_r (multigrapher->plotter);
+ pl_linemod_r (multigrapher->plotter, "solid");
+
+ if (p->have_x_errorbar)
+ {
+ pl_fline_r (multigrapher->plotter,
+ XV(p->xmin), YV(p->y) - 0.5 * SS(p->symbol_size),
+ XV(p->xmin), YV(p->y) + 0.5 * SS(p->symbol_size));
+ pl_fline_r (multigrapher->plotter,
+ XV(p->xmin), YV(p->y), XV(p->xmax), YV(p->y));
+ pl_fline_r (multigrapher->plotter,
+ XV(p->xmax), YV(p->y) - 0.5 * SS(p->symbol_size),
+ XV(p->xmax), YV(p->y) + 0.5 * SS(p->symbol_size));
+ }
+ if (p->have_y_errorbar)
+ {
+ pl_fline_r (multigrapher->plotter,
+ XV(p->x) - 0.5 * SS(p->symbol_size), YV(p->ymin),
+ XV(p->x) + 0.5 * SS(p->symbol_size), YV(p->ymin));
+ pl_fline_r (multigrapher->plotter,
+ XV(p->x), YV(p->ymin), XV(p->x), YV(p->ymax));
+ pl_fline_r (multigrapher->plotter,
+ XV(p->x) - 0.5 * SS(p->symbol_size), YV(p->ymax),
+ XV(p->x) + 0.5 * SS(p->symbol_size), YV(p->ymax));
+ }
+
+ pl_restorestate_r (multigrapher->plotter);
+ }
+}
+
+/* An alternative means of ending a polyline in progress. Rather than
+ ending it by passing plot_point() a point with the `pendown' flag not
+ set, one may call this function. This yields faster response in
+ real-time work; e.g. in reader.c it is called by read_and_plot_file()
+ after all dataset(s) have been read from the file and plotted. */
+
+void
+end_polyline_and_flush (Multigrapher *multigrapher)
+{
+ pl_endpath_r (multigrapher->plotter);
+ pl_flushpl_r (multigrapher->plotter);
+ multigrapher->first_point_of_polyline = true;
+}
generated by cgit v1.2.3 (git 2.39.1) at 2025年09月28日 03:45:54 +0000

AltStyle によって変換されたページ (->オリジナル) /