PLplot 5.9.6
plgradient.c
00001 /* $Id$
00002  *
00003  *      Implement linear gradients for PLplot.
00004  *
00005  * Copyright (C) 2009  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 General Library 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  */
00024 
00025 #include "plplotP.h"
00026 
00027 /* To keep track of whether a sofware fallback warning has been issued. */
00028 
00029 static int foo;
00030 /* software fallback for gradient. */
00031 static void
00032 plgradient_soft( PLINT n, PLFLT *x, PLFLT *y, PLFLT angle );
00033 
00034 /* define where plshades plots gradient for software fallback for
00035  * gradient.  */
00036 
00037 static PLINT
00038 gradient_defined( PLFLT x, PLFLT y );
00039 
00040 /*----------------------------------------------------------------------*\
00041  * void plgradient()
00042  *
00043  * Draws a linear gradient at an angle relative to the increasing x
00044  * direction for the polygon bounded by the x and y vertices.  x, and
00045  * y are expressed in world coordinates, and angle (in the world
00046  * coordinate system) is expressed in degrees.  The gradient is
00047  * expressed using colour and transparency information from cmap1.  The
00048  * geometrical gradient direction is specified by the angle argument.
00049  * The 0. to 1. range of the independent variable of cmap1 corresponds
00050  * to the range of the polygon in the direction specified by angle.
00051  \*----------------------------------------------------------------------*/
00052 
00053 void
00054 c_plgradient( PLINT n, PLFLT *x, PLFLT *y, PLFLT angle )
00055 {
00056     if ( plsc->level < 3 )
00057     {
00058         plabort( "plgradient: Please set up window first" );
00059         return;
00060     }
00061     if ( n < 3 )
00062     {
00063         plabort( "plgradient: Not enough vertices in polygon" );
00064         return;
00065     }
00066 
00067     if ( !plsc->dev_gradient )
00068     {
00069         if ( !foo )
00070         {
00071             plwarn( "Driver does not support native gradients, switching to software fallback gradient.\n" );
00072             foo = 1;
00073         }
00074 
00075         plgradient_soft( n, x, y, angle );
00076     }
00077     else
00078     {
00079       #define NGRAD    2
00080         int   i, irot_min, irot_max;
00081         PLINT xpoly[PL_MAXPOLY], ypoly[PL_MAXPOLY];
00082         PLINT xgrad[NGRAD], ygrad[NGRAD], clpxmi, clpxma, clpymi, clpyma;
00083         PLFLT dxgrad[NGRAD], dygrad[NGRAD], xrot, xrot_min, xrot_max;
00084 
00085         /* Find (x1, y1) and (x2, y2) corresponding to beginning and end
00086          * of gradient vector. */
00087         double cosangle = cos( PI * angle / 180. );
00088         double sinangle = sin( PI * angle / 180. );
00089         xrot     = x[0] * cosangle + y[0] * sinangle;
00090         xrot_min = xrot;
00091         xrot_max = xrot;
00092         irot_min = 0;
00093         irot_max = 0;
00094         for ( i = 1; i < n; i++ )
00095         {
00096             xrot = x[i] * cosangle + y[i] * sinangle;
00097             if ( xrot < xrot_min )
00098             {
00099                 xrot_min = xrot;
00100                 irot_min = i;
00101             }
00102             else if ( xrot > xrot_max )
00103             {
00104                 xrot_max = xrot;
00105                 irot_max = i;
00106             }
00107         }
00108         /* xrot_min and xrot_max are the minimum and maximum rotated x
00109          * coordinate of polygon vertices. Use the vertex corresponding
00110          * to the minimum  as the (xgrad[0], ygrad[0]) base of the
00111          * gradient vector, and calculate the (xgrad[1], ygrad[1]) tip of
00112          * the gradient vector from the range in rotated x coordinate and
00113          * the angle of the gradient. */
00114         dxgrad[0] = x[irot_min];
00115         dxgrad[1] = dxgrad[0] + ( xrot_max - xrot_min ) * cosangle;
00116         dygrad[0] = y[irot_min];
00117         dygrad[1] = dygrad[0] + ( xrot_max - xrot_min ) * sinangle;
00118         for ( i = 0; i < NGRAD; i++ )
00119         {
00120             xgrad[i] = plP_wcpcx( dxgrad[i] );
00121             ygrad[i] = plP_wcpcy( dygrad[i] );
00122         }
00123         if ( plsc->difilt )
00124             difilt( xgrad, ygrad, NGRAD, &clpxmi, &clpxma, &clpymi, &clpyma );
00125         plsc->xgradient = xgrad;
00126         plsc->ygradient = ygrad;
00127         plsc->ngradient = NGRAD;
00128 
00129 
00130         if ( n > PL_MAXPOLY - 1 )
00131         {
00132             plwarn( "plgradient: too many points in polygon" );
00133             n = PL_MAXPOLY;
00134         }
00135         for ( i = 0; i < n; i++ )
00136         {
00137             xpoly[i] = plP_wcpcx( x[i] );
00138             ypoly[i] = plP_wcpcy( y[i] );
00139         }
00140         if ( x[0] != x[n - 1] || y[0] != y[n - 1] )
00141         {
00142             if ( n < PL_MAXPOLY )
00143                 n++;
00144             xpoly[n - 1] = plP_wcpcx( x[0] );
00145             ypoly[n - 1] = plP_wcpcy( y[0] );
00146         }
00147         plP_plfclp( xpoly, ypoly, n, plsc->clpxmi, plsc->clpxma,
00148             plsc->clpymi, plsc->clpyma, plP_gradient );
00149         /* Plot line corresponding to gradient to give visual
00150          * debugging cue. */
00151         plline( NGRAD, dxgrad, dygrad );
00152     }
00153 }
00154 
00155 /*----------------------------------------------------------------------*\
00156  * void plgradient_soft()
00157  *
00158  * Software fallback for gradient.  See c_plgradient for an explanation
00159  * of the arguments.
00160  \*----------------------------------------------------------------------*/
00161 
00162 void
00163 plgradient_soft( PLINT n, PLFLT *x, PLFLT *y, PLFLT angle )
00164 {
00165     PLFLT xrot, xrot_min, xrot_max, cosangle, sinangle;
00166     PLFLT xmin, xmax, ymin, ymax;
00167     PLFLT **z, *edge, xcoord, ycoord;
00168     PLINT i, j;
00169 
00170     if ( n < 3 )
00171     {
00172         plabort( "plgradient_soft: Not enough vertices in polygon" );
00173         return;
00174     }
00175 
00176 
00177     /* Define polygon boundary so it is accessible from gradient_defined. */
00178     plsc->n_polygon = n;
00179     plsc->x_polygon = x;
00180     plsc->y_polygon = y;
00181 
00182     /* Find x and y range of polygon. */
00183     xmin = x[0];
00184     xmax = xmin;
00185     ymin = y[0];
00186     ymax = ymin;
00187     /* Also find x range in rotated coordinate system where
00188      * xrot = x*cosangle + y*sinangle. */
00189     cosangle = cos( PI / 180. * angle );
00190     sinangle = sin( PI / 180. * angle );
00191     xrot     = x[0] * cosangle + y[0] * sinangle;
00192     xrot_min = xrot;
00193     xrot_max = xrot;
00194     for ( i = 1; i < n; i++ )
00195     {
00196         if ( x[i] < xmin )
00197             xmin = x[i];
00198         else if ( x[i] > xmax )
00199             xmax = x[i];
00200 
00201         if ( y[i] < ymin )
00202             ymin = y[i];
00203         else if ( y[i] > ymax )
00204             ymax = y[i];
00205 
00206         xrot = x[i] * cosangle + y[i] * sinangle;
00207         if ( xrot < xrot_min )
00208             xrot_min = xrot;
00209         else if ( xrot > xrot_max )
00210             xrot_max = xrot;
00211     }
00212 
00213     /* 2 x 2 array more than sufficient to define plane. */
00214     /* Temporarily use more to overcome irregular edge issue on defined
00215      * region. */
00216     #define NX    20
00217     #define NY    20
00218     plAlloc2dGrid( &z, NX, NY );
00219     for ( i = 0; i < NX; i++ )
00220     {
00221         xcoord = xmin + ( (PLFLT) i ) * ( xmax - xmin ) / (PLFLT) ( NX - 1 );
00222         for ( j = 0; j < NY; j++ )
00223         {
00224             ycoord  = ymin + ( (PLFLT) j ) * ( ymax - ymin ) / (PLFLT) ( NY - 1 );
00225             xrot    = xcoord * cosangle + ycoord * sinangle;
00226             z[i][j] = ( xrot - xrot_min ) / ( xrot_max - xrot_min );
00227         }
00228     }
00229     /* 101 edges gives reasonably smooth results for example 30. */
00230     #define NEDGE    101
00231     /* Define NEDGE shade edges (or NEDGE-1 shade levels)
00232      * from 0. to 1. */
00233     if ( ( edge = (PLFLT *) malloc( NEDGE * sizeof ( PLFLT ) ) ) == NULL )
00234         plexit( "plgradient_soft: Insufficient memory" );
00235     for ( i = 0; i < NEDGE; i++ )
00236         edge[i] = (PLFLT) i / (PLFLT) ( NEDGE - 1 );
00237 
00238     plshades( z, NX, NY, gradient_defined, xmin, xmax, ymin, ymax,
00239         edge, NEDGE, 0, 0, 0, plfill, 1, NULL, NULL );
00240     free( (void *) edge );
00241     plFree2dGrid( z, NX, NY );
00242 }
00243 
00244 static PLINT
00245 gradient_defined( PLFLT x, PLFLT y )
00246 {
00247     return plP_pointinpolygon( plsc->n_polygon, plsc->x_polygon, plsc->y_polygon,
00248         x, y );
00249 }
 All Data Structures Files Functions