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 }