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