PLplot: pllegend.c Source File

PLplot 5.10.0

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

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
313 static PLFLT get_character_or_symbol_height( PLBOOL ifcharacter )
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 {
1087 if ( opt & PL_COLORBAR_ORIENT_RIGHT || opt & PL_COLORBAR_ORIENT_LEFT )
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";
1108 if ( opt & PL_COLORBAR_ORIENT_TOP || opt & PL_COLORBAR_ORIENT_BOTTOM )
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 {
1236 if ( opt & PL_COLORBAR_ORIENT_TOP || opt & PL_COLORBAR_ORIENT_BOTTOM )
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 {
1267 if ( opt & PL_COLORBAR_ORIENT_TOP || opt & PL_COLORBAR_ORIENT_BOTTOM )
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
1349 calculate_limits( PLINT position, PLFLT x, PLFLT y,
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.
1743 if ( opt & PL_COLORBAR_ORIENT_RIGHT || opt & PL_COLORBAR_ORIENT_LEFT )
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 }