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