PLplot 5.9.6
|
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 }