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( PLCHAR_VECTOR opt, PLCHAR_VECTOR 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( PLCHAR_VECTOR opt, PLCHAR_VECTOR 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, PLCHAR_VECTOR text );
51 
52 static void
53 plztx( PLCHAR_VECTOR opt, PLFLT dx, PLFLT dy, PLFLT wx, PLFLT wy1,
54        PLFLT wy2, PLFLT disp, PLFLT pos, PLFLT just, PLCHAR_VECTOR 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( PLCHAR_VECTOR xopt, PLFLT xtick1, PLINT nxsub1,
61           PLCHAR_VECTOR yopt, PLFLT ytick1, PLINT nysub1 );
62 
63 static void
64 label_box( PLCHAR_VECTOR xopt, PLFLT xtick1, PLCHAR_VECTOR 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 PLCHAR_VECTOR
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
c_plbox(PLCHAR_VECTOR xopt,PLFLT xtick,PLINT nxsub,PLCHAR_VECTOR yopt,PLFLT ytick,PLINT nysub)89 c_plbox( PLCHAR_VECTOR xopt, PLFLT xtick, PLINT nxsub,
90          PLCHAR_VECTOR 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
c_plaxes(PLFLT x0,PLFLT y0,PLCHAR_VECTOR xopt,PLFLT xtick,PLINT nxsub,PLCHAR_VECTOR yopt,PLFLT ytick,PLINT nysub)135 c_plaxes( PLFLT x0, PLFLT y0,
136           PLCHAR_VECTOR xopt, PLFLT xtick, PLINT nxsub,
137           PLCHAR_VECTOR 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
c_plbox3(PLCHAR_VECTOR xopt,PLCHAR_VECTOR xlabel,PLFLT xtick,PLINT nxsub,PLCHAR_VECTOR yopt,PLCHAR_VECTOR ylabel,PLFLT ytick,PLINT nysub,PLCHAR_VECTOR zopt,PLCHAR_VECTOR zlabel,PLFLT ztick,PLINT nzsub)593 c_plbox3( PLCHAR_VECTOR xopt, PLCHAR_VECTOR xlabel, PLFLT xtick, PLINT nxsub,
594           PLCHAR_VECTOR yopt, PLCHAR_VECTOR ylabel, PLFLT ytick, PLINT nysub,
595           PLCHAR_VECTOR zopt, PLCHAR_VECTOR 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
plxybx(PLCHAR_VECTOR opt,PLCHAR_VECTOR label,PLINT axis,PLFLT wx1,PLFLT wy1,PLFLT wx2,PLFLT wy2,PLFLT vmin_in,PLFLT vmax_in,PLFLT tick,PLINT nsub,PLINT PL_UNUSED (nolast),PLINT * digits)785 plxybx( PLCHAR_VECTOR opt, PLCHAR_VECTOR 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     PLCHAR_VECTOR esc_string = plgesc_string();
799     PLFLT         tstart, factor;
800     PLCHAR_VECTOR 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
plxytx(PLFLT wx1,PLFLT wy1,PLFLT wx2,PLFLT wy2,PLFLT disp,PLFLT pos,PLFLT just,PLCHAR_VECTOR text)963 plxytx( PLFLT wx1, PLFLT wy1, PLFLT wx2, PLFLT wy2,
964         PLFLT disp, PLFLT pos, PLFLT just, PLCHAR_VECTOR 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
plzbx(PLCHAR_VECTOR opt,PLCHAR_VECTOR label,PLINT right,PLFLT dx,PLFLT dy,PLFLT wx,PLFLT wy1,PLFLT wy2,PLFLT vmin_in,PLFLT vmax_in,PLFLT tick,PLINT nsub,PLINT * digits)1035 plzbx( PLCHAR_VECTOR opt, PLCHAR_VECTOR 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     PLCHAR_VECTOR esc_string = plgesc_string();
1047     PLFLT         tstart, factor;
1048     PLCHAR_VECTOR 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
plztx(PLCHAR_VECTOR opt,PLFLT dx,PLFLT dy,PLFLT wx,PLFLT wy1,PLFLT wy2,PLFLT disp,PLFLT pos,PLFLT just,PLCHAR_VECTOR text)1232 plztx( PLCHAR_VECTOR opt, PLFLT dx, PLFLT dy, PLFLT wx, PLFLT wy1,
1233        PLFLT wy2, PLFLT disp, PLFLT pos, PLFLT just, PLCHAR_VECTOR 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
grid_box(PLCHAR_VECTOR xopt,PLFLT xtick1,PLINT nxsub1,PLCHAR_VECTOR yopt,PLFLT ytick1,PLINT nysub1)1301 grid_box( PLCHAR_VECTOR xopt, PLFLT xtick1, PLINT nxsub1,
1302           PLCHAR_VECTOR 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
label_box(PLCHAR_VECTOR xopt,PLFLT xtick1,PLCHAR_VECTOR yopt,PLFLT ytick1)1432 label_box( PLCHAR_VECTOR xopt, PLFLT xtick1, PLCHAR_VECTOR 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     PLCHAR_VECTOR 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     PLCHAR_VECTOR 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
label_box_custom(PLCHAR_VECTOR xopt,PLINT n_xticks,PLFLT_VECTOR xticks,PLCHAR_VECTOR yopt,PLINT n_yticks,PLFLT_VECTOR yticks)1925 label_box_custom( PLCHAR_VECTOR xopt, PLINT n_xticks, PLFLT_VECTOR xticks, PLCHAR_VECTOR yopt, PLINT n_yticks, PLFLT_VECTOR 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     PLCHAR_VECTOR 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     PLCHAR_VECTOR 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 //--------------------------------------------------------------------------
plP_default_label_log(PLINT PL_UNUSED (axis),PLFLT value,char * string,PLINT len,void * PL_UNUSED (data))2501 void plP_default_label_log( PLINT PL_UNUSED( axis ), PLFLT value, char *string, PLINT len, void * PL_UNUSED( data ) )
2502 {
2503     PLCHAR_VECTOR 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 
plP_default_label_log_fixed(PLINT PL_UNUSED (axis),PLFLT value,char * string,PLINT len,void * PL_UNUSED (data))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 
plP_default_label(PLINT PL_UNUSED (axis),PLFLT value,char * string,PLINT len,void * data)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
plform(PLINT axis,PLFLT value,PLINT scale,PLINT prec,char * string,PLINT len,PLBOOL ll,PLBOOL lf,PLBOOL lo)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
c_plslabelfunc(PLLABEL_FUNC_callback label_func,PLPointer label_data)2645 c_plslabelfunc( PLLABEL_FUNC_callback label_func, PLPointer label_data )
2646 {
2647     plsc->label_func = label_func;
2648     plsc->label_data = label_data;
2649 }
2650 
2651 static PLCHAR_VECTOR
plgesc_string(void)2652 plgesc_string( void )
2653 {
2654     static PLCHAR_VECTOR 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 }
2697