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