PLplot  5.10.0
pllegend.c
Go to the documentation of this file.
00001 // All routines that help to create a discrete legend (pllegend) or
00002 // a continuous legend (plcolorbar).
00003 //
00004 // Copyright (C) 2010-2013  Hezekiah M. Carty
00005 // Copyright (C) 2010-2014 Alan W. Irwin
00006 //
00007 // This file is part of PLplot.
00008 //
00009 // PLplot is free software; you can redistribute it and/or modify
00010 // it under the terms of the GNU Library General Public License as published
00011 // by the Free Software Foundation; either version 2 of the License, or
00012 // (at your option) any later version.
00013 //
00014 // PLplot is distributed in the hope that it will be useful,
00015 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017 // GNU Library General Public License for more details.
00018 //
00019 // You should have received a copy of the GNU Library General Public License
00020 // along with PLplot; if not, write to the Free Software
00021 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00022 //
00023 
00028 #include "plplotP.h"
00029 
00030 //--------------------------------------------------------------------------
00045 
00046 static void plgvpsp( PLFLT *p_xmin, PLFLT *p_xmax, PLFLT *p_ymin, PLFLT *p_ymax )
00047 {
00048     if ( plsc->level < 1 )
00049     {
00050         plabort( "plgvpsp: Please call plinit first" );
00051         return;
00052     }
00053     if ( ( plsc->cursub <= 0 ) || ( plsc->cursub > ( plsc->nsubx * plsc->nsuby ) ) )
00054     {
00055         plabort( "plgvpsp: Please call pladv or plenv to go to a subpage" );
00056         return;
00057     }
00058     *p_xmin = ( plsc->vpdxmi - plsc->spdxmi ) / ( plsc->spdxma - plsc->spdxmi );
00059     *p_xmax = ( plsc->vpdxma - plsc->spdxmi ) / ( plsc->spdxma - plsc->spdxmi );
00060     *p_ymin = ( plsc->vpdymi - plsc->spdymi ) / ( plsc->spdyma - plsc->spdymi );
00061     *p_ymax = ( plsc->vpdyma - plsc->spdymi ) / ( plsc->spdyma - plsc->spdymi );
00062 }
00063 
00064 //--------------------------------------------------------------------------
00086 
00087 static void legend_position( PLINT position, PLFLT legend_width, PLFLT legend_height,
00088                              PLFLT *x_legend_position, PLFLT *y_legend_position,
00089                              PLFLT *xsign, PLFLT *ysign )
00090 {
00091     // xorigin, yorigin, xlegend, and ylegend are all calculated for
00092     // one of the 16 standard positions specified by position and are
00093     // expressed in adopted coordinates.  xorigin is the X value of
00094     // the reference point of the adopted coordinates.  yorigin is the
00095     // Y value of the reference point of the adopted coordinates.
00096     // xlegend is the X coordinate of the top-left of the legend box
00097     // relative to the legend box reference point.  ylegend is the y
00098     // coordinate of the top-left of the legend box relative to the
00099     // legend box reference point.
00100 
00101     PLFLT xorigin = 0.0, yorigin = 0.0, xlegend = 0.0, ylegend = 0.0;
00102     // By default the sign of the x and y offsets is positive.
00103     *xsign = 1.;
00104     *ysign = 1.;
00105     if ( position & PL_POSITION_RIGHT )
00106     {
00107         xorigin = 1.;
00108         if ( position & PL_POSITION_TOP )
00109         {
00110             yorigin = 1.;
00111             if ( position & PL_POSITION_INSIDE )
00112             {
00113                 xlegend = -legend_width;
00114                 ylegend = 0.;
00115                 *xsign  = -1.;
00116                 *ysign  = -1.;
00117             }
00118             else if ( position & PL_POSITION_OUTSIDE )
00119             {
00120                 xlegend = 0.;
00121                 ylegend = legend_height;
00122             }
00123             else
00124             {
00125                 plexit( "legend_position: internal logic error 1" );
00126             }
00127         }
00128         else if ( !( position & PL_POSITION_TOP ) && !( position & PL_POSITION_BOTTOM ) )
00129         {
00130             yorigin = 0.5;
00131             ylegend = 0.5 * legend_height;
00132             if ( position & PL_POSITION_INSIDE )
00133             {
00134                 xlegend = -legend_width;
00135                 *xsign  = -1.;
00136             }
00137             else if ( position & PL_POSITION_OUTSIDE )
00138             {
00139                 xlegend = 0.;
00140             }
00141             else
00142             {
00143                 plexit( "legend_position: internal logic error 2" );
00144             }
00145         }
00146         else if ( position & PL_POSITION_BOTTOM )
00147         {
00148             yorigin = 0.;
00149             if ( position & PL_POSITION_INSIDE )
00150             {
00151                 xlegend = -legend_width;
00152                 ylegend = legend_height;
00153                 *xsign  = -1.;
00154             }
00155             else if ( position & PL_POSITION_OUTSIDE )
00156             {
00157                 xlegend = 0.;
00158                 ylegend = 0.;
00159                 *ysign  = -1.;
00160             }
00161             else
00162             {
00163                 plexit( "legend_position: internal logic error 3" );
00164             }
00165         }
00166         else
00167         {
00168             plexit( "legend_position: internal logic error 4" );
00169         }
00170     }
00171     else if ( !( position & PL_POSITION_RIGHT ) && !( position & PL_POSITION_LEFT ) )
00172     {
00173         xorigin = 0.5;
00174         xlegend = -0.5 * legend_width;
00175         if ( position & PL_POSITION_TOP )
00176         {
00177             yorigin = 1.;
00178             if ( position & PL_POSITION_INSIDE )
00179             {
00180                 ylegend = 0.;
00181                 *ysign  = -1.;
00182             }
00183             else if ( position & PL_POSITION_OUTSIDE )
00184             {
00185                 ylegend = legend_height;
00186             }
00187             else
00188             {
00189                 plexit( "legend_position: internal logic error 5" );
00190             }
00191         }
00192         else if ( position & PL_POSITION_BOTTOM )
00193         {
00194             yorigin = 0.;
00195             if ( position & PL_POSITION_INSIDE )
00196             {
00197                 ylegend = legend_height;
00198             }
00199             else if ( position & PL_POSITION_OUTSIDE )
00200             {
00201                 ylegend = 0.;
00202                 *ysign  = -1.;
00203             }
00204             else
00205             {
00206                 plexit( "legend_position: internal logic error 6" );
00207             }
00208         }
00209         else
00210         {
00211             plexit( "legend_position: internal logic error 7" );
00212         }
00213     }
00214     else if ( position & PL_POSITION_LEFT )
00215     {
00216         xorigin = 0.;
00217         if ( position & PL_POSITION_TOP )
00218         {
00219             yorigin = 1.;
00220             if ( position & PL_POSITION_INSIDE )
00221             {
00222                 xlegend = 0.;
00223                 ylegend = 0.;
00224                 *ysign  = -1.;
00225             }
00226             else if ( position & PL_POSITION_OUTSIDE )
00227             {
00228                 xlegend = -legend_width;
00229                 ylegend = legend_height;
00230                 *xsign  = -1.;
00231             }
00232             else
00233             {
00234                 plexit( "legend_position: internal logic error 8" );
00235             }
00236         }
00237         else if ( !( position & PL_POSITION_TOP ) && !( position & PL_POSITION_BOTTOM ) )
00238         {
00239             yorigin = 0.5;
00240             ylegend = 0.5 * legend_height;
00241             if ( position & PL_POSITION_INSIDE )
00242             {
00243                 xlegend = 0.;
00244             }
00245             else if ( position & PL_POSITION_OUTSIDE )
00246             {
00247                 xlegend = -legend_width;
00248                 *xsign  = -1.;
00249             }
00250             else
00251             {
00252                 plexit( "legend_position: internal logic error 9" );
00253             }
00254         }
00255         else if ( position & PL_POSITION_BOTTOM )
00256         {
00257             yorigin = 0.;
00258             if ( position & PL_POSITION_INSIDE )
00259             {
00260                 ylegend = legend_height;
00261                 xlegend = 0.;
00262             }
00263             else if ( position & PL_POSITION_OUTSIDE )
00264             {
00265                 xlegend = -legend_width;
00266                 ylegend = 0.;
00267                 *xsign  = -1.;
00268                 *ysign  = -1.;
00269             }
00270             else
00271             {
00272                 plexit( "legend_position: internal logic error 10" );
00273             }
00274         }
00275         else
00276         {
00277             plexit( "legend_position: internal logic error 11" );
00278         }
00279     }
00280     else
00281     {
00282         plexit( "legend_position: internal logic error 12" );
00283     }
00284     *x_legend_position = xorigin + xlegend;
00285     *y_legend_position = yorigin + ylegend;
00286 }
00287 
00288 //--------------------------------------------------------------------------
00294 
00295 static void get_subpage_per_mm( PLFLT *x_subpage_per_mm, PLFLT *y_subpage_per_mm )
00296 {
00297     // Size of subpage in mm
00298     PLFLT mxmin, mxmax, mymin, mymax;
00299     plgspa( &mxmin, &mxmax, &mymin, &mymax );
00300     *x_subpage_per_mm = 1. / ( mxmax - mxmin );
00301     *y_subpage_per_mm = 1. / ( mymax - mymin );
00302 }
00303 
00304 //--------------------------------------------------------------------------
00311 
00312 static PLFLT get_character_or_symbol_height( PLBOOL ifcharacter )
00313 {
00314     // Character height in mm
00315     PLFLT default_mm, char_height_mm;
00316     PLFLT x_subpage_per_mm, y_subpage_per_mm;
00317 
00318     if ( ifcharacter )
00319     {
00320         plgchr( &default_mm, &char_height_mm );
00321     }
00322     else
00323     {
00324         default_mm     = plsc->symdef;
00325         char_height_mm = plsc->symht;
00326     }
00327     get_subpage_per_mm( &x_subpage_per_mm, &y_subpage_per_mm );
00328     return ( char_height_mm * y_subpage_per_mm );
00329 }
00330 
00331 //--------------------------------------------------------------------------
00343 
00344 #define adopted_to_subpage_x( nx )    ( ( xdmin_adopted ) + ( nx ) * ( ( xdmax_adopted ) - ( xdmin_adopted ) ) )
00345 
00346 //--------------------------------------------------------------------------
00358 
00359 #define subpage_to_adopted_x( nx )    ( ( nx - xdmin_adopted ) / ( ( xdmax_adopted ) - ( xdmin_adopted ) ) )
00360 
00361 //--------------------------------------------------------------------------
00373 
00374 #define adopted_to_subpage_y( ny )    ( ( ydmin_adopted ) + ( ny ) * ( ( ydmax_adopted ) - ( ydmin_adopted ) ) )
00375 
00376 //--------------------------------------------------------------------------
00388 
00389 #define subpage_to_adopted_y( ny )    ( ( ny - ydmin_adopted ) / ( ( ydmax_adopted ) - ( ydmin_adopted ) ) )
00390 
00391 //--------------------------------------------------------------------------
00529 
00530 void
00531 c_pllegend( PLFLT *p_legend_width, PLFLT *p_legend_height,
00532             PLINT opt, PLINT position, PLFLT x, PLFLT y, PLFLT plot_width,
00533             PLINT bg_color, PLINT bb_color, PLINT bb_style,
00534             PLINT nrow, PLINT ncolumn,
00535             PLINT nlegend, const PLINT *opt_array,
00536             PLFLT text_offset, PLFLT text_scale, PLFLT text_spacing,
00537             PLFLT text_justification,
00538             const PLINT *text_colors, const char * const *text,
00539             const PLINT *box_colors, const PLINT *box_patterns,
00540             const PLFLT *box_scales, const PLFLT *box_line_widths,
00541             const PLINT *line_colors, const PLINT *line_styles,
00542             const PLFLT *line_widths,
00543             const PLINT *symbol_colors, const PLFLT *symbol_scales,
00544             const PLINT *symbol_numbers, const char * const *symbols )
00545 
00546 {
00547     // Legend position
00548     PLFLT plot_x, plot_x_end, plot_x_subpage, plot_x_end_subpage;
00549     PLFLT plot_y, plot_y_subpage;
00550     PLFLT text_x, text_y, text_x_subpage, text_y_subpage;
00551     // Character height (normalized subpage coordinates)
00552     PLFLT character_height, character_width, symbol_width = 0.0;
00553     // x, y-position of the current legend entry
00554     PLFLT ty, xshift, drow, dcolumn;
00555     // Positions of the legend entries
00556     PLFLT dxs, *xs = NULL, *ys = NULL, xl[2], yl[2], xbox[4], ybox[4];
00557     PLINT i, j;
00558     // Active attributes to be saved and restored afterward.
00559     PLINT col0_save       = plsc->icol0,
00560           line_style_save = plsc->line_style,
00561           pattern_save    = plsc->patt;
00562     PLFLT line_width_save = plsc->width;
00563     PLFLT text_scale_save = plsc->chrht / plsc->chrdef;
00564     // Saved external world coordinates of viewport.
00565     PLFLT xwmin_save, xwmax_save, ywmin_save, ywmax_save;
00566     // Saved external normalized coordinates of viewport.
00567     // (These are actual values used only for the restore.)
00568     PLFLT xdmin_save = 0.0, xdmax_save = 0.0, ydmin_save = 0.0, ydmax_save = 0.0;
00569     // Limits of adopted coordinates used to calculate all coordinate
00570     // transformations.
00571     PLFLT xdmin_adopted = 0.0, xdmax_adopted = 0.0, ydmin_adopted = 0.0, ydmax_adopted = 0.0;
00572 
00573     PLFLT x_subpage_per_mm, y_subpage_per_mm, text_width0 = 0., text_width;
00574     PLFLT width_border, column_separation,
00575           legend_width, legend_height, legend_width_ac, legend_height_ac;
00576     PLFLT x_legend_position, y_legend_position, xsign, ysign;
00577 
00578     PLINT some_symbols       = 0;
00579     PLINT max_symbol_numbers = 0;
00580     PLINT irow       = 0, icolumn = 0;
00581     int   some_boxes = 0, some_lines = 0;
00582 
00583     // Default nrow, ncolumn.
00584     nrow    = MAX( nrow, 1 );
00585     ncolumn = MAX( ncolumn, 1 );
00586     if ( nrow * ncolumn < nlegend )
00587     {
00588         // Make smaller one large enough to accomodate nlegend.
00589         if ( ncolumn < nrow )
00590             ncolumn = ( nlegend % nrow ) ? ( nlegend / nrow ) + 1 : nlegend / nrow;
00591         else
00592             nrow = ( nlegend % ncolumn ) ? ( nlegend / ncolumn ) + 1 : nlegend / ncolumn;
00593     }
00594     // fprintf(stdout, "nrow, ncolumn = %d, %d\n", nrow, ncolumn);
00595 
00596     // Default position flags and sanity checks for position flags.
00597     if ( !( position & PL_POSITION_RIGHT ) && !( position & PL_POSITION_LEFT ) && !( position & PL_POSITION_TOP ) && !( position & PL_POSITION_BOTTOM ) )
00598     {
00599         position = position | PL_POSITION_RIGHT | PL_POSITION_TOP;
00600     }
00601     else if ( ( position & PL_POSITION_RIGHT ) && ( position & PL_POSITION_LEFT ) )
00602     {
00603         plabort( "pllegend: PL_POSITION_RIGHT and PL_POSITION_LEFT cannot be simultaneously set." );
00604         return;
00605     }
00606 
00607     else if ( ( position & PL_POSITION_TOP ) && ( position & PL_POSITION_BOTTOM ) )
00608     {
00609         plabort( "pllegend: PL_POSITION_TOP and PL_POSITION_BOTTOM cannot be simultaneously set." );
00610         return;
00611     }
00612 
00613     if ( !( position & PL_POSITION_INSIDE ) && !( position & PL_POSITION_OUTSIDE ) )
00614     {
00615         position = position | PL_POSITION_INSIDE;
00616     }
00617     else if ( ( position & PL_POSITION_INSIDE ) && ( position & PL_POSITION_OUTSIDE ) )
00618     {
00619         plabort( "pllegend: PL_POSITION_INSIDE and PL_POSITION_OUTSIDE cannot be simultaneously set." );
00620         return;
00621     }
00622 
00623     if ( !( position & PL_POSITION_VIEWPORT ) && !( position & PL_POSITION_SUBPAGE ) )
00624     {
00625         position = position | PL_POSITION_VIEWPORT;
00626     }
00627     else if ( ( position & PL_POSITION_VIEWPORT ) && ( position & PL_POSITION_SUBPAGE ) )
00628     {
00629         plabort( "pllegend: PL_POSITION_VIEWPORT and PL_POSITION_SUBPAGE cannot be simultaneously set." );
00630         return;
00631     }
00632 
00633     // xdmin_save, etc., are the actual external relative viewport
00634     // coordinates within the current sub-page used only for
00635     // restoration at the end.
00636     plgvpsp( &xdmin_save, &xdmax_save, &ydmin_save, &ydmax_save );
00637 
00638     // Choose adopted coordinates.
00639     if ( position & PL_POSITION_SUBPAGE )
00640         plvpor( 0., 1., 0., 1. );
00641 
00642     // xdmin_adopted, etc., are the adopted limits of the coordinates
00643     // within the current sub-page used for all coordinate
00644     // transformations.
00645     // If position & PL_POSITION_VIEWPORT is true, these limits
00646     // are the external relative viewport limits.
00647     // If position & PL_POSITION_SUBPAGE is true, these
00648     // coordinates are the relative subpage coordinates.
00649     plgvpsp( &xdmin_adopted, &xdmax_adopted, &ydmin_adopted, &ydmax_adopted );
00650 
00651     // xwmin_save, etc., are the external world coordinates corresponding
00652     // to the external viewport boundaries.
00653     plgvpw( &xwmin_save, &xwmax_save, &ywmin_save, &ywmax_save );
00654 
00655     // Internal viewport corresponds to sub-page so that all legends will
00656     // be clipped at sub-page boundaries.
00657     plvpor( 0., 1., 0., 1. );
00658 
00659     // Internal world coordinates are the same as normalized internal
00660     // viewport coordinates which are the same as normalized subpage coordinates.
00661     plwind( 0., 1., 0., 1. );
00662 
00663     for ( i = 0; i < nlegend; i++ )
00664     {
00665         if ( opt_array[i] & PL_LEGEND_COLOR_BOX )
00666             some_boxes = 1;
00667         if ( opt_array[i] & PL_LEGEND_LINE )
00668             some_lines = 1;
00669         if ( opt_array[i] & PL_LEGEND_SYMBOL )
00670         {
00671             if ( symbol_numbers != NULL )
00672                 max_symbol_numbers = MAX( max_symbol_numbers, symbol_numbers[i] );
00673             some_symbols = 1;
00674         }
00675     }
00676 
00677     // Sanity checks on NULL arrays:
00678     if ( some_boxes && ( box_colors == NULL || box_patterns == NULL || box_scales == NULL || box_line_widths == NULL ) )
00679     {
00680         plabort( "pllegend: all box_* arrays must be defined when the PL_LEGEND_COLOR_BOX bit is set." );
00681         return;
00682     }
00683 
00684     if ( some_lines && ( line_colors == NULL || line_styles == NULL || line_widths == NULL ) )
00685     {
00686         plabort( "pllegend: all line_* arrays must be defined when the PL_LEGEND_LINE bit is set." );
00687         return;
00688     }
00689 
00690     if ( some_symbols && ( symbol_colors == NULL || symbol_scales == NULL || symbol_numbers == NULL ) )
00691     {
00692         plabort( "pllegend: all symbol_* arrays must be defined when the PL_LEGEND_SYMBOL bit is set." );
00693         return;
00694     }
00695 
00696     // Get character height and width in normalized subpage coordinates.
00697     character_height = get_character_or_symbol_height( TRUE );
00698     character_width  = character_height;
00699 
00700     // Calculate maximum width of text area.
00701     plschr( 0., text_scale );
00702     for ( i = 0; i < nlegend; i++ )
00703     {
00704         // units are mm.
00705         text_width0 = MAX( text_width0, plstrl( text[i] ) );
00706     }
00707     get_subpage_per_mm( &x_subpage_per_mm, &y_subpage_per_mm );
00708 
00709     // units are normalized subpage coordinates.
00710     text_width0 = x_subpage_per_mm * text_width0;
00711 
00712     // Allow gap on end closest to legend plot.
00713     text_width = text_width0 + text_offset * character_width;
00714 
00715     // Allow small border area where only the background is plotted
00716     // for left and right of legend.  0.4 seems to be a reasonable factor
00717     // that gives a good-looking result.
00718     width_border = 0.4 * character_width;
00719     // Separate columns (if any) by 2.0 * character_width.
00720     column_separation = 2.0 * character_width;
00721 
00722     // Total width and height of legend area in normalized subpage coordinates.
00723     legend_width = 2. * width_border + ( ncolumn - 1 ) * column_separation +
00724                    ncolumn * ( text_width +
00725                                adopted_to_subpage_x( plot_width ) - adopted_to_subpage_x( 0. ) );
00726     legend_height = nrow * text_spacing * character_height;
00727 
00728     // Total width and height of legend area in adopted coordinates.
00729 
00730     legend_width_ac  = subpage_to_adopted_x( legend_width ) - subpage_to_adopted_x( 0. );
00731     legend_height_ac = subpage_to_adopted_y( legend_height ) - subpage_to_adopted_y( 0. );
00732     *p_legend_width  = legend_width_ac;
00733     *p_legend_height = legend_height_ac;
00734 
00735     // dcolumn is the spacing from one column to the next and
00736     // drow is the spacing from one row to the next.
00737     dcolumn = column_separation + text_width +
00738               adopted_to_subpage_x( plot_width ) - adopted_to_subpage_x( 0. );
00739     drow = text_spacing * character_height;
00740 
00741     legend_position( position, legend_width_ac, legend_height_ac, &x_legend_position, &y_legend_position, &xsign, &ysign );
00742     plot_x     = x * xsign + x_legend_position;
00743     plot_y     = y * ysign + y_legend_position;
00744     plot_x_end = plot_x + plot_width;
00745     // Normalized subpage coordinates for legend plots
00746     plot_x_subpage     = adopted_to_subpage_x( plot_x );
00747     plot_y_subpage     = adopted_to_subpage_y( plot_y );
00748     plot_x_end_subpage = adopted_to_subpage_x( plot_x_end );
00749 
00750     // Get normalized subpage positions of the start of the legend text
00751     text_x         = plot_x_end;
00752     text_y         = plot_y;
00753     text_x_subpage = adopted_to_subpage_x( text_x ) +
00754                      text_offset * character_width;
00755     text_y_subpage = adopted_to_subpage_y( text_y );
00756 
00757     if ( opt & PL_LEGEND_BACKGROUND )
00758     {
00759         PLFLT xbg[4] = {
00760             plot_x_subpage,
00761             plot_x_subpage,
00762             plot_x_subpage + legend_width,
00763             plot_x_subpage + legend_width,
00764         };
00765         PLFLT ybg[4] = {
00766             plot_y_subpage,
00767             plot_y_subpage - legend_height,
00768             plot_y_subpage - legend_height,
00769             plot_y_subpage,
00770         };
00771         plpsty( 0 );
00772         plcol0( bg_color );
00773         plfill( 4, xbg, ybg );
00774         plcol0( col0_save );
00775     }
00776 
00777     if ( opt & PL_LEGEND_BOUNDING_BOX )
00778     {
00779         PLFLT xbb[5] = {
00780             plot_x_subpage,
00781             plot_x_subpage,
00782             plot_x_subpage + legend_width,
00783             plot_x_subpage + legend_width,
00784             plot_x_subpage,
00785         };
00786         PLFLT ybb[5] = {
00787             plot_y_subpage,
00788             plot_y_subpage - legend_height,
00789             plot_y_subpage - legend_height,
00790             plot_y_subpage,
00791             plot_y_subpage,
00792         };
00793         pllsty( bb_style );
00794         plcol0( bb_color );
00795         plline( 5, xbb, ybb );
00796         plcol0( col0_save );
00797         pllsty( line_style_save );
00798     }
00799 
00800     if ( opt & PL_LEGEND_TEXT_LEFT )
00801     {
00802         // text area on left, plot area on right.
00803         text_x_subpage      = plot_x_subpage;
00804         plot_x_subpage     += text_width;
00805         plot_x_end_subpage += text_width;
00806     }
00807     // adjust border after background is drawn.
00808     plot_x_subpage     += width_border;
00809     plot_x_end_subpage += width_border;
00810     text_x_subpage     += width_border;
00811 
00812     if ( some_symbols )
00813     {
00814         max_symbol_numbers = MAX( 2, max_symbol_numbers );
00815         if ( ( ( xs = (PLFLT *) malloc( (size_t) max_symbol_numbers * sizeof ( PLFLT ) ) ) == NULL ) ||
00816              ( ( ys = (PLFLT *) malloc( (size_t) max_symbol_numbers * sizeof ( PLFLT ) ) ) == NULL ) )
00817         {
00818             plexit( "pllegend: Insufficient memory" );
00819         }
00820 
00821         // Get symbol width in normalized subpage coordinates if symbols are plotted to
00822         // adjust ends of line of symbols.
00823         // AWI, no idea why must use 0.5 factor to get ends of symbol lines
00824         // to line up approximately correctly with plotted legend lines.
00825         // Factor should be unity.
00826         symbol_width = 0.5 * get_character_or_symbol_height( TRUE );
00827     }
00828 
00829     // Draw each legend entry
00830     for ( i = 0; i < nlegend; i++ )
00831     {
00832         // y position of text, lines, symbols, and/or centre of cmap0 box.
00833         ty     = text_y_subpage - ( (double) irow + 0.5 ) * drow;
00834         xshift = (double) icolumn * dcolumn;
00835         // Label/name for the legend
00836         plcol0( text_colors[i] );
00837         plschr( 0., text_scale );
00838         plptex( text_x_subpage + xshift + text_justification * text_width0, ty, 0.1, 0.0, text_justification, text[i] );
00839 
00840         if ( !( opt_array[i] & PL_LEGEND_NONE ) )
00841         {
00842             if ( opt_array[i] & PL_LEGEND_COLOR_BOX )
00843             {
00844                 plcol0( box_colors[i] );
00845                 plpsty( box_patterns[i] );
00846                 plwidth( box_line_widths[i] );
00847                 xbox[0] = plot_x_subpage + xshift;
00848                 xbox[1] = xbox[0];
00849                 xbox[2] = plot_x_end_subpage + xshift;
00850                 xbox[3] = xbox[2];
00851                 ybox[0] = ty + 0.5 * drow * box_scales[i];
00852                 ybox[1] = ty - 0.5 * drow * box_scales[i];
00853                 ybox[2] = ty - 0.5 * drow * box_scales[i];
00854                 ybox[3] = ty + 0.5 * drow * box_scales[i];
00855                 plfill( 4, xbox, ybox );
00856                 pllsty( line_style_save );
00857                 plwidth( line_width_save );
00858             }
00859             if ( opt_array[i] & PL_LEGEND_LINE )
00860             {
00861                 plcol0( line_colors[i] );
00862                 pllsty( line_styles[i] );
00863                 plwidth( line_widths[i] );
00864                 xl[0] = plot_x_subpage + xshift;
00865                 xl[1] = plot_x_end_subpage + xshift;
00866                 yl[0] = ty;
00867                 yl[1] = ty;
00868                 plline( 2, xl, yl );
00869                 pllsty( line_style_save );
00870                 plwidth( line_width_save );
00871             }
00872 
00873             if ( opt_array[i] & PL_LEGEND_SYMBOL )
00874             {
00875                 plcol0( symbol_colors[i] );
00876                 plschr( 0., symbol_scales[i] );
00877                 dxs = ( plot_x_end_subpage - plot_x_subpage - symbol_width ) / (double) ( MAX( symbol_numbers[i], 2 ) - 1 );
00878                 for ( j = 0; j < symbol_numbers[i]; j++ )
00879                 {
00880                     xs[j] = plot_x_subpage + xshift +
00881                             0.5 * symbol_width + dxs * (double) j;
00882                     ys[j] = ty;
00883                 }
00884                 plstring( symbol_numbers[i], xs, ys, symbols[i] );
00885             }
00886         }
00887 
00888         // Set irow, icolumn for next i value.
00889         if ( opt & PL_LEGEND_ROW_MAJOR )
00890         {
00891             icolumn++;
00892             if ( icolumn >= ncolumn )
00893             {
00894                 icolumn = 0;
00895                 irow++;
00896             }
00897         }
00898         else
00899         {
00900             irow++;
00901             if ( irow >= nrow )
00902             {
00903                 irow = 0;
00904                 icolumn++;
00905             }
00906         }
00907     }
00908     if ( some_symbols )
00909     {
00910         free( xs );
00911         free( ys );
00912     }
00913 
00914     // Restore
00915     plcol0( col0_save );
00916     plschr( 0., text_scale_save );
00917     plpsty( pattern_save );
00918     plvpor( xdmin_save, xdmax_save, ydmin_save, ydmax_save );
00919     plwind( xwmin_save, xwmax_save, ywmin_save, ywmax_save );
00920 
00921     return;
00922 }
00923 
00924 //--------------------------------------------------------------------------
00931 
00932 static void
00933 remove_characters( char *string, const char *characters )
00934 {
00935     char       *src, *dst;
00936     const char *ptr;
00937     for ( src = dst = string; *src != '\0'; src++ )
00938     {
00939         ptr = characters;
00940         while ( ( *ptr != '\0' ) && ( *src != *ptr ) )
00941             ptr++;
00942         if ( *src != *ptr )
00943         {
00944             *dst = *src;
00945             dst++;
00946         }
00947     }
00948     *dst = '\0';
00949 }
00950 
00951 //--------------------------------------------------------------------------
00971 
00972 static void
00973 draw_cap( PLBOOL if_edge, PLINT orientation, PLFLT xmin, PLFLT xmax,
00974           PLFLT ymin, PLFLT ymax, PLFLT color )
00975 {
00976     // Save current drawing color.
00977     PLINT col0_save = plsc->icol0;
00978     PLFLT xhalf     = 0.5 * ( xmin + xmax );
00979     PLFLT yhalf     = 0.5 * ( ymin + ymax );
00980 
00981     // World coordinates for the triangle.  Due to setup in the
00982     // plcolorbar routine that calls this, these are also normalized
00983     // subpage coordinates.
00984     PLFLT xs[3];
00985     PLFLT ys[3];
00986 
00987     if ( orientation == PL_COLORBAR_ORIENT_RIGHT )
00988     {
00989         xs[0] = xmin;
00990         ys[0] = ymin;
00991         xs[1] = xmax;
00992         ys[1] = yhalf;
00993         xs[2] = xmin;
00994         ys[2] = ymax;
00995     }
00996     else if ( orientation == PL_COLORBAR_ORIENT_TOP )
00997     {
00998         xs[0] = xmax;
00999         ys[0] = ymin;
01000         xs[1] = xhalf;
01001         ys[1] = ymax;
01002         xs[2] = xmin;
01003         ys[2] = ymin;
01004     }
01005     else if ( orientation == PL_COLORBAR_ORIENT_LEFT )
01006     {
01007         xs[0] = xmax;
01008         ys[0] = ymax;
01009         xs[1] = xmin;
01010         ys[1] = yhalf;
01011         xs[2] = xmax;
01012         ys[2] = ymin;
01013     }
01014     else if ( orientation == PL_COLORBAR_ORIENT_BOTTOM )
01015     {
01016         xs[0] = xmin;
01017         ys[0] = ymax;
01018         xs[1] = xhalf;
01019         ys[1] = ymin;
01020         xs[2] = xmax;
01021         ys[2] = ymax;
01022     }
01023     else
01024     {
01025         plexit( "draw_cap: internal error. Incorrect orientation" );
01026     }
01027 
01028     plcol1( color );
01029     plfill( 3, xs, ys );
01030     // Restore the drawing color
01031     plcol0( col0_save );
01032 
01033     // Draw cap outline
01034     if ( if_edge )
01035         plline( 3, xs, ys );
01036 }
01037 
01038 //--------------------------------------------------------------------------
01060 
01061 static void
01062 draw_box( PLBOOL if_bb, PLINT opt, const char *axis_opts, PLBOOL if_edge,
01063           PLFLT ticks, PLINT sub_ticks, PLINT n_values, const PLFLT *values )
01064 {
01065     // axis option strings.
01066     const char *edge_string;
01067     size_t     length_axis_opts = strlen( axis_opts );
01068     char       *local_axis_opts;
01069 
01070     // local_axis_opts is local version that can be modified from
01071     // const input version.
01072     if ( ( local_axis_opts = (char *) malloc( ( length_axis_opts + 1 ) * sizeof ( char ) ) ) == NULL )
01073     {
01074         plexit( "draw_box: Insufficient memory" );
01075     }
01076     strcpy( local_axis_opts, axis_opts );
01077 
01078     plsc->if_boxbb = if_bb;
01079     // Draw numerical labels and tick marks if this is a shade color bar
01080     // TODO: A better way to handle this would be to update the
01081     // internals of plbox to support custom tick and label positions
01082     // along an axis.
01083 
01084     if ( opt & PL_COLORBAR_SHADE && opt & PL_COLORBAR_SHADE_LABEL )
01085     {
01086         if ( opt & PL_COLORBAR_ORIENT_RIGHT || opt & PL_COLORBAR_ORIENT_LEFT )
01087             label_box_custom( local_axis_opts, n_values, values, "", 0, NULL );
01088         else
01089             label_box_custom( "", 0, NULL, local_axis_opts, n_values, values );
01090         if ( if_bb )
01091         {
01092             plsc->if_boxbb = FALSE;
01093             free( local_axis_opts );
01094             return;
01095         }
01096         // Exclude ticks for plbox call below since those tick marks and
01097         // associated labels have already been handled in a custom way above.
01098         remove_characters( local_axis_opts, "TtXx" );
01099     }
01100 
01101     // Draw the outline for the entire color bar, tick marks, tick labels.
01102 
01103     if ( if_edge )
01104         edge_string = "bc";
01105     else
01106         edge_string = "uw";
01107     if ( opt & PL_COLORBAR_ORIENT_TOP || opt & PL_COLORBAR_ORIENT_BOTTOM )
01108     {
01109         plbox( edge_string, 0.0, 0, local_axis_opts, ticks, sub_ticks );
01110     }
01111     else
01112     {
01113         plbox( local_axis_opts, ticks, sub_ticks, edge_string, 0.0, 0 );
01114     }
01115     plsc->if_boxbb = FALSE;
01116 
01117     free( local_axis_opts );
01118 }
01119 
01120 //--------------------------------------------------------------------------
01136 
01137 static void
01138 draw_label( PLBOOL if_bb, PLINT opt, const char *label )
01139 {
01140     // Justification of label text
01141     PLFLT just = 0.0;
01142 
01143     // How far away from the axis should the label be drawn in units of
01144     // the character height?
01145     PLFLT label_offset = 1.2;
01146 
01147     // For building plmtex option string.
01148 #define max_opts    25
01149     char opt_label[max_opts];
01150     char perp;
01151 
01152     // To help sanity check number of specified labels.
01153     PLINT nlabel = 0;
01154 
01155     // aspect ratio of physical area of subpage.
01156     //PLFLT aspspp = ( ( plsc->sppxma - plsc->sppxmi ) / plsc->xpmm ) /
01157     //               ( ( plsc->sppyma - plsc->sppymi ) / plsc->ypmm );
01158 
01159     // Character height in y and x normalized subpage coordinates.
01160     //PLFLT character_height_y = get_character_or_symbol_height( TRUE );
01161     // character height _in normalized subpage coordinates_ is smaller
01162     // in the x direction if the subpage aspect ratio is larger than one.
01163     //PLFLT character_height_x = character_height_y / aspspp;
01164 
01165     // Ratio of normalized subpage coordinates to mm coordinates in
01166     // x and y.
01167     //PLFLT spxpmm          = plsc->xpmm / ( plsc->sppxma - plsc->sppxmi );
01168     //PLFLT spypmm          = plsc->ypmm / ( plsc->sppyma - plsc->sppymi );
01169     PLFLT label_length_mm = plstrl( label );
01170 
01171     PLFLT parallel_height_mm = 0.0, perpendicular_height_mm = 0.0,
01172           default_mm, char_height_mm;
01173 
01174     // Only honor first bit in list of
01175     // PL_COLORBAR_LABEL_(RIGHT|TOP|LEFT|BOTTOM).
01176     if ( opt & PL_COLORBAR_LABEL_RIGHT )
01177     {
01178         nlabel = 1;
01179     }
01180     if ( opt & PL_COLORBAR_LABEL_TOP )
01181     {
01182         if ( nlabel == 1 )
01183             opt = opt & ~PL_COLORBAR_LABEL_TOP;
01184         else
01185             nlabel = 1;
01186     }
01187     if ( opt & PL_COLORBAR_LABEL_LEFT )
01188     {
01189         if ( nlabel == 1 )
01190             opt = opt & ~PL_COLORBAR_LABEL_LEFT;
01191         else
01192             nlabel = 1;
01193     }
01194     if ( opt & PL_COLORBAR_LABEL_BOTTOM )
01195     {
01196         if ( nlabel == 1 )
01197             opt = opt & ~PL_COLORBAR_LABEL_BOTTOM;
01198         else
01199             nlabel = 1;
01200     }
01201     // If no label wanted, then return.
01202     if ( nlabel == 0 )
01203         return;
01204 
01205     plgchr( &default_mm, &char_height_mm );
01206 
01207     // Start preparing data to help plot the label or
01208     // calculate the corresponding bounding box changes.
01209 
01210     if ( if_bb )
01211     {
01212         // Bounding-box limits are the viewport limits in mm before corrections
01213         // for decorations are applied.
01214         plsc->boxbb_xmin = plsc->vppxmi / plsc->xpmm;
01215         plsc->boxbb_xmax = plsc->vppxma / plsc->xpmm;
01216         plsc->boxbb_ymin = plsc->vppymi / plsc->ypmm;
01217         plsc->boxbb_ymax = plsc->vppyma / plsc->ypmm;
01218 
01219         // For labels written parallel to axis, label_offset of zero
01220         // corresponds to character centred on edge so should add 0.5 to
01221         // height to obtain bounding box edge in direction away from
01222         // edge.  However, experimentally found 0.8 gave a better
01223         // looking result.
01224         parallel_height_mm = ( label_offset + 0.8 ) * char_height_mm;
01225 
01226         // For labels written perpendicular to axis, label_offset of
01227         // zero corresponds to a character whose edge just touches the
01228         // edge of the box so should add 0. to label_offset (corrected
01229         // by -0.5 below) to obtain bounding box edge in direction away
01230         // from edge, and that value apparently works.
01231         perpendicular_height_mm = ( label_offset - 0.5 + 0.0 ) * char_height_mm;
01232     }
01233     if ( opt & PL_COLORBAR_LABEL_LEFT )
01234     {
01235         if ( opt & PL_COLORBAR_ORIENT_TOP || opt & PL_COLORBAR_ORIENT_BOTTOM )
01236         {
01237             perp = '\0';
01238             just = 0.5;
01239             if ( if_bb )
01240             {
01241                 plsc->boxbb_xmin = MIN( plsc->boxbb_xmin, plsc->vppxmi /
01242                     plsc->xpmm - parallel_height_mm );
01243                 plsc->boxbb_ymin = MIN( plsc->boxbb_ymin,
01244                     0.5 * ( plsc->vppymi + plsc->vppyma ) /
01245                     plsc->ypmm - 0.5 * label_length_mm );
01246                 plsc->boxbb_ymax = MAX( plsc->boxbb_ymax,
01247                     0.5 * ( plsc->vppymi + plsc->vppyma ) /
01248                     plsc->ypmm + 0.5 * label_length_mm );
01249             }
01250         }
01251         else
01252         {
01253             perp          = 'v';
01254             just          = 1.0;
01255             label_offset -= 0.5;
01256             if ( if_bb )
01257             {
01258                 plsc->boxbb_xmin = MIN( plsc->boxbb_xmin, plsc->vppxmi /
01259                     plsc->xpmm - perpendicular_height_mm - label_length_mm );
01260             }
01261         }
01262         snprintf( opt_label, max_opts, "l%c", perp );
01263     }
01264     else if ( opt & PL_COLORBAR_LABEL_RIGHT )
01265     {
01266         if ( opt & PL_COLORBAR_ORIENT_TOP || opt & PL_COLORBAR_ORIENT_BOTTOM )
01267         {
01268             perp = '\0';
01269             just = 0.5;
01270             if ( if_bb )
01271             {
01272                 plsc->boxbb_xmax = MAX( plsc->boxbb_xmax, plsc->vppxma /
01273                     plsc->xpmm + parallel_height_mm );
01274                 plsc->boxbb_ymin = MIN( plsc->boxbb_ymin,
01275                     0.5 * ( plsc->vppymi + plsc->vppyma ) /
01276                     plsc->ypmm - 0.5 * label_length_mm );
01277                 plsc->boxbb_ymax = MAX( plsc->boxbb_ymax,
01278                     0.5 * ( plsc->vppymi + plsc->vppyma ) /
01279                     plsc->ypmm + 0.5 * label_length_mm );
01280             }
01281         }
01282         else
01283         {
01284             perp          = 'v';
01285             just          = 0.0;
01286             label_offset -= 0.5;
01287             if ( if_bb )
01288             {
01289                 plsc->boxbb_xmax = MAX( plsc->boxbb_xmax, plsc->vppxma /
01290                     plsc->xpmm + perpendicular_height_mm + label_length_mm );
01291             }
01292         }
01293         snprintf( opt_label, max_opts, "r%c", perp );
01294     }
01295     else if ( opt & PL_COLORBAR_LABEL_TOP )
01296     {
01297         perp = '\0';
01298         just = 0.5;
01299         snprintf( opt_label, max_opts, "t%c", perp );
01300         if ( if_bb )
01301         {
01302             plsc->boxbb_ymax = MAX( plsc->boxbb_ymax, plsc->vppyma /
01303                 plsc->ypmm + parallel_height_mm );
01304             plsc->boxbb_xmin = MIN( plsc->boxbb_xmin,
01305                 0.5 * ( plsc->vppxmi + plsc->vppxma ) /
01306                 plsc->xpmm - 0.5 * label_length_mm );
01307             plsc->boxbb_xmax = MAX( plsc->boxbb_xmax,
01308                 0.5 * ( plsc->vppxmi + plsc->vppxma ) /
01309                 plsc->xpmm + 0.5 * label_length_mm );
01310         }
01311     }
01312     else if ( opt & PL_COLORBAR_LABEL_BOTTOM )
01313     {
01314         perp = '\0';
01315         just = 0.5;
01316         snprintf( opt_label, max_opts, "b%c", perp );
01317         if ( if_bb )
01318         {
01319             plsc->boxbb_ymin = MIN( plsc->boxbb_ymin, plsc->vppymi /
01320                 plsc->ypmm - parallel_height_mm );
01321             plsc->boxbb_xmin = MIN( plsc->boxbb_xmin,
01322                 0.5 * ( plsc->vppxmi + plsc->vppxma ) /
01323                 plsc->xpmm - 0.5 * label_length_mm );
01324             plsc->boxbb_xmax = MAX( plsc->boxbb_xmax,
01325                 0.5 * ( plsc->vppxmi + plsc->vppxma ) /
01326                 plsc->xpmm + 0.5 * label_length_mm );
01327         }
01328     }
01329     if ( !if_bb )
01330         plmtex( opt_label, label_offset, 0.5, just, label );
01331 }
01332 
01333 //--------------------------------------------------------------------------
01344 // dx_subpage, dy_subpageDifferences between normalized subpage coordinates for the old
01345 // bounding box and the new one.
01346 
01347 static void
01348 calculate_limits( PLINT position, PLFLT x, PLFLT y,
01349                   PLFLT xdmin_adopted, PLFLT xdmax_adopted, PLFLT ydmin_adopted, PLFLT ydmax_adopted,
01350                   PLFLT prior_bb_height,
01351                   PLFLT *p_colorbar_width_bb, PLFLT *p_colorbar_height_bb,
01352                   PLFLT *p_colorbar_width_ac, PLFLT *p_colorbar_height_ac,
01353                   PLFLT *p_plot_x_subpage_bb, PLFLT *p_plot_y_subpage_bb,
01354                   PLFLT *p_dx_subpage, PLFLT *p_dy_subpage
01355                   )
01356 {
01357     PLFLT x_colorbar_position, y_colorbar_position, xsign, ysign;
01358     PLFLT plot_x, plot_y;
01359     // Ratio of normalized subpage coordinates to mm coordinates in
01360     // x and y.
01361     PLFLT spxpmm = plsc->xpmm / ( plsc->sppxma - plsc->sppxmi );
01362     PLFLT spypmm = plsc->ypmm / ( plsc->sppyma - plsc->sppymi );
01363 
01364     // New bounding box width and height in normalized subpage coordinates.
01365     *p_colorbar_width_bb  = ( plsc->boxbb_xmax - plsc->boxbb_xmin ) * spxpmm;
01366     *p_colorbar_height_bb = ( plsc->boxbb_ymax - plsc->boxbb_ymin ) * spypmm;
01367 
01368     // Offsets (in sense of prior minus current) of upper left corner of prior bounding box
01369     // relative to current bounding box in normalized subpage coordinates.  From
01370     // the above comments, the upper left corner of prior bounding box
01371     // has the coordinates (0., prior_bb_height).
01372     *p_dx_subpage = -plsc->boxbb_xmin * spxpmm;
01373     *p_dy_subpage = prior_bb_height - plsc->boxbb_ymax * spypmm;
01374 
01375     // Total width and height of new bounding box in adopted
01376     // coordinates.
01377     *p_colorbar_width_ac = subpage_to_adopted_x( *p_colorbar_width_bb ) -
01378                            subpage_to_adopted_x( 0. );
01379     *p_colorbar_height_ac = subpage_to_adopted_y( *p_colorbar_height_bb ) -
01380                             subpage_to_adopted_y( 0. );
01381 
01382     // Calculate parameters that help determine the position of the top
01383     // left of the legend in adopted coordinates.  See pllegend
01384     // documentation for definition of adopted coordinates.
01385     legend_position( position, *p_colorbar_width_ac, *p_colorbar_height_ac, &x_colorbar_position, &y_colorbar_position, &xsign, &ysign );
01386     plot_x = x * xsign + x_colorbar_position;
01387     plot_y = y * ysign + y_colorbar_position;
01388     // Calculate normalized subpage coordinates of top left of new bounding box.
01389     *p_plot_x_subpage_bb = adopted_to_subpage_x( plot_x );
01390     *p_plot_y_subpage_bb = adopted_to_subpage_y( plot_y );
01391 }
01392 
01393 
01394 //--------------------------------------------------------------------------
01523 
01524 void
01525 c_plcolorbar( PLFLT *p_colorbar_width, PLFLT *p_colorbar_height,
01526               PLINT opt, PLINT position, PLFLT x, PLFLT y,
01527               PLFLT x_length, PLFLT y_length,
01528               PLINT bg_color, PLINT bb_color, PLINT bb_style,
01529               PLFLT low_cap_color, PLFLT high_cap_color,
01530               PLINT cont_color, PLFLT cont_width,
01531               PLINT n_labels, const PLINT *label_opts, const char * const *labels,
01532               PLINT n_axes, const char * const *axis_opts,
01533               const PLFLT *ticks, const PLINT *sub_ticks,
01534               const PLINT *n_values, const PLFLT * const *values )
01535 {
01536     // Min and max values
01537     // Assumes that the values array is sorted from smallest to largest
01538     // OR from largest to smallest.
01539     // Unnecessarily initialize min_value, max_value, and max_abs
01540     // to quiet -O1 -Wuninitialized warnings that are false alarms.
01541     // (n_axes is guaranteed to be 1 or greater so that the loops that
01542     // define these values must be executed at least once.)
01543     PLFLT min_value = 0., max_value = 0., max_abs = 0.;
01544     // Length of cap in orientation direction in normalized subpage
01545     // coordinates and mm.
01546     PLFLT cap_extent, cap_extent_mm;
01547 
01548     // The color bar cap is an equilateral triangle with cap_angle
01549     // the angle (in degrees) of the unequal angle pointing in the
01550     // direction of the orientation of the cap.  In other words,
01551     // cap_angle completely controls the shape of the triangle, but
01552     // not its scale.
01553     PLFLT cap_angle = 45.;
01554     // Ratio of length of cap in orientation direction
01555     // to the width of the bar (and cap) in the direction
01556     // perpendicular to the orientation in physical coordinates
01557     // (i.e., independent of aspect ratio).
01558     PLFLT cap_ratio = 0.5 / tan( PI / 360. * cap_angle );
01559 
01560     // aspect ratio of physical area of subpage.
01561     PLFLT aspspp = ( ( plsc->sppxma - plsc->sppxmi ) / plsc->xpmm ) /
01562                    ( ( plsc->sppyma - plsc->sppymi ) / plsc->ypmm );
01563 
01564     // Min and max colors
01565     PLFLT min_color, max_color;
01566 
01567     // Saved external world coordinates of viewport.
01568     PLFLT xwmin_save, xwmax_save, ywmin_save, ywmax_save;
01569     // Saved external normalized coordinates of viewport.
01570     // (These are actual values used only for the restore.)
01571     PLFLT xdmin_save = 0.0, xdmax_save = 0.0, ydmin_save = 0.0, ydmax_save = 0.0;
01572 
01573     // Limits of adopted coordinates used to calculate all coordinate
01574     // transformations.
01575     PLFLT xdmin_adopted = 0.0, xdmax_adopted = 0.0, ydmin_adopted = 0.0, ydmax_adopted = 0.0;
01576 
01577     // Active attributes to be saved and restored afterward.
01578     PLINT col0_save       = plsc->icol0,
01579           line_style_save = plsc->line_style;
01580 
01581     // Normalized subpage coordinates of top left of the bounding box.
01582     PLFLT plot_x_subpage_bb, plot_y_subpage_bb;
01583 
01584     // color bar width and height in normalized subpage coordinates.
01585     // No suffix refers to bonding box of undecorated color bar, d
01586     // suffix refers to bounding box of decorated color bar, and l
01587     // suffix refers to bounding box of labelled and decorated
01588     // color bar.
01589     PLFLT colorbar_width, colorbar_height,
01590           colorbar_width_d, colorbar_height_d,
01591           colorbar_width_l, colorbar_height_l;
01592 
01593     // ac suffix refers to latest color bar_width (d or l suffix) converted to
01594     // adopted coordinates.
01595     // mm suffix refers to colorbar_width and colorbar_height (with no suffix)
01596     // converted from normalized subpage coordinates to mm.
01597     PLFLT colorbar_width_ac, colorbar_height_ac,
01598           colorbar_width_mm, colorbar_height_mm;
01599 
01600     // Change in normalized subpage coordinates of the top left of
01601     // undecorated color bar.  (The omd suffix refers to original
01602     // color bar minus decorated color bar, and the dml suffix refers to
01603     // decorated color bar minus labelled and decorated color bar.)
01604     PLFLT dx_subpage_omd, dy_subpage_omd, dx_subpage_dml, dy_subpage_dml;
01605     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;
01606     // Normalized subpage coordinates of the top left of undecorated
01607     // color bar,
01608     PLFLT plot_x_subpage, plot_y_subpage;
01609 
01610     // Position of the undecorated color bar in normalized subpage coordinates.
01611     PLFLT vx_min = 0.0, vx_max = 0.0, vy_min = 0.0, vy_max = 0.0;
01612 
01613     // World coordinate limits describing undecorated color bar.
01614     PLFLT wx_min = 0.0, wx_max = 0.0, wy_min = 0.0, wy_max = 0.0;
01615 
01616     // The data to plot
01617     PLFLT **color_data;
01618 
01619     // Setting up the data for display
01620     PLINT  i, j, ni = 0, nj = 0, n_steps;
01621     PLFLT  step_size;
01622 
01623     PLBOOL if_edge   = 0;
01624     PLBOOL if_edge_b = 0, if_edge_c = 0, if_edge_u = 0, if_edge_w = 0;
01625 
01626     // Ratio of normalized subpage coordinates to mm coordinates in
01627     // x and y.
01628     PLFLT spxpmm = plsc->xpmm / ( plsc->sppxma - plsc->sppxmi );
01629     PLFLT spypmm = plsc->ypmm / ( plsc->sppyma - plsc->sppymi );
01630 
01631     // plvpor limits for label.
01632     PLFLT label_vpor_xmin, label_vpor_xmax, label_vpor_ymin, label_vpor_ymax;
01633 
01634     // Default position flags and sanity checks for position flags.
01635     if ( !( position & PL_POSITION_RIGHT ) && !( position & PL_POSITION_LEFT ) && !( position & PL_POSITION_TOP ) && !( position & PL_POSITION_BOTTOM ) )
01636     {
01637         position = position | PL_POSITION_RIGHT;
01638     }
01639     else if ( ( position & PL_POSITION_RIGHT ) && ( position & PL_POSITION_LEFT ) )
01640     {
01641         plabort( "plcolorbar: PL_POSITION_RIGHT and PL_POSITION_LEFT cannot be simultaneously set." );
01642         return;
01643     }
01644 
01645     else if ( ( position & PL_POSITION_TOP ) && ( position & PL_POSITION_BOTTOM ) )
01646     {
01647         plabort( "plcolorbar: PL_POSITION_TOP and PL_POSITION_BOTTOM cannot be simultaneously set." );
01648         return;
01649     }
01650 
01651     if ( !( position & PL_POSITION_INSIDE ) && !( position & PL_POSITION_OUTSIDE ) )
01652     {
01653         position = position | PL_POSITION_OUTSIDE;
01654     }
01655     else if ( ( position & PL_POSITION_INSIDE ) && ( position & PL_POSITION_OUTSIDE ) )
01656     {
01657         plabort( "plcolorbar: PL_POSITION_INSIDE and PL_POSITION_OUTSIDE cannot be simultaneously set." );
01658         return;
01659     }
01660 
01661     if ( !( position & PL_POSITION_VIEWPORT ) && !( position & PL_POSITION_SUBPAGE ) )
01662     {
01663         position = position | PL_POSITION_VIEWPORT;
01664     }
01665     else if ( ( position & PL_POSITION_VIEWPORT ) && ( position & PL_POSITION_SUBPAGE ) )
01666     {
01667         plabort( "plcolorbar: PL_POSITION_VIEWPORT and PL_POSITION_SUBPAGE cannot be simultaneously set." );
01668         return;
01669     }
01670 
01671     // Sanity check for NULL label arrays.
01672     if ( n_labels > 0 && ( label_opts == NULL || labels == NULL ) )
01673     {
01674         plabort( "plcolorbar: label_opts and labels arrays must be defined when n_labels > 0." );
01675         return;
01676     }
01677 
01678     if ( n_axes < 1 )
01679     {
01680         plabort( "plcolorbar: At least one axis must be specified" );
01681         return;
01682     }
01683 
01684     // xdmin_save, etc., are the actual external relative viewport
01685     // coordinates within the current sub-page used only for
01686     // restoration at the end.
01687     plgvpsp( &xdmin_save, &xdmax_save, &ydmin_save, &ydmax_save );
01688 
01689     // Choose adopted coordinates.
01690     if ( position & PL_POSITION_SUBPAGE )
01691         plvpor( 0., 1., 0., 1. );
01692 
01693     // xdmin_adopted, etc., are the adopted limits of the coordinates
01694     // within the current sub-page used for all coordinate
01695     // transformations.
01696     // If position & PL_POSITION_VIEWPORT is true, these limits
01697     // are the external relative viewport limits.
01698     // If position & PL_POSITION_SUBPAGE is true, these
01699     // coordinates are the relative subpage coordinates.
01700     plgvpsp( &xdmin_adopted, &xdmax_adopted, &ydmin_adopted, &ydmax_adopted );
01701 
01702     // xwmin_save, etc., are the external world coordinates corresponding
01703     // to the external viewport boundaries.
01704     plgvpw( &xwmin_save, &xwmax_save, &ywmin_save, &ywmax_save );
01705 
01706     // Default orientation.
01707     if ( !( opt & PL_COLORBAR_ORIENT_RIGHT ||
01708             opt & PL_COLORBAR_ORIENT_TOP ||
01709             opt & PL_COLORBAR_ORIENT_LEFT ||
01710             opt & PL_COLORBAR_ORIENT_BOTTOM ) )
01711     {
01712         if ( position & PL_POSITION_LEFT || position & PL_POSITION_RIGHT )
01713             opt = opt | PL_COLORBAR_ORIENT_TOP;
01714         else
01715             opt = opt | PL_COLORBAR_ORIENT_RIGHT;
01716     }
01717 
01718     for ( i = 0; i < n_axes; i++ )
01719     {
01720         if_edge_b = if_edge_b || plP_stsearch( axis_opts[i], 'b' );
01721         if_edge_c = if_edge_c || plP_stsearch( axis_opts[i], 'c' );
01722         if_edge_u = if_edge_u || plP_stsearch( axis_opts[i], 'u' );
01723         if_edge_w = if_edge_w || plP_stsearch( axis_opts[i], 'w' );
01724     }
01725     if_edge = if_edge_b && if_edge_c && !( if_edge_u || if_edge_w );
01726 
01727     // Assumes that the colors array is sorted from smallest to largest.
01728     plgcmap1_range( &min_color, &max_color );
01729 
01730     // Width and height of the undecorated color bar in normalized
01731     // subpage coordinates and mm.
01732     colorbar_width = adopted_to_subpage_x( x_length ) -
01733                      adopted_to_subpage_x( 0. );
01734     colorbar_height = adopted_to_subpage_y( y_length ) -
01735                       adopted_to_subpage_y( 0. );
01736     colorbar_width_d   = colorbar_width;
01737     colorbar_height_d  = colorbar_height;
01738     colorbar_width_mm  = colorbar_width / spxpmm;
01739     colorbar_height_mm = colorbar_height / spypmm;
01740     // Extent of cap in normalized subpage coordinates in either X or Y
01741     // direction as appropriate in normalized subpage coordinates and  mm.
01742     if ( opt & PL_COLORBAR_ORIENT_RIGHT || opt & PL_COLORBAR_ORIENT_LEFT )
01743     {
01744         cap_extent    = cap_ratio * colorbar_height / aspspp;
01745         cap_extent_mm = cap_extent / spxpmm;
01746     }
01747     else
01748     {
01749         cap_extent    = cap_ratio * colorbar_width * aspspp;
01750         cap_extent_mm = cap_extent / spypmm;
01751     }
01752 
01753     for ( i = n_axes - 1; i >= 0; i-- )
01754     {
01755         min_value = values[i][0];
01756         max_value = values[i][ n_values[i] - 1 ];
01757         max_abs   = MAX( fabs( min_value ), fabs( max_value ) );
01758 
01759         // Specify the proper window ranges for color bar depending on
01760         // orientation.
01761         if ( opt & PL_COLORBAR_ORIENT_RIGHT )
01762         {
01763             wx_min = min_value;
01764             wx_max = max_value;
01765             wy_min = 0.0;
01766             wy_max = max_abs;
01767         }
01768         else if ( opt & PL_COLORBAR_ORIENT_TOP )
01769         {
01770             wx_min = 0.0;
01771             wx_max = max_abs;
01772             wy_min = min_value;
01773             wy_max = max_value;
01774         }
01775         else if ( opt & PL_COLORBAR_ORIENT_LEFT )
01776         {
01777             wx_min = max_value;
01778             wx_max = min_value;
01779             wy_min = 0.0;
01780             wy_max = max_abs;
01781         }
01782         else if ( opt & PL_COLORBAR_ORIENT_BOTTOM )
01783         {
01784             wx_min = 0.0;
01785             wx_max = max_abs;
01786             wy_min = max_value;
01787             wy_max = min_value;
01788         }
01789         else
01790         {
01791             plabort( "plcolorbar: Invalid PL_COLORBAR_ORIENT_* bits" );
01792         }
01793 
01794         // Viewport has correct size but has a shifted zero-point
01795         // convention required by bounding-box calculations in draw_box,
01796         // further bounding-box calculations in the cap_extent section
01797         // below, and also in the calculate_limits call below that.
01798         plvpor( 0., colorbar_width_d, 0., colorbar_height_d );
01799         plwind( wx_min, wx_max, wy_min, wy_max );
01800 
01801         // Calculate the bounding box for decorated (i.e., including tick
01802         // marks + numerical tick labels) box.
01803         draw_box( TRUE, opt, axis_opts[i], if_edge,
01804             ticks[i], sub_ticks[i], n_values[i], values[i] );
01805 
01806         if ( i == n_axes - 1 )
01807         {
01808             if ( opt & PL_COLORBAR_CAP_LOW )
01809             {
01810                 if ( opt & PL_COLORBAR_ORIENT_RIGHT )
01811                     plsc->boxbb_xmin = MIN( plsc->boxbb_xmin, -cap_extent_mm );
01812                 if ( opt & PL_COLORBAR_ORIENT_TOP )
01813                     plsc->boxbb_ymin = MIN( plsc->boxbb_ymin, -cap_extent_mm );
01814                 if ( opt & PL_COLORBAR_ORIENT_LEFT )
01815                     plsc->boxbb_xmax = MAX( plsc->boxbb_xmax, colorbar_width_mm + cap_extent_mm );
01816                 if ( opt & PL_COLORBAR_ORIENT_BOTTOM )
01817                     plsc->boxbb_ymax = MAX( plsc->boxbb_ymax, colorbar_height_mm + cap_extent_mm );
01818             }
01819             if ( opt & PL_COLORBAR_CAP_HIGH )
01820             {
01821                 if ( opt & PL_COLORBAR_ORIENT_RIGHT )
01822                     plsc->boxbb_xmax = MAX( plsc->boxbb_xmax, colorbar_width_mm + cap_extent_mm );
01823                 if ( opt & PL_COLORBAR_ORIENT_TOP )
01824                     plsc->boxbb_ymax = MAX( plsc->boxbb_ymax, colorbar_height_mm + cap_extent_mm );
01825                 if ( opt & PL_COLORBAR_ORIENT_LEFT )
01826                     plsc->boxbb_xmin = MIN( plsc->boxbb_xmin, -cap_extent_mm );
01827                 if ( opt & PL_COLORBAR_ORIENT_BOTTOM )
01828                     plsc->boxbb_ymin = MIN( plsc->boxbb_ymin, -cap_extent_mm );
01829             }
01830         }
01831 
01832         // Calculate limits relevant to label position.
01833         calculate_limits( position, x, y,
01834             xdmin_adopted, xdmax_adopted, ydmin_adopted, ydmax_adopted,
01835             colorbar_height_d,
01836             &colorbar_width_d, &colorbar_height_d,
01837             &colorbar_width_ac, &colorbar_height_ac,
01838             &plot_x_subpage_bb, &plot_y_subpage_bb,
01839             &dx_subpage_omd, &dy_subpage_omd );
01840 
01841         // Viewport has correct size but has a shifted zero point
01842         // convention required by bounding-box calculations in draw_label
01843         // and further calculations in calculate_limits.
01844         plvpor( 0., colorbar_width_d, 0., colorbar_height_d );
01845 
01846         dx_subpage_omd_accu += dx_subpage_omd;
01847         dy_subpage_omd_accu += dy_subpage_omd;
01848     }
01849 
01850     // Capture the current bounding box dimensions
01851     colorbar_width_l  = colorbar_width_d;
01852     colorbar_height_l = colorbar_height_d;
01853 
01854     for ( i = 0; i < n_labels; i++ )
01855     {
01856         // Viewport has correct size but has a shifted zero-point
01857         // convention required by bounding-box calculations in draw_box,
01858         // further bounding-box calculations in the cap_extent section
01859         // below, and also in the calculate_limits call below that.
01860         plvpor( 0., colorbar_width_l, 0., colorbar_height_l );
01861         plwind( wx_min, wx_max, wy_min, wy_max );
01862 
01863         // Calculate the bounding box for combined label + decorated box.
01864         draw_label( TRUE, opt | label_opts[i], labels[i] );
01865 
01866         // Calculate overall limits.
01867         calculate_limits( position, x, y,
01868             xdmin_adopted, xdmax_adopted, ydmin_adopted, ydmax_adopted,
01869             colorbar_height_l,
01870             &colorbar_width_l, &colorbar_height_l,
01871             &colorbar_width_ac, &colorbar_height_ac,
01872             &plot_x_subpage_bb, &plot_y_subpage_bb,
01873             &dx_subpage_dml, &dy_subpage_dml );
01874 
01875         dx_subpage_dml_accu += dx_subpage_dml;
01876         dy_subpage_dml_accu += dy_subpage_dml;
01877     }
01878 
01879     // Normalized subpage coordinates (top-left corner) for undecorated
01880     // color bar
01881     plot_x_subpage = plot_x_subpage_bb + dx_subpage_omd_accu + dx_subpage_dml_accu;
01882     plot_y_subpage = plot_y_subpage_bb + dy_subpage_omd_accu + dy_subpage_dml_accu;
01883 
01884     // Coordinates of bounding box for decorated color bar (without overall label).
01885     label_vpor_xmin = plot_x_subpage_bb + dx_subpage_dml_accu;
01886     label_vpor_xmax = label_vpor_xmin + colorbar_width_d;
01887     label_vpor_ymax = plot_y_subpage_bb + dy_subpage_dml_accu;
01888     label_vpor_ymin = label_vpor_ymax - colorbar_height_d;
01889 
01890     // Return bounding box width and height in adopted coordinates for
01891     // labelled and decorated color bar.
01892     *p_colorbar_width  = colorbar_width_ac;
01893     *p_colorbar_height = colorbar_height_ac;
01894 
01895     // Specify the proper viewport ranges for color bar depending on
01896     // orientation.
01897     if ( opt & PL_COLORBAR_ORIENT_RIGHT )
01898     {
01899         vx_min = plot_x_subpage;
01900         vx_max = plot_x_subpage + colorbar_width;
01901         vy_min = plot_y_subpage - colorbar_height;
01902         vy_max = plot_y_subpage;
01903     }
01904     else if ( opt & PL_COLORBAR_ORIENT_TOP )
01905     {
01906         vx_min = plot_x_subpage;
01907         vx_max = plot_x_subpage + colorbar_width;
01908         vy_min = plot_y_subpage - colorbar_height;
01909         vy_max = plot_y_subpage;
01910     }
01911     else if ( opt & PL_COLORBAR_ORIENT_LEFT )
01912     {
01913         vx_min = plot_x_subpage;
01914         vx_max = plot_x_subpage + colorbar_width;
01915         vy_min = plot_y_subpage - colorbar_height;
01916         vy_max = plot_y_subpage;
01917     }
01918     else if ( opt & PL_COLORBAR_ORIENT_BOTTOM )
01919     {
01920         vx_min = plot_x_subpage;
01921         vx_max = plot_x_subpage + colorbar_width;
01922         vy_min = plot_y_subpage - colorbar_height;
01923         vy_max = plot_y_subpage;
01924     }
01925     else
01926     {
01927         plabort( "plcolorbar: Invalid PL_COLORBAR_ORIENT_* bits" );
01928     }
01929 
01930     // Viewport and world coordinate ranges for bounding-box.
01931     plvpor( 0., 1., 0., 1. );
01932     plwind( 0., 1., 0., 1. );
01933 
01934     if ( opt & PL_COLORBAR_BACKGROUND )
01935     {
01936         PLFLT xbg[4] = {
01937             plot_x_subpage_bb,
01938             plot_x_subpage_bb,
01939             plot_x_subpage_bb + colorbar_width_l,
01940             plot_x_subpage_bb + colorbar_width_l,
01941         };
01942         PLFLT ybg[4] = {
01943             plot_y_subpage_bb,
01944             plot_y_subpage_bb - colorbar_height_l,
01945             plot_y_subpage_bb - colorbar_height_l,
01946             plot_y_subpage_bb,
01947         };
01948         plpsty( 0 );
01949         plcol0( bg_color );
01950         plfill( 4, xbg, ybg );
01951         plcol0( col0_save );
01952     }
01953 
01954     // Viewport and world coordinate ranges for color bar.
01955     plvpor( vx_min, vx_max, vy_min, vy_max );
01956     plwind( wx_min, wx_max, wy_min, wy_max );
01957 
01958     // What kind of color bar are we making?
01959     if ( opt & PL_COLORBAR_IMAGE )
01960     {
01961         // Interpolate
01962         // TODO: Should this be decided with an extra opt option instead of by
01963         // counting n_values?
01964         if ( n_values[0] == 2 )
01965         {
01966             // Use the same number of steps as there are steps in
01967             // color palette 1.
01968             // TODO: Determine a better way to specify the steps here?
01969             n_steps   = plsc->ncol1;
01970             step_size = ( max_value - min_value ) / (PLFLT) n_steps;
01971             if ( opt & PL_COLORBAR_ORIENT_RIGHT )
01972             {
01973                 ni = n_steps;
01974                 nj = 2;
01975                 plAlloc2dGrid( &color_data, ni, nj );
01976                 for ( i = 0; i < ni; i++ )
01977                 {
01978                     for ( j = 0; j < nj; j++ )
01979                     {
01980                         color_data[i][j] = min_value + (PLFLT) i * step_size;
01981                     }
01982                 }
01983             }
01984             else if ( opt & PL_COLORBAR_ORIENT_TOP )
01985             {
01986                 ni = 2;
01987                 nj = n_steps;
01988                 plAlloc2dGrid( &color_data, ni, nj );
01989                 for ( i = 0; i < ni; i++ )
01990                 {
01991                     for ( j = 0; j < nj; j++ )
01992                     {
01993                         color_data[i][j] = min_value + (PLFLT) j * step_size;
01994                     }
01995                 }
01996             }
01997             else if ( opt & PL_COLORBAR_ORIENT_LEFT )
01998             {
01999                 ni = n_steps;
02000                 nj = 2;
02001                 plAlloc2dGrid( &color_data, ni, nj );
02002                 for ( i = 0; i < ni; i++ )
02003                 {
02004                     for ( j = 0; j < nj; j++ )
02005                     {
02006                         color_data[i][j] = max_value - (PLFLT) i * step_size;
02007                     }
02008                 }
02009             }
02010             else if ( opt & PL_COLORBAR_ORIENT_BOTTOM )
02011             {
02012                 ni = 2;
02013                 nj = n_steps;
02014                 plAlloc2dGrid( &color_data, ni, nj );
02015                 for ( i = 0; i < ni; i++ )
02016                 {
02017                     for ( j = 0; j < nj; j++ )
02018                     {
02019                         color_data[i][j] = max_value - (PLFLT) j * step_size;
02020                     }
02021                 }
02022             }
02023             else
02024             {
02025                 plabort( "plcolorbar: Invalid orientation bits" );
02026             }
02027         }
02028         // No interpolation - use values array as-is
02029         else
02030         {
02031             n_steps = n_values[0];
02032             // Use the provided values in this case.
02033             if ( opt & PL_COLORBAR_ORIENT_RIGHT )
02034             {
02035                 ni = n_steps;
02036                 nj = 2;
02037                 plAlloc2dGrid( &color_data, ni, nj );
02038                 for ( i = 0; i < ni; i++ )
02039                 {
02040                     for ( j = 0; j < nj; j++ )
02041                     {
02042                         color_data[i][j] = values[0][i];
02043                     }
02044                 }
02045             }
02046             else if ( opt & PL_COLORBAR_ORIENT_TOP )
02047             {
02048                 ni = 2;
02049                 nj = n_steps;
02050                 plAlloc2dGrid( &color_data, ni, nj );
02051                 for ( i = 0; i < ni; i++ )
02052                 {
02053                     for ( j = 0; j < nj; j++ )
02054                     {
02055                         color_data[i][j] = values[0][j];
02056                     }
02057                 }
02058             }
02059             else if ( opt & PL_COLORBAR_ORIENT_LEFT )
02060             {
02061                 ni = n_steps;
02062                 nj = 2;
02063                 plAlloc2dGrid( &color_data, ni, nj );
02064                 for ( i = 0; i < ni; i++ )
02065                 {
02066                     for ( j = 0; j < nj; j++ )
02067                     {
02068                         color_data[i][j] = values[0][ni - 1 - i];
02069                     }
02070                 }
02071             }
02072             else if ( opt & PL_COLORBAR_ORIENT_BOTTOM )
02073             {
02074                 ni = 2;
02075                 nj = n_steps;
02076                 plAlloc2dGrid( &color_data, ni, nj );
02077                 for ( i = 0; i < ni; i++ )
02078                 {
02079                     for ( j = 0; j < nj; j++ )
02080                     {
02081                         color_data[i][j] = values[0][nj - 1 - j];
02082                     }
02083                 }
02084             }
02085             else
02086             {
02087                 plabort( "plcolorbar: Invalid side" );
02088             }
02089         }
02090         // Draw the color bar
02091         plimage( (const PLFLT * const *) color_data, ni, nj, wx_min, wx_max, wy_min, wy_max,
02092             min_value, max_value, wx_min, wx_max, wy_min, wy_max );
02093         plFree2dGrid( color_data, ni, nj );
02094     }
02095     else if ( opt & PL_COLORBAR_SHADE )
02096     {
02097         // Transform grid
02098         // The transform grid is used to make the size of the shaded segments
02099         // scale relative to other segments.  For example, if segment A
02100         // makes up 10% of the scale and segment B makes up 20% of the scale
02101         // then segment B will be twice the length of segment A.
02102         PLcGrid grid;
02103         PLFLT   grid_axis[2] = { 0.0, max_abs };
02104         n_steps = n_values[0];
02105         // Use the provided values.
02106         if ( opt & PL_COLORBAR_ORIENT_RIGHT )
02107         {
02108             grid.xg = (PLFLT *) values[0];
02109             grid.yg = grid_axis;
02110             grid.nx = n_steps;
02111             grid.ny = 2;
02112             ni      = n_steps;
02113             nj      = 2;
02114             plAlloc2dGrid( &color_data, ni, nj );
02115             for ( i = 0; i < ni; i++ )
02116             {
02117                 for ( j = 0; j < nj; j++ )
02118                 {
02119                     color_data[i][j] = values[0][i];
02120                 }
02121             }
02122         }
02123         else if ( opt & PL_COLORBAR_ORIENT_TOP )
02124         {
02125             grid.xg = grid_axis;
02126             grid.yg = (PLFLT *) values[0];
02127             grid.nx = 2;
02128             grid.ny = n_steps;
02129             ni      = 2;
02130             nj      = n_steps;
02131             plAlloc2dGrid( &color_data, ni, nj );
02132             for ( i = 0; i < ni; i++ )
02133             {
02134                 for ( j = 0; j < nj; j++ )
02135                 {
02136                     color_data[i][j] = values[0][j];
02137                 }
02138             }
02139         }
02140         else if ( opt & PL_COLORBAR_ORIENT_LEFT )
02141         {
02142             grid.xg = (PLFLT *) values[0];
02143             grid.yg = grid_axis;
02144             grid.nx = n_steps;
02145             grid.ny = 2;
02146             ni      = n_steps;
02147             nj      = 2;
02148             plAlloc2dGrid( &color_data, ni, nj );
02149             for ( i = 0; i < ni; i++ )
02150             {
02151                 for ( j = 0; j < nj; j++ )
02152                 {
02153                     color_data[i][j] = values[0][ni - 1 - i];
02154                 }
02155             }
02156         }
02157         else if ( opt & PL_COLORBAR_ORIENT_BOTTOM )
02158         {
02159             grid.xg = grid_axis;
02160             grid.yg = (PLFLT *) values[0];
02161             grid.nx = 2;
02162             grid.ny = n_steps;
02163             ni      = 2;
02164             nj      = n_steps;
02165             plAlloc2dGrid( &color_data, ni, nj );
02166             for ( i = 0; i < ni; i++ )
02167             {
02168                 for ( j = 0; j < nj; j++ )
02169                 {
02170                     color_data[i][j] = values[0][nj - 1 - j];
02171                 }
02172             }
02173         }
02174         else
02175         {
02176             plabort( "plcolorbar: Invalid orientation" );
02177         }
02178 
02179         // Draw the color bar
02180         plshades( (const PLFLT * const *) color_data, ni, nj, NULL, wx_min, wx_max, wy_min, wy_max,
02181             values[0], n_steps, 0, cont_color, cont_width, plfill, TRUE,
02182             pltr1, (void *) ( &grid ) );
02183         plFree2dGrid( color_data, ni, nj );
02184     }
02185     else if ( opt & PL_COLORBAR_GRADIENT )
02186     {
02187         PLFLT xs[4], ys[4];
02188         PLFLT angle = 0.0;
02189         xs[0] = wx_min;
02190         ys[0] = wy_min;
02191         xs[1] = wx_max;
02192         ys[1] = wy_min;
02193         xs[2] = wx_max;
02194         ys[2] = wy_max;
02195         xs[3] = wx_min;
02196         ys[3] = wy_max;
02197         // Make sure the gradient runs in the proper direction
02198         if ( opt & PL_COLORBAR_ORIENT_RIGHT )
02199         {
02200             angle = 0.0;
02201         }
02202         else if ( opt & PL_COLORBAR_ORIENT_TOP )
02203         {
02204             angle = 90.0;
02205         }
02206         else if ( opt & PL_COLORBAR_ORIENT_LEFT )
02207         {
02208             angle = 180.0;
02209         }
02210         else if ( opt & PL_COLORBAR_ORIENT_BOTTOM )
02211         {
02212             angle = 270.0;
02213         }
02214         else
02215         {
02216             plabort( "plcolorbar: Invalid orientation" );
02217         }
02218         plgradient( 4, xs, ys, angle );
02219     }
02220     else
02221     {
02222         plabort( "plcolorbar: One of PL_COLORBAR_IMAGE, PL_COLORBAR_SHADE, or PL_COLORBAR_GRADIENT bits must be set in opt" );
02223     }
02224 
02225     // Restore the previous drawing color to use for outlines and text
02226     plcol0( col0_save );
02227 
02228     // Draw end-caps
02229 
02230     // Viewport and world coordinate ranges for cap.
02231     plvpor( 0., 1., 0., 1. );
02232     plwind( 0., 1., 0., 1. );
02233 
02234     if ( opt & PL_COLORBAR_CAP_LOW )
02235     {
02236         // Draw a filled triangle (cap/arrow) at the low end of the scale
02237         if ( opt & PL_COLORBAR_ORIENT_RIGHT )
02238             draw_cap( if_edge, PL_COLORBAR_ORIENT_LEFT,
02239                 plot_x_subpage - cap_extent, plot_x_subpage,
02240                 plot_y_subpage - colorbar_height, plot_y_subpage,
02241                 low_cap_color );
02242         else if ( opt & PL_COLORBAR_ORIENT_TOP )
02243             draw_cap( if_edge, PL_COLORBAR_ORIENT_BOTTOM,
02244                 plot_x_subpage, plot_x_subpage + colorbar_width,
02245                 plot_y_subpage - colorbar_height - cap_extent, plot_y_subpage - colorbar_height,
02246                 low_cap_color );
02247         else if ( opt & PL_COLORBAR_ORIENT_LEFT )
02248             draw_cap( if_edge, PL_COLORBAR_ORIENT_RIGHT,
02249                 plot_x_subpage + colorbar_width, plot_x_subpage + colorbar_width + cap_extent,
02250                 plot_y_subpage - colorbar_height, plot_y_subpage,
02251                 low_cap_color );
02252         else if ( opt & PL_COLORBAR_ORIENT_BOTTOM )
02253             draw_cap( if_edge, PL_COLORBAR_ORIENT_TOP,
02254                 plot_x_subpage, plot_x_subpage + colorbar_width,
02255                 plot_y_subpage, plot_y_subpage + cap_extent,
02256                 low_cap_color );
02257     }
02258     if ( opt & PL_COLORBAR_CAP_HIGH )
02259     {
02260         // Draw a filled triangle (cap/arrow) at the high end of the scale
02261         if ( opt & PL_COLORBAR_ORIENT_RIGHT )
02262             draw_cap( if_edge, PL_COLORBAR_ORIENT_RIGHT,
02263                 plot_x_subpage + colorbar_width, plot_x_subpage + colorbar_width + cap_extent,
02264                 plot_y_subpage - colorbar_height, plot_y_subpage,
02265                 high_cap_color );
02266         else if ( opt & PL_COLORBAR_ORIENT_TOP )
02267             draw_cap( if_edge, PL_COLORBAR_ORIENT_TOP,
02268                 plot_x_subpage, plot_x_subpage + colorbar_width,
02269                 plot_y_subpage, plot_y_subpage + cap_extent,
02270                 high_cap_color );
02271         else if ( opt & PL_COLORBAR_ORIENT_LEFT )
02272             draw_cap( if_edge, PL_COLORBAR_ORIENT_LEFT,
02273                 plot_x_subpage - cap_extent, plot_x_subpage,
02274                 plot_y_subpage - colorbar_height, plot_y_subpage,
02275                 high_cap_color );
02276         else if ( opt & PL_COLORBAR_ORIENT_BOTTOM )
02277             draw_cap( if_edge, PL_COLORBAR_ORIENT_BOTTOM,
02278                 plot_x_subpage, plot_x_subpage + colorbar_width,
02279                 plot_y_subpage - colorbar_height - cap_extent, plot_y_subpage - colorbar_height,
02280                 high_cap_color );
02281     }
02282 
02283     for ( i = n_axes - 1; i >= 0; i-- )
02284     {
02285         min_value = values[i][0];
02286         max_value = values[i][ n_values[i] - 1 ];
02287         max_abs   = MAX( fabs( min_value ), fabs( max_value ) );
02288 
02289         // Specify the proper window ranges for color bar depending on
02290         // orientation.
02291         if ( opt & PL_COLORBAR_ORIENT_RIGHT )
02292         {
02293             wx_min = min_value;
02294             wx_max = max_value;
02295             wy_min = 0.0;
02296             wy_max = max_abs;
02297         }
02298         else if ( opt & PL_COLORBAR_ORIENT_TOP )
02299         {
02300             wx_min = 0.0;
02301             wx_max = max_abs;
02302             wy_min = min_value;
02303             wy_max = max_value;
02304         }
02305         else if ( opt & PL_COLORBAR_ORIENT_LEFT )
02306         {
02307             wx_min = max_value;
02308             wx_max = min_value;
02309             wy_min = 0.0;
02310             wy_max = max_abs;
02311         }
02312         else if ( opt & PL_COLORBAR_ORIENT_BOTTOM )
02313         {
02314             wx_min = 0.0;
02315             wx_max = max_abs;
02316             wy_min = max_value;
02317             wy_max = min_value;
02318         }
02319         else
02320         {
02321             plabort( "plcolorbar: Invalid PL_COLORBAR_ORIENT_* bits" );
02322         }
02323 
02324         // Viewport and world coordinate ranges for box.
02325         plvpor( vx_min, vx_max, vy_min, vy_max );
02326         plwind( wx_min, wx_max, wy_min, wy_max );
02327 
02328         // draw decorated (i.e., including tick marks + numerical tick
02329         // labels) box.
02330         draw_box( FALSE, opt, axis_opts[i], if_edge,
02331             ticks[i], sub_ticks[i], n_values[i], values[i] );
02332     }
02333 
02334     // Viewport and world coordinate ranges for bounding-box.
02335     plvpor( 0., 1., 0., 1. );
02336     plwind( 0., 1., 0., 1. );
02337 
02338     if ( opt & PL_COLORBAR_BOUNDING_BOX )
02339     {
02340         PLFLT xbb[5] = {
02341             plot_x_subpage_bb,
02342             plot_x_subpage_bb,
02343             plot_x_subpage_bb + colorbar_width_l,
02344             plot_x_subpage_bb + colorbar_width_l,
02345             plot_x_subpage_bb,
02346         };
02347         PLFLT ybb[5] = {
02348             plot_y_subpage_bb,
02349             plot_y_subpage_bb - colorbar_height_l,
02350             plot_y_subpage_bb - colorbar_height_l,
02351             plot_y_subpage_bb,
02352             plot_y_subpage_bb,
02353         };
02354         pllsty( bb_style );
02355         plcol0( bb_color );
02356         plline( 5, xbb, ybb );
02357         plcol0( col0_save );
02358         pllsty( line_style_save );
02359     }
02360 
02361     // Write label.
02362     // Viewport coordinate ranges for label.
02363     plvpor( label_vpor_xmin, label_vpor_xmax, label_vpor_ymin, label_vpor_ymax );
02364     for ( i = 0; i < n_labels; i++ )
02365     {
02366         draw_label( FALSE, opt | label_opts[i], labels[i] );
02367     }
02368 
02369     // Restore previous plot characteristics.
02370     plcol0( col0_save );
02371     plvpor( xdmin_save, xdmax_save, ydmin_save, ydmax_save );
02372     plwind( xwmin_save, xwmax_save, ywmin_save, ywmax_save );
02373 
02374     return;
02375 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines