PLplot  5.11.1
 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-2015 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 // d: Interpret axis as a date/time when writing labels
774 // f: Always use fixed point numeric labels
775 // i: Inverts tick marks (i.e. drawn downwards)
776 // l: Logarithmic axes, major ticks at decades, minor ticks at units
777 // n: Write numeric label
778 // o: Use custom label function
779 // s: Draw minor tick marks
780 // t: Draw major tick marks
781 // u: Write label on line
782 //--------------------------------------------------------------------------
783 
784 static void
785 plxybx( const char *opt, const char *label, PLINT axis, PLFLT wx1, PLFLT wy1,
786  PLFLT wx2, PLFLT wy2, PLFLT vmin_in, PLFLT vmax_in,
787  PLFLT tick, PLINT nsub, PLINT PL_UNUSED( nolast ), PLINT *digits )
788 {
789  static char string[STRING_LEN];
790  PLINT lb, ld, lf, li, ll, ln, ls, lt, lu, lo;
791  PLINT major, minor, mode, prec, scale;
792  PLINT i, i1, i2, i3, i4;
793  PLINT nsub1;
794  PLFLT pos, tn, tp, temp, height, tick1, vmin, vmax;
795 // Note that 'tspace' is the minimim distance away (in fractional number
796 // of ticks) from the boundary that an X or Y numerical label can be drawn.
797  PLFLT dwx, dwy, lambda, tcrit, tspace = 0.1;
798  const char * esc_string = plgesc_string();
799  PLFLT tstart, factor;
800  const char *timefmt = NULL;
801  vmin = ( vmax_in > vmin_in ) ? vmin_in : vmax_in;
802  vmax = ( vmax_in > vmin_in ) ? vmax_in : vmin_in;
803 
804  dwx = wx2 - wx1;
805  dwy = wy2 - wy1;
806 
807 // Tick and subtick sizes in device coords
808 
809  major = MAX( ROUND( plsc->majht * plsc->ypmm ), 1 );
810  minor = MAX( ROUND( plsc->minht * plsc->ypmm ), 1 );
811 
812  tick1 = tick;
813  nsub1 = nsub;
814 
815  lb = plP_stsearch( opt, 'b' );
816  ld = plP_stsearch( opt, 'd' );
817  lf = plP_stsearch( opt, 'f' );
818  li = plP_stsearch( opt, 'i' );
819  ll = plP_stsearch( opt, 'l' );
820  ln = plP_stsearch( opt, 'n' );
821  lo = plP_stsearch( opt, 'o' );
822  ls = plP_stsearch( opt, 's' );
823  lt = plP_stsearch( opt, 't' );
824  lu = plP_stsearch( opt, 'u' );
825 
826  if ( lu )
827  plxytx( wx1, wy1, wx2, wy2, 3.2, 0.5, 0.5, label );
828  if ( !lb )
829  return;
830 
831  if ( ll )
832  tick1 = ( vmax > vmin ) ? 1.0 : -1.0;
833  if ( lt )
834  pldtik( vmin, vmax, &tick1, &nsub1, ld );
835 
836  if ( li )
837  {
838  i1 = minor;
839  i2 = 0;
840  i3 = major;
841  i4 = 0;
842  }
843  else
844  {
845  i1 = 0;
846  i2 = minor;
847  i3 = 0;
848  i4 = major;
849  }
850 
851 // Draw the line
852 
853  plP_movwor( wx1, wy1 );
854  plP_drawor( wx2, wy2 );
855  if ( lt )
856  {
857  if ( ld )
858  {
859  pldtfac( vmin, vmax, &factor, &tstart );
860  tp = tick1 * ( floor( ( vmin - tstart ) / tick1 ) ) + tstart;
861  }
862  else
863  tp = tick1 * floor( vmin / tick1 );
864  for (;; )
865  {
866  tn = tp + tick1;
867  if ( ls )
868  {
869  if ( ll )
870  {
871  for ( i = 0; i <= 7; i++ )
872  {
873  temp = tp + xlog[i];
874  if ( BETW( temp, vmin, vmax ) )
875  {
876  lambda = ( vmax_in > vmin_in ) ?
877  ( temp - vmin ) / ( vmax - vmin ) :
878  ( vmax - temp ) / ( vmax - vmin );
879  plxtik( plP_wcpcx( (PLFLT) ( wx1 + lambda * dwx ) ),
880  plP_wcpcy( (PLFLT) ( wy1 + lambda * dwy ) ),
881  i1, i2 );
882  }
883  }
884  }
885  else
886  {
887  for ( i = 1; i <= nsub1 - 1; i++ )
888  {
889  temp = tp + i * ( tn - tp ) / nsub1;
890  if ( BETW( temp, vmin, vmax ) )
891  {
892  lambda = ( vmax_in > vmin_in ) ?
893  ( temp - vmin ) / ( vmax - vmin ) :
894  ( vmax - temp ) / ( vmax - vmin );
895  plxtik( plP_wcpcx( (PLFLT) ( wx1 + lambda * dwx ) ),
896  plP_wcpcy( (PLFLT) ( wy1 + lambda * dwy ) ),
897  i1, i2 );
898  }
899  }
900  }
901  }
902  temp = tn;
903  if ( !BETW( temp, vmin, vmax ) )
904  break;
905 
906  lambda = ( vmax_in > vmin_in ) ?
907  ( temp - vmin ) / ( vmax - vmin ) :
908  ( vmax - temp ) / ( vmax - vmin );
909  plxtik( plP_wcpcx( (PLFLT) ( wx1 + lambda * dwx ) ),
910  plP_wcpcy( (PLFLT) ( wy1 + lambda * dwy ) ), i3, i4 );
911  tp = tn;
912  }
913  }
914 
915 
916 // Label the line
917 
918  if ( ln && lt )
919  {
920  pldprec( vmin, vmax, tick1, lf, &mode, &prec, *digits, &scale );
921  timefmt = plP_gtimefmt();
922  pos = 1.0;
923  height = 3.2;
924  tcrit = tspace * tick1;
925  if ( ld )
926  {
927  pldtfac( vmin, vmax, &factor, &tstart );
928  tp = tick1 * ( 1. + floor( ( vmin - tstart ) / tick1 ) ) + tstart;
929  }
930  else
931  tp = tick1 * ( 1. + floor( vmin / tick1 ) );
932  for ( tn = tp; BETW( tn, vmin, vmax ); tn += tick1 )
933  {
934  if ( BETW( tn, vmin + tcrit, vmax - tcrit ) )
935  {
936  if ( ld )
937  strfqsas( string, STRING_LEN, timefmt, (double) tn, plsc->qsasconfig );
938  else
939  plform( axis, tn, scale, prec, string, STRING_LEN, ll, lf, lo );
940  pos = ( vmax_in > vmin_in ) ?
941  ( tn - vmin ) / ( vmax - vmin ) :
942  ( vmax - tn ) / ( vmax - vmin );
943  plxytx( wx1, wy1, wx2, wy2, 1.5, pos, 0.5, string );
944  }
945  }
946  *digits = 2;
947  if ( !ll && !lo && !ld && mode )
948  {
949  snprintf( string, STRING_LEN, "(x10%su%d%sd)", esc_string, (int) scale, esc_string );
950  plxytx( wx1, wy1, wx2, wy2, height, 1.0, 0.5, string );
951  }
952  }
953 }
954 
955 //--------------------------------------------------------------------------
956 // void plxytx()
957 //
958 // Prints out text along a sloping axis joining world coordinates
959 // (wx1,wy1) to (wx2,wy2). Parameters are as for plmtext.
960 //--------------------------------------------------------------------------
961 
962 static void
963 plxytx( PLFLT wx1, PLFLT wy1, PLFLT wx2, PLFLT wy2,
964  PLFLT disp, PLFLT pos, PLFLT just, const char *text )
965 {
966  PLINT x, y, refx, refy;
967  PLFLT shift, cc, ss, wx, wy;
968  PLFLT xdv, ydv, xmm, ymm, refxmm, refymm, xform[4], diag;
969  PLFLT dispx, dispy;
970  PLFLT chrdef, chrht;
971 
972  cc = plsc->wmxscl * ( wx2 - wx1 );
973  ss = plsc->wmyscl * ( wy2 - wy1 );
974  diag = sqrt( cc * cc + ss * ss );
975  cc /= diag;
976  ss /= diag;
977  wx = wx1 + pos * ( wx2 - wx1 );
978  wy = wy1 + pos * ( wy2 - wy1 );
979 
980  xform[0] = cc;
981  xform[1] = 0.0;
982  xform[2] = ss;
983  xform[3] = 1.0;
984 
985  xdv = plP_wcdcx( wx );
986  ydv = plP_wcdcy( wy );
987 
988  dispx = 0.;
989  dispy = -disp;
990 
991  plgchr( &chrdef, &chrht );
992  shift = ( just == 0.0 ) ? 0.0 : plstrl( text ) * just;
993 
994  xmm = plP_dcmmx( xdv ) + dispx * chrht;
995  ymm = plP_dcmmy( ydv ) + dispy * chrht;
996  refxmm = xmm - shift * xform[0];
997  refymm = ymm - shift * xform[2];
998 
999  x = plP_mmpcx( xmm );
1000  y = plP_mmpcy( ymm );
1001  refx = plP_mmpcx( refxmm );
1002  refy = plP_mmpcy( refymm );
1003 
1004  plP_text( 0, just, xform, x, y, refx, refy, text );
1005 }
1006 
1007 //--------------------------------------------------------------------------
1008 // void plzbx()
1009 //
1010 // This draws a vertical line from (wx,wy1) to (wx,wy2) which represents the
1011 // vertical axis of a 3-d graph with data values from "vmin" to "vmax".
1012 // Depending on "opt", ticks and/or subticks are placed on the line at major
1013 // tick interval "tick" with "nsub" subticks between major ticks. If "tick"
1014 // and/or "nsub" is zero, automatic tick positions are computed
1015 //
1016 // b: Draws left-hand axis
1017 // c: Draws right-hand axis
1018 // N.B. d is already used for another purpose (back grid) in zopt
1019 // before this routine is called so chose e here to be as close to d
1020 // as possible without interfering with the historical use of d.
1021 // e: Interpret axis as a date/time when writing labels
1022 // f: Always use fixed point numeric labels
1023 // i: Inverts tick marks (i.e. drawn to the left)
1024 // l: Logarithmic axes, major ticks at decades, minor ticks at units
1025 // m: Write numeric label on right axis
1026 // n: Write numeric label on left axis
1027 // o: Use custom label function
1028 // s: Draw minor tick marks
1029 // t: Draw major tick marks
1030 // u: Writes left-hand label
1031 // v: Writes right-hand label
1032 //--------------------------------------------------------------------------
1033 
1034 static void
1035 plzbx( const char *opt, const char *label, PLINT right, PLFLT dx, PLFLT dy,
1036  PLFLT wx, PLFLT wy1, PLFLT wy2, PLFLT vmin_in, PLFLT vmax_in,
1037  PLFLT tick, PLINT nsub, PLINT *digits )
1038 {
1039  static char string[STRING_LEN];
1040  PLINT lb, lc, ld, lf, li, ll, lm, ln, ls, lt, lu, lv, lo;
1041  PLINT i, mode, prec, scale;
1042  PLINT nsub1, lstring;
1043  PLFLT pos, tn, tp, temp, height, tick1;
1044  PLFLT dwy, lambda, diag, major, minor, xmajor, xminor;
1045  PLFLT ymajor, yminor, dxm, dym, vmin, vmax;
1046  const char * esc_string = plgesc_string();
1047  PLFLT tstart, factor;
1048  const char *timefmt = NULL;
1049 
1050  vmin = ( vmax_in > vmin_in ) ? vmin_in : vmax_in;
1051  vmax = ( vmax_in > vmin_in ) ? vmax_in : vmin_in;
1052 
1053  dwy = wy2 - wy1;
1054 
1055 // Tick and subtick sizes in device coords
1056 
1057  major = plsc->majht;
1058  minor = plsc->minht;
1059 
1060  tick1 = tick;
1061  nsub1 = nsub;
1062 
1063  lb = plP_stsearch( opt, 'b' );
1064  lc = plP_stsearch( opt, 'c' );
1065  ld = plP_stsearch( opt, 'e' );
1066  lf = plP_stsearch( opt, 'f' );
1067  li = plP_stsearch( opt, 'i' );
1068  ll = plP_stsearch( opt, 'l' );
1069  lm = plP_stsearch( opt, 'm' );
1070  ln = plP_stsearch( opt, 'n' );
1071  lo = plP_stsearch( opt, 'o' );
1072  ls = plP_stsearch( opt, 's' );
1073  lt = plP_stsearch( opt, 't' );
1074  lu = plP_stsearch( opt, 'u' );
1075  lv = plP_stsearch( opt, 'v' );
1076 
1077  if ( lu && !right )
1078  plztx( "h", dx, dy, wx, wy1, wy2, 5.0, 0.5, 0.5, label );
1079 
1080  if ( lv && right )
1081  plztx( "h", dx, dy, wx, wy1, wy2, -5.0, 0.5, 0.5, label );
1082 
1083  if ( right && !lc )
1084  return;
1085 
1086  if ( !right && !lb )
1087  return;
1088 
1089  if ( ll )
1090  tick1 = 1.0;
1091 
1092  if ( lt )
1093  pldtik( vmin, vmax, &tick1, &nsub1, ld );
1094 
1095  if ( ( li && !right ) || ( !li && right ) )
1096  {
1097  minor = -minor;
1098  major = -major;
1099  }
1100 
1101  dxm = dx * plsc->wmxscl;
1102  dym = dy * plsc->wmyscl;
1103  diag = sqrt( dxm * dxm + dym * dym );
1104 
1105  xminor = minor * dxm / diag;
1106  xmajor = major * dxm / diag;
1107  yminor = minor * dym / diag;
1108  ymajor = major * dym / diag;
1109 
1110 // Draw the line
1111 
1112  plP_movwor( wx, wy1 );
1113  plP_drawor( wx, wy2 );
1114  if ( lt )
1115  {
1116  if ( ld )
1117  {
1118  pldtfac( vmin, vmax, &factor, &tstart );
1119  tp = tick1 * ( floor( ( vmin - tstart ) / tick1 ) ) + tstart;
1120  }
1121  else
1122  tp = tick1 * floor( vmin / tick1 );
1123  for (;; )
1124  {
1125  tn = tp + tick1;
1126  if ( ls )
1127  {
1128  if ( ll )
1129  {
1130  for ( i = 0; i <= 7; i++ )
1131  {
1132  temp = tp + xlog[i];
1133  if ( BETW( temp, vmin, vmax ) )
1134  {
1135  lambda = ( vmax_in > vmin_in ) ?
1136  ( temp - vmin ) / ( vmax - vmin ) :
1137  ( vmax - temp ) / ( vmax - vmin );
1138  plstik( plP_wcmmx( wx ),
1139  plP_wcmmy( (PLFLT) ( wy1 + lambda * dwy ) ),
1140  xminor, yminor );
1141  }
1142  }
1143  }
1144  else
1145  {
1146  for ( i = 1; i <= nsub1 - 1; i++ )
1147  {
1148  temp = tp + i * tick1 / nsub1;
1149  if ( BETW( temp, vmin, vmax ) )
1150  {
1151  lambda = ( vmax_in > vmin_in ) ?
1152  ( temp - vmin ) / ( vmax - vmin ) :
1153  ( vmax - temp ) / ( vmax - vmin );
1154  plstik( plP_wcmmx( wx ),
1155  plP_wcmmy( (PLFLT) ( wy1 + lambda * dwy ) ),
1156  xminor, yminor );
1157  }
1158  }
1159  }
1160  }
1161  temp = tn;
1162  if ( !BETW( temp, vmin, vmax ) )
1163  break;
1164  lambda = ( vmax_in > vmin_in ) ?
1165  ( temp - vmin ) / ( vmax - vmin ) :
1166  ( vmax - temp ) / ( vmax - vmin );
1167  plstik( plP_wcmmx( wx ), plP_wcmmy( (PLFLT) ( wy1 + lambda * dwy ) ),
1168  xmajor, ymajor );
1169  tp = tn;
1170  }
1171  }
1172 
1173 
1174 // Label the line
1175 
1176  if ( ( ln || lm ) && lt )
1177  {
1178  pldprec( vmin, vmax, tick1, lf, &mode, &prec, *digits, &scale );
1179  timefmt = plP_gtimefmt();
1180  *digits = 0;
1181  if ( ld )
1182  {
1183  pldtfac( vmin, vmax, &factor, &tstart );
1184  tp = tick1 * ( floor( ( vmin - tstart ) / tick1 ) ) + tstart;
1185  }
1186  else
1187  tp = tick1 * floor( vmin / tick1 );
1188  for ( tn = tp + tick1; BETW( tn, vmin, vmax ); tn += tick1 )
1189  {
1190  if ( ld )
1191  strfqsas( string, STRING_LEN, timefmt, (double) tn, plsc->qsasconfig );
1192  else
1193  plform( PL_Z_AXIS, tn, scale, prec, string, STRING_LEN, ll, lf, lo );
1194  pos = ( vmax_in > vmin_in ) ?
1195  ( tn - vmin ) / ( vmax - vmin ) :
1196  ( vmax - tn ) / ( vmax - vmin );
1197  if ( ln && !right )
1198  plztx( "v", dx, dy, wx, wy1, wy2, 0.5, pos, 1.0, string );
1199 
1200  if ( lm && right )
1201  plztx( "v", dx, dy, wx, wy1, wy2, -0.5, pos, 0.0, string );
1202 
1203  lstring = (PLINT) strlen( string );
1204  *digits = MAX( *digits, lstring );
1205  }
1206  if ( !ll && !lo && !ld && mode )
1207  {
1208  snprintf( string, STRING_LEN, "(x10%su%d%sd)", esc_string, (int) scale, esc_string );
1209  pos = 1.15;
1210  height = 0.5;
1211  if ( ln && !right )
1212  {
1213  plztx( "v", dx, dy, wx, wy1, wy2, height, pos, 0.5, string );
1214  }
1215  if ( lm && right )
1216  {
1217  plztx( "v", dx, dy, wx, wy1, wy2,
1218  (PLFLT) -height, pos, 0.5, string );
1219  }
1220  }
1221  }
1222 }
1223 
1224 //--------------------------------------------------------------------------
1225 // void plztx()
1226 //
1227 // Prints out text along a vertical axis for a 3d plot joining
1228 // world coordinates (wx,wy1) to (wx,wy2).
1229 //--------------------------------------------------------------------------
1230 
1231 static void
1232 plztx( const char *opt, PLFLT dx, PLFLT dy, PLFLT wx, PLFLT wy1,
1233  PLFLT wy2, PLFLT disp, PLFLT pos, PLFLT just, const char *text )
1234 {
1235  PLINT refx = 0, refy = 0, x = 0, y = 0, vert = 0;
1236  PLFLT shift, cc, ss, wy;
1237  PLFLT xdv, ydv, xmm, ymm, refxmm, refymm, xform[4], diag;
1238  PLFLT dispx, dispy;
1239  PLFLT chrdef, chrht;
1240 
1241  cc = plsc->wmxscl * dx;
1242  ss = plsc->wmyscl * dy;
1243  diag = sqrt( cc * cc + ss * ss );
1244  cc /= diag;
1245  ss /= diag;
1246  wy = wy1 + pos * ( wy2 - wy1 );
1247 
1248  if ( plP_stsearch( opt, 'v' ) )
1249  vert = 0;
1250  else if ( plP_stsearch( opt, 'h' ) )
1251  vert = 1;
1252 
1253  if ( vert )
1254  {
1255  xform[0] = 0.0;
1256  xform[1] = -cc;
1257  xform[2] = 1.0;
1258  xform[3] = -ss;
1259  }
1260  else
1261  {
1262  xform[0] = cc;
1263  xform[1] = 0.0;
1264  xform[2] = ss;
1265  xform[3] = 1.0;
1266  }
1267 
1268  xdv = plP_wcdcx( wx );
1269  ydv = plP_wcdcy( wy );
1270 
1271  dispx = -disp * cc;
1272  dispy = -disp * ss;
1273 
1274  plgchr( &chrdef, &chrht );
1275  shift = ( just == 0.0 ) ? 0.0 : plstrl( text ) * just;
1276 
1277  xmm = plP_dcmmx( xdv ) + dispx * chrht;
1278  ymm = plP_dcmmy( ydv ) + dispy * chrht;
1279  refxmm = xmm - shift * xform[0];
1280  refymm = ymm - shift * xform[2];
1281 
1282  x = plP_mmpcx( xmm );
1283  y = plP_mmpcy( ymm );
1284  refx = plP_mmpcx( refxmm );
1285  refy = plP_mmpcy( refymm );
1286 
1287  plP_text( 0, just, xform, x, y, refx, refy, text );
1288 }
1289 
1290 //--------------------------------------------------------------------------
1291 // void grid_box()
1292 //
1293 // Draws grids at tick locations (major and/or minor).
1294 //
1295 // Note that 'tspace' is the minimim distance away (in fractional number
1296 // of ticks or subticks) from the boundary a grid line can be drawn. If
1297 // you are too close, it looks bad.
1298 //--------------------------------------------------------------------------
1299 
1300 static void
1301 grid_box( const char *xopt, PLFLT xtick1, PLINT nxsub1,
1302  const char *yopt, PLFLT ytick1, PLINT nysub1 )
1303 {
1304  PLINT lgx, lhx, llx, ldx;
1305  PLINT lgy, lhy, lly, ldy;
1306  PLFLT vpwxmi, vpwxma, vpwymi, vpwyma;
1307  PLFLT vpwxmin, vpwxmax, vpwymin, vpwymax;
1308  PLFLT tn, temp, tcrit, tspace = 0.1;
1309  PLFLT tstart, factor;
1310  PLINT i;
1311 
1312 // Set plot options from input
1313 
1314  lgx = plP_stsearch( xopt, 'g' );
1315  lhx = plP_stsearch( xopt, 'h' );
1316  llx = plP_stsearch( xopt, 'l' );
1317  ldx = plP_stsearch( xopt, 'd' );
1318 
1319  lgy = plP_stsearch( yopt, 'g' );
1320  lhy = plP_stsearch( yopt, 'h' );
1321  lly = plP_stsearch( yopt, 'l' );
1322  ldy = plP_stsearch( yopt, 'd' );
1323 
1324  plP_xgvpw( &vpwxmin, &vpwxmax, &vpwymin, &vpwymax );
1325 // n.b. large change; vpwxmi always numerically less than vpwxma, and
1326 // similarly for vpwymi
1327  vpwxmi = ( vpwxmax > vpwxmin ) ? vpwxmin : vpwxmax;
1328  vpwxma = ( vpwxmax > vpwxmin ) ? vpwxmax : vpwxmin;
1329  vpwymi = ( vpwymax > vpwymin ) ? vpwymin : vpwymax;
1330  vpwyma = ( vpwymax > vpwymin ) ? vpwymax : vpwymin;
1331 
1332 // Draw grid in x direction.
1333 
1334  if ( lgx )
1335  {
1336  if ( ldx )
1337  {
1338  pldtfac( vpwxmi, vpwxma, &factor, &tstart );
1339  tn = xtick1 * ( floor( ( vpwxmi - tstart ) / xtick1 ) ) + tstart;
1340  }
1341  else
1342  {
1343  tn = xtick1 * floor( vpwxmi / xtick1 );
1344  }
1345  for (; tn <= vpwxma; tn += xtick1 )
1346  {
1347  if ( lhx )
1348  {
1349  if ( llx )
1350  {
1351  PLFLT otemp = tn;
1352  for ( i = 0; i <= 7; i++ )
1353  {
1354  temp = tn + xlog[i];
1355  tcrit = ( temp - otemp ) * tspace;
1356  otemp = temp;
1357  if ( BETW( temp, vpwxmi + tcrit, vpwxma - tcrit ) )
1358  pljoin( temp, vpwymi, temp, vpwyma );
1359  }
1360  }
1361  else
1362  {
1363  for ( i = 1; i <= nxsub1 - 1; i++ )
1364  {
1365  temp = tn + i * xtick1 / nxsub1;
1366  tcrit = xtick1 / nxsub1 * tspace;
1367  if ( BETW( temp, vpwxmi + tcrit, vpwxma - tcrit ) )
1368  pljoin( temp, vpwymi, temp, vpwyma );
1369  }
1370  }
1371  }
1372  tcrit = xtick1 * tspace;
1373  if ( BETW( tn, vpwxmi + tcrit, vpwxma - tcrit ) )
1374  pljoin( tn, vpwymi, tn, vpwyma );
1375  }
1376  }
1377 
1378 // Draw grid in y direction
1379 
1380  if ( lgy )
1381  {
1382  if ( ldy )
1383  {
1384  pldtfac( vpwymi, vpwyma, &factor, &tstart );
1385  tn = ytick1 * ( floor( ( vpwymi - tstart ) / ytick1 ) ) + tstart;
1386  }
1387  else
1388  {
1389  tn = ytick1 * floor( vpwymi / ytick1 );
1390  }
1391  for (; tn <= vpwyma; tn += ytick1 )
1392  {
1393  if ( lhy )
1394  {
1395  if ( lly )
1396  {
1397  PLFLT otemp = tn;
1398  for ( i = 0; i <= 7; i++ )
1399  {
1400  temp = tn + xlog[i];
1401  tcrit = ( temp - otemp ) * tspace;
1402  otemp = temp;
1403  if ( BETW( temp, vpwymi + tcrit, vpwyma - tcrit ) )
1404  pljoin( vpwxmi, temp, vpwxma, temp );
1405  }
1406  }
1407  else
1408  {
1409  for ( i = 1; i <= nysub1 - 1; i++ )
1410  {
1411  temp = tn + i * ytick1 / nysub1;
1412  tcrit = ytick1 / nysub1 * tspace;
1413  if ( BETW( temp, vpwymi + tcrit, vpwyma - tcrit ) )
1414  pljoin( vpwxmi, temp, vpwxma, temp );
1415  }
1416  }
1417  }
1418  tcrit = ytick1 * tspace;
1419  if ( BETW( tn, vpwymi + tcrit, vpwyma - tcrit ) )
1420  pljoin( vpwxmi, tn, vpwxma, tn );
1421  }
1422  }
1423 }
1424 
1425 //--------------------------------------------------------------------------
1426 // void label_box()
1427 //
1428 // Writes numeric labels on side(s) of box.
1429 //--------------------------------------------------------------------------
1430 
1431 static void
1432 label_box( const char *xopt, PLFLT xtick1, const char *yopt, PLFLT ytick1 )
1433 {
1434  static char string[STRING_LEN];
1435  PLBOOL ldx, lfx, lix, llx, lmx, lnx, ltx, lox, lxx;
1436  PLBOOL ldy, lfy, liy, lly, lmy, lny, lty, lvy, loy, lxy;
1437  PLFLT vpwxmi, vpwxma, vpwymi, vpwyma;
1438  PLFLT vpwxmin, vpwxmax, vpwymin, vpwymax;
1439  PLFLT tn, tp, offset;
1440  PLFLT factor, tstart;
1441  const char *timefmt = NULL;
1442  PLFLT default_mm, char_height_mm, height_mm;
1443  PLFLT string_length_mm = 0.0, pos_mm = 0.0;
1444 
1445  // Assume label data is for placement of exponents if no custom
1446  // label function is provided.
1447  PLBOOL custom_exponent_placement = !plsc->label_func && plsc->label_data;
1448 
1449  // pos, height, and just are unnecessarily set to quiet
1450  // -O3 -Wuninitialized warnings that are obvious false alarms from
1451  // the clarity of the code associated with the true or false
1452  // result for custom_exponent_placement.
1453  PLFLT pos = 0.0, height = 0.0, just = 0.0;
1454  const char * esc_string = plgesc_string();
1455 
1456  plgchr( &default_mm, &char_height_mm );
1457 
1458 // Set plot options from input
1459 
1460  ldx = plP_stsearch( xopt, 'd' );
1461  lfx = plP_stsearch( xopt, 'f' );
1462  lix = plP_stsearch( xopt, 'i' );
1463  llx = plP_stsearch( xopt, 'l' );
1464  lmx = plP_stsearch( xopt, 'm' );
1465  lnx = plP_stsearch( xopt, 'n' );
1466  ltx = plP_stsearch( xopt, 't' );
1467  lox = plP_stsearch( xopt, 'o' );
1468  lxx = plP_stsearch( xopt, 'x' );
1469 
1470  ldy = plP_stsearch( yopt, 'd' );
1471  lfy = plP_stsearch( yopt, 'f' );
1472  liy = plP_stsearch( yopt, 'i' );
1473  lly = plP_stsearch( yopt, 'l' );
1474  lmy = plP_stsearch( yopt, 'm' );
1475  lny = plP_stsearch( yopt, 'n' );
1476  lty = plP_stsearch( yopt, 't' );
1477  lvy = plP_stsearch( yopt, 'v' );
1478  loy = plP_stsearch( yopt, 'o' );
1479  lxy = plP_stsearch( yopt, 'x' );
1480 
1481  plP_xgvpw( &vpwxmin, &vpwxmax, &vpwymin, &vpwymax );
1482 // vpwxmi always numerically less than vpwxma, and
1483 // similarly for vpwymi
1484  vpwxmi = ( vpwxmax > vpwxmin ) ? vpwxmin : vpwxmax;
1485  vpwxma = ( vpwxmax > vpwxmin ) ? vpwxmax : vpwxmin;
1486  vpwymi = ( vpwymax > vpwymin ) ? vpwymin : vpwymax;
1487  vpwyma = ( vpwymax > vpwymin ) ? vpwymax : vpwymin;
1488 
1489  // Write label(s) for horizontal axes.
1490  if ( ( lmx || lnx ) && ( ltx || lxx ) )
1491  {
1492  PLINT xmode, xprec, xdigmax, xdigits, xscale;
1493 
1494  plgxax( &xdigmax, &xdigits );
1495  pldprec( vpwxmi, vpwxma, xtick1, lfx, &xmode, &xprec, xdigmax, &xscale );
1496  timefmt = plP_gtimefmt();
1497 
1498  if ( ldx )
1499  {
1500  pldtfac( vpwxmi, vpwxma, &factor, &tstart );
1501  tp = xtick1 * ( 1. + floor( ( vpwxmi - tstart ) / xtick1 ) ) + tstart;
1502  }
1503  else
1504  {
1505  tp = xtick1 * ( 1. + floor( vpwxmi / xtick1 ) );
1506  }
1507  height = lix ? 1.75 : 1.5;
1508  if ( plsc->if_boxbb )
1509  {
1510  // For horizontal axes, height of zero corresponds to
1511  // character centred on edge so should add 0.5 to height
1512  // to obtain bounding box edge in direction away from
1513  // edge. However, experimentally found 0.7 gave a better
1514  // looking result.
1515  height_mm = ( height + 0.7 ) * char_height_mm;
1516  if ( lnx )
1517  plsc->boxbb_ymin = MIN( plsc->boxbb_ymin, plsc->vppymi /
1518  plsc->ypmm - height_mm );
1519  if ( lmx )
1520  plsc->boxbb_ymax = MAX( plsc->boxbb_ymax, plsc->vppyma /
1521  plsc->ypmm + height_mm );
1522  }
1523 
1524  for ( tn = tp; BETW( tn, vpwxmi, vpwxma ); tn += xtick1 )
1525  {
1526  if ( ldx )
1527  {
1528  strfqsas( string, STRING_LEN, timefmt, (double) tn, plsc->qsasconfig );
1529  }
1530  else
1531  {
1532  plform( PL_X_AXIS, tn, xscale, xprec, string, STRING_LEN, llx, lfx, lox );
1533  }
1534  pos = ( vpwxmax > vpwxmin ) ?
1535  ( tn - vpwxmi ) / ( vpwxma - vpwxmi ) :
1536  ( vpwxma - tn ) / ( vpwxma - vpwxmi );
1537  if ( plsc->if_boxbb )
1538  {
1539  string_length_mm = plstrl( string );
1540  pos_mm = ( plsc->vppxmi + pos *
1541  ( plsc->vppxma - plsc->vppxmi ) ) /
1542  plsc->xpmm;
1543  }
1544  if ( lnx )
1545  {
1546  // Bottom axis.
1547  if ( plsc->if_boxbb )
1548  {
1549  plsc->boxbb_xmin = MIN( plsc->boxbb_xmin,
1550  pos_mm - 0.5 * string_length_mm );
1551  plsc->boxbb_xmax = MAX( plsc->boxbb_xmax,
1552  pos_mm + 0.5 * string_length_mm );
1553  }
1554  else
1555  {
1556  plmtex( "b", height, pos, 0.5, string );
1557  }
1558  }
1559  if ( lmx )
1560  {
1561  // Top axis.
1562  if ( plsc->if_boxbb )
1563  {
1564  plsc->boxbb_xmin = MIN( plsc->boxbb_xmin,
1565  pos_mm - 0.5 * string_length_mm );
1566  plsc->boxbb_xmax = MAX( plsc->boxbb_xmax,
1567  pos_mm + 0.5 * string_length_mm );
1568  }
1569  else
1570  {
1571  plmtex( "t", height, pos, 0.5, string );
1572  }
1573  }
1574  }
1575  xdigits = 2;
1576  plsxax( xdigmax, xdigits );
1577 
1578  // Write separate exponential label if mode = 1.
1579 
1580  if ( !llx && !ldx && !lox && xmode )
1581  {
1582  if ( custom_exponent_placement )
1583  {
1584  height = ( (PLLabelDefaults *) plsc->label_data )->exp_label_disp;
1585  pos = ( (PLLabelDefaults *) plsc->label_data )->exp_label_pos;
1586  just = ( (PLLabelDefaults *) plsc->label_data )->exp_label_just;
1587  }
1588  else
1589  {
1590  height = 3.2;
1591  pos = 1.0;
1592  just = 0.5;
1593  }
1594  snprintf( string, STRING_LEN, "(x10%su%d%sd)", esc_string, (int) xscale, esc_string );
1595  if ( lnx )
1596  {
1597  // Bottom axis exponent.
1598  if ( plsc->if_boxbb )
1599  {
1600  // For horizontal axes, height of zero corresponds
1601  // to character centred on edge so should add 0.5
1602  // to height to obtain bounding box edge in
1603  // direction away from edge if no exponent. Add
1604  // an additional offset to make exponent fit.
1605  height_mm = ( height + 0.9 ) * char_height_mm;
1606  plsc->boxbb_ymin = MIN( plsc->boxbb_ymin, plsc->vppymi /
1607  plsc->ypmm - height_mm );
1608  string_length_mm = plstrl( string );
1609  pos_mm = ( plsc->vppxmi + pos *
1610  ( plsc->vppxma - plsc->vppxmi ) ) /
1611  plsc->xpmm;
1612  plsc->boxbb_xmin = MIN( plsc->boxbb_xmin,
1613  pos_mm - 0.5 * string_length_mm );
1614  plsc->boxbb_xmax = MAX( plsc->boxbb_xmax,
1615  pos_mm + 0.5 * string_length_mm );
1616  }
1617  else
1618  {
1619  plmtex( "b", height, pos, just, string );
1620  }
1621  }
1622  if ( lmx )
1623  {
1624  // Top axis exponent.
1625  if ( plsc->if_boxbb )
1626  {
1627  // For horizontal axes, height of zero corresponds
1628  // to character centred on edge so should add 0.5
1629  // to height to obtain bounding box edge in
1630  // direction away from edge if no exponent. Add
1631  // an additional offset to make exponent fit.
1632  height_mm = ( height + 1.4 ) * char_height_mm;
1633  plsc->boxbb_ymax = MAX( plsc->boxbb_ymax, plsc->vppyma /
1634  plsc->ypmm + height_mm );
1635  string_length_mm = plstrl( string );
1636  pos_mm = ( plsc->vppxmi + pos *
1637  ( plsc->vppxma - plsc->vppxmi ) ) /
1638  plsc->xpmm;
1639  plsc->boxbb_xmin = MIN( plsc->boxbb_xmin,
1640  pos_mm - 0.5 * string_length_mm );
1641  plsc->boxbb_xmax = MAX( plsc->boxbb_xmax,
1642  pos_mm + 0.5 * string_length_mm );
1643  }
1644  else
1645  {
1646  plmtex( "t", height, pos, just, string );
1647  }
1648  }
1649  }
1650  }
1651 
1652  // Write label(s) for vertical axes.
1653 
1654  if ( ( lmy || lny ) && ( lty || lxy ) )
1655  {
1656  PLINT ymode, yprec, ydigmax, ydigits, yscale;
1657 
1658  plgyax( &ydigmax, &ydigits );
1659  pldprec( vpwymi, vpwyma, ytick1, lfy, &ymode, &yprec, ydigmax, &yscale );
1660 
1661  ydigits = 0;
1662  if ( ldy )
1663  {
1664  pldtfac( vpwymi, vpwyma, &factor, &tstart );
1665  tp = ytick1 * ( 1. + floor( ( vpwymi - tstart ) / ytick1 ) ) + tstart;
1666  }
1667  else
1668  {
1669  tp = ytick1 * ( 1. + floor( vpwymi / ytick1 ) );
1670  }
1671  for ( tn = tp; BETW( tn, vpwymi, vpwyma ); tn += ytick1 )
1672  {
1673  if ( ldy )
1674  {
1675  strfqsas( string, STRING_LEN, timefmt, (double) tn, plsc->qsasconfig );
1676  }
1677  else
1678  {
1679  plform( PL_Y_AXIS, tn, yscale, yprec, string, STRING_LEN, lly, lfy, loy );
1680  }
1681  pos = ( vpwymax > vpwymin ) ?
1682  ( tn - vpwymi ) / ( vpwyma - vpwymi ) :
1683  ( vpwyma - tn ) / ( vpwyma - vpwymi );
1684  if ( lny )
1685  {
1686  if ( lvy )
1687  {
1688  // Left axis with text written perpendicular to edge.
1689  height = liy ? 1.0 : 0.5;
1690  if ( plsc->if_boxbb )
1691  {
1692  // For vertical axes with text written
1693  // perpendicular to edge, height of zero
1694  // corresponds character centred on edge so
1695  // should add 0.5 to height to obtain bounding
1696  // box edge in direction away from edge, and
1697  // that value apparently works.
1698  height_mm = ( height + 0.0 ) * char_height_mm;
1699  string_length_mm = plstrl( string );
1700  plsc->boxbb_xmin = MIN( plsc->boxbb_xmin, plsc->vppxmi /
1701  plsc->xpmm - height_mm - string_length_mm );
1702  pos_mm = ( plsc->vppymi + pos *
1703  ( plsc->vppyma - plsc->vppymi ) ) /
1704  plsc->ypmm;
1705  // Expected offset is 0.5, but adjust to improve
1706  // look of result.
1707  plsc->boxbb_ymin = MIN( plsc->boxbb_ymin,
1708  pos_mm - 0.6 * char_height_mm );
1709  plsc->boxbb_ymax = MAX( plsc->boxbb_ymax,
1710  pos_mm + 0.7 * char_height_mm );
1711  }
1712  else
1713  {
1714  plmtex( "lv", height, pos, 1.0, string );
1715  }
1716  }
1717  else
1718  {
1719  // Left axis with text written parallel to edge.
1720  height = liy ? 1.75 : 1.5;
1721  if ( plsc->if_boxbb )
1722  {
1723  // For vertical axes with text written
1724  // parallel to edge, height of zero
1725  // corresponds to character centred on edge so
1726  // should add 0.5 to height to obtain bounding
1727  // box edge in direction away from edge,
1728  // However, experimentally found 0.8 gave a
1729  // better looking result.
1730  height_mm = ( height + 0.8 ) * char_height_mm;
1731  plsc->boxbb_xmin = MIN( plsc->boxbb_xmin, plsc->vppxmi /
1732  plsc->xpmm - height_mm );
1733  pos_mm = ( plsc->vppymi + pos *
1734  ( plsc->vppyma - plsc->vppymi ) ) /
1735  plsc->ypmm;
1736  string_length_mm = plstrl( string );
1737  plsc->boxbb_ymin = MIN( plsc->boxbb_ymin,
1738  pos_mm - 0.5 * string_length_mm );
1739  plsc->boxbb_ymax = MAX( plsc->boxbb_ymax,
1740  pos_mm + 0.5 * string_length_mm );
1741  }
1742  else
1743  {
1744  plmtex( "l", height, pos, 0.5, string );
1745  }
1746  }
1747  }
1748  if ( lmy )
1749  {
1750  if ( lvy )
1751  {
1752  // Right axis with text written perpendicular to edge.
1753  height = liy ? 1.0 : 0.5;
1754  if ( plsc->if_boxbb )
1755  {
1756  // For vertical axes with text written
1757  // perpendicular to edge, height of zero
1758  // corresponds character centred on edge so
1759  // should add 0.5 to height to obtain bounding
1760  // box edge in direction away from edge, and
1761  // that value apparently works.
1762  height_mm = ( height + 0.0 ) * char_height_mm;
1763  string_length_mm = plstrl( string );
1764  plsc->boxbb_xmax = MAX( plsc->boxbb_xmax, plsc->vppxma /
1765  plsc->xpmm + height_mm + string_length_mm );
1766  pos_mm = ( plsc->vppymi + pos *
1767  ( plsc->vppyma - plsc->vppymi ) ) /
1768  plsc->ypmm;
1769  // Expected offset is 0.5, but adjust to improve
1770  // look of result.
1771  plsc->boxbb_ymin = MIN( plsc->boxbb_ymin,
1772  pos_mm - 0.6 * char_height_mm );
1773  plsc->boxbb_ymax = MAX( plsc->boxbb_ymax,
1774  pos_mm + 0.7 * char_height_mm );
1775  }
1776  else
1777  {
1778  plmtex( "rv", height, pos, 0.0, string );
1779  }
1780  }
1781  else
1782  {
1783  // Right axis with text written parallel to edge.
1784  height = liy ? 1.75 : 1.5;
1785  if ( plsc->if_boxbb )
1786  {
1787  // For vertical axes with text written
1788  // parallel to edge, height of zero
1789  // corresponds to character centred on edge so
1790  // should add 0.5 to height to obtain bounding
1791  // box edge in direction away from edge,
1792  // However, experimentally found 0.8 gave a
1793  // better looking result.
1794  height_mm = ( height + 0.8 ) * char_height_mm;
1795  plsc->boxbb_xmax = MAX( plsc->boxbb_xmax, plsc->vppxma /
1796  plsc->xpmm + height_mm );
1797  pos_mm = ( plsc->vppymi + pos *
1798  ( plsc->vppyma - plsc->vppymi ) ) /
1799  plsc->ypmm;
1800  string_length_mm = plstrl( string );
1801  plsc->boxbb_ymin = MIN( plsc->boxbb_ymin,
1802  pos_mm - 0.5 * string_length_mm );
1803  plsc->boxbb_ymax = MAX( plsc->boxbb_ymax,
1804  pos_mm + 0.5 * string_length_mm );
1805  }
1806  else
1807  {
1808  plmtex( "r", height, pos, 0.5, string );
1809  }
1810  }
1811  }
1812  ydigits = MAX( ydigits, (PLINT) strlen( string ) );
1813  }
1814  if ( !lvy )
1815  ydigits = 2;
1816 
1817  plsyax( ydigmax, ydigits );
1818 
1819  // Write separate exponential label if mode = 1.
1820 
1821  if ( !lly && !ldy && !loy && ymode )
1822  {
1823  snprintf( string, STRING_LEN, "(x10%su%d%sd)", esc_string, (int) yscale, esc_string );
1824  if ( custom_exponent_placement )
1825  {
1826  height = ( (PLLabelDefaults *) plsc->label_data )->exp_label_disp;
1827  pos = ( (PLLabelDefaults *) plsc->label_data )->exp_label_pos;
1828  just = ( (PLLabelDefaults *) plsc->label_data )->exp_label_just;
1829  }
1830  if ( lvy )
1831  {
1832  offset = 0.1; // more space to clear labels in "v" mode
1833  }
1834  else
1835  {
1836  offset = 0.02;
1837  }
1838  // Left axis exponent
1839  if ( lny )
1840  {
1841  if ( !custom_exponent_placement )
1842  {
1843  height = 3.2;
1844  pos = 1.0 + offset;
1845  just = 0.5;
1846  }
1847  if ( plsc->if_boxbb )
1848  {
1849  // For horizontal axes, height of zero corresponds
1850  // to character centred on edge so should add 0.5
1851  // to height to obtain bounding box edge in
1852  // direction away from edge if no exponent. Add
1853  // an additional offset to make exponent fit.
1854  height_mm = ( height + 1.4 ) * char_height_mm;
1855  plsc->boxbb_ymax = MAX( plsc->boxbb_ymax, plsc->vppyma /
1856  plsc->ypmm + height_mm );
1857  string_length_mm = plstrl( string );
1858  pos_mm = ( plsc->vppxmi + pos *
1859  ( plsc->vppxma - plsc->vppxmi ) ) /
1860  plsc->xpmm;
1861  plsc->boxbb_xmin = MIN( plsc->boxbb_xmin,
1862  pos_mm - string_length_mm );
1863  }
1864  else
1865  {
1866  if ( lvy )
1867  {
1868  plmtex( "lv", height, pos, just, string );
1869  }
1870  else
1871  {
1872  plmtex( "l", height, pos, just, string );
1873  }
1874  }
1875  }
1876  // Right axis exponent.
1877  if ( lmy )
1878  {
1879  if ( !custom_exponent_placement )
1880  {
1881  height = 3.4; // Extra space for superscript
1882  pos = 1.0 + offset;
1883  just = 0.5;
1884  }
1885  if ( plsc->if_boxbb )
1886  {
1887  // For horizontal axes, height of zero corresponds
1888  // to character centred on edge so should add 0.5
1889  // to height to obtain bounding box edge in
1890  // direction away from edge if no exponent. Add
1891  // an additional offset to make exponent fit.
1892  height_mm = ( height + 1.4 ) * char_height_mm;
1893  plsc->boxbb_ymax = MAX( plsc->boxbb_ymax, plsc->vppyma /
1894  plsc->ypmm + height_mm );
1895  string_length_mm = plstrl( string );
1896  pos_mm = ( plsc->vppxmi + pos *
1897  ( plsc->vppxma - plsc->vppxmi ) ) /
1898  plsc->xpmm;
1899  plsc->boxbb_xmax = MAX( plsc->boxbb_xmin,
1900  pos_mm + string_length_mm );
1901  }
1902  else
1903  {
1904  if ( lvy )
1905  {
1906  plmtex( "rv", height, pos, just, string );
1907  }
1908  else
1909  {
1910  plmtex( "r", height, pos, just, string );
1911  }
1912  }
1913  }
1914  }
1915  }
1916 }
1917 
1918 //--------------------------------------------------------------------------
1919 // void label_box_custom()
1920 //
1921 // Writes numeric labels on side(s) of box in custom locations
1922 //--------------------------------------------------------------------------
1923 
1924 void
1925 label_box_custom( const char *xopt, PLINT n_xticks, const PLFLT *xticks, const char *yopt, PLINT n_yticks, const PLFLT *yticks )
1926 {
1927  static char string[STRING_LEN];
1928  PLBOOL ldx, lfx, lix, llx, lmx, lnx, ltx, lox, lxx;
1929  PLBOOL ldy, lfy, liy, lly, lmy, lny, lty, lvy, loy, lxy;
1930  PLFLT vpwxmi, vpwxma, vpwymi, vpwyma;
1931  PLFLT vpwxmin, vpwxmax, vpwymin, vpwymax;
1932  PLFLT tn, offset;
1933  const char *timefmt;
1934  PLINT i;
1935  PLINT xdigmax, xdigits, xdigmax_old, xdigits_old;
1936  PLINT ydigmax, ydigits, ydigmax_old, ydigits_old;
1937  PLINT lxmin, lxmax, lymin, lymax;
1938  PLINT pxmin, pxmax, pymin, pymax;
1939  PLFLT default_mm, char_height_mm, height_mm;
1940  PLFLT string_length_mm = 0.0, pos_mm = 0.0;
1941 
1942  // Assume label data is for placement of exponents if no custom
1943  // label function is provided.
1944  PLBOOL custom_exponent_placement = !plsc->label_func && plsc->label_data;
1945 
1946  // pos, height, and just are unnecessarily set to quiet
1947  // -O3 -Wuninitialized warnings that are obvious false alarms from
1948  // the clarity of the code associated with the true or false
1949  // result for custom_exponent_placement.
1950  PLFLT pos = 0.0, height = 0.0, just = 0.0;
1951  const char * esc_string = plgesc_string();
1952 
1953  plgchr( &default_mm, &char_height_mm );
1954 
1955  // Save some parameters
1956  plgxax( &xdigmax, &xdigits );
1957  plgyax( &ydigmax, &ydigits );
1958  xdigmax_old = xdigmax;
1959  xdigits_old = xdigits;
1960  ydigmax_old = ydigmax;
1961  ydigits_old = ydigits;
1962 
1963 // Open the clip limits to the subpage limits
1964 
1965  plP_gclp( &lxmin, &lxmax, &lymin, &lymax );
1966  plP_gphy( &pxmin, &pxmax, &pymin, &pymax );
1967  plP_sclp( pxmin, pxmax, pymin, pymax );
1968 
1969 // Set plot options from input
1970 
1971  ldx = plP_stsearch( xopt, 'd' );
1972  lfx = plP_stsearch( xopt, 'f' );
1973  lix = plP_stsearch( xopt, 'i' );
1974  llx = plP_stsearch( xopt, 'l' );
1975  lmx = plP_stsearch( xopt, 'm' );
1976  lnx = plP_stsearch( xopt, 'n' );
1977  ltx = plP_stsearch( xopt, 't' );
1978  lox = plP_stsearch( xopt, 'o' );
1979  lxx = plP_stsearch( xopt, 'x' );
1980 
1981  ldy = plP_stsearch( yopt, 'd' );
1982  lfy = plP_stsearch( yopt, 'f' );
1983  liy = plP_stsearch( yopt, 'i' );
1984  lly = plP_stsearch( yopt, 'l' );
1985  lmy = plP_stsearch( yopt, 'm' );
1986  lny = plP_stsearch( yopt, 'n' );
1987  lty = plP_stsearch( yopt, 't' );
1988  lvy = plP_stsearch( yopt, 'v' );
1989  loy = plP_stsearch( yopt, 'o' );
1990  lxy = plP_stsearch( yopt, 'x' );
1991 
1992  plP_xgvpw( &vpwxmin, &vpwxmax, &vpwymin, &vpwymax );
1993 // n.b. large change; vpwxmi always numerically less than vpwxma, and
1994 // similarly for vpwymi
1995  vpwxmi = ( vpwxmax > vpwxmin ) ? vpwxmin : vpwxmax;
1996  vpwxma = ( vpwxmax > vpwxmin ) ? vpwxmax : vpwxmin;
1997  vpwymi = ( vpwymax > vpwymin ) ? vpwymin : vpwymax;
1998  vpwyma = ( vpwymax > vpwymin ) ? vpwymax : vpwymin;
1999 
2000  if ( plsc->if_boxbb )
2001  {
2002  PLINT xmajor = MAX( ROUND( plsc->majht * plsc->ypmm ), 1 );
2003  PLINT ymajor = MAX( ROUND( plsc->majht * plsc->xpmm ), 1 );
2004  // Bounding-box limits for the box in mm before corrections
2005  // for decorations are applied.
2006  plsc->boxbb_xmin = plsc->vppxmi / plsc->xpmm;
2007  plsc->boxbb_xmax = plsc->vppxma / plsc->xpmm;
2008  plsc->boxbb_ymin = plsc->vppymi / plsc->ypmm;
2009  plsc->boxbb_ymax = plsc->vppyma / plsc->ypmm;
2010  // Carefully follow logic below for the case where
2011  // an inverted major tick mark is written (in the X direction
2012  // for a Y axis and vice versa). Ignore minor tick marks
2013  // which are assumed to be smaller.
2014  if ( lix && ( lmx || lnx ) && ( ltx && !lxx ) )
2015  {
2016  plsc->boxbb_ymin -= xmajor / plsc->ypmm;
2017  plsc->boxbb_ymax += xmajor / plsc->ypmm;
2018  }
2019  if ( liy && ( lmy || lny ) && ( lty && !lxy ) )
2020  {
2021  plsc->boxbb_xmin -= ymajor / plsc->xpmm;
2022  plsc->boxbb_xmax += ymajor / plsc->xpmm;
2023  }
2024  }
2025  // Write label(s) for horizontal axes.
2026 
2027  if ( ( lmx || lnx ) && ( ltx || lxx ) )
2028  {
2029  PLINT xmode, xprec, xscale;
2030  PLFLT x_spacing, x_spacing_tmp;
2031 
2032  // Determine spacing between ticks
2033  // Use the x-size of the window
2034  x_spacing = vpwxma - vpwxmi;
2035  if ( n_xticks > 1 )
2036  {
2037  // Use the smallest space between ticks
2038  for ( i = 1; i < n_xticks; i++ )
2039  {
2040  x_spacing_tmp = fabs( xticks[i] - xticks[i - 1] );
2041  x_spacing = MIN( x_spacing, x_spacing_tmp );
2042  }
2043  }
2044 
2045  plgxax( &xdigmax, &xdigits );
2046  pldprec( vpwxmi, vpwxma, x_spacing, lfx, &xmode, &xprec, xdigmax, &xscale );
2047  timefmt = plP_gtimefmt();
2048 
2049  height = lix ? 1.75 : 1.5;
2050  if ( plsc->if_boxbb )
2051  {
2052  // For horizontal axes, height of zero corresponds to
2053  // character centred on edge so should add 0.5 to height
2054  // to obtain bounding box edge in direction away from
2055  // edge. However, experimentally found 0.7 gave a better
2056  // looking result.
2057  height_mm = ( height + 0.7 ) * char_height_mm;
2058  if ( lnx )
2059  plsc->boxbb_ymin = MIN( plsc->boxbb_ymin, plsc->vppymi /
2060  plsc->ypmm - height_mm );
2061  if ( lmx )
2062  plsc->boxbb_ymax = MAX( plsc->boxbb_ymax, plsc->vppyma /
2063  plsc->ypmm + height_mm );
2064  }
2065  // Loop through all of the tick marks
2066  for ( i = 0; i < n_xticks; i++ )
2067  {
2068  tn = xticks[i];
2069  if ( BETW( tn, vpwxmi, vpwxma ) )
2070  {
2071  if ( !lxx && !plsc->if_boxbb )
2072  {
2073  plwxtik( tn, vpwymin, FALSE, !lix );
2074  plwxtik( tn, vpwymax, FALSE, lix );
2075  }
2076  if ( ldx )
2077  {
2078  strfqsas( string, STRING_LEN, timefmt, (double) tn, plsc->qsasconfig );
2079  }
2080  else
2081  {
2082  plform( PL_X_AXIS, tn, xscale, xprec, string, STRING_LEN, llx, lfx, lox );
2083  }
2084  pos = ( vpwxmax > vpwxmin ) ?
2085  ( tn - vpwxmi ) / ( vpwxma - vpwxmi ) :
2086  ( vpwxma - tn ) / ( vpwxma - vpwxmi );
2087  if ( plsc->if_boxbb )
2088  {
2089  string_length_mm = plstrl( string );
2090  pos_mm = ( plsc->vppxmi + pos *
2091  ( plsc->vppxma - plsc->vppxmi ) ) /
2092  plsc->xpmm;
2093  }
2094  if ( lnx )
2095  {
2096  // Bottom axis.
2097  if ( plsc->if_boxbb )
2098  {
2099  plsc->boxbb_xmin = MIN( plsc->boxbb_xmin,
2100  pos_mm - 0.5 * string_length_mm );
2101  plsc->boxbb_xmax = MAX( plsc->boxbb_xmax,
2102  pos_mm + 0.5 * string_length_mm );
2103  }
2104  else
2105  {
2106  plmtex( "b", height, pos, 0.5, string );
2107  }
2108  }
2109  if ( lmx )
2110  {
2111  // Top axis.
2112  if ( plsc->if_boxbb )
2113  {
2114  plsc->boxbb_xmin = MIN( plsc->boxbb_xmin,
2115  pos_mm - 0.5 * string_length_mm );
2116  plsc->boxbb_xmax = MAX( plsc->boxbb_xmax,
2117  pos_mm + 0.5 * string_length_mm );
2118  }
2119  else
2120  {
2121  plmtex( "t", height, pos, 0.5, string );
2122  }
2123  }
2124  }
2125  }
2126  xdigits = 2;
2127  plsxax( xdigmax, xdigits );
2128 
2129  // Write separate exponential label if mode = 1.
2130 
2131  if ( !llx && !ldx && !lox && xmode )
2132  {
2133  if ( custom_exponent_placement )
2134  {
2135  height = ( (PLLabelDefaults *) plsc->label_data )->exp_label_disp;
2136  pos = ( (PLLabelDefaults *) plsc->label_data )->exp_label_pos;
2137  just = ( (PLLabelDefaults *) plsc->label_data )->exp_label_just;
2138  }
2139  else
2140  {
2141  height = 3.2;
2142  pos = 1.0;
2143  just = 0.5;
2144  }
2145  snprintf( string, STRING_LEN, "(x10%su%d%sd)", esc_string, (int) xscale, esc_string );
2146  if ( lnx )
2147  {
2148  // Bottom axis exponent.
2149  if ( plsc->if_boxbb )
2150  {
2151  // For horizontal axes, height of zero corresponds
2152  // to character centred on edge so should add 0.5
2153  // to height to obtain bounding box edge in
2154  // direction away from edge if no exponent. Add
2155  // an additional offset to make exponent fit.
2156  height_mm = ( height + 0.9 ) * char_height_mm;
2157  plsc->boxbb_ymin = MIN( plsc->boxbb_ymin, plsc->vppymi /
2158  plsc->ypmm - height_mm );
2159  string_length_mm = plstrl( string );
2160  pos_mm = ( plsc->vppxmi + pos *
2161  ( plsc->vppxma - plsc->vppxmi ) ) /
2162  plsc->xpmm;
2163  plsc->boxbb_xmin = MIN( plsc->boxbb_xmin,
2164  pos_mm - 0.5 * string_length_mm );
2165  plsc->boxbb_xmax = MAX( plsc->boxbb_xmax,
2166  pos_mm + 0.5 * string_length_mm );
2167  }
2168  else
2169  {
2170  plmtex( "b", height, pos, just, string );
2171  }
2172  }
2173  if ( lmx )
2174  {
2175  // Top axis exponent.
2176  if ( plsc->if_boxbb )
2177  {
2178  // For horizontal axes, height of zero corresponds
2179  // to character centred on edge so should add 0.5
2180  // to height to obtain bounding box edge in
2181  // direction away from edge if no exponent. Add
2182  // an additional offset to make exponent fit.
2183  height_mm = ( height + 1.4 ) * char_height_mm;
2184  plsc->boxbb_ymax = MAX( plsc->boxbb_ymax, plsc->vppyma /
2185  plsc->ypmm + height_mm );
2186  string_length_mm = plstrl( string );
2187  pos_mm = ( plsc->vppxmi + pos *
2188  ( plsc->vppxma - plsc->vppxmi ) ) /
2189  plsc->xpmm;
2190  plsc->boxbb_xmin = MIN( plsc->boxbb_xmin,
2191  pos_mm - 0.5 * string_length_mm );
2192  plsc->boxbb_xmax = MAX( plsc->boxbb_xmax,
2193  pos_mm + 0.5 * string_length_mm );
2194  }
2195  else
2196  {
2197  plmtex( "t", height, pos, just, string );
2198  }
2199  }
2200  }
2201  }
2202 
2203  // Write label(s) for vertical axes.
2204  if ( ( lmy || lny ) && ( lty || lxy ) )
2205  {
2206  PLINT ymode, yprec, yscale;
2207  PLFLT y_spacing, y_spacing_tmp;
2208 
2209  // Determine spacing between ticks
2210  // Use the y-size of the window
2211  y_spacing = vpwyma - vpwymi;
2212  if ( n_yticks > 1 )
2213  {
2214  // Use the smallest space between ticks
2215  for ( i = 1; i < n_yticks; i++ )
2216  {
2217  y_spacing_tmp = fabs( yticks[i] - yticks[i - 1] );
2218  y_spacing = MIN( y_spacing, y_spacing_tmp );
2219  }
2220  }
2221 
2222  plgyax( &ydigmax, &ydigits );
2223  pldprec( vpwymi, vpwyma, y_spacing, lfy, &ymode, &yprec, ydigmax, &yscale );
2224  timefmt = plP_gtimefmt();
2225 
2226  ydigits = 0;
2227  for ( i = 0; i < n_yticks; i++ )
2228  {
2229  tn = yticks[i];
2230  if ( BETW( tn, vpwymi, vpwyma ) )
2231  {
2232  if ( !lxy && !plsc->if_boxbb )
2233  {
2234  plwytik( vpwxmin, tn, FALSE, !liy );
2235  plwytik( vpwxmax, tn, FALSE, liy );
2236  }
2237  if ( ldy )
2238  {
2239  strfqsas( string, STRING_LEN, timefmt, (double) tn, plsc->qsasconfig );
2240  }
2241  else
2242  {
2243  plform( PL_Y_AXIS, tn, yscale, yprec, string, STRING_LEN, lly, lfy, loy );
2244  }
2245  pos = ( vpwymax > vpwymin ) ?
2246  ( tn - vpwymi ) / ( vpwyma - vpwymi ) :
2247  ( vpwyma - tn ) / ( vpwyma - vpwymi );
2248  if ( lny )
2249  {
2250  if ( lvy )
2251  {
2252  // Left axis with text written perpendicular to edge.
2253  height = liy ? 1.0 : 0.5;
2254  if ( plsc->if_boxbb )
2255  {
2256  // For vertical axes with text written
2257  // perpendicular to edge, height of zero
2258  // corresponds character centred on edge so
2259  // should add 0.5 to height to obtain bounding
2260  // box edge in direction away from edge, and
2261  // that value apparently works.
2262  height_mm = ( height + 0.0 ) * char_height_mm;
2263  string_length_mm = plstrl( string );
2264  plsc->boxbb_xmin = MIN( plsc->boxbb_xmin, plsc->vppxmi /
2265  plsc->xpmm - height_mm - string_length_mm );
2266  pos_mm = ( plsc->vppymi + pos *
2267  ( plsc->vppyma - plsc->vppymi ) ) /
2268  plsc->ypmm;
2269  // Expected offset is 0.5, but adjust to improve
2270  // look of result.
2271  plsc->boxbb_ymin = MIN( plsc->boxbb_ymin,
2272  pos_mm - 0.6 * char_height_mm );
2273  plsc->boxbb_ymax = MAX( plsc->boxbb_ymax,
2274  pos_mm + 0.7 * char_height_mm );
2275  }
2276  else
2277  {
2278  plmtex( "lv", height, pos, 1.0, string );
2279  }
2280  }
2281  else
2282  {
2283  // Left axis with text written parallel to edge.
2284  height = liy ? 1.75 : 1.5;
2285  if ( plsc->if_boxbb )
2286  {
2287  // For vertical axes with text written
2288  // parallel to edge, height of zero
2289  // corresponds to character centred on edge so
2290  // should add 0.5 to height to obtain bounding
2291  // box edge in direction away from edge,
2292  // However, experimentally found 0.8 gave a
2293  // better looking result.
2294  height_mm = ( height + 0.8 ) * char_height_mm;
2295  plsc->boxbb_xmin = MIN( plsc->boxbb_xmin, plsc->vppxmi /
2296  plsc->xpmm - height_mm );
2297  pos_mm = ( plsc->vppymi + pos *
2298  ( plsc->vppyma - plsc->vppymi ) ) /
2299  plsc->ypmm;
2300  string_length_mm = plstrl( string );
2301  plsc->boxbb_ymin = MIN( plsc->boxbb_ymin,
2302  pos_mm - 0.5 * string_length_mm );
2303  plsc->boxbb_ymax = MAX( plsc->boxbb_ymax,
2304  pos_mm + 0.5 * string_length_mm );
2305  }
2306  else
2307  {
2308  plmtex( "l", height, pos, 0.5, string );
2309  }
2310  }
2311  }
2312  if ( lmy )
2313  {
2314  if ( lvy )
2315  {
2316  // Right axis with text written perpendicular to edge.
2317  height = liy ? 1.0 : 0.5;
2318  if ( plsc->if_boxbb )
2319  {
2320  // For vertical axes with text written
2321  // perpendicular to edge, height of zero
2322  // corresponds character centred on edge so
2323  // should add 0.5 to height to obtain bounding
2324  // box edge in direction away from edge, and
2325  // that value apparently works.
2326  height_mm = ( height + 0.0 ) * char_height_mm;
2327  string_length_mm = plstrl( string );
2328  plsc->boxbb_xmax = MAX( plsc->boxbb_xmax, plsc->vppxma /
2329  plsc->xpmm + height_mm + string_length_mm );
2330  pos_mm = ( plsc->vppymi + pos *
2331  ( plsc->vppyma - plsc->vppymi ) ) /
2332  plsc->ypmm;
2333  // Expected offset is 0.5, but adjust to improve
2334  // look of result.
2335  plsc->boxbb_ymin = MIN( plsc->boxbb_ymin,
2336  pos_mm - 0.6 * char_height_mm );
2337  plsc->boxbb_ymax = MAX( plsc->boxbb_ymax,
2338  pos_mm + 0.7 * char_height_mm );
2339  }
2340  else
2341  {
2342  plmtex( "rv", height, pos, 0.0, string );
2343  }
2344  }
2345  else
2346  {
2347  // Right axis with text written parallel to edge.
2348  height = liy ? 1.75 : 1.5;
2349  if ( plsc->if_boxbb )
2350  {
2351  // For vertical axes with text written
2352  // parallel to edge, height of zero
2353  // corresponds to character centred on edge so
2354  // should add 0.5 to height to obtain bounding
2355  // box edge in direction away from edge,
2356  // However, experimentally found 0.8 gave a
2357  // better looking result.
2358  height_mm = ( height + 0.8 ) * char_height_mm;
2359  plsc->boxbb_xmax = MAX( plsc->boxbb_xmax, plsc->vppxma /
2360  plsc->xpmm + height_mm );
2361  pos_mm = ( plsc->vppymi + pos *
2362  ( plsc->vppyma - plsc->vppymi ) ) /
2363  plsc->ypmm;
2364  string_length_mm = plstrl( string );
2365  plsc->boxbb_ymin = MIN( plsc->boxbb_ymin,
2366  pos_mm - 0.5 * string_length_mm );
2367  plsc->boxbb_ymax = MAX( plsc->boxbb_ymax,
2368  pos_mm + 0.5 * string_length_mm );
2369  }
2370  else
2371  {
2372  plmtex( "r", height, pos, 0.5, string );
2373  }
2374  }
2375  }
2376  ydigits = MAX( ydigits, (PLINT) strlen( string ) );
2377  }
2378  }
2379  if ( !lvy )
2380  ydigits = 2;
2381 
2382  plsyax( ydigmax, ydigits );
2383 
2384  // Write separate exponential label if mode = 1.
2385 
2386  if ( !lly && !ldy && !loy && ymode )
2387  {
2388  snprintf( string, STRING_LEN, "(x10%su%d%sd)", esc_string, (int) yscale, esc_string );
2389  if ( custom_exponent_placement )
2390  {
2391  height = ( (PLLabelDefaults *) plsc->label_data )->exp_label_disp;
2392  pos = ( (PLLabelDefaults *) plsc->label_data )->exp_label_pos;
2393  just = ( (PLLabelDefaults *) plsc->label_data )->exp_label_just;
2394  }
2395  if ( lvy )
2396  {
2397  offset = 0.1; // more space to clear labels in "v" mode
2398  }
2399  else
2400  {
2401  offset = 0.02;
2402  }
2403  // Left axis exponent.
2404  if ( lny )
2405  {
2406  if ( !custom_exponent_placement )
2407  {
2408  height = 3.2;
2409  pos = 1.0 + offset;
2410  just = 0.5;
2411  }
2412  if ( plsc->if_boxbb )
2413  {
2414  // For horizontal axes, height of zero corresponds
2415  // to character centred on edge so should add 0.5
2416  // to height to obtain bounding box edge in
2417  // direction away from edge if no exponent. Add
2418  // an additional offset to make exponent fit.
2419  height_mm = ( height + 1.4 ) * char_height_mm;
2420  plsc->boxbb_ymax = MAX( plsc->boxbb_ymax, plsc->vppyma /
2421  plsc->ypmm + height_mm );
2422  string_length_mm = plstrl( string );
2423  pos_mm = ( plsc->vppxmi + pos *
2424  ( plsc->vppxma - plsc->vppxmi ) ) /
2425  plsc->xpmm;
2426  plsc->boxbb_xmin = MIN( plsc->boxbb_xmin,
2427  pos_mm - string_length_mm );
2428  }
2429  else
2430  {
2431  if ( lvy )
2432  {
2433  plmtex( "lv", height, pos, just, string );
2434  }
2435  else
2436  {
2437  plmtex( "l", height, pos, just, string );
2438  }
2439  }
2440  }
2441  // Right axis exponent.
2442  if ( lmy )
2443  {
2444  if ( !custom_exponent_placement )
2445  {
2446  height = 3.4; // Extra space for superscript
2447  pos = 1.0 + offset;
2448  just = 0.5;
2449  }
2450  if ( plsc->if_boxbb )
2451  {
2452  // For horizontal axes, height of zero corresponds
2453  // to character centred on edge so should add 0.5
2454  // to height to obtain bounding box edge in
2455  // direction away from edge if no exponent. Add
2456  // an additional offset to make exponent fit.
2457  height_mm = ( height + 1.4 ) * char_height_mm;
2458  plsc->boxbb_ymax = MAX( plsc->boxbb_ymax, plsc->vppyma /
2459  plsc->ypmm + height_mm );
2460  string_length_mm = plstrl( string );
2461  pos_mm = ( plsc->vppxmi + pos *
2462  ( plsc->vppxma - plsc->vppxmi ) ) /
2463  plsc->xpmm;
2464  plsc->boxbb_xmax = MAX( plsc->boxbb_xmin,
2465  pos_mm + string_length_mm );
2466  }
2467  else
2468  {
2469  if ( lvy )
2470  {
2471  plmtex( "rv", height, pos, just, string );
2472  }
2473  else
2474  {
2475  plmtex( "r", height, pos, just, string );
2476  }
2477  }
2478  }
2479  }
2480  }
2481 
2482  // Restore saved parameters
2483  plsxax( xdigmax_old, xdigits_old );
2484  plsyax( ydigmax_old, ydigits_old );
2485 
2486  // Restore the clip limits to viewport edge
2487  plP_sclp( lxmin, lxmax, lymin, lymax );
2488 }
2489 
2490 //--------------------------------------------------------------------------
2491 //
2492 // Default labeling functions for PLplot
2493 //
2494 // These are the functions which are used internally by PLplot under various
2495 // conditions.
2496 //
2497 // They have been separated out for use in other PLplot functions and
2498 // potential exposure in the PLplot API.
2499 //
2500 //--------------------------------------------------------------------------
2501 void plP_default_label_log( PLINT PL_UNUSED( axis ), PLFLT value, char *string, PLINT len, void * PL_UNUSED( data ) )
2502 {
2503  const char * esc_string = plgesc_string();
2504  // Exponential, i.e. 10^-1, 10^0, 10^1, etc
2505  snprintf( string, (size_t) len, "10%su%d", esc_string, (int) ROUND( value ) );
2506 }
2507 
2508 void plP_default_label_log_fixed( PLINT PL_UNUSED( axis ), PLFLT value, char *string, PLINT len, void * PL_UNUSED( data ) )
2509 {
2510  // Fixed point, i.e. .1, 1, 10, etc
2511 
2512  int exponent = ROUND( value );
2513 
2514  value = pow( 10.0, exponent );
2515  if ( exponent < 0 )
2516  {
2517  char form[FORMAT_LEN];
2518  snprintf( form, FORMAT_LEN, "%%.%df", ABS( exponent ) );
2519  snprintf( string, (size_t) len, form, value );
2520  }
2521  else
2522  {
2523  snprintf( string, (size_t) len, "%d", (int) value );
2524  }
2525 }
2526 
2527 void plP_default_label( PLINT PL_UNUSED( axis ), PLFLT value, char *string, PLINT len, void *data )
2528 {
2529  PLINT scale, prec;
2530  PLINT setpre, precis;
2531  char form[FORMAT_LEN], temp[TEMP_LEN];
2532  double scale2;
2533 
2534  scale = ( (PLINT *) data )[0];
2535  prec = ( (PLINT *) data )[1];
2536 
2537  plP_gprec( &setpre, &precis );
2538 
2539  if ( setpre )
2540  prec = precis;
2541 
2542  if ( scale )
2543  value /= pow( 10., (double) scale );
2544 
2545  // This is necessary to prevent labels like "-0.0" on some systems
2546 
2547  scale2 = pow( 10., prec );
2548  value = floor( ( value * scale2 ) + .5 ) / scale2;
2549 
2550  snprintf( form, FORMAT_LEN, "%%.%df", (int) prec );
2551  snprintf( temp, TEMP_LEN, form, value );
2552  strncpy( string, temp, (size_t) ( len - 1 ) );
2553  string[len - 1] = '\0';
2554 }
2555 
2556 //--------------------------------------------------------------------------
2557 // void plform()
2558 //
2559 // Formats a PLFLT value in one of the following formats.
2560 //
2561 // If ll (logarithmic), then:
2562 //
2563 // - If lf (fixed), then used fixed point notation, i.e. .1, 1, 10, etc,
2564 // with unnecessary trailing .'s or 0's removed.
2565 //
2566 // - If !lf (default), then use exponential notation, i.e. 10^-1, etc.
2567 //
2568 // If !ll (linear), then:
2569 //
2570 // - If scale == 0, use fixed point format with "prec" places after the
2571 // decimal point.
2572 //
2573 // - If scale == 1, use scientific notation with one place before the
2574 // decimal point and "prec" places after. In this case, the value
2575 // must be divided by 10^scale.
2576 //
2577 // The axis argument is included to support PLplot's custom axis labeling. It
2578 // is passed on to the custom labeling function if it exists. Otherwise, it
2579 // is ignored.
2580 //--------------------------------------------------------------------------
2581 
2582 static void
2583 plform( PLINT axis, PLFLT value, PLINT scale, PLINT prec, char *string, PLINT len, PLBOOL ll, PLBOOL lf, PLBOOL lo )
2584 {
2585  // Check to see if a custom labeling function is defined. If not,
2586  // use default.
2587  if ( lo && plsc->label_func )
2588  {
2589  ( *plsc->label_func )( axis, value, string, len, plsc->label_data );
2590  }
2591  else
2592  {
2593  if ( lo )
2594  {
2595  plwarn( "Custom axis labels requested without a labeling function \
2596  - using default." );
2597  }
2598  if ( ll )
2599  {
2600  // Logarithmic
2601 
2602  if ( lf )
2603  {
2604  // Fixed point, i.e. .1, 1, 10, etc
2605  plP_default_label_log_fixed( axis, value, string, len, NULL );
2606  }
2607  else
2608  {
2609  // Exponential, i.e. 10^-1, 10^0, 10^1, etc
2610  plP_default_label_log( axis, value, string, len, NULL );
2611  }
2612  }
2613  else
2614  {
2615  // Linear
2616  PLINT scale_prec[2] = { scale, prec };
2617  plP_default_label( axis, value, string, len, (void *) scale_prec );
2618  }
2619  }
2620 }
2621 
2622 //--------------------------------------------------------------------------
2623 // plslabelfunc
2624 //
2625 // Formats a PLFLT value in one of the following formats.
2626 //
2627 // label_func - A pointer to a function which will provide a string to use
2628 // as the label for the given floating point value.
2629 // Pass this as NULL to clear the custom function and reset it to
2630 // the default PLplot labeling function.
2631 //
2632 // label_data - Extra data to pass to the label function.
2633 //
2634 // The label_func function arguments are, in order:
2635 //
2636 // axis: PL_X_AXIS, PL_Y_AXIS or PL_Z_AXIS to indicate which axis is being
2637 // labeled
2638 // value: The value at this position on the axis
2639 // string: The resulting label string should be stored here
2640 // data: A pointer to whatever extra information the custom plotting function
2641 // requires
2642 //
2643 //--------------------------------------------------------------------------
2644 void
2645 c_plslabelfunc( void ( *label_func )( PLINT, PLFLT, char *, PLINT, PLPointer ), PLPointer label_data )
2646 {
2647  plsc->label_func = label_func;
2648  plsc->label_data = label_data;
2649 }
2650 
2651 static const char *
2653 {
2654  static const char *esc_strings = { "!\0#\0$\0%\0&\0*\0@\0^\0~\0" };
2655  int d;
2656  // Follow plgesc logic here which is to set the default escape
2657  // if plsc->esc is in its initial state.
2658  if ( plsc->esc == '\0' )
2659  plsc->esc = '#';
2660 
2661  switch ( plsc->esc )
2662  {
2663  case '!':
2664  d = 0;
2665  break;
2666  case '#':
2667  d = 1;
2668  break;
2669  case '$':
2670  d = 2;
2671  break;
2672  case '%':
2673  d = 3;
2674  break;
2675  case '&':
2676  d = 4;
2677  break;
2678  case '*':
2679  d = 5;
2680  break;
2681  case '@':
2682  d = 6;
2683  break;
2684  case '^':
2685  d = 7;
2686  break;
2687  case '~':
2688  d = 8;
2689  break;
2690  default:
2691  plwarn( "plgesc_string: Invalid escape character, assuming '#' instead" );
2692  d = 1;
2693  break;
2694  }
2695  return &( esc_strings[d * 2] );
2696 }