author | Francesco Abbate <francesco.bbt@gmail.com> | 2009年11月08日 15:22:33 +0000 |
---|---|---|
committer | Francesco Abbate <francesco.bbt@gmail.com> | 2009年11月08日 15:22:33 +0000 |
commit | e8a10279d92ab7a66402bae02a8fc423a6162fd5 (patch) | |
tree | 895eda2d7fcea54f2b9a311437f5e86fbbac5bfe | |
parent | cfa10f2e681fd6e8321ecd24f11b1f58b4209553 (diff) | |
download | gsl-shell-e8a10279d92ab7a66402bae02a8fc423a6162fd5.tar.gz |
-rw-r--r-- | DIST-FILES | 3 | ||||
-rw-r--r-- | doc/source/acknowledgments.rst | 4 | ||||
-rw-r--r-- | doc/source/examples.rst | 42 | ||||
-rw-r--r-- | doc/source/index.rst | 8 | ||||
-rw-r--r-- | doc/source/intro.rst | 5 | ||||
-rw-r--r-- | doc/source/ode.rst | 4 | ||||
-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 | ||||
-rw-r--r-- | scripts/www-cvs-sync.sh | 53 |
diff --git a/DIST-FILES b/DIST-FILES index dab491a7..80ddf885 100644 --- a/DIST-FILES +++ b/DIST-FILES @@ -1,5 +1,4 @@ DIST-FILES -makedist.sh integ.h code.h cnlinfit.h @@ -200,6 +199,8 @@ doc/source/random.rst doc/source/randist.rst doc/source/pdf.rst doc/source/cdf.rst +doc/source/examples.rst scripts/project-diff.sh scripts/makedist.sh scripts/www-cvs-sync.sh +scripts/makedist.sh diff --git a/doc/source/acknowledgments.rst b/doc/source/acknowledgments.rst index b3b09ecc..d4d1a2fd 100644 --- a/doc/source/acknowledgments.rst +++ b/doc/source/acknowledgments.rst @@ -1,6 +1,8 @@ Acknowldgemets -------------- -I would like to thanks the contributors of all the excellent free softwares that made GSL Shell possible. In particular thanks to the GSL Team for their excellent work, to the LUA team for their excellent scripting language. +I would like to thanks the contributors of all the excellent free +softwares that made GSL Shell possible. In particular thanks to the +GSL Team for their excellent work, to the Lua team for their excellent scripting language. Finally, thanks to my girlfriend, Gosia, for her patience and her support. diff --git a/doc/source/examples.rst b/doc/source/examples.rst new file mode 100644 index 00000000..f0630460 --- /dev/null +++ b/doc/source/examples.rst @@ -0,0 +1,42 @@ +.. highlight:: lua + +.. include:: <isogrk1.txt> + +GSL Shell Examples +================== + +In this chapter we gives some examples about the usage of GSL Shell. + +Home-made Bessel Functions +-------------------------- + +The Bessel's function J\ :sub:`n` for integer values of n can de defined with the following integral: + +.. math:: + J_n(x) = {1 \over \pi} \int_0^\pi \cos(n \tau - x \sin \tau) \textrm{d}\tau + +We can use this definition to define our home-made Bessel function. To perform the integral we need to use the ``integ`` function and provide the function to integrate. This is easy like a piece of cake:: + + function bessJ(x,n) + local f = |t| cos(n*t - x*sin(t)) -- we define the function to integrate + return 1/pi * integ {f= f, points={0, pi}} + end + +The definition of ``bessJ`` takes x and n as arguments and calculate the definite integral between 0 and |pgr|. Then we can calculate a matrix with the tabulated values. For examples we can use the columns of the matrix to span different values of n. We write then:: + + m = new(200, 6, |k,n| bessJ((k-1)/10, n-1)) + +And we obtain the following matrix:: + + [ 1 0 0 0 0 0 ] + [ 0.997502 0.0499375 0.00124896 2.08203e-05 2.60286e-07 0 ] + [ 0.990025 0.0995008 0.00498335 0.00016625 4.15834e-06 8.31945e-08 ] + [ 0.977626 0.148319 0.0111659 0.000559343 2.0999e-05 6.30443e-07 ] + [ 0.960398 0.196027 0.0197347 0.00132005 6.61351e-05 2.64894e-06 ] + [ 0.93847 0.242268 0.030604 0.00256373 0.000160736 8.05363e-06 ] + [ 0.912005 0.286701 0.0436651 0.00439966 0.00033147 1.99482e-05 ] + [ 0.881201 0.328996 0.0587869 0.00692965 0.000610097 4.28824e-05 ] + [ 0.846287 0.368842 0.0758178 0.0102468 0.00103298 8.30836e-05 ] + [ 0.807524 0.40595 0.0945863 0.014434 0.00164055 0.000148658 ] + [ 0.765198 0.440051 0.114903 0.0195634 0.00247664 0.000249758 ] + [ ... ] diff --git a/doc/source/index.rst b/doc/source/index.rst index e0bb92cf..2160b67d 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -8,10 +8,10 @@ numerical computations. GSL shell can be used interactively to perform calculations with matrices or
vectors but it does allow also to write complex user defined functions with
-the LUA scripting interpreter.
+the Lua scripting interpreter.
-LUA is a very interesting and easy to learn scripting language that features
-advanced functionalities like closures and metamethods. LUA is very
+Lua is a very interesting and easy to learn scripting language that features
+advanced functionalities like closures and metamethods. Lua is very
easy to learn and will give you the power of defining your own complex routines
to use the GSL routines more effectively.
@@ -28,7 +28,6 @@ Contents: .. toctree::
:maxdepth: 2
- author.rst
news.rst
intro.rst
matrices.rst
@@ -41,4 +40,5 @@ Contents: fft.rst
ode.rst
integ.rst
+ examples.rst
acknowledgments.rst
diff --git a/doc/source/intro.rst b/doc/source/intro.rst index 0448d93a..55db7147 100644 --- a/doc/source/intro.rst +++ b/doc/source/intro.rst @@ -5,7 +5,10 @@ GSL Shell introduction ====================== -In order to use GSL Shell effectively you need to know the basics of the LUA scripting language. Luckily LUA is very easy to learn so you can be productive with it in a very short time. In order to have an introduction to LUA you can look have a look at at `LUA.org <http://www.lua.org>`_. +In order to use GSL Shell effectively you need to know the basics of +the Lua scripting language. Luckily Lua is very easy to learn so you +can be productive with it in a very short time. In order to have an +introduction to Lua you can look have a look at at `Lua.org <http://www.lua.org>`_. First steps ----------- diff --git a/doc/source/ode.rst b/doc/source/ode.rst index 6787a4cb..9de742a8 100644 --- a/doc/source/ode.rst +++ b/doc/source/ode.rst @@ -46,8 +46,8 @@ Here an examples about the usage of an ODE solver for *real* numbers:: -- define the ODE function function odef(t,y,f) - f:set(0,0, y[1]) - f:set(1,0, -y[0] - mu*y[1]*(y[0]*y[0]-1)) + f:set(1,1, y[2]) + f:set(2,1, -y[1] - mu*y[2]*(y[1]*y[1]-1)) end -- create the ODE solver 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; +} diff --git a/scripts/www-cvs-sync.sh b/scripts/www-cvs-sync.sh index 3ee5b94b..d050a517 100644 --- a/scripts/www-cvs-sync.sh +++ b/scripts/www-cvs-sync.sh @@ -10,35 +10,60 @@ SRCLST=`find $SRC_DIR -type f -a -not -path '*/.doctree*' -a -not -iname '.build ADDLST=() FULLLST=() for NM in $SRCLST; do - DNM=`echo $NM | awk '{gsub(/doc\/html/, "www/gsl-shell"); print 0ドル }'` - echo "Treating: $NM" + DNM=${NM/doc\/html/www\/gsl-shell} RDNM=`echo $DNM | awk '{gsub(/^.*www\/gsl-shell\//, ""); print 0ドル }'` if test ! -f $DNM; then ADDLST[${#ADDLST[@]}]=$RDNM + FULLLST[${#FULLLST[@]}]=$RDNM + else + cmp -s "$NM" "$DNM" + if test $? -eq 1; then + FULLLST[${#FULLLST[@]}]=$RDNM + fi fi - FULLLST[${#FULLLST[@]}]=$RDNM done WWWLST=`find $WWW_DIR -type f -a -not -path '*CVS*'` RMLST=() for NM in $WWWLST; do - SNM=`echo $NM | awk '{gsub(/www\/gsl-shell/, "doc/html"); print 0ドル }'` - echo "Treating: $NM" + SNM=${NM/www\/gsl-shell/doc\/html} if test ! -f $SNM; then RMLST[${#RMLST[@]}]=`echo $NM | awk '{gsub(/^.*www\/gsl-shell\//, ""); print 0ドル }'` fi; done -cd www/gsl-shell +echo "Files to copy: " for NM in ${FULLLST[@]}; do - cp "$SRC_DIR/$NM" "$NM" + echo $NM done -if test ${#ADDLST[@]} -gt 0; then - cvs add "${ADDLST[@]}" -fi -if test ${#RMLST[@]} -gt 0; then - rm "${RMLST[@]}" - cvs remove "${RMLST[@]}" + +echo "Files to add:" +for NM in ${ADDLST[@]}; do + echo $NM +done + +echo "Files to remove:" +for NM in ${RMLST[@]}; do + echo $NM +done + +echo -n "Commit the changes [Yes/No] ? " +read ANSWER + +if test "${ANSWER:-no}" = "Yes" -o "${ANSWER:-no}" = "yes"; then + cd www/gsl-shell + for NM in ${FULLLST[@]}; do + cp "$SRC_DIR/$NM" "$NM" + done + if test ${#ADDLST[@]} -gt 0; then + cvs add "${ADDLST[@]}" + fi + if test ${#RMLST[@]} -gt 0; then + rm "${RMLST[@]}" + cvs remove "${RMLST[@]}" + fi + cd - +else + echo "Changes not done" fi -cd - |