PLplot  5.10.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
plgradient.c
Go to the documentation of this file.
1 // Implement linear gradients for PLplot.
2 //
3 // Copyright (C) 2009-2014 Alan W. Irwin
4 //
5 // This file is part of PLplot.
6 //
7 // PLplot is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU Library General Public License as published
9 // by the Free Software Foundation; either version 2 of the License, or
10 // (at your option) any later version.
11 //
12 // PLplot is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Library General Public License for more details.
16 //
17 // You should have received a copy of the GNU Library General Public License
18 // along with PLplot; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 //
21 //
22 
23 #include "plplotP.h"
24 
25 // To keep track of whether a sofware fallback warning has been issued.
26 
27 static int foo;
28 // software fallback for gradient.
29 static void
30 plgradient_soft( PLINT n, const PLFLT *x, const PLFLT *y, PLFLT angle );
31 
32 // define where plshades plots gradient for software fallback for
33 // gradient.
34 
35 static PLINT
37 
38 //--------------------------------------------------------------------------
39 // void plgradient()
40 //
41 // Draws a linear gradient at an angle relative to the increasing x
42 // direction for the polygon bounded by the x and y vertices. x, and
43 // y are expressed in world coordinates, and angle (in the world
44 // coordinate system) is expressed in degrees. The gradient is
45 // expressed using colour and transparency information from cmap1. The
46 // geometrical gradient direction is specified by the angle argument.
47 // The 0. to 1. range of the independent variable of cmap1 corresponds
48 // to the range of the polygon in the direction specified by angle.
49 //--------------------------------------------------------------------------
50 
51 void
52 c_plgradient( PLINT n, const PLFLT *x, const PLFLT *y, PLFLT angle )
53 {
54  if ( plsc->level < 3 )
55  {
56  plabort( "plgradient: Please set up window first" );
57  return;
58  }
59  if ( n < 3 )
60  {
61  plabort( "plgradient: Not enough vertices in polygon" );
62  return;
63  }
64 
65  if ( !plsc->dev_gradient )
66  {
67  if ( !foo )
68  {
69  plwarn( "Driver does not support native gradients, switching to software fallback gradient.\n" );
70  foo = 1;
71  }
72 
73  plgradient_soft( n, x, y, angle );
74  }
75  else
76  {
77  #define NGRAD 2
78  int i, irot_min;
79  PLINT _xpoly[PL_MAXPOLY], _ypoly[PL_MAXPOLY];
80  PLINT *xpoly, *ypoly;
81  PLINT xgrad[NGRAD], ygrad[NGRAD], clpxmi, clpxma, clpymi, clpyma;
82  PLFLT dxgrad[NGRAD], dygrad[NGRAD], xrot, xrot_min, xrot_max;
83  PLINT npts;
84 
85  // Find (x1, y1) and (x2, y2) corresponding to beginning and end
86  // of gradient vector.
87  double cosangle = cos( PI * angle / 180. );
88  double sinangle = sin( PI * angle / 180. );
89  xrot = x[0] * cosangle + y[0] * sinangle;
90  xrot_min = xrot;
91  xrot_max = xrot;
92  irot_min = 0;
93  for ( i = 1; i < n; i++ )
94  {
95  xrot = x[i] * cosangle + y[i] * sinangle;
96  if ( xrot < xrot_min )
97  {
98  xrot_min = xrot;
99  irot_min = i;
100  }
101  else if ( xrot > xrot_max )
102  {
103  xrot_max = xrot;
104  }
105  }
106  // xrot_min and xrot_max are the minimum and maximum rotated x
107  // coordinate of polygon vertices. Use the vertex corresponding
108  // to the minimum as the (xgrad[0], ygrad[0]) base of the
109  // gradient vector, and calculate the (xgrad[1], ygrad[1]) tip of
110  // the gradient vector from the range in rotated x coordinate and
111  // the angle of the gradient.
112  dxgrad[0] = x[irot_min];
113  dxgrad[1] = dxgrad[0] + ( xrot_max - xrot_min ) * cosangle;
114  dygrad[0] = y[irot_min];
115  dygrad[1] = dygrad[0] + ( xrot_max - xrot_min ) * sinangle;
116  for ( i = 0; i < NGRAD; i++ )
117  {
118  xgrad[i] = plP_wcpcx( dxgrad[i] );
119  ygrad[i] = plP_wcpcy( dygrad[i] );
120  }
121  if ( plsc->difilt )
122  difilt( xgrad, ygrad, NGRAD, &clpxmi, &clpxma, &clpymi, &clpyma );
123  plsc->xgradient = xgrad;
124  plsc->ygradient = ygrad;
125  plsc->ngradient = NGRAD;
126 
127  npts = n;
128  if ( n > PL_MAXPOLY - 1 )
129  {
130  xpoly = (PLINT *) malloc( (size_t) ( n + 1 ) * sizeof ( PLINT ) );
131  ypoly = (PLINT *) malloc( (size_t) ( n + 1 ) * sizeof ( PLINT ) );
132 
133  if ( ( xpoly == NULL ) || ( ypoly == NULL ) )
134  {
135  plexit( "plgradient: Insufficient memory for large polygon" );
136  }
137  }
138  else
139  {
140  xpoly = _xpoly;
141  ypoly = _ypoly;
142  }
143 
144  for ( i = 0; i < n; i++ )
145  {
146  xpoly[i] = plP_wcpcx( x[i] );
147  ypoly[i] = plP_wcpcy( y[i] );
148  }
149  if ( x[0] != x[n - 1] || y[0] != y[n - 1] )
150  {
151  n++;
152  xpoly[n - 1] = plP_wcpcx( x[0] );
153  ypoly[n - 1] = plP_wcpcy( y[0] );
154  }
155  plP_plfclp( xpoly, ypoly, n, plsc->clpxmi, plsc->clpxma,
156  plsc->clpymi, plsc->clpyma, plP_gradient );
157  // Plot line corresponding to gradient to give visual
158  // debugging cue.
159  //plline( NGRAD, dxgrad, dygrad );
160 
161  // Check the original number of points
162  if ( npts > PL_MAXPOLY - 1 )
163  {
164  free( xpoly );
165  free( ypoly );
166  }
167  }
168 }
169 
170 //--------------------------------------------------------------------------
171 // void plgradient_soft()
172 //
173 // Software fallback for gradient. See c_plgradient for an explanation
174 // of the arguments.
175 //--------------------------------------------------------------------------
176 
177 void
178 plgradient_soft( PLINT n, const PLFLT *x, const PLFLT *y, PLFLT angle )
179 {
180  PLFLT xrot, xrot_min, xrot_max, cosangle, sinangle;
181  PLFLT xmin, xmax, ymin, ymax;
182  PLFLT **z, *edge, xcoord, ycoord;
183  PLINT i, j;
184 
185  if ( n < 3 )
186  {
187  plabort( "plgradient_soft: Not enough vertices in polygon" );
188  return;
189  }
190 
191 
192  // Define polygon boundary so it is accessible from gradient_defined.
193  plsc->n_polygon = n;
194  plsc->x_polygon = x;
195  plsc->y_polygon = y;
196 
197  // Find x and y range of polygon.
198  xmin = x[0];
199  xmax = xmin;
200  ymin = y[0];
201  ymax = ymin;
202  // Also find x range in rotated coordinate system where
203  // xrot = x*cosangle + y*sinangle.
204  cosangle = cos( PI / 180. * angle );
205  sinangle = sin( PI / 180. * angle );
206  xrot = x[0] * cosangle + y[0] * sinangle;
207  xrot_min = xrot;
208  xrot_max = xrot;
209  for ( i = 1; i < n; i++ )
210  {
211  if ( x[i] < xmin )
212  xmin = x[i];
213  else if ( x[i] > xmax )
214  xmax = x[i];
215 
216  if ( y[i] < ymin )
217  ymin = y[i];
218  else if ( y[i] > ymax )
219  ymax = y[i];
220 
221  xrot = x[i] * cosangle + y[i] * sinangle;
222  if ( xrot < xrot_min )
223  xrot_min = xrot;
224  else if ( xrot > xrot_max )
225  xrot_max = xrot;
226  }
227 
228  // 2 x 2 array more than sufficient to define plane.
229  // Temporarily use more to overcome irregular edge issue on defined
230  // region.
231  #define NX 20
232  #define NY 20
233  plAlloc2dGrid( &z, NX, NY );
234  for ( i = 0; i < NX; i++ )
235  {
236  xcoord = xmin + ( (PLFLT) i ) * ( xmax - xmin ) / (PLFLT) ( NX - 1 );
237  for ( j = 0; j < NY; j++ )
238  {
239  ycoord = ymin + ( (PLFLT) j ) * ( ymax - ymin ) / (PLFLT) ( NY - 1 );
240  xrot = xcoord * cosangle + ycoord * sinangle;
241  z[i][j] = ( xrot - xrot_min ) / ( xrot_max - xrot_min );
242  }
243  }
244  // 101 edges gives reasonably smooth results for example 30.
245  #define NEDGE 101
246  // Define NEDGE shade edges (or NEDGE-1 shade levels)
247  // from 0. to 1.
248  if ( ( edge = (PLFLT *) malloc( NEDGE * sizeof ( PLFLT ) ) ) == NULL )
249  plexit( "plgradient_soft: Insufficient memory for large polygon"
250  );
251  for ( i = 0; i < NEDGE; i++ )
252  edge[i] = (PLFLT) i / (PLFLT) ( NEDGE - 1 );
253 
254  plshades( (const PLFLT * const *) z, NX, NY, gradient_defined, xmin, xmax, ymin, ymax,
255  edge, NEDGE, 0, 0, 0, plfill, 1, NULL, NULL );
256  free( (void *) edge );
257  plFree2dGrid( z, NX, NY );
258 }
259 
260 static PLINT
262 {
263  return plP_pointinpolygon( plsc->n_polygon, plsc->x_polygon, plsc->y_polygon,
264  x, y );
265 }