PLplot  5.10.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
plbox.c
Go to the documentation of this file.
1 // $Id: plbox.c 12950 2014-01-22 20:22:02Z airwin $
2 //
3 // Routines for drawing axes & box around the current viewport.
4 //
5 // Copyright (C) 2004 Joao Cardoso
6 // Copyright (C) 2004 Alan W. Irwin
7 //
8 // This file is part of PLplot.
9 //
10 // PLplot is free software; you can redistribute it and/or modify
11 // it under the terms of the GNU Library General Public License as published
12 // by the Free Software Foundation; either version 2 of the License, or
13 // (at your option) any later version.
14 //
15 // PLplot is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU Library General Public License for more details.
19 //
20 // You should have received a copy of the GNU Library General Public License
21 // along with PLplot; if not, write to the Free Software
22 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 //
24 
25 #include "plplotP.h"
26 
27 #define STRING_LEN 40
28 #define FORMAT_LEN 10
29 #define TEMP_LEN 30
30 #define N_EDGE_SEGMENTS 50
31 
32 static PLFLT xlog[8] =
33 {
34  0.301030, 0.477121, 0.602060, 0.698970,
35  0.778151, 0.845098, 0.903090, 0.954243
36 };
37 
38 // Static function prototypes
39 
40 static void
41 plxybx( const char *opt, const char *label, PLINT axis, PLFLT wx1, PLFLT wy1,
42  PLFLT wx2, PLFLT wy2, PLFLT vmin, PLFLT vmax,
43  PLFLT tick, PLINT nsub, PLINT nolast, PLINT *digits );
44 
45 static void
46 plzbx( const char *opt, const char *label, PLINT right, PLFLT dx, PLFLT dy,
47  PLFLT wx, PLFLT wy1, PLFLT wy2, PLFLT vmin, PLFLT vmax,
48  PLFLT tick, PLINT nsub, PLINT *digits );
49 
50 static void
51 plxytx( PLFLT wx1, PLFLT wy1, PLFLT wx2, PLFLT wy2,
52  PLFLT disp, PLFLT pos, PLFLT just, const char *text );
53 
54 static void
55 plztx( const char *opt, PLFLT dx, PLFLT dy, PLFLT wx, PLFLT wy1,
56  PLFLT wy2, PLFLT disp, PLFLT pos, PLFLT just, const char *text );
57 
58 static void
59 plform( PLINT axis, PLFLT value, PLINT scale, PLINT prec, char *result, PLINT len, PLBOOL ll, PLBOOL lf, PLBOOL lo );
60 
61 static void
62 grid_box( const char *xopt, PLFLT xtick1, PLINT nxsub1,
63  const char *yopt, PLFLT ytick1, PLINT nysub1 );
64 
65 static void
66 label_box( const char *xopt, PLFLT xtick1, const char *yopt, PLFLT ytick1 );
67 
68 static void
69 plP_default_label_log( PLINT axis, PLFLT value, char *string, PLINT len, void *data );
70 
71 static void
72 plP_default_label_log_fixed( PLINT axis, PLFLT value, char *string, PLINT len, void *data );
73 
74 static void
75 plP_default_label( PLINT axis, PLFLT value, char *string, PLINT len, void *data );
76 
77 static const char *
78 plgesc_string( void );
79 
80 //--------------------------------------------------------------------------
81 // void plbox()
82 //
83 // This draws a box around the current viewport, complete with axes, ticks,
84 // numeric labels, and grids, according to input specification. Just a
85 // front-end to plaxes(), which allows arbitrary placement of coordinate
86 // axes when plotted (here the origin is at 0,0). See the documentation for
87 // plaxes() for more info.
88 //--------------------------------------------------------------------------
89 
90 void
91 c_plbox( const char *xopt, PLFLT xtick, PLINT nxsub,
92  const char *yopt, PLFLT ytick, PLINT nysub )
93 {
94  c_plaxes( 0.0, 0.0, xopt, xtick, nxsub, yopt, ytick, nysub );
95 }
96 
97 //--------------------------------------------------------------------------
98 // void plaxes()
99 //
100 // This draws a box around the current viewport, complete with axes,
101 // ticks, numeric labels, and grids, according to input specification.
102 //
103 // x0 and y0 specify the origin of the axes.
104 //
105 // xopt and yopt are character strings which define the box as follows:
106 //
107 // a: Draw axis (X is horizontal line Y=0, Y is vertical line X=0)
108 // b: Draw bottom (X) or left (Y) frame of box
109 // c: Draw top (X) or right (Y) frame of box
110 // d: Interpret axis as a date/time when writing labels
111 // f: Always use fixed point numeric labels
112 // g: Draws a grid at the major tick interval
113 // h: Draws a grid at the minor tick interval
114 // i: Inverts tick marks
115 // l: Logarithmic axes, major ticks at decades, minor ticks at units
116 // n: Write numeric label at conventional location
117 // m: Write numeric label at unconventional location
118 // o: Label text is generated by a user-defined function
119 // t: Draw major tick marks
120 // s: Draw minor tick marks
121 // u: like b (including all side effects such as tick marks and numerical
122 // labels for those) except exclude drawing the edge.
123 // w: like c (including all side effects such as tick marks and numerical
124 // labels for those) except exclude drawing the edge.
125 // v: (for Y only) Label vertically
126 // x: like t (including the side effect of the numerical labels for the major
127 // ticks) except exclude drawing the major and minor tick marks.
128 //
129 // xtick, ytick are the major tick intervals required, zero for
130 // automatic selection
131 //
132 // nxsub, nysub are the number of subtick intervals in a major tick
133 // interval
134 //--------------------------------------------------------------------------
135 
136 void
138  const char *xopt, PLFLT xtick, PLINT nxsub,
139  const char *yopt, PLFLT ytick, PLINT nysub )
140 {
141  PLBOOL lax, lbx, lcx, ldx, lgx, lix, llx, lsx, ltx, lux, lwx, lxx;
142  PLBOOL lay, lby, lcy, ldy, lgy, liy, lly, lsy, lty, luy, lwy, lxy;
143  PLINT xmajor, xminor, ymajor, yminor;
144  PLINT i, i1x, i2x, i3x, i4x, i1y, i2y, i3y, i4y;
145  PLINT nxsub1, nysub1;
146  PLINT lxmin, lxmax, lymin, lymax;
147  PLINT pxmin, pxmax, pymin, pymax;
148  PLINT vppxmi, vppxma, vppymi, vppyma;
149  PLFLT xtick1, ytick1, vpwxmi, vpwxma, vpwymi, vpwyma;
150  PLFLT vpwxmin, vpwxmax, vpwymin, vpwymax;
151  PLFLT xp0, yp0, tn, tp, temp;
152  PLFLT factor, tstart;
153 
154  if ( plsc->level < 3 )
155  {
156  plabort( "plbox: Please set up window first" );
157  return;
158  }
159 
160 // Open the clip limits to the subpage limits
161 
162  plP_gclp( &lxmin, &lxmax, &lymin, &lymax );
163  plP_gphy( &pxmin, &pxmax, &pymin, &pymax );
164  plP_sclp( pxmin, pxmax, pymin, pymax );
165 
166  vppxmi = plsc->vppxmi;
167  vppxma = plsc->vppxma;
168  vppymi = plsc->vppymi;
169  vppyma = plsc->vppyma;
170 
171  if ( plsc->if_boxbb )
172  {
173  // Bounding-box limits for the box in mm before corrections
174  // for decorations are applied.
175  plsc->boxbb_xmin = plsc->vppxmi / plsc->xpmm;
176  plsc->boxbb_xmax = plsc->vppxma / plsc->xpmm;
177  plsc->boxbb_ymin = plsc->vppymi / plsc->ypmm;
178  plsc->boxbb_ymax = plsc->vppyma / plsc->ypmm;
179  }
180 
181 // Convert world coordinates to physical
182 
183  xp0 = plP_wcpcx( x0 );
184  yp0 = plP_wcpcy( y0 );
185 
186 // Set plot options from input
187 
188  lax = plP_stsearch( xopt, 'a' );
189  lbx = plP_stsearch( xopt, 'b' );
190  lcx = plP_stsearch( xopt, 'c' );
191  ldx = plP_stsearch( xopt, 'd' );
192  lgx = plP_stsearch( xopt, 'g' );
193  lix = plP_stsearch( xopt, 'i' );
194  llx = plP_stsearch( xopt, 'l' );
195  lsx = plP_stsearch( xopt, 's' );
196  ltx = plP_stsearch( xopt, 't' );
197  lux = plP_stsearch( xopt, 'u' );
198  lwx = plP_stsearch( xopt, 'w' );
199  lxx = plP_stsearch( xopt, 'x' );
200 
201  lay = plP_stsearch( yopt, 'a' );
202  lby = plP_stsearch( yopt, 'b' );
203  lcy = plP_stsearch( yopt, 'c' );
204  ldy = plP_stsearch( yopt, 'd' );
205  lgy = plP_stsearch( yopt, 'g' );
206  liy = plP_stsearch( yopt, 'i' );
207  lly = plP_stsearch( yopt, 'l' );
208  lsy = plP_stsearch( yopt, 's' );
209  lty = plP_stsearch( yopt, 't' );
210  luy = plP_stsearch( yopt, 'u' );
211  lwy = plP_stsearch( yopt, 'w' );
212  lxy = plP_stsearch( yopt, 'x' );
213 
214 // Tick and subtick sizes in device coords
215 
216  xmajor = MAX( ROUND( plsc->majht * plsc->ypmm ), 1 );
217  ymajor = MAX( ROUND( plsc->majht * plsc->xpmm ), 1 );
218  xminor = MAX( ROUND( plsc->minht * plsc->ypmm ), 1 );
219  yminor = MAX( ROUND( plsc->minht * plsc->xpmm ), 1 );
220 
221  nxsub1 = nxsub;
222  nysub1 = nysub;
223  xtick1 = llx ? 1.0 : xtick;
224  ytick1 = lly ? 1.0 : ytick;
225 
226  plP_xgvpw( &vpwxmin, &vpwxmax, &vpwymin, &vpwymax );
227 // vpwxmi always numerically less than vpwxma, and
228 // similarly for vpwymi
229  vpwxmi = ( vpwxmax > vpwxmin ) ? vpwxmin : vpwxmax;
230  vpwxma = ( vpwxmax > vpwxmin ) ? vpwxmax : vpwxmin;
231  vpwymi = ( vpwymax > vpwymin ) ? vpwymin : vpwymax;
232  vpwyma = ( vpwymax > vpwymin ) ? vpwymax : vpwymin;
233 
234 // Plot axes only if they are inside viewport.
235  lax = lax && vpwymi < y0 && y0 < vpwyma;
236  lay = lay && vpwxmi < x0 && x0 < vpwxma;
237 
238 // Calculate tick spacing
239 
240  if ( ltx || lgx || lxx )
241  pldtik( vpwxmi, vpwxma, &xtick1, &nxsub1, ldx );
242 
243  if ( lty || lgy || lxy )
244  pldtik( vpwymi, vpwyma, &ytick1, &nysub1, ldy );
245 // n.b. large change; xtick1, nxsub1, ytick1, nysub1 always positive.
246 
247 // Set up tick variables
248 
249  if ( lix )
250  {
251  i1x = xminor;
252  i2x = 0;
253  i3x = xmajor;
254  i4x = 0;
255  }
256  else
257  {
258  i1x = 0;
259  i2x = xminor;
260  i3x = 0;
261  i4x = xmajor;
262  }
263 
264  if ( liy )
265  {
266  i1y = yminor;
267  i2y = 0;
268  i3y = ymajor;
269  i4y = 0;
270  }
271  else
272  {
273  i1y = 0;
274  i2y = yminor;
275  i3y = 0;
276  i4y = ymajor;
277  }
278 
279  if ( plsc->if_boxbb )
280  {
281  // Carefully follow logic below (and above) for the case where
282  // an inverted major tick mark is written (in the X direction
283  // for a Y axis and vice versa). Ignore minor tick marks
284  // which are assumed to be smaller. Ignore axes and grids
285  // which are all contained within the viewport.
286  if ( lix && ( lbx || lux ) && ( ltx && !lxx ) )
287  plsc->boxbb_ymin -= xmajor / plsc->ypmm;
288  if ( liy && ( lcy || lwy ) && ( lty && !lxy ) )
289  plsc->boxbb_xmax += ymajor / plsc->xpmm;
290  if ( lix && ( lcx || lwx ) && ( ltx && !lxx ) )
291  plsc->boxbb_ymax += xmajor / plsc->ypmm;
292  if ( liy && ( lby || luy ) && ( lty && !lxy ) )
293  plsc->boxbb_xmin -= ymajor / plsc->xpmm;
294  }
295  else
296  {
297 // Draw the bottom frame of the box
298 
299  if ( lbx || lux )
300  {
301  if ( !lux )
302  {
303  plP_movphy( vppxmi, vppymi );
304  plP_draphy( vppxma, vppymi );
305  }
306  if ( ltx && !lxx )
307  {
308  if ( ldx )
309  {
310  pldtfac( vpwxmi, vpwxma, &factor, &tstart );
311  tp = xtick1 * ( floor( ( vpwxmi - tstart ) / xtick1 ) ) + tstart;
312  }
313  else
314  tp = xtick1 * floor( vpwxmi / xtick1 );
315  for (;; )
316  {
317  tn = tp + xtick1;
318  if ( lsx )
319  {
320  if ( llx )
321  {
322  for ( i = 0; i <= 7; i++ )
323  {
324  temp = tp + xlog[i];
325  if ( BETW( temp, vpwxmi, vpwxma ) )
326  plxtik( plP_wcpcx( temp ), vppymi, i1x, i2x );
327  }
328  }
329  else
330  {
331  for ( i = 1; i <= nxsub1 - 1; i++ )
332  {
333  temp = tp + i * xtick1 / nxsub1;
334  if ( BETW( temp, vpwxmi, vpwxma ) )
335  plxtik( plP_wcpcx( temp ), vppymi, i1x, i2x );
336  }
337  }
338  }
339  if ( !BETW( tn, vpwxmi, vpwxma ) )
340  break;
341  plxtik( plP_wcpcx( tn ), vppymi, i3x, i4x );
342  tp = tn;
343  }
344  }
345  }
346 
347 // Draw the right-hand frame of box
348 
349  if ( lcy || lwy )
350  {
351  if ( !lwy )
352  {
353  plP_movphy( vppxma, vppymi );
354  plP_draphy( vppxma, vppyma );
355  }
356  if ( lty && !lxy )
357  {
358  if ( ldy )
359  {
360  pldtfac( vpwymi, vpwyma, &factor, &tstart );
361  tp = ytick1 * ( floor( ( vpwymi - tstart ) / ytick1 ) ) + tstart;
362  }
363  else
364  tp = ytick1 * floor( vpwymi / ytick1 );
365  for (;; )
366  {
367  tn = tp + ytick1;
368  if ( lsy )
369  {
370  if ( lly )
371  {
372  for ( i = 0; i <= 7; i++ )
373  {
374  temp = tp + xlog[i];
375  if ( BETW( temp, vpwymi, vpwyma ) )
376  plytik( vppxma, plP_wcpcy( temp ), i2y, i1y );
377  }
378  }
379  else
380  {
381  for ( i = 1; i <= nysub1 - 1; i++ )
382  {
383  temp = tp + i * ytick1 / nysub1;
384  if ( BETW( temp, vpwymi, vpwyma ) )
385  plytik( vppxma, plP_wcpcy( temp ), i2y, i1y );
386  }
387  }
388  }
389  if ( !BETW( tn, vpwymi, vpwyma ) )
390  break;
391  plytik( vppxma, plP_wcpcy( tn ), i4y, i3y );
392  tp = tn;
393  }
394  }
395  }
396 
397 // Draw the top frame of the box
398 
399  if ( lcx || lwx )
400  {
401  if ( !lwx )
402  {
403  plP_movphy( vppxma, vppyma );
404  plP_draphy( vppxmi, vppyma );
405  }
406  if ( ltx && !lxx )
407  {
408  if ( ldx )
409  {
410  pldtfac( vpwxmi, vpwxma, &factor, &tstart );
411  tp = xtick1 * ( floor( ( vpwxma - tstart ) / xtick1 ) + 1 ) + tstart;
412  }
413  else
414  tp = xtick1 * ( floor( vpwxma / xtick1 ) + 1 );
415  for (;; )
416  {
417  tn = tp - xtick1;
418  if ( lsx )
419  {
420  if ( llx )
421  {
422  for ( i = 7; i >= 0; i-- )
423  {
424  temp = tn + xlog[i];
425  if ( BETW( temp, vpwxmi, vpwxma ) )
426  plxtik( plP_wcpcx( temp ), vppyma, i2x, i1x );
427  }
428  }
429  else
430  {
431  for ( i = nxsub1 - 1; i >= 1; i-- )
432  {
433  temp = tn + i * xtick1 / nxsub1;
434  if ( BETW( temp, vpwxmi, vpwxma ) )
435  plxtik( plP_wcpcx( temp ), vppyma, i2x, i1x );
436  }
437  }
438  }
439  if ( !BETW( tn, vpwxmi, vpwxma ) )
440  break;
441  plxtik( plP_wcpcx( tn ), vppyma, i4x, i3x );
442  tp = tn;
443  }
444  }
445  }
446 
447 // Draw the left-hand frame of box
448 
449  if ( lby || luy )
450  {
451  if ( !luy )
452  {
453  plP_movphy( vppxmi, vppyma );
454  plP_draphy( vppxmi, vppymi );
455  }
456  if ( lty && !lxy )
457  {
458  if ( ldy )
459  {
460  pldtfac( vpwymi, vpwyma, &factor, &tstart );
461  tp = ytick1 * ( floor( ( vpwyma - tstart ) / ytick1 ) + 1 ) + tstart;
462  }
463  else
464  tp = ytick1 * ( floor( vpwyma / ytick1 ) + 1 );
465  for (;; )
466  {
467  tn = tp - ytick1;
468  if ( lsy )
469  {
470  if ( lly )
471  {
472  for ( i = 7; i >= 0; i-- )
473  {
474  temp = tn + xlog[i];
475  if ( BETW( temp, vpwymi, vpwyma ) )
476  plytik( vppxmi, plP_wcpcy( temp ), i1y, i2y );
477  }
478  }
479  else
480  {
481  for ( i = nysub1 - 1; i >= 1; i-- )
482  {
483  temp = tn + i * ytick1 / nysub1;
484  if ( BETW( temp, vpwymi, vpwyma ) )
485  plytik( vppxmi, plP_wcpcy( temp ), i1y, i2y );
486  }
487  }
488  }
489  if ( !BETW( tn, vpwymi, vpwyma ) )
490  break;
491  plytik( vppxmi, plP_wcpcy( tn ), i3y, i4y );
492  tp = tn;
493  }
494  }
495  }
496 
497  // Draw the horizontal axis.
498  if ( lax )
499  {
500  plP_movphy( vppxmi, (PLINT) yp0 );
501  plP_draphy( vppxma, (PLINT) yp0 );
502  if ( ltx && !lxx )
503  {
504  tp = xtick1 * floor( vpwxmi / xtick1 );
505  for (;; )
506  {
507  tn = tp + xtick1;
508  if ( lsx )
509  {
510  if ( llx )
511  {
512  for ( i = 0; i <= 7; i++ )
513  {
514  temp = tp + xlog[i];
515  if ( BETW( temp, vpwxmi, vpwxma ) )
516  plxtik( plP_wcpcx( temp ), (PLINT) yp0, xminor, xminor );
517  }
518  }
519  else
520  {
521  for ( i = 1; i <= nxsub1 - 1; i++ )
522  {
523  temp = tp + i * xtick1 / nxsub1;
524  if ( BETW( temp, vpwxmi, vpwxma ) )
525  plxtik( plP_wcpcx( temp ), (PLINT) yp0, xminor, xminor );
526  }
527  }
528  }
529  if ( !BETW( tn, vpwxmi, vpwxma ) )
530  break;
531  plxtik( plP_wcpcx( tn ), (PLINT) yp0, xmajor, xmajor );
532  tp = tn;
533  }
534  }
535  }
536 
537  // Draw the vertical axis.
538  if ( lay )
539  {
540  plP_movphy( (PLINT) xp0, vppymi );
541  plP_draphy( (PLINT) xp0, vppyma );
542  if ( lty && !lxy )
543  {
544  tp = ytick1 * floor( vpwymi / ytick1 );
545  for (;; )
546  {
547  tn = tp + ytick1;
548  if ( lsy )
549  {
550  if ( lly )
551  {
552  for ( i = 0; i <= 7; i++ )
553  {
554  temp = tp + xlog[i];
555  if ( BETW( temp, vpwymi, vpwyma ) )
556  plytik( (PLINT) xp0, plP_wcpcy( temp ), yminor, yminor );
557  }
558  }
559  else
560  {
561  for ( i = 1; i <= nysub1 - 1; i++ )
562  {
563  temp = tp + i * ytick1 / nysub1;
564  if ( BETW( temp, vpwymi, vpwyma ) )
565  plytik( (PLINT) xp0, plP_wcpcy( temp ), yminor, yminor );
566  }
567  }
568  }
569  if ( !BETW( tn, vpwymi, vpwyma ) )
570  break;
571  plytik( (PLINT) xp0, plP_wcpcy( tn ), ymajor, ymajor );
572  tp = tn;
573  }
574  }
575  }
576 
577  // Draw grids.
578  grid_box( xopt, xtick1, nxsub1, yopt, ytick1, nysub1 );
579  }
580 
581  // Write labels.
582  label_box( xopt, xtick1, yopt, ytick1 );
583 
584 // Restore the clip limits to viewport edge
585 
586  plP_sclp( lxmin, lxmax, lymin, lymax );
587 }
588 
589 //--------------------------------------------------------------------------
590 // void plbox3()
591 //
592 // This is the 3-d analogue of plbox().
593 //--------------------------------------------------------------------------
594 
595 void
596 c_plbox3( const char *xopt, const char *xlabel, PLFLT xtick, PLINT nxsub,
597  const char *yopt, const char *ylabel, PLFLT ytick, PLINT nysub,
598  const char *zopt, const char *zlabel, PLFLT ztick, PLINT nzsub )
599 {
600  PLFLT dx, dy, tx, ty, ux, uy;
601  PLFLT xmin, xmax, ymin, ymax, zmin, zmax, zscale;
602  PLFLT cxx, cxy, cyx, cyy, cyz;
603  PLINT ln;
604  PLINT *zbflg, *zbcol;
605  PLFLT *zbwidth;
606  PLFLT *zbtck;
607  PLINT xdigmax, xdigits;
608  PLINT ydigmax, ydigits;
609  PLINT zdigmax, zdigits;
610 
611  if ( plsc->level < 3 )
612  {
613  plabort( "plbox3: Please set up window first" );
614  return;
615  }
616 
617  plP_gw3wc( &cxx, &cxy, &cyx, &cyy, &cyz );
618  plP_gdom( &xmin, &xmax, &ymin, &ymax );
619  plP_grange( &zscale, &zmin, &zmax );
620 
621  plgxax( &xdigmax, &xdigits );
622  plgyax( &ydigmax, &ydigits );
623  plgzax( &zdigmax, &zdigits );
624 
625  xdigits = xdigmax;
626  ydigits = ydigmax;
627  zdigits = zdigmax;
628 
629 // We have to wait until after the plot is drawn to draw back
630 // grid so store this stuff.
631 
632  plP_gzback( &zbflg, &zbcol, &zbtck, &zbwidth );
633  *zbflg = plP_stsearch( zopt, 'd' );
634  if ( *zbflg )
635  {
636  *zbtck = ztick; // save tick spacing
637  *zbcol = plsc->icol0; // and color
638  *zbwidth = plsc->width; // and line width
639  }
640 
641  if ( cxx >= 0.0 && cxy <= 0.0 )
642  {
643  ln = plP_stsearch( xopt, 'n' );
644  tx = plP_w3wcx( xmin, ymin, zmin );
645  ty = plP_w3wcy( xmin, ymin, zmin );
646  ux = plP_w3wcx( xmax, ymin, zmin );
647  uy = plP_w3wcy( xmax, ymin, zmin );
648  plxybx( xopt, xlabel, PL_X_AXIS, tx, ty, ux, uy,
649  xmin, xmax, xtick, nxsub, 0, &xdigits );
650 
651  dx = ux - tx;
652  dy = uy - ty;
653  plzbx( zopt, zlabel, 1, dx, dy, ux, uy,
654  plP_w3wcy( xmax, ymin, zmax ), zmin, zmax, ztick, nzsub, &zdigits );
655 
656  tx = plP_w3wcx( xmin, ymax, zmin );
657  ty = plP_w3wcy( xmin, ymax, zmin );
658  ux = plP_w3wcx( xmin, ymin, zmin );
659  uy = plP_w3wcy( xmin, ymin, zmin );
660  plxybx( yopt, ylabel, PL_Y_AXIS, tx, ty, ux, uy,
661  ymax, ymin, ytick, nysub, ln, &ydigits );
662 
663  dx = ux - tx;
664  dy = uy - ty;
665 // restore zdigits to initial value for second call
666  zdigits = zdigmax;
667  plzbx( zopt, zlabel, 0, dx, dy, tx, ty,
668  plP_w3wcy( xmin, ymax, zmax ), zmin, zmax, ztick, nzsub, &zdigits );
669  }
670  else if ( cxx <= 0.0 && cxy <= 0.0 )
671  {
672  ln = plP_stsearch( yopt, 'n' );
673  tx = plP_w3wcx( xmin, ymax, zmin );
674  ty = plP_w3wcy( xmin, ymax, zmin );
675  ux = plP_w3wcx( xmin, ymin, zmin );
676  uy = plP_w3wcy( xmin, ymin, zmin );
677  plxybx( yopt, ylabel, PL_Y_AXIS, tx, ty, ux, uy,
678  ymax, ymin, ytick, nysub, 0, &ydigits );
679 
680  dx = ux - tx;
681  dy = uy - ty;
682  plzbx( zopt, zlabel, 1, dx, dy, ux, uy,
683  plP_w3wcy( xmin, ymin, zmax ), zmin, zmax, ztick, nzsub, &zdigits );
684 
685  tx = plP_w3wcx( xmax, ymax, zmin );
686  ty = plP_w3wcy( xmax, ymax, zmin );
687  ux = plP_w3wcx( xmin, ymax, zmin );
688  uy = plP_w3wcy( xmin, ymax, zmin );
689  plxybx( xopt, xlabel, PL_X_AXIS, tx, ty, ux, uy,
690  xmax, xmin, xtick, nxsub, ln, &xdigits );
691 
692  dx = ux - tx;
693  dy = uy - ty;
694 // restore zdigits to initial value for second call
695  zdigits = zdigmax;
696  plzbx( zopt, zlabel, 0, dx, dy, tx, ty,
697  plP_w3wcy( xmax, ymax, zmax ), zmin, zmax, ztick, nzsub, &zdigits );
698  }
699  else if ( cxx <= 0.0 && cxy >= 0.0 )
700  {
701  ln = plP_stsearch( xopt, 'n' );
702  tx = plP_w3wcx( xmax, ymax, zmin );
703  ty = plP_w3wcy( xmax, ymax, zmin );
704  ux = plP_w3wcx( xmin, ymax, zmin );
705  uy = plP_w3wcy( xmin, ymax, zmin );
706  plxybx( xopt, xlabel, PL_X_AXIS, tx, ty, ux, uy,
707  xmax, xmin, xtick, nxsub, 0, &xdigits );
708 
709  dx = ux - tx;
710  dy = uy - ty;
711  plzbx( zopt, zlabel, 1, dx, dy, ux, uy,
712  plP_w3wcy( xmin, ymax, zmax ), zmin, zmax, ztick, nzsub, &zdigits );
713 
714  tx = plP_w3wcx( xmax, ymin, zmin );
715  ty = plP_w3wcy( xmax, ymin, zmin );
716  ux = plP_w3wcx( xmax, ymax, zmin );
717  uy = plP_w3wcy( xmax, ymax, zmin );
718  plxybx( yopt, ylabel, PL_Y_AXIS, tx, ty, ux, uy,
719  ymin, ymax, ytick, nysub, ln, &ydigits );
720 
721  dx = ux - tx;
722  dy = uy - ty;
723 // restore zdigits to initial value for second call
724  zdigits = zdigmax;
725  plzbx( zopt, zlabel, 0, dx, dy, tx, ty,
726  plP_w3wcy( xmax, ymin, zmax ), zmin, zmax, ztick, nzsub, &zdigits );
727  }
728  else if ( cxx >= 0.0 && cxy >= 0.0 )
729  {
730  ln = plP_stsearch( yopt, 'n' );
731  tx = plP_w3wcx( xmax, ymin, zmin );
732  ty = plP_w3wcy( xmax, ymin, zmin );
733  ux = plP_w3wcx( xmax, ymax, zmin );
734  uy = plP_w3wcy( xmax, ymax, zmin );
735  plxybx( yopt, ylabel, PL_X_AXIS, tx, ty, ux, uy,
736  ymin, ymax, ytick, nysub, 0, &ydigits );
737 
738  dx = ux - tx;
739  dy = uy - ty;
740  plzbx( zopt, zlabel, 1, dx, dy, ux, uy,
741  plP_w3wcy( xmax, ymax, zmax ), zmin, zmax, ztick, nzsub, &zdigits );
742 
743  tx = plP_w3wcx( xmin, ymin, zmin );
744  ty = plP_w3wcy( xmin, ymin, zmin );
745  ux = plP_w3wcx( xmax, ymin, zmin );
746  uy = plP_w3wcy( xmax, ymin, zmin );
747  plxybx( xopt, xlabel, PL_X_AXIS, tx, ty, ux, uy,
748  xmin, xmax, xtick, nxsub, ln, &xdigits );
749 
750  dx = ux - tx;
751  dy = uy - ty;
752 // restore zdigits to initial value for second call
753  zdigits = zdigmax;
754  plzbx( zopt, zlabel, 0, dx, dy, tx, ty,
755  plP_w3wcy( xmin, ymin, zmax ), zmin, zmax, ztick, nzsub, &zdigits );
756  }
757  plsxax( xdigmax, xdigits );
758  plsyax( ydigmax, ydigits );
759  plszax( zdigmax, zdigits );
760 }
761 
762 //--------------------------------------------------------------------------
763 // Support routines for 3d box draw.
764 //--------------------------------------------------------------------------
765 
766 //--------------------------------------------------------------------------
767 // void plxybx()
768 //
769 // This draws a sloping line from (wx1,wy1) to (wx2,wy2) which represents an
770 // axis of a 3-d graph with data values from "vmin" to "vmax". Depending on
771 // "opt", vertical ticks and/or subticks are placed on the line at major tick
772 // interval "tick" with "nsub" subticks between major ticks. If "tick" and/or
773 // "nsub" is zero, automatic tick positions are computed
774 //
775 // b: Draw box boundary
776 // f: Always use fixed point numeric labels
777 // i: Inverts tick marks (i.e. drawn downwards)
778 // l: Logarithmic axes, major ticks at decades, minor ticks at units
779 // n: Write numeric label
780 // o: Use custom label function
781 // t: Draw major tick marks
782 // s: Draw minor tick marks
783 // u: Write label on line
784 //--------------------------------------------------------------------------
785 
786 static void
787 plxybx( const char *opt, const char *label, PLINT axis, PLFLT wx1, PLFLT wy1,
788  PLFLT wx2, PLFLT wy2, PLFLT vmin_in, PLFLT vmax_in,
789  PLFLT tick, PLINT nsub, PLINT PL_UNUSED( nolast ), PLINT *digits )
790 {
791  static char string[STRING_LEN];
792  PLINT lb, ld, lf, li, ll, ln, ls, lt, lu, lo;
793  PLINT major, minor, mode, prec, scale;
794  PLINT i, i1, i2, i3, i4;
795  PLINT nsub1;
796  PLFLT pos, tn, tp, temp, height, tick1, vmin, vmax;
797 // Note that 'tspace' is the minimim distance away (in fractional number
798 // of ticks) from the boundary that an X or Y numerical label can be drawn.
799  PLFLT dwx, dwy, lambda, tcrit, tspace = 0.1;
800  const char * esc_string = plgesc_string();
801 
802  vmin = ( vmax_in > vmin_in ) ? vmin_in : vmax_in;
803  vmax = ( vmax_in > vmin_in ) ? vmax_in : vmin_in;
804 
805  dwx = wx2 - wx1;
806  dwy = wy2 - wy1;
807 
808 // Tick and subtick sizes in device coords
809 
810  major = MAX( ROUND( plsc->majht * plsc->ypmm ), 1 );
811  minor = MAX( ROUND( plsc->minht * plsc->ypmm ), 1 );
812 
813  tick1 = tick;
814  nsub1 = nsub;
815 
816  lb = plP_stsearch( opt, 'b' );
817  ld = plP_stsearch( opt, 'd' );
818  lf = plP_stsearch( opt, 'f' );
819  li = plP_stsearch( opt, 'i' );
820  ll = plP_stsearch( opt, 'l' );
821  ln = plP_stsearch( opt, 'n' );
822  ls = plP_stsearch( opt, 's' );
823  lt = plP_stsearch( opt, 't' );
824  lu = plP_stsearch( opt, 'u' );
825  lo = plP_stsearch( opt, 'o' );
826 
827  if ( lu )
828  plxytx( wx1, wy1, wx2, wy2, 3.2, 0.5, 0.5, label );
829  if ( !lb )
830  return;
831 
832  if ( ll )
833  tick1 = ( vmax > vmin ) ? 1.0 : -1.0;
834  if ( lt )
835  pldtik( vmin, vmax, &tick1, &nsub1, ld );
836 
837  if ( li )
838  {
839  i1 = minor;
840  i2 = 0;
841  i3 = major;
842  i4 = 0;
843  }
844  else
845  {
846  i1 = 0;
847  i2 = minor;
848  i3 = 0;
849  i4 = major;
850  }
851 
852 // Draw the line
853 
854  plP_movwor( wx1, wy1 );
855  plP_drawor( wx2, wy2 );
856  if ( lt )
857  {
858  tp = tick1 * floor( vmin / tick1 );
859  for (;; )
860  {
861  tn = tp + tick1;
862  if ( ls )
863  {
864  if ( ll )
865  {
866  for ( i = 0; i <= 7; i++ )
867  {
868  temp = tp + xlog[i];
869  if ( BETW( temp, vmin, vmax ) )
870  {
871  lambda = ( vmax_in > vmin_in ) ?
872  ( temp - vmin ) / ( vmax - vmin ) :
873  ( vmax - temp ) / ( vmax - vmin );
874  plxtik( plP_wcpcx( (PLFLT) ( wx1 + lambda * dwx ) ),
875  plP_wcpcy( (PLFLT) ( wy1 + lambda * dwy ) ),
876  i1, i2 );
877  }
878  }
879  }
880  else
881  {
882  for ( i = 1; i <= nsub1 - 1; i++ )
883  {
884  temp = tp + i * ( tn - tp ) / nsub1;
885  if ( BETW( temp, vmin, vmax ) )
886  {
887  lambda = ( vmax_in > vmin_in ) ?
888  ( temp - vmin ) / ( vmax - vmin ) :
889  ( vmax - temp ) / ( vmax - vmin );
890  plxtik( plP_wcpcx( (PLFLT) ( wx1 + lambda * dwx ) ),
891  plP_wcpcy( (PLFLT) ( wy1 + lambda * dwy ) ),
892  i1, i2 );
893  }
894  }
895  }
896  }
897  temp = tn;
898  if ( !BETW( temp, vmin, vmax ) )
899  break;
900 
901  lambda = ( vmax_in > vmin_in ) ?
902  ( temp - vmin ) / ( vmax - vmin ) :
903  ( vmax - temp ) / ( vmax - vmin );
904  plxtik( plP_wcpcx( (PLFLT) ( wx1 + lambda * dwx ) ),
905  plP_wcpcy( (PLFLT) ( wy1 + lambda * dwy ) ), i3, i4 );
906  tp = tn;
907  }
908  }
909 
910 
911 // Label the line
912 
913  if ( ln && lt )
914  {
915  pldprec( vmin, vmax, tick1, lf, &mode, &prec, *digits, &scale );
916  pos = 1.0;
917  height = 3.2;
918  tcrit = tspace * tick1;
919  tp = tick1 * ( 1. + floor( vmin / tick1 ) );
920  for ( tn = tp; BETW( tn, vmin, vmax ); tn += tick1 )
921  {
922  if ( BETW( tn, vmin + tcrit, vmax - tcrit ) )
923  {
924  plform( axis, tn, scale, prec, string, STRING_LEN, ll, lf, lo );
925  pos = ( vmax_in > vmin_in ) ?
926  ( tn - vmin ) / ( vmax - vmin ) :
927  ( vmax - tn ) / ( vmax - vmin );
928  plxytx( wx1, wy1, wx2, wy2, 1.5, pos, 0.5, string );
929  }
930  }
931  *digits = 2;
932  if ( !ll && !lo && mode )
933  {
934  snprintf( string, STRING_LEN, "(x10%su%d%sd)", esc_string, (int) scale, esc_string );
935  plxytx( wx1, wy1, wx2, wy2, height, 1.0, 0.5, string );
936  }
937  }
938 }
939 
940 //--------------------------------------------------------------------------
941 // void plxytx()
942 //
943 // Prints out text along a sloping axis joining world coordinates
944 // (wx1,wy1) to (wx2,wy2). Parameters are as for plmtext.
945 //--------------------------------------------------------------------------
946 
947 static void
948 plxytx( PLFLT wx1, PLFLT wy1, PLFLT wx2, PLFLT wy2,
949  PLFLT disp, PLFLT pos, PLFLT just, const char *text )
950 {
951  PLINT x, y, refx, refy;
952  PLFLT shift, cc, ss, wx, wy;
953  PLFLT xdv, ydv, xmm, ymm, refxmm, refymm, xform[4], diag;
954  PLFLT dispx, dispy;
955  PLFLT chrdef, chrht;
956 
957  cc = plsc->wmxscl * ( wx2 - wx1 );
958  ss = plsc->wmyscl * ( wy2 - wy1 );
959  diag = sqrt( cc * cc + ss * ss );
960  cc /= diag;
961  ss /= diag;
962  wx = wx1 + pos * ( wx2 - wx1 );
963  wy = wy1 + pos * ( wy2 - wy1 );
964 
965  xform[0] = cc;
966  xform[1] = 0.0;
967  xform[2] = ss;
968  xform[3] = 1.0;
969 
970  xdv = plP_wcdcx( wx );
971  ydv = plP_wcdcy( wy );
972 
973  dispx = 0.;
974  dispy = -disp;
975 
976  plgchr( &chrdef, &chrht );
977  shift = ( just == 0.0 ) ? 0.0 : plstrl( text ) * just;
978 
979  xmm = plP_dcmmx( xdv ) + dispx * chrht;
980  ymm = plP_dcmmy( ydv ) + dispy * chrht;
981  refxmm = xmm - shift * xform[0];
982  refymm = ymm - shift * xform[2];
983 
984  x = plP_mmpcx( xmm );
985  y = plP_mmpcy( ymm );
986  refx = plP_mmpcx( refxmm );
987  refy = plP_mmpcy( refymm );
988 
989  plP_text( 0, just, xform, x, y, refx, refy, text );
990 }
991 
992 //--------------------------------------------------------------------------
993 // void plzbx()
994 //
995 // This draws a vertical line from (wx,wy1) to (wx,wy2) which represents the
996 // vertical axis of a 3-d graph with data values from "vmin" to "vmax".
997 // Depending on "opt", ticks and/or subticks are placed on the line at major
998 // tick interval "tick" with "nsub" subticks between major ticks. If "tick"
999 // and/or "nsub" is zero, automatic tick positions are computed
1000 //
1001 // b: Draws left-hand axis
1002 // c: Draws right-hand axis
1003 // f: Always use fixed point numeric labels
1004 // i: Inverts tick marks (i.e. drawn to the left)
1005 // l: Logarithmic axes, major ticks at decades, minor ticks at units
1006 // m: Write numeric label on right axis
1007 // n: Write numeric label on left axis
1008 // o: Use custom label function
1009 // s: Draw minor tick marks
1010 // t: Draw major tick marks
1011 // u: Writes left-hand label
1012 // v: Writes right-hand label
1013 //--------------------------------------------------------------------------
1014 
1015 static void
1016 plzbx( const char *opt, const char *label, PLINT right, PLFLT dx, PLFLT dy,
1017  PLFLT wx, PLFLT wy1, PLFLT wy2, PLFLT vmin_in, PLFLT vmax_in,
1018  PLFLT tick, PLINT nsub, PLINT *digits )
1019 {
1020  static char string[STRING_LEN];
1021  PLINT lb, lc, ld, lf, li, ll, lm, ln, ls, lt, lu, lv, lo;
1022  PLINT i, mode, prec, scale;
1023  PLINT nsub1, lstring;
1024  PLFLT pos, tn, tp, temp, height, tick1;
1025  PLFLT dwy, lambda, diag, major, minor, xmajor, xminor;
1026  PLFLT ymajor, yminor, dxm, dym, vmin, vmax;
1027  const char * esc_string = plgesc_string();
1028 
1029  vmin = ( vmax_in > vmin_in ) ? vmin_in : vmax_in;
1030  vmax = ( vmax_in > vmin_in ) ? vmax_in : vmin_in;
1031 
1032  dwy = wy2 - wy1;
1033 
1034 // Tick and subtick sizes in device coords
1035 
1036  major = plsc->majht;
1037  minor = plsc->minht;
1038 
1039  tick1 = tick;
1040  nsub1 = nsub;
1041 
1042  lb = plP_stsearch( opt, 'b' );
1043  lc = plP_stsearch( opt, 'c' );
1044  ld = plP_stsearch( opt, 'd' );
1045  lf = plP_stsearch( opt, 'f' );
1046  li = plP_stsearch( opt, 'i' );
1047  ll = plP_stsearch( opt, 'l' );
1048  lm = plP_stsearch( opt, 'm' );
1049  ln = plP_stsearch( opt, 'n' );
1050  ls = plP_stsearch( opt, 's' );
1051  lt = plP_stsearch( opt, 't' );
1052  lu = plP_stsearch( opt, 'u' );
1053  lv = plP_stsearch( opt, 'v' );
1054  lo = plP_stsearch( opt, 'o' );
1055 
1056  if ( lu && !right )
1057  plztx( "h", dx, dy, wx, wy1, wy2, 5.0, 0.5, 0.5, label );
1058 
1059  if ( lv && right )
1060  plztx( "h", dx, dy, wx, wy1, wy2, -5.0, 0.5, 0.5, label );
1061 
1062  if ( right && !lc )
1063  return;
1064 
1065  if ( !right && !lb )
1066  return;
1067 
1068  if ( ll )
1069  tick1 = 1.0;
1070 
1071  if ( lt )
1072  pldtik( vmin, vmax, &tick1, &nsub1, ld );
1073 
1074  if ( ( li && !right ) || ( !li && right ) )
1075  {
1076  minor = -minor;
1077  major = -major;
1078  }
1079 
1080  dxm = dx * plsc->wmxscl;
1081  dym = dy * plsc->wmyscl;
1082  diag = sqrt( dxm * dxm + dym * dym );
1083 
1084  xminor = minor * dxm / diag;
1085  xmajor = major * dxm / diag;
1086  yminor = minor * dym / diag;
1087  ymajor = major * dym / diag;
1088 
1089 // Draw the line
1090 
1091  plP_movwor( wx, wy1 );
1092  plP_drawor( wx, wy2 );
1093  if ( lt )
1094  {
1095  tp = tick1 * floor( vmin / tick1 );
1096  for (;; )
1097  {
1098  tn = tp + tick1;
1099  if ( ls )
1100  {
1101  if ( ll )
1102  {
1103  for ( i = 0; i <= 7; i++ )
1104  {
1105  temp = tp + xlog[i];
1106  if ( BETW( temp, vmin, vmax ) )
1107  {
1108  lambda = ( vmax_in > vmin_in ) ?
1109  ( temp - vmin ) / ( vmax - vmin ) :
1110  ( vmax - temp ) / ( vmax - vmin );
1111  plstik( plP_wcmmx( wx ),
1112  plP_wcmmy( (PLFLT) ( wy1 + lambda * dwy ) ),
1113  xminor, yminor );
1114  }
1115  }
1116  }
1117  else
1118  {
1119  for ( i = 1; i <= nsub1 - 1; i++ )
1120  {
1121  temp = tp + i * tick1 / nsub1;
1122  if ( BETW( temp, vmin, vmax ) )
1123  {
1124  lambda = ( vmax_in > vmin_in ) ?
1125  ( temp - vmin ) / ( vmax - vmin ) :
1126  ( vmax - temp ) / ( vmax - vmin );
1127  plstik( plP_wcmmx( wx ),
1128  plP_wcmmy( (PLFLT) ( wy1 + lambda * dwy ) ),
1129  xminor, yminor );
1130  }
1131  }
1132  }
1133  }
1134  temp = tn;
1135  if ( !BETW( temp, vmin, vmax ) )
1136  break;
1137  lambda = ( vmax_in > vmin_in ) ?
1138  ( temp - vmin ) / ( vmax - vmin ) :
1139  ( vmax - temp ) / ( vmax - vmin );
1140  plstik( plP_wcmmx( wx ), plP_wcmmy( (PLFLT) ( wy1 + lambda * dwy ) ),
1141  xmajor, ymajor );
1142  tp = tn;
1143  }
1144  }
1145 
1146 
1147 // Label the line
1148 
1149  if ( ( ln || lm ) && lt )
1150  {
1151  pldprec( vmin, vmax, tick1, lf, &mode, &prec, *digits, &scale );
1152  *digits = 0;
1153  tp = tick1 * floor( vmin / tick1 );
1154  for ( tn = tp + tick1; BETW( tn, vmin, vmax ); tn += tick1 )
1155  {
1156  plform( PL_Z_AXIS, tn, scale, prec, string, STRING_LEN, ll, lf, lo );
1157  pos = ( vmax_in > vmin_in ) ?
1158  ( tn - vmin ) / ( vmax - vmin ) :
1159  ( vmax - tn ) / ( vmax - vmin );
1160  if ( ln && !right )
1161  plztx( "v", dx, dy, wx, wy1, wy2, 0.5, pos, 1.0, string );
1162 
1163  if ( lm && right )
1164  plztx( "v", dx, dy, wx, wy1, wy2, -0.5, pos, 0.0, string );
1165 
1166  lstring = (PLINT) strlen( string );
1167  *digits = MAX( *digits, lstring );
1168  }
1169  if ( !ll && !lo && mode )
1170  {
1171  snprintf( string, STRING_LEN, "(x10%su%d%sd)", esc_string, (int) scale, esc_string );
1172  pos = 1.15;
1173  height = 0.5;
1174  if ( ln && !right )
1175  {
1176  plztx( "v", dx, dy, wx, wy1, wy2, height, pos, 0.5, string );
1177  }
1178  if ( lm && right )
1179  {
1180  plztx( "v", dx, dy, wx, wy1, wy2,
1181  (PLFLT) -height, pos, 0.5, string );
1182  }
1183  }
1184  }
1185 }
1186 
1187 //--------------------------------------------------------------------------
1188 // void plztx()
1189 //
1190 // Prints out text along a vertical axis for a 3d plot joining
1191 // world coordinates (wx,wy1) to (wx,wy2).
1192 //--------------------------------------------------------------------------
1193 
1194 static void
1195 plztx( const char *opt, PLFLT dx, PLFLT dy, PLFLT wx, PLFLT wy1,
1196  PLFLT wy2, PLFLT disp, PLFLT pos, PLFLT just, const char *text )
1197 {
1198  PLINT refx = 0, refy = 0, x = 0, y = 0, vert = 0;
1199  PLFLT shift, cc, ss, wy;
1200  PLFLT xdv, ydv, xmm, ymm, refxmm, refymm, xform[4], diag;
1201  PLFLT dispx, dispy;
1202  PLFLT chrdef, chrht;
1203 
1204  cc = plsc->wmxscl * dx;
1205  ss = plsc->wmyscl * dy;
1206  diag = sqrt( cc * cc + ss * ss );
1207  cc /= diag;
1208  ss /= diag;
1209  wy = wy1 + pos * ( wy2 - wy1 );
1210 
1211  if ( plP_stsearch( opt, 'v' ) )
1212  vert = 0;
1213  else if ( plP_stsearch( opt, 'h' ) )
1214  vert = 1;
1215 
1216  if ( vert )
1217  {
1218  xform[0] = 0.0;
1219  xform[1] = -cc;
1220  xform[2] = 1.0;
1221  xform[3] = -ss;
1222  }
1223  else
1224  {
1225  xform[0] = cc;
1226  xform[1] = 0.0;
1227  xform[2] = ss;
1228  xform[3] = 1.0;
1229  }
1230 
1231  xdv = plP_wcdcx( wx );
1232  ydv = plP_wcdcy( wy );
1233 
1234  dispx = -disp * cc;
1235  dispy = -disp * ss;
1236 
1237  plgchr( &chrdef, &chrht );
1238  shift = ( just == 0.0 ) ? 0.0 : plstrl( text ) * just;
1239 
1240  xmm = plP_dcmmx( xdv ) + dispx * chrht;
1241  ymm = plP_dcmmy( ydv ) + dispy * chrht;
1242  refxmm = xmm - shift * xform[0];
1243  refymm = ymm - shift * xform[2];
1244 
1245  x = plP_mmpcx( xmm );
1246  y = plP_mmpcy( ymm );
1247  refx = plP_mmpcx( refxmm );
1248  refy = plP_mmpcy( refymm );
1249 
1250  plP_text( 0, just, xform, x, y, refx, refy, text );
1251 }
1252 
1253 //--------------------------------------------------------------------------
1254 // void grid_box()
1255 //
1256 // Draws grids at tick locations (major and/or minor).
1257 //
1258 // Note that 'tspace' is the minimim distance away (in fractional number
1259 // of ticks or subticks) from the boundary a grid line can be drawn. If
1260 // you are too close, it looks bad.
1261 //--------------------------------------------------------------------------
1262 
1263 static void
1264 grid_box( const char *xopt, PLFLT xtick1, PLINT nxsub1,
1265  const char *yopt, PLFLT ytick1, PLINT nysub1 )
1266 {
1267  PLINT lgx, lhx, llx, ldx;
1268  PLINT lgy, lhy, lly, ldy;
1269  PLFLT vpwxmi, vpwxma, vpwymi, vpwyma;
1270  PLFLT vpwxmin, vpwxmax, vpwymin, vpwymax;
1271  PLFLT tn, temp, tcrit, tspace = 0.1;
1272  PLFLT tstart, factor;
1273  PLINT i;
1274 
1275 // Set plot options from input
1276 
1277  lgx = plP_stsearch( xopt, 'g' );
1278  lhx = plP_stsearch( xopt, 'h' );
1279  llx = plP_stsearch( xopt, 'l' );
1280  ldx = plP_stsearch( xopt, 'd' );
1281 
1282  lgy = plP_stsearch( yopt, 'g' );
1283  lhy = plP_stsearch( yopt, 'h' );
1284  lly = plP_stsearch( yopt, 'l' );
1285  ldy = plP_stsearch( yopt, 'd' );
1286 
1287  plP_xgvpw( &vpwxmin, &vpwxmax, &vpwymin, &vpwymax );
1288 // n.b. large change; vpwxmi always numerically less than vpwxma, and
1289 // similarly for vpwymi
1290  vpwxmi = ( vpwxmax > vpwxmin ) ? vpwxmin : vpwxmax;
1291  vpwxma = ( vpwxmax > vpwxmin ) ? vpwxmax : vpwxmin;
1292  vpwymi = ( vpwymax > vpwymin ) ? vpwymin : vpwymax;
1293  vpwyma = ( vpwymax > vpwymin ) ? vpwymax : vpwymin;
1294 
1295 // Draw grid in x direction.
1296 
1297  if ( lgx )
1298  {
1299  if ( ldx )
1300  {
1301  pldtfac( vpwxmi, vpwxma, &factor, &tstart );
1302  tn = xtick1 * ( floor( ( vpwxmi - tstart ) / xtick1 ) ) + tstart;
1303  }
1304  else
1305  {
1306  tn = xtick1 * floor( vpwxmi / xtick1 );
1307  }
1308  for (; tn <= vpwxma; tn += xtick1 )
1309  {
1310  if ( lhx )
1311  {
1312  if ( llx )
1313  {
1314  PLFLT otemp = tn;
1315  for ( i = 0; i <= 7; i++ )
1316  {
1317  temp = tn + xlog[i];
1318  tcrit = ( temp - otemp ) * tspace;
1319  otemp = temp;
1320  if ( BETW( temp, vpwxmi + tcrit, vpwxma - tcrit ) )
1321  pljoin( temp, vpwymi, temp, vpwyma );
1322  }
1323  }
1324  else
1325  {
1326  for ( i = 1; i <= nxsub1 - 1; i++ )
1327  {
1328  temp = tn + i * xtick1 / nxsub1;
1329  tcrit = xtick1 / nxsub1 * tspace;
1330  if ( BETW( temp, vpwxmi + tcrit, vpwxma - tcrit ) )
1331  pljoin( temp, vpwymi, temp, vpwyma );
1332  }
1333  }
1334  }
1335  tcrit = xtick1 * tspace;
1336  if ( BETW( tn, vpwxmi + tcrit, vpwxma - tcrit ) )
1337  pljoin( tn, vpwymi, tn, vpwyma );
1338  }
1339  }
1340 
1341 // Draw grid in y direction
1342 
1343  if ( lgy )
1344  {
1345  if ( ldy )
1346  {
1347  pldtfac( vpwymi, vpwyma, &factor, &tstart );
1348  tn = ytick1 * ( floor( ( vpwymi - tstart ) / ytick1 ) ) + tstart;
1349  }
1350  else
1351  {
1352  tn = ytick1 * floor( vpwymi / ytick1 );
1353  }
1354  for (; tn <= vpwyma; tn += ytick1 )
1355  {
1356  if ( lhy )
1357  {
1358  if ( lly )
1359  {
1360  PLFLT otemp = tn;
1361  for ( i = 0; i <= 7; i++ )
1362  {
1363  temp = tn + xlog[i];
1364  tcrit = ( temp - otemp ) * tspace;
1365  otemp = temp;
1366  if ( BETW( temp, vpwymi + tcrit, vpwyma - tcrit ) )
1367  pljoin( vpwxmi, temp, vpwxma, temp );
1368  }
1369  }
1370  else
1371  {
1372  for ( i = 1; i <= nysub1 - 1; i++ )
1373  {
1374  temp = tn + i * ytick1 / nysub1;
1375  tcrit = ytick1 / nysub1 * tspace;
1376  if ( BETW( temp, vpwymi + tcrit, vpwyma - tcrit ) )
1377  pljoin( vpwxmi, temp, vpwxma, temp );
1378  }
1379  }
1380  }
1381  tcrit = ytick1 * tspace;
1382  if ( BETW( tn, vpwymi + tcrit, vpwyma - tcrit ) )
1383  pljoin( vpwxmi, tn, vpwxma, tn );
1384  }
1385  }
1386 }
1387 
1388 //--------------------------------------------------------------------------
1389 // void label_box()
1390 //
1391 // Writes numeric labels on side(s) of box.
1392 //--------------------------------------------------------------------------
1393 
1394 static void
1395 label_box( const char *xopt, PLFLT xtick1, const char *yopt, PLFLT ytick1 )
1396 {
1397  static char string[STRING_LEN];
1398  PLBOOL ldx, lfx, lix, llx, lmx, lnx, ltx, lox, lxx;
1399  PLBOOL ldy, lfy, liy, lly, lmy, lny, lty, lvy, loy, lxy;
1400  PLFLT vpwxmi, vpwxma, vpwymi, vpwyma;
1401  PLFLT vpwxmin, vpwxmax, vpwymin, vpwymax;
1402  PLFLT tn, tp, offset;
1403  PLFLT factor, tstart;
1404  const char *timefmt = NULL;
1405  PLFLT default_mm, char_height_mm, height_mm;
1406  PLFLT string_length_mm = 0.0, pos_mm = 0.0;
1407 
1408  // Assume label data is for placement of exponents if no custom
1409  // label function is provided.
1410  PLBOOL custom_exponent_placement = !plsc->label_func && plsc->label_data;
1411 
1412  // pos, height, and just are unnecessarily set to quiet
1413  // -O3 -Wuninitialized warnings that are obvious false alarms from
1414  // the clarity of the code associated with the true or false
1415  // result for custom_exponent_placement.
1416  PLFLT pos = 0.0, height = 0.0, just = 0.0;
1417  const char * esc_string = plgesc_string();
1418 
1419  plgchr( &default_mm, &char_height_mm );
1420 
1421 // Set plot options from input
1422 
1423  ldx = plP_stsearch( xopt, 'd' );
1424  lfx = plP_stsearch( xopt, 'f' );
1425  lix = plP_stsearch( xopt, 'i' );
1426  llx = plP_stsearch( xopt, 'l' );
1427  lmx = plP_stsearch( xopt, 'm' );
1428  lnx = plP_stsearch( xopt, 'n' );
1429  ltx = plP_stsearch( xopt, 't' );
1430  lox = plP_stsearch( xopt, 'o' );
1431  lxx = plP_stsearch( xopt, 'x' );
1432 
1433  ldy = plP_stsearch( yopt, 'd' );
1434  lfy = plP_stsearch( yopt, 'f' );
1435  liy = plP_stsearch( yopt, 'i' );
1436  lly = plP_stsearch( yopt, 'l' );
1437  lmy = plP_stsearch( yopt, 'm' );
1438  lny = plP_stsearch( yopt, 'n' );
1439  lty = plP_stsearch( yopt, 't' );
1440  lvy = plP_stsearch( yopt, 'v' );
1441  loy = plP_stsearch( yopt, 'o' );
1442  lxy = plP_stsearch( yopt, 'x' );
1443 
1444  plP_xgvpw( &vpwxmin, &vpwxmax, &vpwymin, &vpwymax );
1445 // vpwxmi always numerically less than vpwxma, and
1446 // similarly for vpwymi
1447  vpwxmi = ( vpwxmax > vpwxmin ) ? vpwxmin : vpwxmax;
1448  vpwxma = ( vpwxmax > vpwxmin ) ? vpwxmax : vpwxmin;
1449  vpwymi = ( vpwymax > vpwymin ) ? vpwymin : vpwymax;
1450  vpwyma = ( vpwymax > vpwymin ) ? vpwymax : vpwymin;
1451 
1452  // Write label(s) for horizontal axes.
1453  if ( ( lmx || lnx ) && ( ltx || lxx ) )
1454  {
1455  PLINT xmode, xprec, xdigmax, xdigits, xscale;
1456 
1457  plgxax( &xdigmax, &xdigits );
1458  pldprec( vpwxmi, vpwxma, xtick1, lfx, &xmode, &xprec, xdigmax, &xscale );
1459  timefmt = plP_gtimefmt();
1460 
1461  if ( ldx )
1462  {
1463  pldtfac( vpwxmi, vpwxma, &factor, &tstart );
1464  tp = xtick1 * ( 1. + floor( ( vpwxmi - tstart ) / xtick1 ) ) + tstart;
1465  }
1466  else
1467  {
1468  tp = xtick1 * ( 1. + floor( vpwxmi / xtick1 ) );
1469  }
1470  height = lix ? 1.75 : 1.5;
1471  if ( plsc->if_boxbb )
1472  {
1473  // For horizontal axes, height of zero corresponds to
1474  // character centred on edge so should add 0.5 to height
1475  // to obtain bounding box edge in direction away from
1476  // edge. However, experimentally found 0.7 gave a better
1477  // looking result.
1478  height_mm = ( height + 0.7 ) * char_height_mm;
1479  if ( lnx )
1480  plsc->boxbb_ymin = MIN( plsc->boxbb_ymin, plsc->vppymi /
1481  plsc->ypmm - height_mm );
1482  if ( lmx )
1483  plsc->boxbb_ymax = MAX( plsc->boxbb_ymax, plsc->vppyma /
1484  plsc->ypmm + height_mm );
1485  }
1486 
1487  for ( tn = tp; BETW( tn, vpwxmi, vpwxma ); tn += xtick1 )
1488  {
1489  if ( ldx )
1490  {
1491  strfqsas( string, STRING_LEN, timefmt, (double) tn, plsc->qsasconfig );
1492  }
1493  else
1494  {
1495  plform( PL_X_AXIS, tn, xscale, xprec, string, STRING_LEN, llx, lfx, lox );
1496  }
1497  pos = ( vpwxmax > vpwxmin ) ?
1498  ( tn - vpwxmi ) / ( vpwxma - vpwxmi ) :
1499  ( vpwxma - tn ) / ( vpwxma - vpwxmi );
1500  if ( plsc->if_boxbb )
1501  {
1502  string_length_mm = plstrl( string );
1503  pos_mm = ( plsc->vppxmi + pos *
1504  ( plsc->vppxma - plsc->vppxmi ) ) /
1505  plsc->xpmm;
1506  }
1507  if ( lnx )
1508  {
1509  // Bottom axis.
1510  if ( plsc->if_boxbb )
1511  {
1512  plsc->boxbb_xmin = MIN( plsc->boxbb_xmin,
1513  pos_mm - 0.5 * string_length_mm );
1514  plsc->boxbb_xmax = MAX( plsc->boxbb_xmax,
1515  pos_mm + 0.5 * string_length_mm );
1516  }
1517  else
1518  {
1519  plmtex( "b", height, pos, 0.5, string );
1520  }
1521  }
1522  if ( lmx )
1523  {
1524  // Top axis.
1525  if ( plsc->if_boxbb )
1526  {
1527  plsc->boxbb_xmin = MIN( plsc->boxbb_xmin,
1528  pos_mm - 0.5 * string_length_mm );
1529  plsc->boxbb_xmax = MAX( plsc->boxbb_xmax,
1530  pos_mm + 0.5 * string_length_mm );
1531  }
1532  else
1533  {
1534  plmtex( "t", height, pos, 0.5, string );
1535  }
1536  }
1537  }
1538  xdigits = 2;
1539  plsxax( xdigmax, xdigits );
1540 
1541  // Write separate exponential label if mode = 1.
1542 
1543  if ( !llx && !ldx && !lox && xmode )
1544  {
1545  if ( custom_exponent_placement )
1546  {
1547  height = ( (PLLabelDefaults *) plsc->label_data )->exp_label_disp;
1548  pos = ( (PLLabelDefaults *) plsc->label_data )->exp_label_pos;
1549  just = ( (PLLabelDefaults *) plsc->label_data )->exp_label_just;
1550  }
1551  else
1552  {
1553  height = 3.2;
1554  pos = 1.0;
1555  just = 0.5;
1556  }
1557  snprintf( string, STRING_LEN, "(x10%su%d%sd)", esc_string, (int) xscale, esc_string );
1558  if ( lnx )
1559  {
1560  // Bottom axis exponent.
1561  if ( plsc->if_boxbb )
1562  {
1563  // For horizontal axes, height of zero corresponds
1564  // to character centred on edge so should add 0.5
1565  // to height to obtain bounding box edge in
1566  // direction away from edge if no exponent. Add
1567  // an additional offset to make exponent fit.
1568  height_mm = ( height + 0.9 ) * char_height_mm;
1569  plsc->boxbb_ymin = MIN( plsc->boxbb_ymin, plsc->vppymi /
1570  plsc->ypmm - height_mm );
1571  string_length_mm = plstrl( string );
1572  pos_mm = ( plsc->vppxmi + pos *
1573  ( plsc->vppxma - plsc->vppxmi ) ) /
1574  plsc->xpmm;
1575  plsc->boxbb_xmin = MIN( plsc->boxbb_xmin,
1576  pos_mm - 0.5 * string_length_mm );
1577  plsc->boxbb_xmax = MAX( plsc->boxbb_xmax,
1578  pos_mm + 0.5 * string_length_mm );
1579  }
1580  else
1581  {
1582  plmtex( "b", height, pos, just, string );
1583  }
1584  }
1585  if ( lmx )
1586  {
1587  // Top axis exponent.
1588  if ( plsc->if_boxbb )
1589  {
1590  // For horizontal axes, height of zero corresponds
1591  // to character centred on edge so should add 0.5
1592  // to height to obtain bounding box edge in
1593  // direction away from edge if no exponent. Add
1594  // an additional offset to make exponent fit.
1595  height_mm = ( height + 1.4 ) * char_height_mm;
1596  plsc->boxbb_ymax = MAX( plsc->boxbb_ymax, plsc->vppyma /
1597  plsc->ypmm + height_mm );
1598  string_length_mm = plstrl( string );
1599  pos_mm = ( plsc->vppxmi + pos *
1600  ( plsc->vppxma - plsc->vppxmi ) ) /
1601  plsc->xpmm;
1602  plsc->boxbb_xmin = MIN( plsc->boxbb_xmin,
1603  pos_mm - 0.5 * string_length_mm );
1604  plsc->boxbb_xmax = MAX( plsc->boxbb_xmax,
1605  pos_mm + 0.5 * string_length_mm );
1606  }
1607  else
1608  {
1609  plmtex( "t", height, pos, just, string );
1610  }
1611  }
1612  }
1613  }
1614 
1615  // Write label(s) for vertical axes.
1616 
1617  if ( ( lmy || lny ) && ( lty || lxy ) )
1618  {
1619  PLINT ymode, yprec, ydigmax, ydigits, yscale;
1620 
1621  plgyax( &ydigmax, &ydigits );
1622  pldprec( vpwymi, vpwyma, ytick1, lfy, &ymode, &yprec, ydigmax, &yscale );
1623 
1624  ydigits = 0;
1625  if ( ldy )
1626  {
1627  pldtfac( vpwymi, vpwyma, &factor, &tstart );
1628  tp = ytick1 * ( 1. + floor( ( vpwymi - tstart ) / ytick1 ) ) + tstart;
1629  }
1630  else
1631  {
1632  tp = ytick1 * ( 1. + floor( vpwymi / ytick1 ) );
1633  }
1634  for ( tn = tp; BETW( tn, vpwymi, vpwyma ); tn += ytick1 )
1635  {
1636  if ( ldy )
1637  {
1638  strfqsas( string, STRING_LEN, timefmt, (double) tn, plsc->qsasconfig );
1639  }
1640  else
1641  {
1642  plform( PL_Y_AXIS, tn, yscale, yprec, string, STRING_LEN, lly, lfy, loy );
1643  }
1644  pos = ( vpwymax > vpwymin ) ?
1645  ( tn - vpwymi ) / ( vpwyma - vpwymi ) :
1646  ( vpwyma - tn ) / ( vpwyma - vpwymi );
1647  if ( lny )
1648  {
1649  if ( lvy )
1650  {
1651  // Left axis with text written perpendicular to edge.
1652  height = liy ? 1.0 : 0.5;
1653  if ( plsc->if_boxbb )
1654  {
1655  // For vertical axes with text written
1656  // perpendicular to edge, height of zero
1657  // corresponds character centred on edge so
1658  // should add 0.5 to height to obtain bounding
1659  // box edge in direction away from edge, and
1660  // that value apparently works.
1661  height_mm = ( height + 0.0 ) * char_height_mm;
1662  string_length_mm = plstrl( string );
1663  plsc->boxbb_xmin = MIN( plsc->boxbb_xmin, plsc->vppxmi /
1664  plsc->xpmm - height_mm - string_length_mm );
1665  pos_mm = ( plsc->vppymi + pos *
1666  ( plsc->vppyma - plsc->vppymi ) ) /
1667  plsc->ypmm;
1668  // Expected offset is 0.5, but adjust to improve
1669  // look of result.
1670  plsc->boxbb_ymin = MIN( plsc->boxbb_ymin,
1671  pos_mm - 0.6 * char_height_mm );
1672  plsc->boxbb_ymax = MAX( plsc->boxbb_ymax,
1673  pos_mm + 0.7 * char_height_mm );
1674  }
1675  else
1676  {
1677  plmtex( "lv", height, pos, 1.0, string );
1678  }
1679  }
1680  else
1681  {
1682  // Left axis with text written parallel to edge.
1683  height = liy ? 1.75 : 1.5;
1684  if ( plsc->if_boxbb )
1685  {
1686  // For vertical axes with text written
1687  // parallel to edge, height of zero
1688  // corresponds to character centred on edge so
1689  // should add 0.5 to height to obtain bounding
1690  // box edge in direction away from edge,
1691  // However, experimentally found 0.8 gave a
1692  // better looking result.
1693  height_mm = ( height + 0.8 ) * char_height_mm;
1694  plsc->boxbb_xmin = MIN( plsc->boxbb_xmin, plsc->vppxmi /
1695  plsc->xpmm - height_mm );
1696  pos_mm = ( plsc->vppymi + pos *
1697  ( plsc->vppyma - plsc->vppymi ) ) /
1698  plsc->ypmm;
1699  string_length_mm = plstrl( string );
1700  plsc->boxbb_ymin = MIN( plsc->boxbb_ymin,
1701  pos_mm - 0.5 * string_length_mm );
1702  plsc->boxbb_ymax = MAX( plsc->boxbb_ymax,
1703  pos_mm + 0.5 * string_length_mm );
1704  }
1705  else
1706  {
1707  plmtex( "l", height, pos, 0.5, string );
1708  }
1709  }
1710  }
1711  if ( lmy )
1712  {
1713  if ( lvy )
1714  {
1715  // Right axis with text written perpendicular to edge.
1716  height = liy ? 1.0 : 0.5;
1717  if ( plsc->if_boxbb )
1718  {
1719  // For vertical axes with text written
1720  // perpendicular to edge, height of zero
1721  // corresponds character centred on edge so
1722  // should add 0.5 to height to obtain bounding
1723  // box edge in direction away from edge, and
1724  // that value apparently works.
1725  height_mm = ( height + 0.0 ) * char_height_mm;
1726  string_length_mm = plstrl( string );
1727  plsc->boxbb_xmax = MAX( plsc->boxbb_xmax, plsc->vppxma /
1728  plsc->xpmm + height_mm + string_length_mm );
1729  pos_mm = ( plsc->vppymi + pos *
1730  ( plsc->vppyma - plsc->vppymi ) ) /
1731  plsc->ypmm;
1732  // Expected offset is 0.5, but adjust to improve
1733  // look of result.
1734  plsc->boxbb_ymin = MIN( plsc->boxbb_ymin,
1735  pos_mm - 0.6 * char_height_mm );
1736  plsc->boxbb_ymax = MAX( plsc->boxbb_ymax,
1737  pos_mm + 0.7 * char_height_mm );
1738  }
1739  else
1740  {
1741  plmtex( "rv", height, pos, 0.0, string );
1742  }
1743  }
1744  else
1745  {
1746  // Right axis with text written parallel to edge.
1747  height = liy ? 1.75 : 1.5;
1748  if ( plsc->if_boxbb )
1749  {
1750  // For vertical axes with text written
1751  // parallel to edge, height of zero
1752  // corresponds to character centred on edge so
1753  // should add 0.5 to height to obtain bounding
1754  // box edge in direction away from edge,
1755  // However, experimentally found 0.8 gave a
1756  // better looking result.
1757  height_mm = ( height + 0.8 ) * char_height_mm;
1758  plsc->boxbb_xmax = MAX( plsc->boxbb_xmax, plsc->vppxma /
1759  plsc->xpmm + height_mm );
1760  pos_mm = ( plsc->vppymi + pos *
1761  ( plsc->vppyma - plsc->vppymi ) ) /
1762  plsc->ypmm;
1763  string_length_mm = plstrl( string );
1764  plsc->boxbb_ymin = MIN( plsc->boxbb_ymin,
1765  pos_mm - 0.5 * string_length_mm );
1766  plsc->boxbb_ymax = MAX( plsc->boxbb_ymax,
1767  pos_mm + 0.5 * string_length_mm );
1768  }
1769  else
1770  {
1771  plmtex( "r", height, pos, 0.5, string );
1772  }
1773  }
1774  }
1775  ydigits = MAX( ydigits, (PLINT) strlen( string ) );
1776  }
1777  if ( !lvy )
1778  ydigits = 2;
1779 
1780  plsyax( ydigmax, ydigits );
1781 
1782  // Write separate exponential label if mode = 1.
1783 
1784  if ( !lly && !ldy && !loy && ymode )
1785  {
1786  snprintf( string, STRING_LEN, "(x10%su%d%sd)", esc_string, (int) yscale, esc_string );
1787  if ( custom_exponent_placement )
1788  {
1789  height = ( (PLLabelDefaults *) plsc->label_data )->exp_label_disp;
1790  pos = ( (PLLabelDefaults *) plsc->label_data )->exp_label_pos;
1791  just = ( (PLLabelDefaults *) plsc->label_data )->exp_label_just;
1792  }
1793  if ( lvy )
1794  {
1795  offset = 0.1; // more space to clear labels in "v" mode
1796  }
1797  else
1798  {
1799  offset = 0.02;
1800  }
1801  // Left axis exponent
1802  if ( lny )
1803  {
1804  if ( !custom_exponent_placement )
1805  {
1806  height = 3.2;
1807  pos = 1.0 + offset;
1808  just = 0.5;
1809  }
1810  if ( plsc->if_boxbb )
1811  {
1812  // For horizontal axes, height of zero corresponds
1813  // to character centred on edge so should add 0.5
1814  // to height to obtain bounding box edge in
1815  // direction away from edge if no exponent. Add
1816  // an additional offset to make exponent fit.
1817  height_mm = ( height + 1.4 ) * char_height_mm;
1818  plsc->boxbb_ymax = MAX( plsc->boxbb_ymax, plsc->vppyma /
1819  plsc->ypmm + height_mm );
1820  string_length_mm = plstrl( string );
1821  pos_mm = ( plsc->vppxmi + pos *
1822  ( plsc->vppxma - plsc->vppxmi ) ) /
1823  plsc->xpmm;
1824  plsc->boxbb_xmin = MIN( plsc->boxbb_xmin,
1825  pos_mm - string_length_mm );
1826  }
1827  else
1828  {
1829  if ( lvy )
1830  {
1831  plmtex( "lv", height, pos, just, string );
1832  }
1833  else
1834  {
1835  plmtex( "l", height, pos, just, string );
1836  }
1837  }
1838  }
1839  // Right axis exponent.
1840  if ( lmy )
1841  {
1842  if ( !custom_exponent_placement )
1843  {
1844  height = 3.4; // Extra space for superscript
1845  pos = 1.0 + offset;
1846  just = 0.5;
1847  }
1848  if ( plsc->if_boxbb )
1849  {
1850  // For horizontal axes, height of zero corresponds
1851  // to character centred on edge so should add 0.5
1852  // to height to obtain bounding box edge in
1853  // direction away from edge if no exponent. Add
1854  // an additional offset to make exponent fit.
1855  height_mm = ( height + 1.4 ) * char_height_mm;
1856  plsc->boxbb_ymax = MAX( plsc->boxbb_ymax, plsc->vppyma /
1857  plsc->ypmm + height_mm );
1858  string_length_mm = plstrl( string );
1859  pos_mm = ( plsc->vppxmi + pos *
1860  ( plsc->vppxma - plsc->vppxmi ) ) /
1861  plsc->xpmm;
1862  plsc->boxbb_xmax = MAX( plsc->boxbb_xmin,
1863  pos_mm + string_length_mm );
1864  }
1865  else
1866  {
1867  if ( lvy )
1868  {
1869  plmtex( "rv", height, pos, just, string );
1870  }
1871  else
1872  {
1873  plmtex( "r", height, pos, just, string );
1874  }
1875  }
1876  }
1877  }
1878  }
1879 }
1880 
1881 //--------------------------------------------------------------------------
1882 // void label_box_custom()
1883 //
1884 // Writes numeric labels on side(s) of box in custom locations
1885 //--------------------------------------------------------------------------
1886 
1887 void
1888 label_box_custom( const char *xopt, PLINT n_xticks, const PLFLT *xticks, const char *yopt, PLINT n_yticks, const PLFLT *yticks )
1889 {
1890  static char string[STRING_LEN];
1891  PLBOOL ldx, lfx, lix, llx, lmx, lnx, ltx, lox, lxx;
1892  PLBOOL ldy, lfy, liy, lly, lmy, lny, lty, lvy, loy, lxy;
1893  PLFLT vpwxmi, vpwxma, vpwymi, vpwyma;
1894  PLFLT vpwxmin, vpwxmax, vpwymin, vpwymax;
1895  PLFLT tn, offset;
1896  const char *timefmt;
1897  PLINT i;
1898  PLINT xdigmax, xdigits, xdigmax_old, xdigits_old;
1899  PLINT ydigmax, ydigits, ydigmax_old, ydigits_old;
1900  PLINT lxmin, lxmax, lymin, lymax;
1901  PLINT pxmin, pxmax, pymin, pymax;
1902  PLFLT default_mm, char_height_mm, height_mm;
1903  PLFLT string_length_mm = 0.0, pos_mm = 0.0;
1904 
1905  // Assume label data is for placement of exponents if no custom
1906  // label function is provided.
1907  PLBOOL custom_exponent_placement = !plsc->label_func && plsc->label_data;
1908 
1909  // pos, height, and just are unnecessarily set to quiet
1910  // -O3 -Wuninitialized warnings that are obvious false alarms from
1911  // the clarity of the code associated with the true or false
1912  // result for custom_exponent_placement.
1913  PLFLT pos = 0.0, height = 0.0, just = 0.0;
1914  const char * esc_string = plgesc_string();
1915 
1916  plgchr( &default_mm, &char_height_mm );
1917 
1918  // Save some parameters
1919  plgxax( &xdigmax, &xdigits );
1920  plgyax( &ydigmax, &ydigits );
1921  xdigmax_old = xdigmax;
1922  xdigits_old = xdigits;
1923  ydigmax_old = ydigmax;
1924  ydigits_old = ydigits;
1925 
1926 // Open the clip limits to the subpage limits
1927 
1928  plP_gclp( &lxmin, &lxmax, &lymin, &lymax );
1929  plP_gphy( &pxmin, &pxmax, &pymin, &pymax );
1930  plP_sclp( pxmin, pxmax, pymin, pymax );
1931 
1932 // Set plot options from input
1933 
1934  ldx = plP_stsearch( xopt, 'd' );
1935  lfx = plP_stsearch( xopt, 'f' );
1936  lix = plP_stsearch( xopt, 'i' );
1937  llx = plP_stsearch( xopt, 'l' );
1938  lmx = plP_stsearch( xopt, 'm' );
1939  lnx = plP_stsearch( xopt, 'n' );
1940  ltx = plP_stsearch( xopt, 't' );
1941  lox = plP_stsearch( xopt, 'o' );
1942  lxx = plP_stsearch( xopt, 'x' );
1943 
1944  ldy = plP_stsearch( yopt, 'd' );
1945  lfy = plP_stsearch( yopt, 'f' );
1946  liy = plP_stsearch( yopt, 'i' );
1947  lly = plP_stsearch( yopt, 'l' );
1948  lmy = plP_stsearch( yopt, 'm' );
1949  lny = plP_stsearch( yopt, 'n' );
1950  lty = plP_stsearch( yopt, 't' );
1951  lvy = plP_stsearch( yopt, 'v' );
1952  loy = plP_stsearch( yopt, 'o' );
1953  lxy = plP_stsearch( yopt, 'x' );
1954 
1955  plP_xgvpw( &vpwxmin, &vpwxmax, &vpwymin, &vpwymax );
1956 // n.b. large change; vpwxmi always numerically less than vpwxma, and
1957 // similarly for vpwymi
1958  vpwxmi = ( vpwxmax > vpwxmin ) ? vpwxmin : vpwxmax;
1959  vpwxma = ( vpwxmax > vpwxmin ) ? vpwxmax : vpwxmin;
1960  vpwymi = ( vpwymax > vpwymin ) ? vpwymin : vpwymax;
1961  vpwyma = ( vpwymax > vpwymin ) ? vpwymax : vpwymin;
1962 
1963  if ( plsc->if_boxbb )
1964  {
1965  PLINT xmajor = MAX( ROUND( plsc->majht * plsc->ypmm ), 1 );
1966  PLINT ymajor = MAX( ROUND( plsc->majht * plsc->xpmm ), 1 );
1967  // Bounding-box limits for the box in mm before corrections
1968  // for decorations are applied.
1969  plsc->boxbb_xmin = plsc->vppxmi / plsc->xpmm;
1970  plsc->boxbb_xmax = plsc->vppxma / plsc->xpmm;
1971  plsc->boxbb_ymin = plsc->vppymi / plsc->ypmm;
1972  plsc->boxbb_ymax = plsc->vppyma / plsc->ypmm;
1973  // Carefully follow logic below for the case where
1974  // an inverted major tick mark is written (in the X direction
1975  // for a Y axis and vice versa). Ignore minor tick marks
1976  // which are assumed to be smaller.
1977  if ( lix && ( lmx || lnx ) && ( ltx && !lxx ) )
1978  {
1979  plsc->boxbb_ymin -= xmajor / plsc->ypmm;
1980  plsc->boxbb_ymax += xmajor / plsc->ypmm;
1981  }
1982  if ( liy && ( lmy || lny ) && ( lty && !lxy ) )
1983  {
1984  plsc->boxbb_xmin -= ymajor / plsc->xpmm;
1985  plsc->boxbb_xmax += ymajor / plsc->xpmm;
1986  }
1987  }
1988  // Write label(s) for horizontal axes.
1989 
1990  if ( ( lmx || lnx ) && ( ltx || lxx ) )
1991  {
1992  PLINT xmode, xprec, xscale;
1993  PLFLT x_spacing, x_spacing_tmp;
1994 
1995  // Determine spacing between ticks
1996  // Use the x-size of the window
1997  x_spacing = vpwxma - vpwxmi;
1998  if ( n_xticks > 1 )
1999  {
2000  // Use the smallest space between ticks
2001  for ( i = 1; i < n_xticks; i++ )
2002  {
2003  x_spacing_tmp = fabs( xticks[i] - xticks[i - 1] );
2004  x_spacing = MIN( x_spacing, x_spacing_tmp );
2005  }
2006  }
2007 
2008  plgxax( &xdigmax, &xdigits );
2009  pldprec( vpwxmi, vpwxma, x_spacing, lfx, &xmode, &xprec, xdigmax, &xscale );
2010  timefmt = plP_gtimefmt();
2011 
2012  height = lix ? 1.75 : 1.5;
2013  if ( plsc->if_boxbb )
2014  {
2015  // For horizontal axes, height of zero corresponds to
2016  // character centred on edge so should add 0.5 to height
2017  // to obtain bounding box edge in direction away from
2018  // edge. However, experimentally found 0.7 gave a better
2019  // looking result.
2020  height_mm = ( height + 0.7 ) * char_height_mm;
2021  if ( lnx )
2022  plsc->boxbb_ymin = MIN( plsc->boxbb_ymin, plsc->vppymi /
2023  plsc->ypmm - height_mm );
2024  if ( lmx )
2025  plsc->boxbb_ymax = MAX( plsc->boxbb_ymax, plsc->vppyma /
2026  plsc->ypmm + height_mm );
2027  }
2028  // Loop through all of the tick marks
2029  for ( i = 0; i < n_xticks; i++ )
2030  {
2031  tn = xticks[i];
2032  if ( BETW( tn, vpwxmi, vpwxma ) )
2033  {
2034  if ( !lxx && !plsc->if_boxbb )
2035  {
2036  plwxtik( tn, vpwymin, FALSE, !lix );
2037  plwxtik( tn, vpwymax, FALSE, lix );
2038  }
2039  if ( ldx )
2040  {
2041  strfqsas( string, STRING_LEN, timefmt, (double) tn, plsc->qsasconfig );
2042  }
2043  else
2044  {
2045  plform( PL_X_AXIS, tn, xscale, xprec, string, STRING_LEN, llx, lfx, lox );
2046  }
2047  pos = ( vpwxmax > vpwxmin ) ?
2048  ( tn - vpwxmi ) / ( vpwxma - vpwxmi ) :
2049  ( vpwxma - tn ) / ( vpwxma - vpwxmi );
2050  if ( plsc->if_boxbb )
2051  {
2052  string_length_mm = plstrl( string );
2053  pos_mm = ( plsc->vppxmi + pos *
2054  ( plsc->vppxma - plsc->vppxmi ) ) /
2055  plsc->xpmm;
2056  }
2057  if ( lnx )
2058  {
2059  // Bottom axis.
2060  if ( plsc->if_boxbb )
2061  {
2062  plsc->boxbb_xmin = MIN( plsc->boxbb_xmin,
2063  pos_mm - 0.5 * string_length_mm );
2064  plsc->boxbb_xmax = MAX( plsc->boxbb_xmax,
2065  pos_mm + 0.5 * string_length_mm );
2066  }
2067  else
2068  {
2069  plmtex( "b", height, pos, 0.5, string );
2070  }
2071  }
2072  if ( lmx )
2073  {
2074  // Top axis.
2075  if ( plsc->if_boxbb )
2076  {
2077  plsc->boxbb_xmin = MIN( plsc->boxbb_xmin,
2078  pos_mm - 0.5 * string_length_mm );
2079  plsc->boxbb_xmax = MAX( plsc->boxbb_xmax,
2080  pos_mm + 0.5 * string_length_mm );
2081  }
2082  else
2083  {
2084  plmtex( "t", height, pos, 0.5, string );
2085  }
2086  }
2087  }
2088  }
2089  xdigits = 2;
2090  plsxax( xdigmax, xdigits );
2091 
2092  // Write separate exponential label if mode = 1.
2093 
2094  if ( !llx && !ldx && !lox && xmode )
2095  {
2096  if ( custom_exponent_placement )
2097  {
2098  height = ( (PLLabelDefaults *) plsc->label_data )->exp_label_disp;
2099  pos = ( (PLLabelDefaults *) plsc->label_data )->exp_label_pos;
2100  just = ( (PLLabelDefaults *) plsc->label_data )->exp_label_just;
2101  }
2102  else
2103  {
2104  height = 3.2;
2105  pos = 1.0;
2106  just = 0.5;
2107  }
2108  snprintf( string, STRING_LEN, "(x10%su%d%sd)", esc_string, (int) xscale, esc_string );
2109  if ( lnx )
2110  {
2111  // Bottom axis exponent.
2112  if ( plsc->if_boxbb )
2113  {
2114  // For horizontal axes, height of zero corresponds
2115  // to character centred on edge so should add 0.5
2116  // to height to obtain bounding box edge in
2117  // direction away from edge if no exponent. Add
2118  // an additional offset to make exponent fit.
2119  height_mm = ( height + 0.9 ) * char_height_mm;
2120  plsc->boxbb_ymin = MIN( plsc->boxbb_ymin, plsc->vppymi /
2121  plsc->ypmm - height_mm );
2122  string_length_mm = plstrl( string );
2123  pos_mm = ( plsc->vppxmi + pos *
2124  ( plsc->vppxma - plsc->vppxmi ) ) /
2125  plsc->xpmm;
2126  plsc->boxbb_xmin = MIN( plsc->boxbb_xmin,
2127  pos_mm - 0.5 * string_length_mm );
2128  plsc->boxbb_xmax = MAX( plsc->boxbb_xmax,
2129  pos_mm + 0.5 * string_length_mm );
2130  }
2131  else
2132  {
2133  plmtex( "b", height, pos, just, string );
2134  }
2135  }
2136  if ( lmx )
2137  {
2138  // Top axis exponent.
2139  if ( plsc->if_boxbb )
2140  {
2141  // For horizontal axes, height of zero corresponds
2142  // to character centred on edge so should add 0.5
2143  // to height to obtain bounding box edge in
2144  // direction away from edge if no exponent. Add
2145  // an additional offset to make exponent fit.
2146  height_mm = ( height + 1.4 ) * char_height_mm;
2147  plsc->boxbb_ymax = MAX( plsc->boxbb_ymax, plsc->vppyma /
2148  plsc->ypmm + height_mm );
2149  string_length_mm = plstrl( string );
2150  pos_mm = ( plsc->vppxmi + pos *
2151  ( plsc->vppxma - plsc->vppxmi ) ) /
2152  plsc->xpmm;
2153  plsc->boxbb_xmin = MIN( plsc->boxbb_xmin,
2154  pos_mm - 0.5 * string_length_mm );
2155  plsc->boxbb_xmax = MAX( plsc->boxbb_xmax,
2156  pos_mm + 0.5 * string_length_mm );
2157  }
2158  else
2159  {
2160  plmtex( "t", height, pos, just, string );
2161  }
2162  }
2163  }
2164  }
2165 
2166  // Write label(s) for vertical axes.
2167  if ( ( lmy || lny ) && ( lty || lxy ) )
2168  {
2169  PLINT ymode, yprec, yscale;
2170  PLFLT y_spacing, y_spacing_tmp;
2171 
2172  // Determine spacing between ticks
2173  // Use the y-size of the window
2174  y_spacing = vpwyma - vpwymi;
2175  if ( n_yticks > 1 )
2176  {
2177  // Use the smallest space between ticks
2178  for ( i = 1; i < n_yticks; i++ )
2179  {
2180  y_spacing_tmp = fabs( yticks[i] - yticks[i - 1] );
2181  y_spacing = MIN( y_spacing, y_spacing_tmp );
2182  }
2183  }
2184 
2185  plgyax( &ydigmax, &ydigits );
2186  pldprec( vpwymi, vpwyma, y_spacing, lfy, &ymode, &yprec, ydigmax, &yscale );
2187  timefmt = plP_gtimefmt();
2188 
2189  ydigits = 0;
2190  for ( i = 0; i < n_yticks; i++ )
2191  {
2192  tn = yticks[i];
2193  if ( BETW( tn, vpwymi, vpwyma ) )
2194  {
2195  if ( !lxy && !plsc->if_boxbb )
2196  {
2197  plwytik( vpwxmin, tn, FALSE, !liy );
2198  plwytik( vpwxmax, tn, FALSE, liy );
2199  }
2200  if ( ldy )
2201  {
2202  strfqsas( string, STRING_LEN, timefmt, (double) tn, plsc->qsasconfig );
2203  }
2204  else
2205  {
2206  plform( PL_Y_AXIS, tn, yscale, yprec, string, STRING_LEN, lly, lfy, loy );
2207  }
2208  pos = ( vpwymax > vpwymin ) ?
2209  ( tn - vpwymi ) / ( vpwyma - vpwymi ) :
2210  ( vpwyma - tn ) / ( vpwyma - vpwymi );
2211  if ( lny )
2212  {
2213  if ( lvy )
2214  {
2215  // Left axis with text written perpendicular to edge.
2216  height = liy ? 1.0 : 0.5;
2217  if ( plsc->if_boxbb )
2218  {
2219  // For vertical axes with text written
2220  // perpendicular to edge, height of zero
2221  // corresponds character centred on edge so
2222  // should add 0.5 to height to obtain bounding
2223  // box edge in direction away from edge, and
2224  // that value apparently works.
2225  height_mm = ( height + 0.0 ) * char_height_mm;
2226  string_length_mm = plstrl( string );
2227  plsc->boxbb_xmin = MIN( plsc->boxbb_xmin, plsc->vppxmi /
2228  plsc->xpmm - height_mm - string_length_mm );
2229  pos_mm = ( plsc->vppymi + pos *
2230  ( plsc->vppyma - plsc->vppymi ) ) /
2231  plsc->ypmm;
2232  // Expected offset is 0.5, but adjust to improve
2233  // look of result.
2234  plsc->boxbb_ymin = MIN( plsc->boxbb_ymin,
2235  pos_mm - 0.6 * char_height_mm );
2236  plsc->boxbb_ymax = MAX( plsc->boxbb_ymax,
2237  pos_mm + 0.7 * char_height_mm );
2238  }
2239  else
2240  {
2241  plmtex( "lv", height, pos, 1.0, string );
2242  }
2243  }
2244  else
2245  {
2246  // Left axis with text written parallel to edge.
2247  height = liy ? 1.75 : 1.5;
2248  if ( plsc->if_boxbb )
2249  {
2250  // For vertical axes with text written
2251  // parallel to edge, height of zero
2252  // corresponds to character centred on edge so
2253  // should add 0.5 to height to obtain bounding
2254  // box edge in direction away from edge,
2255  // However, experimentally found 0.8 gave a
2256  // better looking result.
2257  height_mm = ( height + 0.8 ) * char_height_mm;
2258  plsc->boxbb_xmin = MIN( plsc->boxbb_xmin, plsc->vppxmi /
2259  plsc->xpmm - height_mm );
2260  pos_mm = ( plsc->vppymi + pos *
2261  ( plsc->vppyma - plsc->vppymi ) ) /
2262  plsc->ypmm;
2263  string_length_mm = plstrl( string );
2264  plsc->boxbb_ymin = MIN( plsc->boxbb_ymin,
2265  pos_mm - 0.5 * string_length_mm );
2266  plsc->boxbb_ymax = MAX( plsc->boxbb_ymax,
2267  pos_mm + 0.5 * string_length_mm );
2268  }
2269  else
2270  {
2271  plmtex( "l", height, pos, 0.5, string );
2272  }
2273  }
2274  }
2275  if ( lmy )
2276  {
2277  if ( lvy )
2278  {
2279  // Right axis with text written perpendicular to edge.
2280  height = liy ? 1.0 : 0.5;
2281  if ( plsc->if_boxbb )
2282  {
2283  // For vertical axes with text written
2284  // perpendicular to edge, height of zero
2285  // corresponds character centred on edge so
2286  // should add 0.5 to height to obtain bounding
2287  // box edge in direction away from edge, and
2288  // that value apparently works.
2289  height_mm = ( height + 0.0 ) * char_height_mm;
2290  string_length_mm = plstrl( string );
2291  plsc->boxbb_xmax = MAX( plsc->boxbb_xmax, plsc->vppxma /
2292  plsc->xpmm + height_mm + string_length_mm );
2293  pos_mm = ( plsc->vppymi + pos *
2294  ( plsc->vppyma - plsc->vppymi ) ) /
2295  plsc->ypmm;
2296  // Expected offset is 0.5, but adjust to improve
2297  // look of result.
2298  plsc->boxbb_ymin = MIN( plsc->boxbb_ymin,
2299  pos_mm - 0.6 * char_height_mm );
2300  plsc->boxbb_ymax = MAX( plsc->boxbb_ymax,
2301  pos_mm + 0.7 * char_height_mm );
2302  }
2303  else
2304  {
2305  plmtex( "rv", height, pos, 0.0, string );
2306  }
2307  }
2308  else
2309  {
2310  // Right axis with text written parallel to edge.
2311  height = liy ? 1.75 : 1.5;
2312  if ( plsc->if_boxbb )
2313  {
2314  // For vertical axes with text written
2315  // parallel to edge, height of zero
2316  // corresponds to character centred on edge so
2317  // should add 0.5 to height to obtain bounding
2318  // box edge in direction away from edge,
2319  // However, experimentally found 0.8 gave a
2320  // better looking result.
2321  height_mm = ( height + 0.8 ) * char_height_mm;
2322  plsc->boxbb_xmax = MAX( plsc->boxbb_xmax, plsc->vppxma /
2323  plsc->xpmm + height_mm );
2324  pos_mm = ( plsc->vppymi + pos *
2325  ( plsc->vppyma - plsc->vppymi ) ) /
2326  plsc->ypmm;
2327  string_length_mm = plstrl( string );
2328  plsc->boxbb_ymin = MIN( plsc->boxbb_ymin,
2329  pos_mm - 0.5 * string_length_mm );
2330  plsc->boxbb_ymax = MAX( plsc->boxbb_ymax,
2331  pos_mm + 0.5 * string_length_mm );
2332  }
2333  else
2334  {
2335  plmtex( "r", height, pos, 0.5, string );
2336  }
2337  }
2338  }
2339  ydigits = MAX( ydigits, (PLINT) strlen( string ) );
2340  }
2341  }
2342  if ( !lvy )
2343  ydigits = 2;
2344 
2345  plsyax( ydigmax, ydigits );
2346 
2347  // Write separate exponential label if mode = 1.
2348 
2349  if ( !lly && !ldy && !loy && ymode )
2350  {
2351  snprintf( string, STRING_LEN, "(x10%su%d%sd)", esc_string, (int) yscale, esc_string );
2352  if ( custom_exponent_placement )
2353  {
2354  height = ( (PLLabelDefaults *) plsc->label_data )->exp_label_disp;
2355  pos = ( (PLLabelDefaults *) plsc->label_data )->exp_label_pos;
2356  just = ( (PLLabelDefaults *) plsc->label_data )->exp_label_just;
2357  }
2358  if ( lvy )
2359  {
2360  offset = 0.1; // more space to clear labels in "v" mode
2361  }
2362  else
2363  {
2364  offset = 0.02;
2365  }
2366  // Left axis exponent.
2367  if ( lny )
2368  {
2369  if ( !custom_exponent_placement )
2370  {
2371  height = 3.2;
2372  pos = 1.0 + offset;
2373  just = 0.5;
2374  }
2375  if ( plsc->if_boxbb )
2376  {
2377  // For horizontal axes, height of zero corresponds
2378  // to character centred on edge so should add 0.5
2379  // to height to obtain bounding box edge in
2380  // direction away from edge if no exponent. Add
2381  // an additional offset to make exponent fit.
2382  height_mm = ( height + 1.4 ) * char_height_mm;
2383  plsc->boxbb_ymax = MAX( plsc->boxbb_ymax, plsc->vppyma /
2384  plsc->ypmm + height_mm );
2385  string_length_mm = plstrl( string );
2386  pos_mm = ( plsc->vppxmi + pos *
2387  ( plsc->vppxma - plsc->vppxmi ) ) /
2388  plsc->xpmm;
2389  plsc->boxbb_xmin = MIN( plsc->boxbb_xmin,
2390  pos_mm - string_length_mm );
2391  }
2392  else
2393  {
2394  if ( lvy )
2395  {
2396  plmtex( "lv", height, pos, just, string );
2397  }
2398  else
2399  {
2400  plmtex( "l", height, pos, just, string );
2401  }
2402  }
2403  }
2404  // Right axis exponent.
2405  if ( lmy )
2406  {
2407  if ( !custom_exponent_placement )
2408  {
2409  height = 3.4; // Extra space for superscript
2410  pos = 1.0 + offset;
2411  just = 0.5;
2412  }
2413  if ( plsc->if_boxbb )
2414  {
2415  // For horizontal axes, height of zero corresponds
2416  // to character centred on edge so should add 0.5
2417  // to height to obtain bounding box edge in
2418  // direction away from edge if no exponent. Add
2419  // an additional offset to make exponent fit.
2420  height_mm = ( height + 1.4 ) * char_height_mm;
2421  plsc->boxbb_ymax = MAX( plsc->boxbb_ymax, plsc->vppyma /
2422  plsc->ypmm + height_mm );
2423  string_length_mm = plstrl( string );
2424  pos_mm = ( plsc->vppxmi + pos *
2425  ( plsc->vppxma - plsc->vppxmi ) ) /
2426  plsc->xpmm;
2427  plsc->boxbb_xmax = MAX( plsc->boxbb_xmin,
2428  pos_mm + string_length_mm );
2429  }
2430  else
2431  {
2432  if ( lvy )
2433  {
2434  plmtex( "rv", height, pos, just, string );
2435  }
2436  else
2437  {
2438  plmtex( "r", height, pos, just, string );
2439  }
2440  }
2441  }
2442  }
2443  }
2444 
2445  // Restore saved parameters
2446  plsxax( xdigmax_old, xdigits_old );
2447  plsyax( ydigmax_old, ydigits_old );
2448 
2449  // Restore the clip limits to viewport edge
2450  plP_sclp( lxmin, lxmax, lymin, lymax );
2451 }
2452 
2453 //--------------------------------------------------------------------------
2454 //
2455 // Default labeling functions for PLplot
2456 //
2457 // These are the functions which are used internally by PLplot under various
2458 // conditions.
2459 //
2460 // They have been separated out for use in other PLplot functions and
2461 // potential exposure in the PLplot API.
2462 //
2463 //--------------------------------------------------------------------------
2464 void plP_default_label_log( PLINT PL_UNUSED( axis ), PLFLT value, char *string, PLINT len, void * PL_UNUSED( data ) )
2465 {
2466  const char * esc_string = plgesc_string();
2467  // Exponential, i.e. 10^-1, 10^0, 10^1, etc
2468  snprintf( string, (size_t) len, "10%su%d", esc_string, (int) ROUND( value ) );
2469 }
2470 
2471 void plP_default_label_log_fixed( PLINT PL_UNUSED( axis ), PLFLT value, char *string, PLINT len, void * PL_UNUSED( data ) )
2472 {
2473  // Fixed point, i.e. .1, 1, 10, etc
2474 
2475  int exponent = ROUND( value );
2476 
2477  value = pow( 10.0, exponent );
2478  if ( exponent < 0 )
2479  {
2480  char form[FORMAT_LEN];
2481  snprintf( form, FORMAT_LEN, "%%.%df", ABS( exponent ) );
2482  snprintf( string, (size_t) len, form, value );
2483  }
2484  else
2485  {
2486  snprintf( string, (size_t) len, "%d", (int) value );
2487  }
2488 }
2489 
2490 void plP_default_label( PLINT PL_UNUSED( axis ), PLFLT value, char *string, PLINT len, void *data )
2491 {
2492  PLINT scale, prec;
2493  PLINT setpre, precis;
2494  char form[FORMAT_LEN], temp[TEMP_LEN];
2495  double scale2;
2496 
2497  scale = ( (PLINT *) data )[0];
2498  prec = ( (PLINT *) data )[1];
2499 
2500  plP_gprec( &setpre, &precis );
2501 
2502  if ( setpre )
2503  prec = precis;
2504 
2505  if ( scale )
2506  value /= pow( 10., (double) scale );
2507 
2508  // This is necessary to prevent labels like "-0.0" on some systems
2509 
2510  scale2 = pow( 10., prec );
2511  value = floor( ( value * scale2 ) + .5 ) / scale2;
2512 
2513  snprintf( form, FORMAT_LEN, "%%.%df", (int) prec );
2514  snprintf( temp, TEMP_LEN, form, value );
2515  strncpy( string, temp, (size_t) ( len - 1 ) );
2516  string[len - 1] = '\0';
2517 }
2518 
2519 //--------------------------------------------------------------------------
2520 // void plform()
2521 //
2522 // Formats a PLFLT value in one of the following formats.
2523 //
2524 // If ll (logarithmic), then:
2525 //
2526 // - If lf (fixed), then used fixed point notation, i.e. .1, 1, 10, etc,
2527 // with unnecessary trailing .'s or 0's removed.
2528 //
2529 // - If !lf (default), then use exponential notation, i.e. 10^-1, etc.
2530 //
2531 // If !ll (linear), then:
2532 //
2533 // - If scale == 0, use fixed point format with "prec" places after the
2534 // decimal point.
2535 //
2536 // - If scale == 1, use scientific notation with one place before the
2537 // decimal point and "prec" places after. In this case, the value
2538 // must be divided by 10^scale.
2539 //
2540 // The axis argument is included to support PLplot's custom axis labeling. It
2541 // is passed on to the custom labeling function if it exists. Otherwise, it
2542 // is ignored.
2543 //--------------------------------------------------------------------------
2544 
2545 static void
2546 plform( PLINT axis, PLFLT value, PLINT scale, PLINT prec, char *string, PLINT len, PLBOOL ll, PLBOOL lf, PLBOOL lo )
2547 {
2548  // Check to see if a custom labeling function is defined. If not,
2549  // use default.
2550  if ( lo && plsc->label_func )
2551  {
2552  ( *plsc->label_func )( axis, value, string, len, plsc->label_data );
2553  }
2554  else
2555  {
2556  if ( lo )
2557  {
2558  plwarn( "Custom axis labels requested without a labeling function \
2559  - using default." );
2560  }
2561  if ( ll )
2562  {
2563  // Logarithmic
2564 
2565  if ( lf )
2566  {
2567  // Fixed point, i.e. .1, 1, 10, etc
2568  plP_default_label_log_fixed( axis, value, string, len, NULL );
2569  }
2570  else
2571  {
2572  // Exponential, i.e. 10^-1, 10^0, 10^1, etc
2573  plP_default_label_log( axis, value, string, len, NULL );
2574  }
2575  }
2576  else
2577  {
2578  // Linear
2579  PLINT scale_prec[2] = { scale, prec };
2580  plP_default_label( axis, value, string, len, (void *) scale_prec );
2581  }
2582  }
2583 }
2584 
2585 //--------------------------------------------------------------------------
2586 // plslabelfunc
2587 //
2588 // Formats a PLFLT value in one of the following formats.
2589 //
2590 // label_func - A pointer to a function which will provide a string to use
2591 // as the label for the given floating point value.
2592 // Pass this as NULL to clear the custom function and reset it to
2593 // the default PLplot labeling function.
2594 //
2595 // label_data - Extra data to pass to the label function.
2596 //
2597 // The label_func function arguments are, in order:
2598 //
2599 // axis: PL_X_AXIS, PL_Y_AXIS or PL_Z_AXIS to indicate which axis is being
2600 // labeled
2601 // value: The value at this position on the axis
2602 // string: The resulting label string should be stored here
2603 // data: A pointer to whatever extra information the custom plotting function
2604 // requires
2605 //
2606 //--------------------------------------------------------------------------
2607 void
2608 c_plslabelfunc( void ( *label_func )( PLINT, PLFLT, char *, PLINT, PLPointer ), PLPointer label_data )
2609 {
2610  plsc->label_func = label_func;
2611  plsc->label_data = label_data;
2612 }
2613 
2614 static const char *
2616 {
2617  static const char *esc_strings = { "!\0#\0$\0%\0&\0*\0@\0^\0~\0" };
2618  int d;
2619  // Follow plgesc logic here which is to set the default escape
2620  // if plsc->esc is in its initial state.
2621  if ( plsc->esc == '\0' )
2622  plsc->esc = '#';
2623 
2624  switch ( plsc->esc )
2625  {
2626  case '!':
2627  d = 0;
2628  break;
2629  case '#':
2630  d = 1;
2631  break;
2632  case '$':
2633  d = 2;
2634  break;
2635  case '%':
2636  d = 3;
2637  break;
2638  case '&':
2639  d = 4;
2640  break;
2641  case '*':
2642  d = 5;
2643  break;
2644  case '@':
2645  d = 6;
2646  break;
2647  case '^':
2648  d = 7;
2649  break;
2650  case '~':
2651  d = 8;
2652  break;
2653  default:
2654  plwarn( "plgesc_string: Invalid escape character, assuming '#' instead" );
2655  d = 1;
2656  break;
2657  }
2658  return &( esc_strings[d * 2] );
2659 }