PLplot
5.10.0
|
00001 // Implement linear gradients for PLplot. 00002 // 00003 // Copyright (C) 2009-2014 Alan W. Irwin 00004 // 00005 // This file is part of PLplot. 00006 // 00007 // PLplot is free software; you can redistribute it and/or modify 00008 // it under the terms of the GNU Library General Public License as published 00009 // by the Free Software Foundation; either version 2 of the License, or 00010 // (at your option) any later version. 00011 // 00012 // PLplot is distributed in the hope that it will be useful, 00013 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00015 // GNU Library General Public License for more details. 00016 // 00017 // You should have received a copy of the GNU Library General Public License 00018 // along with PLplot; if not, write to the Free Software 00019 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00020 // 00021 // 00022 00023 #include "plplotP.h" 00024 00025 // To keep track of whether a sofware fallback warning has been issued. 00026 00027 static int foo; 00028 // software fallback for gradient. 00029 static void 00030 plgradient_soft( PLINT n, const PLFLT *x, const PLFLT *y, PLFLT angle ); 00031 00032 // define where plshades plots gradient for software fallback for 00033 // gradient. 00034 00035 static PLINT 00036 gradient_defined( PLFLT x, PLFLT y ); 00037 00038 //-------------------------------------------------------------------------- 00039 // void plgradient() 00040 // 00041 // Draws a linear gradient at an angle relative to the increasing x 00042 // direction for the polygon bounded by the x and y vertices. x, and 00043 // y are expressed in world coordinates, and angle (in the world 00044 // coordinate system) is expressed in degrees. The gradient is 00045 // expressed using colour and transparency information from cmap1. The 00046 // geometrical gradient direction is specified by the angle argument. 00047 // The 0. to 1. range of the independent variable of cmap1 corresponds 00048 // to the range of the polygon in the direction specified by angle. 00049 //-------------------------------------------------------------------------- 00050 00051 void 00052 c_plgradient( PLINT n, const PLFLT *x, const PLFLT *y, PLFLT angle ) 00053 { 00054 if ( plsc->level < 3 ) 00055 { 00056 plabort( "plgradient: Please set up window first" ); 00057 return; 00058 } 00059 if ( n < 3 ) 00060 { 00061 plabort( "plgradient: Not enough vertices in polygon" ); 00062 return; 00063 } 00064 00065 if ( !plsc->dev_gradient ) 00066 { 00067 if ( !foo ) 00068 { 00069 plwarn( "Driver does not support native gradients, switching to software fallback gradient.\n" ); 00070 foo = 1; 00071 } 00072 00073 plgradient_soft( n, x, y, angle ); 00074 } 00075 else 00076 { 00077 #define NGRAD 2 00078 int i, irot_min; 00079 PLINT _xpoly[PL_MAXPOLY], _ypoly[PL_MAXPOLY]; 00080 PLINT *xpoly, *ypoly; 00081 PLINT xgrad[NGRAD], ygrad[NGRAD], clpxmi, clpxma, clpymi, clpyma; 00082 PLFLT dxgrad[NGRAD], dygrad[NGRAD], xrot, xrot_min, xrot_max; 00083 PLINT npts; 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 for ( i = 1; i < n; i++ ) 00094 { 00095 xrot = x[i] * cosangle + y[i] * sinangle; 00096 if ( xrot < xrot_min ) 00097 { 00098 xrot_min = xrot; 00099 irot_min = i; 00100 } 00101 else if ( xrot > xrot_max ) 00102 { 00103 xrot_max = xrot; 00104 } 00105 } 00106 // xrot_min and xrot_max are the minimum and maximum rotated x 00107 // coordinate of polygon vertices. Use the vertex corresponding 00108 // to the minimum as the (xgrad[0], ygrad[0]) base of the 00109 // gradient vector, and calculate the (xgrad[1], ygrad[1]) tip of 00110 // the gradient vector from the range in rotated x coordinate and 00111 // the angle of the gradient. 00112 dxgrad[0] = x[irot_min]; 00113 dxgrad[1] = dxgrad[0] + ( xrot_max - xrot_min ) * cosangle; 00114 dygrad[0] = y[irot_min]; 00115 dygrad[1] = dygrad[0] + ( xrot_max - xrot_min ) * sinangle; 00116 for ( i = 0; i < NGRAD; i++ ) 00117 { 00118 xgrad[i] = plP_wcpcx( dxgrad[i] ); 00119 ygrad[i] = plP_wcpcy( dygrad[i] ); 00120 } 00121 if ( plsc->difilt ) 00122 difilt( xgrad, ygrad, NGRAD, &clpxmi, &clpxma, &clpymi, &clpyma ); 00123 plsc->xgradient = xgrad; 00124 plsc->ygradient = ygrad; 00125 plsc->ngradient = NGRAD; 00126 00127 npts = n; 00128 if ( n > PL_MAXPOLY - 1 ) 00129 { 00130 xpoly = (PLINT *) malloc( (size_t) ( n + 1 ) * sizeof ( PLINT ) ); 00131 ypoly = (PLINT *) malloc( (size_t) ( n + 1 ) * sizeof ( PLINT ) ); 00132 00133 if ( ( xpoly == NULL ) || ( ypoly == NULL ) ) 00134 { 00135 plexit( "plgradient: Insufficient memory for large polygon" ); 00136 } 00137 } 00138 else 00139 { 00140 xpoly = _xpoly; 00141 ypoly = _ypoly; 00142 } 00143 00144 for ( i = 0; i < n; i++ ) 00145 { 00146 xpoly[i] = plP_wcpcx( x[i] ); 00147 ypoly[i] = plP_wcpcy( y[i] ); 00148 } 00149 if ( x[0] != x[n - 1] || y[0] != y[n - 1] ) 00150 { 00151 n++; 00152 xpoly[n - 1] = plP_wcpcx( x[0] ); 00153 ypoly[n - 1] = plP_wcpcy( y[0] ); 00154 } 00155 plP_plfclp( xpoly, ypoly, n, plsc->clpxmi, plsc->clpxma, 00156 plsc->clpymi, plsc->clpyma, plP_gradient ); 00157 // Plot line corresponding to gradient to give visual 00158 // debugging cue. 00159 //plline( NGRAD, dxgrad, dygrad ); 00160 00161 // Check the original number of points 00162 if ( npts > PL_MAXPOLY - 1 ) 00163 { 00164 free( xpoly ); 00165 free( ypoly ); 00166 } 00167 } 00168 } 00169 00170 //-------------------------------------------------------------------------- 00171 // void plgradient_soft() 00172 // 00173 // Software fallback for gradient. See c_plgradient for an explanation 00174 // of the arguments. 00175 //-------------------------------------------------------------------------- 00176 00177 void 00178 plgradient_soft( PLINT n, const PLFLT *x, const PLFLT *y, PLFLT angle ) 00179 { 00180 PLFLT xrot, xrot_min, xrot_max, cosangle, sinangle; 00181 PLFLT xmin, xmax, ymin, ymax; 00182 PLFLT **z, *edge, xcoord, ycoord; 00183 PLINT i, j; 00184 00185 if ( n < 3 ) 00186 { 00187 plabort( "plgradient_soft: Not enough vertices in polygon" ); 00188 return; 00189 } 00190 00191 00192 // Define polygon boundary so it is accessible from gradient_defined. 00193 plsc->n_polygon = n; 00194 plsc->x_polygon = x; 00195 plsc->y_polygon = y; 00196 00197 // Find x and y range of polygon. 00198 xmin = x[0]; 00199 xmax = xmin; 00200 ymin = y[0]; 00201 ymax = ymin; 00202 // Also find x range in rotated coordinate system where 00203 // xrot = x*cosangle + y*sinangle. 00204 cosangle = cos( PI / 180. * angle ); 00205 sinangle = sin( PI / 180. * angle ); 00206 xrot = x[0] * cosangle + y[0] * sinangle; 00207 xrot_min = xrot; 00208 xrot_max = xrot; 00209 for ( i = 1; i < n; i++ ) 00210 { 00211 if ( x[i] < xmin ) 00212 xmin = x[i]; 00213 else if ( x[i] > xmax ) 00214 xmax = x[i]; 00215 00216 if ( y[i] < ymin ) 00217 ymin = y[i]; 00218 else if ( y[i] > ymax ) 00219 ymax = y[i]; 00220 00221 xrot = x[i] * cosangle + y[i] * sinangle; 00222 if ( xrot < xrot_min ) 00223 xrot_min = xrot; 00224 else if ( xrot > xrot_max ) 00225 xrot_max = xrot; 00226 } 00227 00228 // 2 x 2 array more than sufficient to define plane. 00229 // Temporarily use more to overcome irregular edge issue on defined 00230 // region. 00231 #define NX 20 00232 #define NY 20 00233 plAlloc2dGrid( &z, NX, NY ); 00234 for ( i = 0; i < NX; i++ ) 00235 { 00236 xcoord = xmin + ( (PLFLT) i ) * ( xmax - xmin ) / (PLFLT) ( NX - 1 ); 00237 for ( j = 0; j < NY; j++ ) 00238 { 00239 ycoord = ymin + ( (PLFLT) j ) * ( ymax - ymin ) / (PLFLT) ( NY - 1 ); 00240 xrot = xcoord * cosangle + ycoord * sinangle; 00241 z[i][j] = ( xrot - xrot_min ) / ( xrot_max - xrot_min ); 00242 } 00243 } 00244 // 101 edges gives reasonably smooth results for example 30. 00245 #define NEDGE 101 00246 // Define NEDGE shade edges (or NEDGE-1 shade levels) 00247 // from 0. to 1. 00248 if ( ( edge = (PLFLT *) malloc( NEDGE * sizeof ( PLFLT ) ) ) == NULL ) 00249 plexit( "plgradient_soft: Insufficient memory for large polygon" 00250 ); 00251 for ( i = 0; i < NEDGE; i++ ) 00252 edge[i] = (PLFLT) i / (PLFLT) ( NEDGE - 1 ); 00253 00254 plshades( (const PLFLT * const *) z, NX, NY, gradient_defined, xmin, xmax, ymin, ymax, 00255 edge, NEDGE, 0, 0, 0, plfill, 1, NULL, NULL ); 00256 free( (void *) edge ); 00257 plFree2dGrid( z, NX, NY ); 00258 } 00259 00260 static PLINT 00261 gradient_defined( PLFLT x, PLFLT y ) 00262 { 00263 return plP_pointinpolygon( plsc->n_polygon, plsc->x_polygon, plsc->y_polygon, 00264 x, y ); 00265 }