PLplot: plbox.c Source File

PLplot 5.10.0

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

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