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