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