PLplot  5.9.9
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
pllegend.c
Go to the documentation of this file.
1 // $Id: pllegend.c 12343 2013-05-22 22:35:01Z andrewross $
2 // All routines that help to create a discrete legend (pllegend) or
3 // a continuous legend (plcolorbar).
4 //
5 // Copyright (C) 2010-2011 Hezekiah M. Carty
6 // Copyright (C) 2010-2011 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 
29 #include "plplotP.h"
30 
31 //--------------------------------------------------------------------------
46 
47 static void plgvpsp( PLFLT *p_xmin, PLFLT *p_xmax, PLFLT *p_ymin, PLFLT *p_ymax )
48 {
49  if ( plsc->level < 1 )
50  {
51  plabort( "plgvpsp: Please call plinit first" );
52  return;
53  }
54  if ( ( plsc->cursub <= 0 ) || ( plsc->cursub > ( plsc->nsubx * plsc->nsuby ) ) )
55  {
56  plabort( "plgvpsp: Please call pladv or plenv to go to a subpage" );
57  return;
58  }
59  *p_xmin = ( plsc->vpdxmi - plsc->spdxmi ) / ( plsc->spdxma - plsc->spdxmi );
60  *p_xmax = ( plsc->vpdxma - plsc->spdxmi ) / ( plsc->spdxma - plsc->spdxmi );
61  *p_ymin = ( plsc->vpdymi - plsc->spdymi ) / ( plsc->spdyma - plsc->spdymi );
62  *p_ymax = ( plsc->vpdyma - plsc->spdymi ) / ( plsc->spdyma - plsc->spdymi );
63 }
64 
65 //--------------------------------------------------------------------------
87 
88 static void legend_position( PLINT position, PLFLT legend_width, PLFLT legend_height,
89  PLFLT *x_legend_position, PLFLT *y_legend_position,
90  PLFLT *xsign, PLFLT *ysign )
91 {
92  // xorigin, yorigin, xlegend, and ylegend are all calculated for
93  // one of the 16 standard positions specified by position and are
94  // expressed in adopted coordinates. xorigin is the X value of
95  // the reference point of the adopted coordinates. yorigin is the
96  // Y value of the reference point of the adopted coordinates.
97  // xlegend is the X coordinate of the top-left of the legend box
98  // relative to the legend box reference point. ylegend is the y
99  // coordinate of the top-left of the legend box relative to the
100  // legend box reference point.
101 
102  PLFLT xorigin = 0.0, yorigin = 0.0, xlegend = 0.0, ylegend = 0.0;
103  // By default the sign of the x and y offsets is positive.
104  *xsign = 1.;
105  *ysign = 1.;
106  if ( position & PL_POSITION_RIGHT )
107  {
108  xorigin = 1.;
109  if ( position & PL_POSITION_TOP )
110  {
111  yorigin = 1.;
112  if ( position & PL_POSITION_INSIDE )
113  {
114  xlegend = -legend_width;
115  ylegend = 0.;
116  *xsign = -1.;
117  *ysign = -1.;
118  }
119  else if ( position & PL_POSITION_OUTSIDE )
120  {
121  xlegend = 0.;
122  ylegend = legend_height;
123  }
124  else
125  {
126  plexit( "legend_position: internal logic error 1" );
127  }
128  }
129  else if ( !( position & PL_POSITION_TOP ) && !( position & PL_POSITION_BOTTOM ) )
130  {
131  yorigin = 0.5;
132  ylegend = 0.5 * legend_height;
133  if ( position & PL_POSITION_INSIDE )
134  {
135  xlegend = -legend_width;
136  *xsign = -1.;
137  }
138  else if ( position & PL_POSITION_OUTSIDE )
139  {
140  xlegend = 0.;
141  }
142  else
143  {
144  plexit( "legend_position: internal logic error 2" );
145  }
146  }
147  else if ( position & PL_POSITION_BOTTOM )
148  {
149  yorigin = 0.;
150  if ( position & PL_POSITION_INSIDE )
151  {
152  xlegend = -legend_width;
153  ylegend = legend_height;
154  *xsign = -1.;
155  }
156  else if ( position & PL_POSITION_OUTSIDE )
157  {
158  xlegend = 0.;
159  ylegend = 0.;
160  *ysign = -1.;
161  }
162  else
163  {
164  plexit( "legend_position: internal logic error 3" );
165  }
166  }
167  else
168  {
169  plexit( "legend_position: internal logic error 4" );
170  }
171  }
172  else if ( !( position & PL_POSITION_RIGHT ) && !( position & PL_POSITION_LEFT ) )
173  {
174  xorigin = 0.5;
175  xlegend = -0.5 * legend_width;
176  if ( position & PL_POSITION_TOP )
177  {
178  yorigin = 1.;
179  if ( position & PL_POSITION_INSIDE )
180  {
181  ylegend = 0.;
182  *ysign = -1.;
183  }
184  else if ( position & PL_POSITION_OUTSIDE )
185  {
186  ylegend = legend_height;
187  }
188  else
189  {
190  plexit( "legend_position: internal logic error 5" );
191  }
192  }
193  else if ( position & PL_POSITION_BOTTOM )
194  {
195  yorigin = 0.;
196  if ( position & PL_POSITION_INSIDE )
197  {
198  ylegend = legend_height;
199  }
200  else if ( position & PL_POSITION_OUTSIDE )
201  {
202  ylegend = 0.;
203  *ysign = -1.;
204  }
205  else
206  {
207  plexit( "legend_position: internal logic error 6" );
208  }
209  }
210  else
211  {
212  plexit( "legend_position: internal logic error 7" );
213  }
214  }
215  else if ( position & PL_POSITION_LEFT )
216  {
217  xorigin = 0.;
218  if ( position & PL_POSITION_TOP )
219  {
220  yorigin = 1.;
221  if ( position & PL_POSITION_INSIDE )
222  {
223  xlegend = 0.;
224  ylegend = 0.;
225  *ysign = -1.;
226  }
227  else if ( position & PL_POSITION_OUTSIDE )
228  {
229  xlegend = -legend_width;
230  ylegend = legend_height;
231  *xsign = -1.;
232  }
233  else
234  {
235  plexit( "legend_position: internal logic error 8" );
236  }
237  }
238  else if ( !( position & PL_POSITION_TOP ) && !( position & PL_POSITION_BOTTOM ) )
239  {
240  yorigin = 0.5;
241  ylegend = 0.5 * legend_height;
242  if ( position & PL_POSITION_INSIDE )
243  {
244  xlegend = 0.;
245  }
246  else if ( position & PL_POSITION_OUTSIDE )
247  {
248  xlegend = -legend_width;
249  *xsign = -1.;
250  }
251  else
252  {
253  plexit( "legend_position: internal logic error 9" );
254  }
255  }
256  else if ( position & PL_POSITION_BOTTOM )
257  {
258  yorigin = 0.;
259  if ( position & PL_POSITION_INSIDE )
260  {
261  ylegend = legend_height;
262  xlegend = 0.;
263  }
264  else if ( position & PL_POSITION_OUTSIDE )
265  {
266  xlegend = -legend_width;
267  ylegend = 0.;
268  *xsign = -1.;
269  *ysign = -1.;
270  }
271  else
272  {
273  plexit( "legend_position: internal logic error 10" );
274  }
275  }
276  else
277  {
278  plexit( "legend_position: internal logic error 11" );
279  }
280  }
281  else
282  {
283  plexit( "legend_position: internal logic error 12" );
284  }
285  *x_legend_position = xorigin + xlegend;
286  *y_legend_position = yorigin + ylegend;
287 }
288 
289 //--------------------------------------------------------------------------
295 
296 static void get_subpage_per_mm( PLFLT *x_subpage_per_mm, PLFLT *y_subpage_per_mm )
297 {
298  // Size of subpage in mm
299  PLFLT mxmin, mxmax, mymin, mymax;
300  plgspa( &mxmin, &mxmax, &mymin, &mymax );
301  *x_subpage_per_mm = 1. / ( mxmax - mxmin );
302  *y_subpage_per_mm = 1. / ( mymax - mymin );
303 }
304 
305 //--------------------------------------------------------------------------
312 
314 {
315  // Character height in mm
316  PLFLT default_mm, char_height_mm;
317  PLFLT x_subpage_per_mm, y_subpage_per_mm;
318 
319  if ( ifcharacter )
320  {
321  plgchr( &default_mm, &char_height_mm );
322  }
323  else
324  {
325  default_mm = plsc->symdef;
326  char_height_mm = plsc->symht;
327  }
328  get_subpage_per_mm( &x_subpage_per_mm, &y_subpage_per_mm );
329  return ( char_height_mm * y_subpage_per_mm );
330 }
331 
332 //--------------------------------------------------------------------------
344 
345 #define adopted_to_subpage_x( nx ) ( ( xdmin_adopted ) + ( nx ) * ( ( xdmax_adopted ) - ( xdmin_adopted ) ) )
346 
347 //--------------------------------------------------------------------------
359 
360 #define subpage_to_adopted_x( nx ) ( ( nx - xdmin_adopted ) / ( ( xdmax_adopted ) - ( xdmin_adopted ) ) )
361 
362 //--------------------------------------------------------------------------
374 
375 #define adopted_to_subpage_y( ny ) ( ( ydmin_adopted ) + ( ny ) * ( ( ydmax_adopted ) - ( ydmin_adopted ) ) )
376 
377 //--------------------------------------------------------------------------
389 
390 #define subpage_to_adopted_y( ny ) ( ( ny - ydmin_adopted ) / ( ( ydmax_adopted ) - ( ydmin_adopted ) ) )
391 
392 //--------------------------------------------------------------------------
526 
527 void
528 c_pllegend( PLFLT *p_legend_width, PLFLT *p_legend_height,
529  PLINT opt, PLINT position, PLFLT x, PLFLT y, PLFLT plot_width,
531  PLINT nrow, PLINT ncolumn,
532  PLINT nlegend, const PLINT *opt_array,
533  PLFLT text_offset, PLFLT text_scale, PLFLT text_spacing,
534  PLFLT text_justification,
535  const PLINT *text_colors, const char * const *text,
536  const PLINT *box_colors, const PLINT *box_patterns,
537  const PLFLT *box_scales, const PLFLT *box_line_widths,
538  const PLINT *line_colors, const PLINT *line_styles,
539  const PLFLT *line_widths,
540  const PLINT *symbol_colors, const PLFLT *symbol_scales,
541  const PLINT *symbol_numbers, const char * const *symbols )
542 
543 {
544  // Legend position
545  PLFLT plot_x, plot_x_end, plot_x_subpage, plot_x_end_subpage;
546  PLFLT plot_y, plot_y_subpage;
547  PLFLT text_x, text_y, text_x_subpage, text_y_subpage;
548  // Character height (normalized subpage coordinates)
549  PLFLT character_height, character_width, symbol_width = 0.0;
550  // x, y-position of the current legend entry
551  PLFLT ty, xshift, drow, dcolumn;
552  // Positions of the legend entries
553  PLFLT dxs, *xs = NULL, *ys = NULL, xl[2], yl[2], xbox[4], ybox[4];
554  PLINT i, j;
555  // Active attributes to be saved and restored afterward.
556  PLINT col0_save = plsc->icol0,
557  line_style_save = plsc->line_style,
558  pattern_save = plsc->patt;
559  PLFLT line_width_save = plsc->width;
560  PLFLT text_scale_save = plsc->chrht / plsc->chrdef;
561  // Saved external world coordinates of viewport.
562  PLFLT xwmin_save, xwmax_save, ywmin_save, ywmax_save;
563  // Saved external normalized coordinates of viewport.
564  // (These are actual values used only for the restore.)
565  PLFLT xdmin_save = 0.0, xdmax_save = 0.0, ydmin_save = 0.0, ydmax_save = 0.0;
566  // Limits of adopted coordinates used to calculate all coordinate
567  // transformations.
568  PLFLT xdmin_adopted = 0.0, xdmax_adopted = 0.0, ydmin_adopted = 0.0, ydmax_adopted = 0.0;
569 
570  PLFLT x_subpage_per_mm, y_subpage_per_mm, text_width0 = 0., text_width;
571  PLFLT width_border, column_separation,
572  legend_width, legend_height, legend_width_ac, legend_height_ac;
573  PLFLT x_legend_position, y_legend_position, xsign, ysign;
574 
575  //PLINT some_boxes = 0, some_lines = 0;
576  PLINT some_symbols = 0;
577  PLINT max_symbol_numbers = 0;
578  PLINT irow = 0, icolumn = 0;
579 
580  // Default nrow, ncolumn.
581  nrow = MAX( nrow, 1 );
582  ncolumn = MAX( ncolumn, 1 );
583  if ( nrow * ncolumn < nlegend )
584  {
585  // Make smaller one large enough to accomodate nlegend.
586  if ( ncolumn < nrow )
587  ncolumn = ( nlegend % nrow ) ? ( nlegend / nrow ) + 1 : nlegend / nrow;
588  else
589  nrow = ( nlegend % ncolumn ) ? ( nlegend / ncolumn ) + 1 : nlegend / ncolumn;
590  }
591  // fprintf(stdout, "nrow, ncolumn = %d, %d\n", nrow, ncolumn);
592 
593  // Default position flags and sanity checks for position flags.
594  if ( !( position & PL_POSITION_RIGHT ) && !( position & PL_POSITION_LEFT ) && !( position & PL_POSITION_TOP ) && !( position & PL_POSITION_BOTTOM ) )
595  {
596  position = position | PL_POSITION_RIGHT | PL_POSITION_TOP;
597  }
598  else if ( ( position & PL_POSITION_RIGHT ) && ( position & PL_POSITION_LEFT ) )
599  {
600  plabort( "pllegend: PL_POSITION_RIGHT and PL_POSITION_LEFT cannot be simultaneously set." );
601  return;
602  }
603 
604  else if ( ( position & PL_POSITION_TOP ) && ( position & PL_POSITION_BOTTOM ) )
605  {
606  plabort( "pllegend: PL_POSITION_TOP and PL_POSITION_BOTTOM cannot be simultaneously set." );
607  return;
608  }
609 
610  if ( !( position & PL_POSITION_INSIDE ) && !( position & PL_POSITION_OUTSIDE ) )
611  {
612  position = position | PL_POSITION_INSIDE;
613  }
614  else if ( ( position & PL_POSITION_INSIDE ) && ( position & PL_POSITION_OUTSIDE ) )
615  {
616  plabort( "pllegend: PL_POSITION_INSIDE and PL_POSITION_OUTSIDE cannot be simultaneously set." );
617  return;
618  }
619 
620  if ( !( position & PL_POSITION_VIEWPORT ) && !( position & PL_POSITION_SUBPAGE ) )
621  {
622  position = position | PL_POSITION_VIEWPORT;
623  }
624  else if ( ( position & PL_POSITION_VIEWPORT ) && ( position & PL_POSITION_SUBPAGE ) )
625  {
626  plabort( "pllegend: PL_POSITION_VIEWPORT and PL_POSITION_SUBPAGE cannot be simultaneously set." );
627  return;
628  }
629 
630  // xdmin_save, etc., are the actual external relative viewport
631  // coordinates within the current sub-page used only for
632  // restoration at the end.
633  plgvpsp( &xdmin_save, &xdmax_save, &ydmin_save, &ydmax_save );
634 
635  // Choose adopted coordinates.
636  if ( position & PL_POSITION_SUBPAGE )
637  plvpor( 0., 1., 0., 1. );
638 
639  // xdmin_adopted, etc., are the adopted limits of the coordinates
640  // within the current sub-page used for all coordinate
641  // transformations.
642  // If position & PL_POSITION_VIEWPORT is true, these limits
643  // are the external relative viewport limits.
644  // If position & PL_POSITION_SUBPAGE is true, these
645  // coordinates are the relative subpage coordinates.
646  plgvpsp( &xdmin_adopted, &xdmax_adopted, &ydmin_adopted, &ydmax_adopted );
647 
648  // xwmin_save, etc., are the external world coordinates corresponding
649  // to the external viewport boundaries.
650  plgvpw( &xwmin_save, &xwmax_save, &ywmin_save, &ywmax_save );
651 
652  // Internal viewport corresponds to sub-page so that all legends will
653  // be clipped at sub-page boundaries.
654  plvpor( 0., 1., 0., 1. );
655 
656  // Internal world coordinates are the same as normalized internal
657  // viewport coordinates which are the same as normalized subpage coordinates.
658  plwind( 0., 1., 0., 1. );
659 
660  for ( i = 0; i < nlegend; i++ )
661  {
662  //if ( opt_array[i] & PL_LEGEND_COLOR_BOX )
663  // some_boxes = 1;
664  //if ( opt_array[i] & PL_LEGEND_LINE )
665  // some_lines = 1;
666  if ( opt_array[i] & PL_LEGEND_SYMBOL )
667  {
668  max_symbol_numbers = MAX( max_symbol_numbers, symbol_numbers[i] );
669  some_symbols = 1;
670  }
671  }
672 
673  // Get character height and width in normalized subpage coordinates.
674  character_height = get_character_or_symbol_height( TRUE );
675  character_width = character_height;
676 
677  // Calculate maximum width of text area.
678  plschr( 0., text_scale );
679  for ( i = 0; i < nlegend; i++ )
680  {
681  // units are mm.
682  text_width0 = MAX( text_width0, plstrl( text[i] ) );
683  }
684  get_subpage_per_mm( &x_subpage_per_mm, &y_subpage_per_mm );
685 
686  // units are normalized subpage coordinates.
687  text_width0 = x_subpage_per_mm * text_width0;
688 
689  // Allow gap on end closest to legend plot.
690  text_width = text_width0 + text_offset * character_width;
691 
692  // Allow small border area where only the background is plotted
693  // for left and right of legend. 0.4 seems to be a reasonable factor
694  // that gives a good-looking result.
695  width_border = 0.4 * character_width;
696  // Separate columns (if any) by 2.0 * character_width.
697  column_separation = 2.0 * character_width;
698 
699  // Total width and height of legend area in normalized subpage coordinates.
700  legend_width = 2. * width_border + ( ncolumn - 1 ) * column_separation +
701  ncolumn * ( text_width +
702  adopted_to_subpage_x( plot_width ) - adopted_to_subpage_x( 0. ) );
703  legend_height = nrow * text_spacing * character_height;
704 
705  // Total width and height of legend area in adopted coordinates.
706 
707  legend_width_ac = subpage_to_adopted_x( legend_width ) - subpage_to_adopted_x( 0. );
708  legend_height_ac = subpage_to_adopted_y( legend_height ) - subpage_to_adopted_y( 0. );
709  *p_legend_width = legend_width_ac;
710  *p_legend_height = legend_height_ac;
711 
712  // dcolumn is the spacing from one column to the next and
713  // drow is the spacing from one row to the next.
714  dcolumn = column_separation + text_width +
715  adopted_to_subpage_x( plot_width ) - adopted_to_subpage_x( 0. );
716  drow = text_spacing * character_height;
717 
718  legend_position( position, legend_width_ac, legend_height_ac, &x_legend_position, &y_legend_position, &xsign, &ysign );
719  plot_x = x * xsign + x_legend_position;
720  plot_y = y * ysign + y_legend_position;
721  plot_x_end = plot_x + plot_width;
722  // Normalized subpage coordinates for legend plots
723  plot_x_subpage = adopted_to_subpage_x( plot_x );
724  plot_y_subpage = adopted_to_subpage_y( plot_y );
725  plot_x_end_subpage = adopted_to_subpage_x( plot_x_end );
726 
727  // Get normalized subpage positions of the start of the legend text
728  text_x = plot_x_end;
729  text_y = plot_y;
730  text_x_subpage = adopted_to_subpage_x( text_x ) +
731  text_offset * character_width;
732  text_y_subpage = adopted_to_subpage_y( text_y );
733 
734  if ( opt & PL_LEGEND_BACKGROUND )
735  {
736  PLFLT xbg[4] = {
737  plot_x_subpage,
738  plot_x_subpage,
739  plot_x_subpage + legend_width,
740  plot_x_subpage + legend_width,
741  };
742  PLFLT ybg[4] = {
743  plot_y_subpage,
744  plot_y_subpage - legend_height,
745  plot_y_subpage - legend_height,
746  plot_y_subpage,
747  };
748  plpsty( 0 );
749  plcol0( bg_color );
750  plfill( 4, xbg, ybg );
751  plcol0( col0_save );
752  }
753 
754  if ( opt & PL_LEGEND_BOUNDING_BOX )
755  {
756  PLFLT xbb[5] = {
757  plot_x_subpage,
758  plot_x_subpage,
759  plot_x_subpage + legend_width,
760  plot_x_subpage + legend_width,
761  plot_x_subpage,
762  };
763  PLFLT ybb[5] = {
764  plot_y_subpage,
765  plot_y_subpage - legend_height,
766  plot_y_subpage - legend_height,
767  plot_y_subpage,
768  plot_y_subpage,
769  };
770  pllsty( bb_style );
771  plcol0( bb_color );
772  plline( 5, xbb, ybb );
773  plcol0( col0_save );
774  pllsty( line_style_save );
775  }
776 
777  if ( opt & PL_LEGEND_TEXT_LEFT )
778  {
779  // text area on left, plot area on right.
780  text_x_subpage = plot_x_subpage;
781  plot_x_subpage += text_width;
782  plot_x_end_subpage += text_width;
783  }
784  // adjust border after background is drawn.
785  plot_x_subpage += width_border;
786  plot_x_end_subpage += width_border;
787  text_x_subpage += width_border;
788 
789  if ( some_symbols )
790  {
791  max_symbol_numbers = MAX( 2, max_symbol_numbers );
792  if ( ( ( xs = (PLFLT *) malloc( (size_t) max_symbol_numbers * sizeof ( PLFLT ) ) ) == NULL ) ||
793  ( ( ys = (PLFLT *) malloc( (size_t) max_symbol_numbers * sizeof ( PLFLT ) ) ) == NULL ) )
794  {
795  plexit( "pllegend: Insufficient memory" );
796  }
797 
798  // Get symbol width in normalized subpage coordinates if symbols are plotted to
799  // adjust ends of line of symbols.
800  // AWI, no idea why must use 0.5 factor to get ends of symbol lines
801  // to line up approximately correctly with plotted legend lines.
802  // Factor should be unity.
803  symbol_width = 0.5 * get_character_or_symbol_height( TRUE );
804  }
805 
806  // Draw each legend entry
807  for ( i = 0; i < nlegend; i++ )
808  {
809  // y position of text, lines, symbols, and/or centre of cmap0 box.
810  ty = text_y_subpage - ( (double) irow + 0.5 ) * drow;
811  xshift = (double) icolumn * dcolumn;
812  // Label/name for the legend
813  plcol0( text_colors[i] );
814  plschr( 0., text_scale );
815  plptex( text_x_subpage + xshift + text_justification * text_width0, ty, 0.1, 0.0, text_justification, text[i] );
816 
817  if ( !( opt_array[i] & PL_LEGEND_NONE ) )
818  {
819  if ( opt_array[i] & PL_LEGEND_COLOR_BOX )
820  {
821  plcol0( box_colors[i] );
822  plpsty( box_patterns[i] );
823  plwidth( box_line_widths[i] );
824  xbox[0] = plot_x_subpage + xshift;
825  xbox[1] = xbox[0];
826  xbox[2] = plot_x_end_subpage + xshift;
827  xbox[3] = xbox[2];
828  ybox[0] = ty + 0.5 * drow * box_scales[i];
829  ybox[1] = ty - 0.5 * drow * box_scales[i];
830  ybox[2] = ty - 0.5 * drow * box_scales[i];
831  ybox[3] = ty + 0.5 * drow * box_scales[i];
832  plfill( 4, xbox, ybox );
833  pllsty( line_style_save );
834  plwidth( line_width_save );
835  }
836  if ( opt_array[i] & PL_LEGEND_LINE )
837  {
838  plcol0( line_colors[i] );
839  pllsty( line_styles[i] );
840  plwidth( line_widths[i] );
841  xl[0] = plot_x_subpage + xshift;
842  xl[1] = plot_x_end_subpage + xshift;
843  yl[0] = ty;
844  yl[1] = ty;
845  plline( 2, xl, yl );
846  pllsty( line_style_save );
847  plwidth( line_width_save );
848  }
849 
850  if ( opt_array[i] & PL_LEGEND_SYMBOL )
851  {
852  plcol0( symbol_colors[i] );
853  plschr( 0., symbol_scales[i] );
854  dxs = ( plot_x_end_subpage - plot_x_subpage - symbol_width ) / (double) ( MAX( symbol_numbers[i], 2 ) - 1 );
855  for ( j = 0; j < symbol_numbers[i]; j++ )
856  {
857  xs[j] = plot_x_subpage + xshift +
858  0.5 * symbol_width + dxs * (double) j;
859  ys[j] = ty;
860  }
861  plstring( symbol_numbers[i], xs, ys, symbols[i] );
862  }
863  }
864 
865  // Set irow, icolumn for next i value.
866  if ( opt & PL_LEGEND_ROW_MAJOR )
867  {
868  icolumn++;
869  if ( icolumn >= ncolumn )
870  {
871  icolumn = 0;
872  irow++;
873  }
874  }
875  else
876  {
877  irow++;
878  if ( irow >= nrow )
879  {
880  irow = 0;
881  icolumn++;
882  }
883  }
884  }
885  if ( some_symbols )
886  {
887  free( xs );
888  free( ys );
889  }
890 
891  // Restore
892  plcol0( col0_save );
893  plschr( 0., text_scale_save );
894  plpsty( pattern_save );
895  plvpor( xdmin_save, xdmax_save, ydmin_save, ydmax_save );
896  plwind( xwmin_save, xwmax_save, ywmin_save, ywmax_save );
897 
898  return;
899 }
900 
901 //--------------------------------------------------------------------------
908 
909 static void
910 remove_characters( char *string, const char *characters )
911 {
912  size_t length = strlen( string );
913  size_t prefix_length = strcspn( string, characters );
914  if ( prefix_length < length )
915  {
916  // Remove first matching character by shifting tail of string
917  // (including null-terminator) down by one.
918  memmove( string + prefix_length, string + prefix_length + 1, length - prefix_length );
919  // Recurse to remove any remaining specified characters.
920  remove_characters( string, characters );
921  }
922 }
923 
924 //--------------------------------------------------------------------------
944 
945 static void
946 draw_cap( PLBOOL if_edge, PLINT orientation, PLFLT xmin, PLFLT xmax,
947  PLFLT ymin, PLFLT ymax, PLFLT color )
948 {
949  // Save current drawing color.
950  PLINT col0_save = plsc->icol0;
951  PLFLT xhalf = 0.5 * ( xmin + xmax );
952  PLFLT yhalf = 0.5 * ( ymin + ymax );
953 
954  // World coordinates for the triangle. Due to setup in the
955  // plcolorbar routine that calls this, these are also normalized
956  // subpage coordinates.
957  PLFLT xs[3];
958  PLFLT ys[3];
959 
960  if ( orientation == PL_COLORBAR_ORIENT_RIGHT )
961  {
962  xs[0] = xmin;
963  ys[0] = ymin;
964  xs[1] = xmax;
965  ys[1] = yhalf;
966  xs[2] = xmin;
967  ys[2] = ymax;
968  }
969  else if ( orientation == PL_COLORBAR_ORIENT_TOP )
970  {
971  xs[0] = xmax;
972  ys[0] = ymin;
973  xs[1] = xhalf;
974  ys[1] = ymax;
975  xs[2] = xmin;
976  ys[2] = ymin;
977  }
978  else if ( orientation == PL_COLORBAR_ORIENT_LEFT )
979  {
980  xs[0] = xmax;
981  ys[0] = ymax;
982  xs[1] = xmin;
983  ys[1] = yhalf;
984  xs[2] = xmax;
985  ys[2] = ymin;
986  }
987  else if ( orientation == PL_COLORBAR_ORIENT_BOTTOM )
988  {
989  xs[0] = xmin;
990  ys[0] = ymax;
991  xs[1] = xhalf;
992  ys[1] = ymin;
993  xs[2] = xmax;
994  ys[2] = ymax;
995  }
996  else
997  {
998  plexit( "draw_cap: internal error. Incorrect orientation" );
999  }
1000 
1001  plcol1( color );
1002  plfill( 3, xs, ys );
1003  // Restore the drawing color
1004  plcol0( col0_save );
1005 
1006  // Draw cap outline
1007  if ( if_edge )
1008  plline( 3, xs, ys );
1009 }
1010 
1011 //--------------------------------------------------------------------------
1033 
1034 static void
1035 draw_box( PLBOOL if_bb, PLINT opt, const char *axis_opts, PLBOOL if_edge,
1036  PLFLT ticks, PLINT sub_ticks, PLINT n_values, const PLFLT *values )
1037 {
1038  // axis option strings.
1039  const char *edge_string;
1040  size_t length_axis_opts = strlen( axis_opts );
1041  char *local_axis_opts;
1042 
1043  // local_axis_opts is local version that can be modified from
1044  // const input version.
1045  if ( ( local_axis_opts = (char *) malloc( ( length_axis_opts + 1 ) * sizeof ( char ) ) ) == NULL )
1046  {
1047  plexit( "draw_box: Insufficient memory" );
1048  }
1049  strcpy( local_axis_opts, axis_opts );
1050 
1051  plsc->if_boxbb = if_bb;
1052  // Draw numerical labels and tick marks if this is a shade color bar
1053  // TODO: A better way to handle this would be to update the
1054  // internals of plbox to support custom tick and label positions
1055  // along an axis.
1056 
1057  if ( opt & PL_COLORBAR_SHADE && opt & PL_COLORBAR_SHADE_LABEL )
1058  {
1060  label_box_custom( local_axis_opts, n_values, values, "", 0, NULL );
1061  else
1062  label_box_custom( "", 0, NULL, local_axis_opts, n_values, values );
1063  if ( if_bb )
1064  {
1065  plsc->if_boxbb = FALSE;
1066  free( local_axis_opts );
1067  return;
1068  }
1069  // Exclude ticks for plbox call below since those tick marks and
1070  // associated labels have already been handled in a custom way above.
1071  remove_characters( local_axis_opts, "TtXx" );
1072  }
1073 
1074  // Draw the outline for the entire colorbar, tick marks, tick labels.
1075 
1076  if ( if_edge )
1077  edge_string = "bc";
1078  else
1079  edge_string = "uw";
1081  {
1082  plbox( edge_string, 0.0, 0, local_axis_opts, ticks, sub_ticks );
1083  }
1084  else
1085  {
1086  plbox( local_axis_opts, ticks, sub_ticks, edge_string, 0.0, 0 );
1087  }
1088  plsc->if_boxbb = FALSE;
1089 
1090  free( local_axis_opts );
1091 }
1092 
1093 //--------------------------------------------------------------------------
1112 
1113 static void
1114 draw_label( PLBOOL if_bb, PLINT opt, const char *label )
1115 {
1116  // Justification of label text
1117  PLFLT just = 0.0;
1118 
1119  // How far away from the axis should the label be drawn in units of
1120  // the character height?
1121  PLFLT label_offset = 1.2;
1122 
1123  // For building plmtex option string.
1124 #define max_opts 25
1125  char opt_label[max_opts];
1126  char perp;
1127 
1128  // To help sanity check number of specified labels.
1129  PLINT nlabel = 0;
1130 
1131  // aspect ratio of physical area of subpage.
1132  //PLFLT aspspp = ( ( plsc->sppxma - plsc->sppxmi ) / plsc->xpmm ) /
1133  // ( ( plsc->sppyma - plsc->sppymi ) / plsc->ypmm );
1134 
1135  // Character height in y and x normalized subpage coordinates.
1136  //PLFLT character_height_y = get_character_or_symbol_height( TRUE );
1137  // character height _in normalized subpage coordinates_ is smaller
1138  // in the x direction if the subpage aspect ratio is larger than one.
1139  //PLFLT character_height_x = character_height_y / aspspp;
1140 
1141  // Ratio of normalized subpage coordinates to mm coordinates in
1142  // x and y.
1143  //PLFLT spxpmm = plsc->xpmm / ( plsc->sppxma - plsc->sppxmi );
1144  //PLFLT spypmm = plsc->ypmm / ( plsc->sppyma - plsc->sppymi );
1145  PLFLT label_length_mm = plstrl( label );
1146 
1147  PLFLT parallel_height_mm = 0.0, perpendicular_height_mm = 0.0,
1148  default_mm, char_height_mm;
1149 
1150  // Only honor first bit in list of
1151  // PL_COLORBAR_LABEL_(RIGHT|TOP|LEFT|BOTTOM).
1152  if ( opt & PL_COLORBAR_LABEL_RIGHT )
1153  {
1154  nlabel = 1;
1155  }
1156  if ( opt & PL_COLORBAR_LABEL_TOP )
1157  {
1158  if ( nlabel == 1 )
1159  opt = opt & ~PL_COLORBAR_LABEL_TOP;
1160  else
1161  nlabel = 1;
1162  }
1163  if ( opt & PL_COLORBAR_LABEL_LEFT )
1164  {
1165  if ( nlabel == 1 )
1166  opt = opt & ~PL_COLORBAR_LABEL_LEFT;
1167  else
1168  nlabel = 1;
1169  }
1170  if ( opt & PL_COLORBAR_LABEL_BOTTOM )
1171  {
1172  if ( nlabel == 1 )
1173  opt = opt & ~PL_COLORBAR_LABEL_BOTTOM;
1174  else
1175  nlabel = 1;
1176  }
1177  // If no label wanted, then return.
1178  if ( nlabel == 0 )
1179  return;
1180 
1181  plgchr( &default_mm, &char_height_mm );
1182 
1183  // Start preparing data to help plot the label or
1184  // calculate the corresponding bounding box changes.
1185 
1186  if ( if_bb )
1187  {
1188  // Bounding-box limits are the viewport limits in mm before corrections
1189  // for decorations are applied.
1190  plsc->boxbb_xmin = plsc->vppxmi / plsc->xpmm;
1191  plsc->boxbb_xmax = plsc->vppxma / plsc->xpmm;
1192  plsc->boxbb_ymin = plsc->vppymi / plsc->ypmm;
1193  plsc->boxbb_ymax = plsc->vppyma / plsc->ypmm;
1194 
1195  // For labels written parallel to axis, label_offset of zero
1196  // corresponds to character centred on edge so should add 0.5 to
1197  // height to obtain bounding box edge in direction away from
1198  // edge. However, experimentally found 0.8 gave a better
1199  // looking result.
1200  parallel_height_mm = ( label_offset + 0.8 ) * char_height_mm;
1201 
1202  // For labels written perpendicular to axis, label_offset of
1203  // zero corresponds to a character whose edge just touches the
1204  // edge of the box so should add 0. to label_offset (corrected
1205  // by -0.5 below) to obtain bounding box edge in direction away
1206  // from edge, and that value apparently works.
1207  perpendicular_height_mm = ( label_offset - 0.5 + 0.0 ) * char_height_mm;
1208  }
1209  if ( opt & PL_COLORBAR_LABEL_LEFT )
1210  {
1212  {
1213  perp = '\0';
1214  just = 0.5;
1215  if ( if_bb )
1216  {
1217  plsc->boxbb_xmin = MIN( plsc->boxbb_xmin, plsc->vppxmi /
1218  plsc->xpmm - parallel_height_mm );
1219  plsc->boxbb_ymin = MIN( plsc->boxbb_ymin,
1220  0.5 * ( plsc->vppymi + plsc->vppyma ) /
1221  plsc->ypmm - 0.5 * label_length_mm );
1222  plsc->boxbb_ymax = MAX( plsc->boxbb_ymax,
1223  0.5 * ( plsc->vppymi + plsc->vppyma ) /
1224  plsc->ypmm + 0.5 * label_length_mm );
1225  }
1226  }
1227  else
1228  {
1229  perp = 'v';
1230  just = 1.0;
1231  label_offset -= 0.5;
1232  if ( if_bb )
1233  {
1234  plsc->boxbb_xmin = MIN( plsc->boxbb_xmin, plsc->vppxmi /
1235  plsc->xpmm - perpendicular_height_mm - label_length_mm );
1236  }
1237  }
1238  snprintf( opt_label, max_opts, "l%c", perp );
1239  }
1240  else if ( opt & PL_COLORBAR_LABEL_RIGHT )
1241  {
1243  {
1244  perp = '\0';
1245  just = 0.5;
1246  if ( if_bb )
1247  {
1248  plsc->boxbb_xmax = MAX( plsc->boxbb_xmax, plsc->vppxma /
1249  plsc->xpmm + parallel_height_mm );
1250  plsc->boxbb_ymin = MIN( plsc->boxbb_ymin,
1251  0.5 * ( plsc->vppymi + plsc->vppyma ) /
1252  plsc->ypmm - 0.5 * label_length_mm );
1253  plsc->boxbb_ymax = MAX( plsc->boxbb_ymax,
1254  0.5 * ( plsc->vppymi + plsc->vppyma ) /
1255  plsc->ypmm + 0.5 * label_length_mm );
1256  }
1257  }
1258  else
1259  {
1260  perp = 'v';
1261  just = 0.0;
1262  label_offset -= 0.5;
1263  if ( if_bb )
1264  {
1265  plsc->boxbb_xmax = MAX( plsc->boxbb_xmax, plsc->vppxma /
1266  plsc->xpmm + perpendicular_height_mm + label_length_mm );
1267  }
1268  }
1269  snprintf( opt_label, max_opts, "r%c", perp );
1270  }
1271  else if ( opt & PL_COLORBAR_LABEL_TOP )
1272  {
1273  perp = '\0';
1274  just = 0.5;
1275  snprintf( opt_label, max_opts, "t%c", perp );
1276  if ( if_bb )
1277  {
1278  plsc->boxbb_ymax = MAX( plsc->boxbb_ymax, plsc->vppyma /
1279  plsc->ypmm + parallel_height_mm );
1280  plsc->boxbb_xmin = MIN( plsc->boxbb_xmin,
1281  0.5 * ( plsc->vppxmi + plsc->vppxma ) /
1282  plsc->xpmm - 0.5 * label_length_mm );
1283  plsc->boxbb_xmax = MAX( plsc->boxbb_xmax,
1284  0.5 * ( plsc->vppxmi + plsc->vppxma ) /
1285  plsc->xpmm + 0.5 * label_length_mm );
1286  }
1287  }
1288  else if ( opt & PL_COLORBAR_LABEL_BOTTOM )
1289  {
1290  perp = '\0';
1291  just = 0.5;
1292  snprintf( opt_label, max_opts, "b%c", perp );
1293  if ( if_bb )
1294  {
1295  plsc->boxbb_ymin = MIN( plsc->boxbb_ymin, plsc->vppymi /
1296  plsc->ypmm - parallel_height_mm );
1297  plsc->boxbb_xmin = MIN( plsc->boxbb_xmin,
1298  0.5 * ( plsc->vppxmi + plsc->vppxma ) /
1299  plsc->xpmm - 0.5 * label_length_mm );
1300  plsc->boxbb_xmax = MAX( plsc->boxbb_xmax,
1301  0.5 * ( plsc->vppxmi + plsc->vppxma ) /
1302  plsc->xpmm + 0.5 * label_length_mm );
1303  }
1304  }
1305  if ( !if_bb )
1306  plmtex( opt_label, label_offset, 0.5, just, label );
1307 }
1308 
1309 //--------------------------------------------------------------------------
1320 // dx_subpage, dy_subpageDifferences between normalized subpage coordinates for the old
1321 // bounding box and the new one.
1322 
1323 static void
1325  PLFLT xdmin_adopted, PLFLT xdmax_adopted, PLFLT ydmin_adopted, PLFLT ydmax_adopted,
1326  PLFLT prior_bb_height,
1327  PLFLT *p_colorbar_width_bb, PLFLT *p_colorbar_height_bb,
1328  PLFLT *p_colorbar_width_ac, PLFLT *p_colorbar_height_ac,
1329  PLFLT *p_plot_x_subpage_bb, PLFLT *p_plot_y_subpage_bb,
1330  PLFLT *p_dx_subpage, PLFLT *p_dy_subpage
1331  )
1332 {
1333  PLFLT x_colorbar_position, y_colorbar_position, xsign, ysign;
1334  PLFLT plot_x, plot_y;
1335  // Ratio of normalized subpage coordinates to mm coordinates in
1336  // x and y.
1337  PLFLT spxpmm = plsc->xpmm / ( plsc->sppxma - plsc->sppxmi );
1338  PLFLT spypmm = plsc->ypmm / ( plsc->sppyma - plsc->sppymi );
1339 
1340  // New bounding box width and height in normalized subpage coordinates.
1341  *p_colorbar_width_bb = ( plsc->boxbb_xmax - plsc->boxbb_xmin ) * spxpmm;
1342  *p_colorbar_height_bb = ( plsc->boxbb_ymax - plsc->boxbb_ymin ) * spypmm;
1343 
1344  // Offsets (in sense of prior minus current) of upper left corner of prior bounding box
1345  // relative to current bounding box in normalized subpage coordinates. From
1346  // the above comments, the upper left corner of prior bounding box
1347  // has the coordinates (0., prior_bb_height).
1348  *p_dx_subpage = -plsc->boxbb_xmin * spxpmm;
1349  *p_dy_subpage = prior_bb_height - plsc->boxbb_ymax * spypmm;
1350 
1351  // Total width and height of new bounding box in adopted
1352  // coordinates.
1353  *p_colorbar_width_ac = subpage_to_adopted_x( *p_colorbar_width_bb ) -
1354  subpage_to_adopted_x( 0. );
1355  *p_colorbar_height_ac = subpage_to_adopted_y( *p_colorbar_height_bb ) -
1356  subpage_to_adopted_y( 0. );
1357 
1358  // Calculate parameters that help determine the position of the top
1359  // left of the legend in adopted coordinates. See pllegend
1360  // documentation for definition of adopted coordinates.
1361  legend_position( position, *p_colorbar_width_ac, *p_colorbar_height_ac, &x_colorbar_position, &y_colorbar_position, &xsign, &ysign );
1362  plot_x = x * xsign + x_colorbar_position;
1363  plot_y = y * ysign + y_colorbar_position;
1364  // Calculate normalized subpage coordinates of top left of new bounding box.
1365  *p_plot_x_subpage_bb = adopted_to_subpage_x( plot_x );
1366  *p_plot_y_subpage_bb = adopted_to_subpage_y( plot_y );
1367 }
1368 
1369 
1370 //--------------------------------------------------------------------------
1461 
1462 void
1469  PLINT n_labels, const PLINT *label_opts, const char * const *labels,
1470  PLINT n_axes, const char * const *axis_opts,
1471  const PLFLT *ticks, const PLINT *sub_ticks,
1472  const PLINT *n_values, const PLFLT * const *values )
1473 {
1474  // Min and max values
1475  // Assumes that the values array is sorted from smallest to largest
1476  // OR from largest to smallest.
1477  PLFLT min_value, max_value, max_abs;
1478  // Length of cap in orientation direction in normalized subpage
1479  // coordinates and mm.
1480  PLFLT cap_extent, cap_extent_mm;
1481 
1482  // The colorbar cap is an equilateral triangle with cap_angle
1483  // the angle (in degrees) of the unequal angle pointing in the
1484  // direction of the orientation of the cap. In other words,
1485  // cap_angle completely controls the shape of the triangle, but
1486  // not its scale.
1487  PLFLT cap_angle = 45.;
1488  // Ratio of length of cap in orientation direction
1489  // to the width of the bar (and cap) in the direction
1490  // perpendicular to the orientation in physical coordinates
1491  // (i.e., independent of aspect ratio).
1492  PLFLT cap_ratio = 0.5 / tan( PI / 360. * cap_angle );
1493 
1494  // aspect ratio of physical area of subpage.
1495  PLFLT aspspp = ( ( plsc->sppxma - plsc->sppxmi ) / plsc->xpmm ) /
1496  ( ( plsc->sppyma - plsc->sppymi ) / plsc->ypmm );
1497 
1498  // Min and max colors
1499  PLFLT min_color, max_color;
1500 
1501  // Saved external world coordinates of viewport.
1502  PLFLT xwmin_save, xwmax_save, ywmin_save, ywmax_save;
1503  // Saved external normalized coordinates of viewport.
1504  // (These are actual values used only for the restore.)
1505  PLFLT xdmin_save = 0.0, xdmax_save = 0.0, ydmin_save = 0.0, ydmax_save = 0.0;
1506 
1507  // Limits of adopted coordinates used to calculate all coordinate
1508  // transformations.
1509  PLFLT xdmin_adopted = 0.0, xdmax_adopted = 0.0, ydmin_adopted = 0.0, ydmax_adopted = 0.0;
1510 
1511  // Active attributes to be saved and restored afterward.
1512  PLINT col0_save = plsc->icol0,
1513  line_style_save = plsc->line_style;
1514 
1515  // Normalized subpage coordinates of top left of the bounding box.
1516  PLFLT plot_x_subpage_bb, plot_y_subpage_bb;
1517 
1518  // colorbar width and height in normalized subpage coordinates.
1519  // No suffix refers to bonding box of undecorated colorbar, d
1520  // suffix refers to bounding box of decorated colorbar, and l
1521  // suffix refers to bounding box of labelled and decorated
1522  // colorbar.
1523  PLFLT colorbar_width, colorbar_height,
1524  colorbar_width_d, colorbar_height_d,
1525  colorbar_width_l, colorbar_height_l;
1526 
1527  // ac suffix refers to latest colorbar_width (d or l suffix) converted to
1528  // adopted coordinates.
1529  // mm suffix refers to colorbar_width and colorbar_height (with no suffix)
1530  // converted from normalized subpage coordinates to mm.
1531  PLFLT colorbar_width_ac, colorbar_height_ac,
1532  colorbar_width_mm, colorbar_height_mm;
1533 
1534  // Change in normalized subpage coordinates of the top left of
1535  // undecorated colorbar. (The omd suffix refers to original
1536  // colorbar minus decorated colorbar, and the dml suffix refers to
1537  // decorated colorbar minus labelled and decorated colorbar.)
1538  PLFLT dx_subpage_omd, dy_subpage_omd, dx_subpage_dml, dy_subpage_dml;
1539  PLFLT dx_subpage_omd_accu = 0.0, dy_subpage_omd_accu = 0.0, dx_subpage_dml_accu = 0.0, dy_subpage_dml_accu = 0.0;
1540  // Normalized subpage coordinates of the top left of undecorated
1541  // colorbar,
1542  PLFLT plot_x_subpage, plot_y_subpage;
1543 
1544  // Position of the undecorated colorbar in normalized subpage coordinates.
1545  PLFLT vx_min = 0.0, vx_max = 0.0, vy_min = 0.0, vy_max = 0.0;
1546 
1547  // World coordinate limits describing undecorated colorbar.
1548  PLFLT wx_min = 0.0, wx_max = 0.0, wy_min = 0.0, wy_max = 0.0;
1549 
1550  // The data to plot
1551  PLFLT **color_data;
1552 
1553  // Setting up the data for display
1554  PLINT i, j, ni = 0, nj = 0, n_steps;
1555  PLFLT step_size;
1556 
1557  PLBOOL if_edge = 0;
1558  PLBOOL if_edge_b = 0, if_edge_c = 0, if_edge_u = 0, if_edge_w = 0;
1559 
1560  // Ratio of normalized subpage coordinates to mm coordinates in
1561  // x and y.
1562  PLFLT spxpmm = plsc->xpmm / ( plsc->sppxma - plsc->sppxmi );
1563  PLFLT spypmm = plsc->ypmm / ( plsc->sppyma - plsc->sppymi );
1564 
1565  // plvpor limits for label.
1566  PLFLT label_vpor_xmin, label_vpor_xmax, label_vpor_ymin, label_vpor_ymax;
1567 
1568  // Default position flags and sanity checks for position flags.
1569  if ( !( position & PL_POSITION_RIGHT ) && !( position & PL_POSITION_LEFT ) && !( position & PL_POSITION_TOP ) && !( position & PL_POSITION_BOTTOM ) )
1570  {
1571  position = position | PL_POSITION_RIGHT;
1572  }
1573  else if ( ( position & PL_POSITION_RIGHT ) && ( position & PL_POSITION_LEFT ) )
1574  {
1575  plabort( "plcolorbar: PL_POSITION_RIGHT and PL_POSITION_LEFT cannot be simultaneously set." );
1576  return;
1577  }
1578 
1579  else if ( ( position & PL_POSITION_TOP ) && ( position & PL_POSITION_BOTTOM ) )
1580  {
1581  plabort( "plcolorbar: PL_POSITION_TOP and PL_POSITION_BOTTOM cannot be simultaneously set." );
1582  return;
1583  }
1584 
1585  if ( !( position & PL_POSITION_INSIDE ) && !( position & PL_POSITION_OUTSIDE ) )
1586  {
1587  position = position | PL_POSITION_OUTSIDE;
1588  }
1589  else if ( ( position & PL_POSITION_INSIDE ) && ( position & PL_POSITION_OUTSIDE ) )
1590  {
1591  plabort( "plcolorbar: PL_POSITION_INSIDE and PL_POSITION_OUTSIDE cannot be simultaneously set." );
1592  return;
1593  }
1594 
1595  if ( !( position & PL_POSITION_VIEWPORT ) && !( position & PL_POSITION_SUBPAGE ) )
1596  {
1597  position = position | PL_POSITION_VIEWPORT;
1598  }
1599  else if ( ( position & PL_POSITION_VIEWPORT ) && ( position & PL_POSITION_SUBPAGE ) )
1600  {
1601  plabort( "plcolorbar: PL_POSITION_VIEWPORT and PL_POSITION_SUBPAGE cannot be simultaneously set." );
1602  return;
1603  }
1604 
1605  if ( n_axes < 1 )
1606  {
1607  plabort( "plcolorbar: At least one axis must be specified" );
1608  return;
1609  }
1610 
1611  // xdmin_save, etc., are the actual external relative viewport
1612  // coordinates within the current sub-page used only for
1613  // restoration at the end.
1614  plgvpsp( &xdmin_save, &xdmax_save, &ydmin_save, &ydmax_save );
1615 
1616  // Choose adopted coordinates.
1617  if ( position & PL_POSITION_SUBPAGE )
1618  plvpor( 0., 1., 0., 1. );
1619 
1620  // xdmin_adopted, etc., are the adopted limits of the coordinates
1621  // within the current sub-page used for all coordinate
1622  // transformations.
1623  // If position & PL_POSITION_VIEWPORT is true, these limits
1624  // are the external relative viewport limits.
1625  // If position & PL_POSITION_SUBPAGE is true, these
1626  // coordinates are the relative subpage coordinates.
1627  plgvpsp( &xdmin_adopted, &xdmax_adopted, &ydmin_adopted, &ydmax_adopted );
1628 
1629  // xwmin_save, etc., are the external world coordinates corresponding
1630  // to the external viewport boundaries.
1631  plgvpw( &xwmin_save, &xwmax_save, &ywmin_save, &ywmax_save );
1632 
1633  // Default orientation.
1634  if ( !( opt & PL_COLORBAR_ORIENT_RIGHT ||
1635  opt & PL_COLORBAR_ORIENT_TOP ||
1636  opt & PL_COLORBAR_ORIENT_LEFT ||
1637  opt & PL_COLORBAR_ORIENT_BOTTOM ) )
1638  {
1639  if ( position & PL_POSITION_LEFT || position & PL_POSITION_RIGHT )
1640  opt = opt | PL_COLORBAR_ORIENT_TOP;
1641  else
1642  opt = opt | PL_COLORBAR_ORIENT_RIGHT;
1643  }
1644 
1645  for ( i = 0; i < n_axes; i++ )
1646  {
1647  if_edge_b = if_edge_b || plP_stsearch( axis_opts[i], 'b' );
1648  if_edge_c = if_edge_c || plP_stsearch( axis_opts[i], 'c' );
1649  if_edge_u = if_edge_u || plP_stsearch( axis_opts[i], 'u' );
1650  if_edge_w = if_edge_w || plP_stsearch( axis_opts[i], 'w' );
1651  }
1652  if_edge = if_edge_b && if_edge_c && !( if_edge_u || if_edge_w );
1653 
1654  // Assumes that the colors array is sorted from smallest to largest.
1655  plgcmap1_range( &min_color, &max_color );
1656 
1657  // Width and height of the undecorated colorbar in normalized
1658  // subpage coordinates and mm.
1659  colorbar_width = adopted_to_subpage_x( x_length ) -
1660  adopted_to_subpage_x( 0. );
1661  colorbar_height = adopted_to_subpage_y( y_length ) -
1662  adopted_to_subpage_y( 0. );
1663  colorbar_width_d = colorbar_width;
1664  colorbar_height_d = colorbar_height;
1665  colorbar_width_mm = colorbar_width / spxpmm;
1666  colorbar_height_mm = colorbar_height / spypmm;
1667  // Extent of cap in normalized subpage coordinates in either X or Y
1668  // direction as appropriate in normalized subpage coordinates and mm.
1670  {
1671  cap_extent = cap_ratio * colorbar_height / aspspp;
1672  cap_extent_mm = cap_extent / spxpmm;
1673  }
1674  else
1675  {
1676  cap_extent = cap_ratio * colorbar_width * aspspp;
1677  cap_extent_mm = cap_extent / spypmm;
1678  }
1679 
1680  for ( i = n_axes - 1; i >= 0; i-- )
1681  {
1682  min_value = values[i][0];
1683  max_value = values[i][ n_values[i] - 1 ];
1684  max_abs = MAX( fabs( min_value ), fabs( max_value ) );
1685 
1686  // Specify the proper window ranges for colorbar depending on
1687  // orientation.
1688  if ( opt & PL_COLORBAR_ORIENT_RIGHT )
1689  {
1690  wx_min = min_value;
1691  wx_max = max_value;
1692  wy_min = 0.0;
1693  wy_max = max_abs;
1694  }
1695  else if ( opt & PL_COLORBAR_ORIENT_TOP )
1696  {
1697  wx_min = 0.0;
1698  wx_max = max_abs;
1699  wy_min = min_value;
1700  wy_max = max_value;
1701  }
1702  else if ( opt & PL_COLORBAR_ORIENT_LEFT )
1703  {
1704  wx_min = max_value;
1705  wx_max = min_value;
1706  wy_min = 0.0;
1707  wy_max = max_abs;
1708  }
1709  else if ( opt & PL_COLORBAR_ORIENT_BOTTOM )
1710  {
1711  wx_min = 0.0;
1712  wx_max = max_abs;
1713  wy_min = max_value;
1714  wy_max = min_value;
1715  }
1716  else
1717  {
1718  plabort( "plcolorbar: Invalid PL_COLORBAR_ORIENT_* bits" );
1719  }
1720 
1721  // Viewport has correct size but has a shifted zero-point
1722  // convention required by bounding-box calculations in draw_box,
1723  // further bounding-box calculations in the cap_extent section
1724  // below, and also in the calculate_limits call below that.
1725  plvpor( 0., colorbar_width_d, 0., colorbar_height_d );
1726  plwind( wx_min, wx_max, wy_min, wy_max );
1727 
1728  // Calculate the bounding box for decorated (i.e., including tick
1729  // marks + numerical tick labels) box.
1730  draw_box( TRUE, opt, axis_opts[i], if_edge,
1731  ticks[i], sub_ticks[i], n_values[i], values[i] );
1732 
1733  if ( i == n_axes - 1 )
1734  {
1735  if ( opt & PL_COLORBAR_CAP_LOW )
1736  {
1737  if ( opt & PL_COLORBAR_ORIENT_RIGHT )
1738  plsc->boxbb_xmin = MIN( plsc->boxbb_xmin, -cap_extent_mm );
1739  if ( opt & PL_COLORBAR_ORIENT_TOP )
1740  plsc->boxbb_ymin = MIN( plsc->boxbb_ymin, -cap_extent_mm );
1741  if ( opt & PL_COLORBAR_ORIENT_LEFT )
1742  plsc->boxbb_xmax = MAX( plsc->boxbb_xmax, colorbar_width_mm + cap_extent_mm );
1743  if ( opt & PL_COLORBAR_ORIENT_BOTTOM )
1744  plsc->boxbb_ymax = MAX( plsc->boxbb_ymax, colorbar_height_mm + cap_extent_mm );
1745  }
1746  if ( opt & PL_COLORBAR_CAP_HIGH )
1747  {
1748  if ( opt & PL_COLORBAR_ORIENT_RIGHT )
1749  plsc->boxbb_xmax = MAX( plsc->boxbb_xmax, colorbar_width_mm + cap_extent_mm );
1750  if ( opt & PL_COLORBAR_ORIENT_TOP )
1751  plsc->boxbb_ymax = MAX( plsc->boxbb_ymax, colorbar_height_mm + cap_extent_mm );
1752  if ( opt & PL_COLORBAR_ORIENT_LEFT )
1753  plsc->boxbb_xmin = MIN( plsc->boxbb_xmin, -cap_extent_mm );
1754  if ( opt & PL_COLORBAR_ORIENT_BOTTOM )
1755  plsc->boxbb_ymin = MIN( plsc->boxbb_ymin, -cap_extent_mm );
1756  }
1757  }
1758 
1759  // Calculate limits relevant to label position.
1760  calculate_limits( position, x, y,
1761  xdmin_adopted, xdmax_adopted, ydmin_adopted, ydmax_adopted,
1762  colorbar_height_d,
1763  &colorbar_width_d, &colorbar_height_d,
1764  &colorbar_width_ac, &colorbar_height_ac,
1765  &plot_x_subpage_bb, &plot_y_subpage_bb,
1766  &dx_subpage_omd, &dy_subpage_omd );
1767 
1768  // Viewport has correct size but has a shifted zero point
1769  // convention required by bounding-box calculations in draw_label
1770  // and further calculations in calculate_limits.
1771  plvpor( 0., colorbar_width_d, 0., colorbar_height_d );
1772 
1773  dx_subpage_omd_accu += dx_subpage_omd;
1774  dy_subpage_omd_accu += dy_subpage_omd;
1775  }
1776 
1777  // Capture the current bounding box dimensions
1778  colorbar_width_l = colorbar_width_d;
1779  colorbar_height_l = colorbar_height_d;
1780 
1781  for ( i = 0; i < n_labels; i++ )
1782  {
1783  // Viewport has correct size but has a shifted zero-point
1784  // convention required by bounding-box calculations in draw_box,
1785  // further bounding-box calculations in the cap_extent section
1786  // below, and also in the calculate_limits call below that.
1787  plvpor( 0., colorbar_width_l, 0., colorbar_height_l );
1788  plwind( wx_min, wx_max, wy_min, wy_max );
1789 
1790  // Calculate the bounding box for combined label + decorated box.
1791  draw_label( TRUE, opt | label_opts[i], labels[i] );
1792 
1793  // Calculate overall limits.
1794  calculate_limits( position, x, y,
1795  xdmin_adopted, xdmax_adopted, ydmin_adopted, ydmax_adopted,
1796  colorbar_height_l,
1797  &colorbar_width_l, &colorbar_height_l,
1798  &colorbar_width_ac, &colorbar_height_ac,
1799  &plot_x_subpage_bb, &plot_y_subpage_bb,
1800  &dx_subpage_dml, &dy_subpage_dml );
1801 
1802  dx_subpage_dml_accu += dx_subpage_dml;
1803  dy_subpage_dml_accu += dy_subpage_dml;
1804  }
1805 
1806  // Normalized subpage coordinates (top-left corner) for undecorated
1807  // colorbar
1808  plot_x_subpage = plot_x_subpage_bb + dx_subpage_omd_accu + dx_subpage_dml_accu;
1809  plot_y_subpage = plot_y_subpage_bb + dy_subpage_omd_accu + dy_subpage_dml_accu;
1810 
1811  // Coordinates of bounding box for decorated colorbar (without overall label).
1812  label_vpor_xmin = plot_x_subpage_bb + dx_subpage_dml_accu;
1813  label_vpor_xmax = label_vpor_xmin + colorbar_width_d;
1814  label_vpor_ymax = plot_y_subpage_bb + dy_subpage_dml_accu;
1815  label_vpor_ymin = label_vpor_ymax - colorbar_height_d;
1816 
1817  // Return bounding box width and height in adopted coordinates for
1818  // labelled and decorated colorbar.
1819  *p_colorbar_width = colorbar_width_ac;
1820  *p_colorbar_height = colorbar_height_ac;
1821 
1822  // Specify the proper viewport ranges for colorbar depending on
1823  // orientation.
1824  if ( opt & PL_COLORBAR_ORIENT_RIGHT )
1825  {
1826  vx_min = plot_x_subpage;
1827  vx_max = plot_x_subpage + colorbar_width;
1828  vy_min = plot_y_subpage - colorbar_height;
1829  vy_max = plot_y_subpage;
1830  }
1831  else if ( opt & PL_COLORBAR_ORIENT_TOP )
1832  {
1833  vx_min = plot_x_subpage;
1834  vx_max = plot_x_subpage + colorbar_width;
1835  vy_min = plot_y_subpage - colorbar_height;
1836  vy_max = plot_y_subpage;
1837  }
1838  else if ( opt & PL_COLORBAR_ORIENT_LEFT )
1839  {
1840  vx_min = plot_x_subpage;
1841  vx_max = plot_x_subpage + colorbar_width;
1842  vy_min = plot_y_subpage - colorbar_height;
1843  vy_max = plot_y_subpage;
1844  }
1845  else if ( opt & PL_COLORBAR_ORIENT_BOTTOM )
1846  {
1847  vx_min = plot_x_subpage;
1848  vx_max = plot_x_subpage + colorbar_width;
1849  vy_min = plot_y_subpage - colorbar_height;
1850  vy_max = plot_y_subpage;
1851  }
1852  else
1853  {
1854  plabort( "plcolorbar: Invalid PL_COLORBAR_ORIENT_* bits" );
1855  }
1856 
1857  // Viewport and world coordinate ranges for bounding-box.
1858  plvpor( 0., 1., 0., 1. );
1859  plwind( 0., 1., 0., 1. );
1860 
1861  if ( opt & PL_COLORBAR_BACKGROUND )
1862  {
1863  PLFLT xbg[4] = {
1864  plot_x_subpage_bb,
1865  plot_x_subpage_bb,
1866  plot_x_subpage_bb + colorbar_width_l,
1867  plot_x_subpage_bb + colorbar_width_l,
1868  };
1869  PLFLT ybg[4] = {
1870  plot_y_subpage_bb,
1871  plot_y_subpage_bb - colorbar_height_l,
1872  plot_y_subpage_bb - colorbar_height_l,
1873  plot_y_subpage_bb,
1874  };
1875  plpsty( 0 );
1876  plcol0( bg_color );
1877  plfill( 4, xbg, ybg );
1878  plcol0( col0_save );
1879  }
1880 
1881  // Viewport and world coordinate ranges for colorbar.
1882  plvpor( vx_min, vx_max, vy_min, vy_max );
1883  plwind( wx_min, wx_max, wy_min, wy_max );
1884 
1885  // What kind of color bar are we making?
1886  if ( opt & PL_COLORBAR_IMAGE )
1887  {
1888  // Interpolate
1889  // TODO: Should this be decided with an extra opt option instead of by
1890  // counting n_values?
1891  if ( n_values[0] == 2 )
1892  {
1893  // Use the same number of steps as there are steps in
1894  // color palette 1.
1895  // TODO: Determine a better way to specify the steps here?
1896  n_steps = plsc->ncol1;
1897  step_size = ( max_value - min_value ) / (PLFLT) n_steps;
1898  if ( opt & PL_COLORBAR_ORIENT_RIGHT )
1899  {
1900  ni = n_steps;
1901  nj = 2;
1902  plAlloc2dGrid( &color_data, ni, nj );
1903  for ( i = 0; i < ni; i++ )
1904  {
1905  for ( j = 0; j < nj; j++ )
1906  {
1907  color_data[i][j] = min_value + (PLFLT) i * step_size;
1908  }
1909  }
1910  }
1911  else if ( opt & PL_COLORBAR_ORIENT_TOP )
1912  {
1913  ni = 2;
1914  nj = n_steps;
1915  plAlloc2dGrid( &color_data, ni, nj );
1916  for ( i = 0; i < ni; i++ )
1917  {
1918  for ( j = 0; j < nj; j++ )
1919  {
1920  color_data[i][j] = min_value + (PLFLT) j * step_size;
1921  }
1922  }
1923  }
1924  else if ( opt & PL_COLORBAR_ORIENT_LEFT )
1925  {
1926  ni = n_steps;
1927  nj = 2;
1928  plAlloc2dGrid( &color_data, ni, nj );
1929  for ( i = 0; i < ni; i++ )
1930  {
1931  for ( j = 0; j < nj; j++ )
1932  {
1933  color_data[i][j] = max_value - (PLFLT) i * step_size;
1934  }
1935  }
1936  }
1937  else if ( opt & PL_COLORBAR_ORIENT_BOTTOM )
1938  {
1939  ni = 2;
1940  nj = n_steps;
1941  plAlloc2dGrid( &color_data, ni, nj );
1942  for ( i = 0; i < ni; i++ )
1943  {
1944  for ( j = 0; j < nj; j++ )
1945  {
1946  color_data[i][j] = max_value - (PLFLT) j * step_size;
1947  }
1948  }
1949  }
1950  else
1951  {
1952  plabort( "plcolorbar: Invalid orientation bits" );
1953  }
1954  }
1955  // No interpolation - use values array as-is
1956  else
1957  {
1958  n_steps = n_values[0];
1959  // Use the provided values in this case.
1960  if ( opt & PL_COLORBAR_ORIENT_RIGHT )
1961  {
1962  ni = n_steps;
1963  nj = 2;
1964  plAlloc2dGrid( &color_data, ni, nj );
1965  for ( i = 0; i < ni; i++ )
1966  {
1967  for ( j = 0; j < nj; j++ )
1968  {
1969  color_data[i][j] = values[0][i];
1970  }
1971  }
1972  }
1973  else if ( opt & PL_COLORBAR_ORIENT_TOP )
1974  {
1975  ni = 2;
1976  nj = n_steps;
1977  plAlloc2dGrid( &color_data, ni, nj );
1978  for ( i = 0; i < ni; i++ )
1979  {
1980  for ( j = 0; j < nj; j++ )
1981  {
1982  color_data[i][j] = values[0][j];
1983  }
1984  }
1985  }
1986  else if ( opt & PL_COLORBAR_ORIENT_LEFT )
1987  {
1988  ni = n_steps;
1989  nj = 2;
1990  plAlloc2dGrid( &color_data, ni, nj );
1991  for ( i = 0; i < ni; i++ )
1992  {
1993  for ( j = 0; j < nj; j++ )
1994  {
1995  color_data[i][j] = values[0][ni - 1 - i];
1996  }
1997  }
1998  }
1999  else if ( opt & PL_COLORBAR_ORIENT_BOTTOM )
2000  {
2001  ni = 2;
2002  nj = n_steps;
2003  plAlloc2dGrid( &color_data, ni, nj );
2004  for ( i = 0; i < ni; i++ )
2005  {
2006  for ( j = 0; j < nj; j++ )
2007  {
2008  color_data[i][j] = values[0][nj - 1 - j];
2009  }
2010  }
2011  }
2012  else
2013  {
2014  plabort( "plcolorbar: Invalid side" );
2015  }
2016  }
2017  // Draw the color bar
2018  plimage( (const PLFLT * const *) color_data, ni, nj, wx_min, wx_max, wy_min, wy_max,
2019  min_value, max_value, wx_min, wx_max, wy_min, wy_max );
2020  plFree2dGrid( color_data, ni, nj );
2021  }
2022  else if ( opt & PL_COLORBAR_SHADE )
2023  {
2024  // Transform grid
2025  // The transform grid is used to make the size of the shaded segments
2026  // scale relative to other segments. For example, if segment A
2027  // makes up 10% of the scale and segment B makes up 20% of the scale
2028  // then segment B will be twice the length of segment A.
2029  PLcGrid grid;
2030  PLFLT grid_axis[2] = { 0.0, max_abs };
2031  n_steps = n_values[0];
2032  // Use the provided values.
2033  if ( opt & PL_COLORBAR_ORIENT_RIGHT )
2034  {
2035  grid.xg = (PLFLT *) values[0];
2036  grid.yg = grid_axis;
2037  grid.nx = n_steps;
2038  grid.ny = 2;
2039  ni = n_steps;
2040  nj = 2;
2041  plAlloc2dGrid( &color_data, ni, nj );
2042  for ( i = 0; i < ni; i++ )
2043  {
2044  for ( j = 0; j < nj; j++ )
2045  {
2046  color_data[i][j] = values[0][i];
2047  }
2048  }
2049  }
2050  else if ( opt & PL_COLORBAR_ORIENT_TOP )
2051  {
2052  grid.xg = grid_axis;
2053  grid.yg = (PLFLT *) values[0];
2054  grid.nx = 2;
2055  grid.ny = n_steps;
2056  ni = 2;
2057  nj = n_steps;
2058  plAlloc2dGrid( &color_data, ni, nj );
2059  for ( i = 0; i < ni; i++ )
2060  {
2061  for ( j = 0; j < nj; j++ )
2062  {
2063  color_data[i][j] = values[0][j];
2064  }
2065  }
2066  }
2067  else if ( opt & PL_COLORBAR_ORIENT_LEFT )
2068  {
2069  grid.xg = (PLFLT *) values[0];
2070  grid.yg = grid_axis;
2071  grid.nx = n_steps;
2072  grid.ny = 2;
2073  ni = n_steps;
2074  nj = 2;
2075  plAlloc2dGrid( &color_data, ni, nj );
2076  for ( i = 0; i < ni; i++ )
2077  {
2078  for ( j = 0; j < nj; j++ )
2079  {
2080  color_data[i][j] = values[0][ni - 1 - i];
2081  }
2082  }
2083  }
2084  else if ( opt & PL_COLORBAR_ORIENT_BOTTOM )
2085  {
2086  grid.xg = grid_axis;
2087  grid.yg = (PLFLT *) values[0];
2088  grid.nx = 2;
2089  grid.ny = n_steps;
2090  ni = 2;
2091  nj = n_steps;
2092  plAlloc2dGrid( &color_data, ni, nj );
2093  for ( i = 0; i < ni; i++ )
2094  {
2095  for ( j = 0; j < nj; j++ )
2096  {
2097  color_data[i][j] = values[0][nj - 1 - j];
2098  }
2099  }
2100  }
2101  else
2102  {
2103  plabort( "plcolorbar: Invalid orientation" );
2104  }
2105 
2106  // Draw the color bar
2107  plshades( (const PLFLT * const *) color_data, ni, nj, NULL, wx_min, wx_max, wy_min, wy_max,
2108  values[0], n_steps, 0, cont_color, cont_width, plfill, TRUE,
2109  pltr1, (void *) ( &grid ) );
2110  plFree2dGrid( color_data, ni, nj );
2111  }
2112  else if ( opt & PL_COLORBAR_GRADIENT )
2113  {
2114  PLFLT xs[4], ys[4];
2115  PLFLT angle = 0.0;
2116  xs[0] = wx_min;
2117  ys[0] = wy_min;
2118  xs[1] = wx_max;
2119  ys[1] = wy_min;
2120  xs[2] = wx_max;
2121  ys[2] = wy_max;
2122  xs[3] = wx_min;
2123  ys[3] = wy_max;
2124  // Make sure the gradient runs in the proper direction
2125  if ( opt & PL_COLORBAR_ORIENT_RIGHT )
2126  {
2127  angle = 0.0;
2128  }
2129  else if ( opt & PL_COLORBAR_ORIENT_TOP )
2130  {
2131  angle = 90.0;
2132  }
2133  else if ( opt & PL_COLORBAR_ORIENT_LEFT )
2134  {
2135  angle = 180.0;
2136  }
2137  else if ( opt & PL_COLORBAR_ORIENT_BOTTOM )
2138  {
2139  angle = 270.0;
2140  }
2141  else
2142  {
2143  plabort( "plcolorbar: Invalid orientation" );
2144  }
2145  plgradient( 4, xs, ys, angle );
2146  }
2147  else
2148  {
2149  plabort( "plcolorbar: One of PL_COLORBAR_IMAGE, PL_COLORBAR_SHADE, or PL_COLORBAR_GRADIENT bits must be set in opt" );
2150  }
2151 
2152  // Restore the previous drawing color to use for outlines and text
2153  plcol0( col0_save );
2154 
2155  // Draw end-caps
2156 
2157  // Viewport and world coordinate ranges for cap.
2158  plvpor( 0., 1., 0., 1. );
2159  plwind( 0., 1., 0., 1. );
2160 
2161  if ( opt & PL_COLORBAR_CAP_LOW )
2162  {
2163  // Draw a filled triangle (cap/arrow) at the low end of the scale
2164  if ( opt & PL_COLORBAR_ORIENT_RIGHT )
2165  draw_cap( if_edge, PL_COLORBAR_ORIENT_LEFT,
2166  plot_x_subpage - cap_extent, plot_x_subpage,
2167  plot_y_subpage - colorbar_height, plot_y_subpage,
2168  low_cap_color );
2169  else if ( opt & PL_COLORBAR_ORIENT_TOP )
2170  draw_cap( if_edge, PL_COLORBAR_ORIENT_BOTTOM,
2171  plot_x_subpage, plot_x_subpage + colorbar_width,
2172  plot_y_subpage - colorbar_height - cap_extent, plot_y_subpage - colorbar_height,
2173  low_cap_color );
2174  else if ( opt & PL_COLORBAR_ORIENT_LEFT )
2175  draw_cap( if_edge, PL_COLORBAR_ORIENT_RIGHT,
2176  plot_x_subpage + colorbar_width, plot_x_subpage + colorbar_width + cap_extent,
2177  plot_y_subpage - colorbar_height, plot_y_subpage,
2178  low_cap_color );
2179  else if ( opt & PL_COLORBAR_ORIENT_BOTTOM )
2180  draw_cap( if_edge, PL_COLORBAR_ORIENT_TOP,
2181  plot_x_subpage, plot_x_subpage + colorbar_width,
2182  plot_y_subpage, plot_y_subpage + cap_extent,
2183  low_cap_color );
2184  }
2185  if ( opt & PL_COLORBAR_CAP_HIGH )
2186  {
2187  // Draw a filled triangle (cap/arrow) at the high end of the scale
2188  if ( opt & PL_COLORBAR_ORIENT_RIGHT )
2189  draw_cap( if_edge, PL_COLORBAR_ORIENT_RIGHT,
2190  plot_x_subpage + colorbar_width, plot_x_subpage + colorbar_width + cap_extent,
2191  plot_y_subpage - colorbar_height, plot_y_subpage,
2192  high_cap_color );
2193  else if ( opt & PL_COLORBAR_ORIENT_TOP )
2194  draw_cap( if_edge, PL_COLORBAR_ORIENT_TOP,
2195  plot_x_subpage, plot_x_subpage + colorbar_width,
2196  plot_y_subpage, plot_y_subpage + cap_extent,
2197  high_cap_color );
2198  else if ( opt & PL_COLORBAR_ORIENT_LEFT )
2199  draw_cap( if_edge, PL_COLORBAR_ORIENT_LEFT,
2200  plot_x_subpage - cap_extent, plot_x_subpage,
2201  plot_y_subpage - colorbar_height, plot_y_subpage,
2202  high_cap_color );
2203  else if ( opt & PL_COLORBAR_ORIENT_BOTTOM )
2204  draw_cap( if_edge, PL_COLORBAR_ORIENT_BOTTOM,
2205  plot_x_subpage, plot_x_subpage + colorbar_width,
2206  plot_y_subpage - colorbar_height - cap_extent, plot_y_subpage - colorbar_height,
2207  high_cap_color );
2208  }
2209 
2210  for ( i = n_axes - 1; i >= 0; i-- )
2211  {
2212  min_value = values[i][0];
2213  max_value = values[i][ n_values[i] - 1 ];
2214  max_abs = MAX( fabs( min_value ), fabs( max_value ) );
2215 
2216  // Specify the proper window ranges for colorbar depending on
2217  // orientation.
2218  if ( opt & PL_COLORBAR_ORIENT_RIGHT )
2219  {
2220  wx_min = min_value;
2221  wx_max = max_value;
2222  wy_min = 0.0;
2223  wy_max = max_abs;
2224  }
2225  else if ( opt & PL_COLORBAR_ORIENT_TOP )
2226  {
2227  wx_min = 0.0;
2228  wx_max = max_abs;
2229  wy_min = min_value;
2230  wy_max = max_value;
2231  }
2232  else if ( opt & PL_COLORBAR_ORIENT_LEFT )
2233  {
2234  wx_min = max_value;
2235  wx_max = min_value;
2236  wy_min = 0.0;
2237  wy_max = max_abs;
2238  }
2239  else if ( opt & PL_COLORBAR_ORIENT_BOTTOM )
2240  {
2241  wx_min = 0.0;
2242  wx_max = max_abs;
2243  wy_min = max_value;
2244  wy_max = min_value;
2245  }
2246  else
2247  {
2248  plabort( "plcolorbar: Invalid PL_COLORBAR_ORIENT_* bits" );
2249  }
2250 
2251  // Viewport and world coordinate ranges for box.
2252  plvpor( vx_min, vx_max, vy_min, vy_max );
2253  plwind( wx_min, wx_max, wy_min, wy_max );
2254 
2255  // draw decorated (i.e., including tick marks + numerical tick
2256  // labels) box.
2257  draw_box( FALSE, opt, axis_opts[i], if_edge,
2258  ticks[i], sub_ticks[i], n_values[i], values[i] );
2259  }
2260 
2261  // Viewport and world coordinate ranges for bounding-box.
2262  plvpor( 0., 1., 0., 1. );
2263  plwind( 0., 1., 0., 1. );
2264 
2265  if ( opt & PL_COLORBAR_BOUNDING_BOX )
2266  {
2267  PLFLT xbb[5] = {
2268  plot_x_subpage_bb,
2269  plot_x_subpage_bb,
2270  plot_x_subpage_bb + colorbar_width_l,
2271  plot_x_subpage_bb + colorbar_width_l,
2272  plot_x_subpage_bb,
2273  };
2274  PLFLT ybb[5] = {
2275  plot_y_subpage_bb,
2276  plot_y_subpage_bb - colorbar_height_l,
2277  plot_y_subpage_bb - colorbar_height_l,
2278  plot_y_subpage_bb,
2279  plot_y_subpage_bb,
2280  };
2281  pllsty( bb_style );
2282  plcol0( bb_color );
2283  plline( 5, xbb, ybb );
2284  plcol0( col0_save );
2285  pllsty( line_style_save );
2286  }
2287 
2288  // Write label.
2289  // Viewport coordinate ranges for label.
2290  plvpor( label_vpor_xmin, label_vpor_xmax, label_vpor_ymin, label_vpor_ymax );
2291  for ( i = 0; i < n_labels; i++ )
2292  {
2293  draw_label( FALSE, opt | label_opts[i], labels[i] );
2294  }
2295 
2296  // Restore previous plot characteristics.
2297  plcol0( col0_save );
2298  plvpor( xdmin_save, xdmax_save, ydmin_save, ydmax_save );
2299  plwind( xwmin_save, xwmax_save, ywmin_save, ywmax_save );
2300 
2301  return;
2302 }
#define max_opts
subroutine plbox(xopt, xtick, nxsub, yopt, ytick, nysub)
Definition: sfstubs.f90:148
static void remove_characters(char *string, const char *characters)
Definition: pllegend.c:910
static PLINT text
Definition: gcw.c:97
void plFree2dGrid(PLFLT **f, PLINT nx, PLINT PL_UNUSED(ny))
Definition: pdfutils.c:1130
void PLFLT PLINT PLINT PLFLT PLFLT PLFLT PLFLT PLINT PLINT PLINT PLFLT PLFLT PLINT PLFLT PLINT const PLINT const char *const * label
#define plvpor
Definition: plplot.h:752
PLFLT plstrl(const char *string)
Definition: plsym.c:949
PLFLT * xg
Definition: plplot.h:428
#define plschr
Definition: plplot.h:683
void PLFLT PLINT PLINT PLFLT PLFLT PLFLT PLFLT PLINT PLINT bb_color
void PLFLT PLINT PLINT position
void PLFLT PLINT PLINT PLFLT PLFLT PLFLT PLFLT PLINT PLINT PLINT PLFLT PLFLT PLINT PLFLT PLINT const PLINT const char *const PLINT const char *const const PLFLT const PLINT const PLINT * n_values
#define pllsty
Definition: plplot.h:658
#define plwind
Definition: plplot.h:757
#define plfill
Definition: plplot.h:612
void plimage(PLFLT **data, PLINT nx, PLINT ny, PLFLT xmin, PLFLT xmax, PLFLT ymin, PLFLT ymax, PLFLT zmin, PLFLT zmax, PLFLT Dxmin, PLFLT Dxmax, PLFLT Dymin, PLFLT Dymax)
subroutine plmtex(side, disp, pos, xjust, text)
Definition: sfstubs.f90:705
subroutine plstring(x, y, string)
Definition: sfstubs.f90:309
static void plgvpsp(PLFLT *p_xmin, PLFLT *p_xmax, PLFLT *p_ymin, PLFLT *p_ymax)
Definition: pllegend.c:47
#define MAX(a, b)
Definition: dsplint.c:28
PLFLT * yg
Definition: plplot.h:428
PLBOOL plP_stsearch(const char *str, int chr)
Definition: plsym.c:1224
void PLFLT PLINT PLINT PLFLT x
tuple xmin
Definition: Plframe.py:907
static PLFLT get_character_or_symbol_height(PLBOOL ifcharacter)
Definition: pllegend.c:313
void plAlloc2dGrid(PLFLT ***f, PLINT nx, PLINT ny)
Definition: pdfutils.c:1104
tuple ymin
Definition: Plframe.py:908
void PLFLT PLINT PLINT PLFLT PLFLT PLFLT PLFLT PLINT PLINT PLINT PLFLT PLFLT PLINT PLFLT PLINT const PLINT const char *const PLINT const char *const * axis_opts
static void get_subpage_per_mm(PLFLT *x_subpage_per_mm, PLFLT *y_subpage_per_mm)
Definition: pllegend.c:296
void c_pllegend(PLFLT *p_legend_width, PLFLT *p_legend_height, PLINT opt, PLINT position, PLFLT x, PLFLT y, PLFLT plot_width, PLINT bg_color, PLINT bb_color, PLINT bb_style, PLINT nrow, PLINT ncolumn, PLINT nlegend, const PLINT *opt_array, PLFLT text_offset, PLFLT text_scale, PLFLT text_spacing, PLFLT text_justification, const PLINT *text_colors, const char *const *text, const PLINT *box_colors, const PLINT *box_patterns, const PLFLT *box_scales, const PLFLT *box_line_widths, const PLINT *line_colors, const PLINT *line_styles, const PLFLT *line_widths, const PLINT *symbol_colors, const PLFLT *symbol_scales, const PLINT *symbol_numbers, const char *const *symbols)
Definition: pllegend.c:528
void PLFLT PLINT PLINT PLFLT PLFLT PLFLT PLFLT PLINT PLINT PLINT PLFLT low_cap_color
int PLINT
Definition: plplot.h:175
#define plshades
Definition: plplot.h:715
#define MIN(a, b)
Definition: dsplint.c:29
PLINT PLBOOL
Definition: plplot.h:198
static void draw_label(PLBOOL if_bb, PLINT opt, const char *label)
Definition: pllegend.c:1114
PLINT ny
Definition: plplot.h:429
static void calculate_limits(PLINT position, PLFLT x, PLFLT y, PLFLT xdmin_adopted, PLFLT xdmax_adopted, PLFLT ydmin_adopted, PLFLT ydmax_adopted, PLFLT prior_bb_height, PLFLT *p_colorbar_width_bb, PLFLT *p_colorbar_height_bb, PLFLT *p_colorbar_width_ac, PLFLT *p_colorbar_height_ac, PLFLT *p_plot_x_subpage_bb, PLFLT *p_plot_y_subpage_bb, PLFLT *p_dx_subpage, PLFLT *p_dy_subpage)
Definition: pllegend.c:1324
static void draw_box(PLBOOL if_bb, PLINT opt, const char *axis_opts, PLBOOL if_edge, PLFLT ticks, PLINT sub_ticks, PLINT n_values, const PLFLT *values)
Definition: pllegend.c:1035
#define subpage_to_adopted_x(nx)
Definition: pllegend.c:360
#define snprintf
Definition: plplotP.h:240
#define plgchr
Definition: plplot.h:617
xl
set sign_dx [expr ($dx > 0) ? 1 : -1] set sign_dy [expr ($dy > 0) ? 1 : -1]
Definition: Plframe.py:623
#define TRUE
Definition: plplotP.h:181
void PLFLT PLINT PLINT PLFLT PLFLT y
#define adopted_to_subpage_y(ny)
Definition: pllegend.c:375
#define FALSE
Definition: plplotP.h:182
static void legend_position(PLINT position, PLFLT legend_width, PLFLT legend_height, PLFLT *x_legend_position, PLFLT *y_legend_position, PLFLT *xsign, PLFLT *ysign)
Definition: pllegend.c:88
#define plcol1
Definition: plplot.h:598
void PLFLT PLINT PLINT PLFLT PLFLT PLFLT PLFLT PLINT PLINT PLINT PLFLT PLFLT high_cap_color
#define plwidth
Definition: plplot.h:756
subroutine plptex(x, y, dx, dy, xjust, text)
Definition: sfstubs.f90:739
tuple xmax
Definition: Plframe.py:909
#define plgspa
Definition: plplot.h:637
void PLFLT PLINT PLINT PLFLT PLFLT PLFLT PLFLT PLINT PLINT PLINT PLFLT PLFLT PLINT cont_color
#define plpsty
Definition: plplot.h:675
void plabort(const char *errormsg)
Definition: plctrl.c:1877
#define plgvpw
Definition: plplot.h:641
void PLFLT PLINT PLINT PLFLT PLFLT PLFLT x_length
float PLFLT
Definition: plplot.h:159
static void draw_cap(PLBOOL if_edge, PLINT orientation, PLFLT xmin, PLFLT xmax, PLFLT ymin, PLFLT ymax, PLFLT color)
Definition: pllegend.c:946
#define subpage_to_adopted_y(ny)
Definition: pllegend.c:390
void PLFLT PLINT opt
void PLFLT PLINT PLINT PLFLT PLFLT PLFLT PLFLT PLINT PLINT PLINT PLFLT PLFLT PLINT PLFLT cont_width
void PLFLT * p_colorbar_height
#define PI
Definition: plplotP.h:295
#define adopted_to_subpage_x(nx)
Definition: pllegend.c:345
#define plgcmap1_range
Definition: plplot.h:693
#define plcol0
Definition: plplot.h:597
void PLFLT PLINT PLINT PLFLT PLFLT PLFLT PLFLT PLINT PLINT PLINT bb_style
tuple ymax
Definition: Plframe.py:910
void PLFLT PLINT PLINT PLFLT PLFLT PLFLT PLFLT PLINT bg_color
void PLFLT PLINT PLINT PLFLT PLFLT PLFLT PLFLT PLINT PLINT PLINT PLFLT PLFLT PLINT PLFLT PLINT const PLINT * label_opts
#define plline
Definition: plplot.h:655
void label_box_custom(const char *xopt, PLINT n_xticks, const PLFLT *xticks, const char *yopt, PLINT n_yticks, const PLFLT *yticks)
Definition: plbox.c:1874
void PLFLT PLINT PLINT PLFLT PLFLT PLFLT PLFLT PLINT PLINT PLINT PLFLT PLFLT PLINT PLFLT PLINT const PLINT const char *const PLINT const char *const const PLFLT const PLINT * sub_ticks
#define plgradient
Definition: plplot.h:635
void PLFLT PLINT PLINT PLFLT PLFLT PLFLT PLFLT PLINT PLINT PLINT PLFLT PLFLT PLINT PLFLT PLINT const PLINT const char *const PLINT const char *const const PLFLT * ticks
void c_plcolorbar(PLFLT *p_colorbar_width, PLFLT *p_colorbar_height, PLINT opt, PLINT position, PLFLT x, PLFLT y, PLFLT x_length, PLFLT y_length, PLINT bg_color, PLINT bb_color, PLINT bb_style, PLFLT low_cap_color, PLFLT high_cap_color, PLINT cont_color, PLFLT cont_width, PLINT n_labels, const PLINT *label_opts, const char *const *labels, PLINT n_axes, const char *const *axis_opts, const PLFLT *ticks, const PLINT *sub_ticks, const PLINT *n_values, const PLFLT *const *values)
Definition: pllegend.c:1463
void PLFLT PLINT PLINT PLFLT PLFLT PLFLT PLFLT y_length
PLINT nx
Definition: plplot.h:429
void PLFLT PLINT PLINT PLFLT PLFLT PLFLT PLFLT PLINT PLINT PLINT PLFLT PLFLT PLINT PLFLT PLINT n_labels
void plexit(const char *errormsg)
Definition: plctrl.c:1941