text: // $Id: plgradient.c 12095 2011-12-03 08:56:15Z andrewross $
  //
  //      Implement linear gradients for PLplot.
  //
  // Copyright (C) 2009  Alan W. Irwin
  //
  // This file is part of PLplot.
  //
  // PLplot is free software; you can redistribute it and/or modify
  // it under the terms of the GNU Library General Public License as published
  // by the Free Software Foundation; either version 2 of the License, or
  // (at your option) any later version.
  //
  // PLplot is distributed in the hope that it will be useful,
  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  // GNU Library General Public License for more details.
  //
  // You should have received a copy of the GNU Library General Public License
  // along with PLplot; if not, write to the Free Software
  // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  //
  //
  #include "stdafx.h"
  #include "plplotP.h"
  
  // To keep track of whether a sofware fallback warning has been issued.
  
  static int foo;
  // software fallback for gradient.
  static void
  plgradient_soft( PLINT n, const PLFLT *x, const PLFLT *y, PLFLT angle );
  
  // define where plshades plots gradient for software fallback for
  // gradient.
  
  static PLINT
  gradient_defined( PLFLT x, PLFLT y );
  
  //--------------------------------------------------------------------------
  // void plgradient()
  //
  // Draws a linear gradient at an angle relative to the increasing x
  // direction for the polygon bounded by the x and y vertices.  x, and
  // y are expressed in world coordinates, and angle (in the world
  // coordinate system) is expressed in degrees.  The gradient is
  // expressed using colour and transparency information from cmap1.  The
  // geometrical gradient direction is specified by the angle argument.
  // The 0. to 1. range of the independent variable of cmap1 corresponds
  // to the range of the polygon in the direction specified by angle.
  //--------------------------------------------------------------------------
  
  void
  c_plgradient( PLINT n, const PLFLT *x, const PLFLT *y, PLFLT angle )
  {
      if ( m_plsc->level < 3 )
      {
          plabort( "plgradient: Please set up window first" );
          return;
      }
      if ( n < 3 )
      {
          plabort( "plgradient: Not enough vertices in polygon" );
          return;
      }
  
      if ( !m_plsc->dev_gradient )
      {
          if ( !foo )
          {
              plwarn( "Driver does not support native gradients, switching to software fallback gradient.\n" );
              foo = 1;
          }
  
          plgradient_soft( n, x, y, angle );
      }
      else
      {
        #define NGRAD    2
          int   i, irot_min;
          PLINT _xpoly[PL_MAXPOLY], _ypoly[PL_MAXPOLY];
          PLINT *xpoly, *ypoly;
          PLINT xgrad[NGRAD], ygrad[NGRAD], clpxmi, clpxma, clpymi, clpyma;
          PLFLT dxgrad[NGRAD], dygrad[NGRAD], xrot, xrot_min, xrot_max;
          PLINT npts;
  
          // Find (x1, y1) and (x2, y2) corresponding to beginning and end
          // of gradient vector.
          double cosangle = cos( PI * angle / 180. );
          double sinangle = sin( PI * angle / 180. );
          xrot     = x[0] * cosangle + y[0] * sinangle;
          xrot_min = xrot;
          xrot_max = xrot;
          irot_min = 0;
          for ( i = 1; i < n; i++ )
          {
              xrot = x[i] * cosangle + y[i] * sinangle;
              if ( xrot < xrot_min )
              {
                  xrot_min = xrot;
                  irot_min = i;
              }
              else if ( xrot > xrot_max )
              {
                  xrot_max = xrot;
              }
          }
          // xrot_min and xrot_max are the minimum and maximum rotated x
          // coordinate of polygon vertices. Use the vertex corresponding
          // to the minimum  as the (xgrad[0], ygrad[0]) base of the
          // gradient vector, and calculate the (xgrad[1], ygrad[1]) tip of
          // the gradient vector from the range in rotated x coordinate and
          // the angle of the gradient.
          dxgrad[0] = x[irot_min];
          dxgrad[1] = dxgrad[0] + ( xrot_max - xrot_min ) * cosangle;
          dygrad[0] = y[irot_min];
          dygrad[1] = dygrad[0] + ( xrot_max - xrot_min ) * sinangle;
          for ( i = 0; i < NGRAD; i++ )
          {
              xgrad[i] = plP_wcpcx( dxgrad[i] );
              ygrad[i] = plP_wcpcy( dygrad[i] );
          }
          if ( m_plsc->difilt )
              difilt( xgrad, ygrad, NGRAD, &clpxmi, &clpxma, &clpymi, &clpyma );
          m_plsc->xgradient = xgrad;
          m_plsc->ygradient = ygrad;
          m_plsc->ngradient = NGRAD;
  
          npts = n;
          if ( n > PL_MAXPOLY - 1 )
          {
              xpoly = (PLINT *) malloc( (size_t) ( n + 1 ) * sizeof ( PLINT ) );
              ypoly = (PLINT *) malloc( (size_t) ( n + 1 ) * sizeof ( PLINT ) );
  
              if ( ( xpoly == NULL ) || ( ypoly == NULL ) )
              {
                  plexit( "plgradient: Insufficient memory for large polygon" );
              }
          }
          else
          {
              xpoly = _xpoly;
              ypoly = _ypoly;
          }
  
          for ( i = 0; i < n; i++ )
          {
              xpoly[i] = plP_wcpcx( x[i] );
              ypoly[i] = plP_wcpcy( y[i] );
          }
          if ( x[0] != x[n - 1] || y[0] != y[n - 1] )
          {
              n++;
              xpoly[n - 1] = plP_wcpcx( x[0] );
              ypoly[n - 1] = plP_wcpcy( y[0] );
          }
          plP_plfclp( xpoly, ypoly, n, m_plsc->clpxmi, m_plsc->clpxma,
              m_plsc->clpymi, m_plsc->clpyma, plP_gradient );
          // Plot line corresponding to gradient to give visual
          // debugging cue.
          //plline( NGRAD, dxgrad, dygrad );
  
          // Check the original number of points
          if ( npts > PL_MAXPOLY - 1 )
          {
              free( xpoly );
              free( ypoly );
          }
      }
  }
  
  //--------------------------------------------------------------------------
  // void plgradient_soft()
  //
  // Software fallback for gradient.  See c_plgradient for an explanation
  // of the arguments.
  //--------------------------------------------------------------------------
  
  void
  plgradient_soft( PLINT n, const PLFLT *x, const PLFLT *y, PLFLT angle )
  {
      PLFLT xrot, xrot_min, xrot_max, cosangle, sinangle;
      PLFLT xmin, xmax, ymin, ymax;
      PLFLT **z, *edge, xcoord, ycoord;
      PLINT i, j;
  
      if ( n < 3 )
      {
          plabort( "plgradient_soft: Not enough vertices in polygon" );
          return;
      }
  
  
      // Define polygon boundary so it is accessible from gradient_defined.
      m_plsc->n_polygon = n;
      m_plsc->x_polygon = x;
      m_plsc->y_polygon = y;
  
      // Find x and y range of polygon.
      xmin = x[0];
      xmax = xmin;
      ymin = y[0];
      ymax = ymin;
      // Also find x range in rotated coordinate system where
      // xrot = x*cosangle + y*sinangle.
      cosangle = cos( PI / 180. * angle );
      sinangle = sin( PI / 180. * angle );
      xrot     = x[0] * cosangle + y[0] * sinangle;
      xrot_min = xrot;
      xrot_max = xrot;
      for ( i = 1; i < n; i++ )
      {
          if ( x[i] < xmin )
              xmin = x[i];
          else if ( x[i] > xmax )
              xmax = x[i];
  
          if ( y[i] < ymin )
              ymin = y[i];
          else if ( y[i] > ymax )
              ymax = y[i];
  
          xrot = x[i] * cosangle + y[i] * sinangle;
          if ( xrot < xrot_min )
              xrot_min = xrot;
          else if ( xrot > xrot_max )
              xrot_max = xrot;
      }
  
      // 2 x 2 array more than sufficient to define plane.
      // Temporarily use more to overcome irregular edge issue on defined
      // region.
      #define NX    20
      #define NY    20
      plAlloc2dGrid( &z, NX, NY );
      for ( i = 0; i < NX; i++ )
      {
          xcoord = xmin + ( (PLFLT) i ) * ( xmax - xmin ) / (PLFLT) ( NX - 1 );
          for ( j = 0; j < NY; j++ )
          {
              ycoord  = ymin + ( (PLFLT) j ) * ( ymax - ymin ) / (PLFLT) ( NY - 1 );
              xrot    = xcoord * cosangle + ycoord * sinangle;
              z[i][j] = ( xrot - xrot_min ) / ( xrot_max - xrot_min );
          }
      }
      // 101 edges gives reasonably smooth results for example 30.
      #define NEDGE    101
      // Define NEDGE shade edges (or NEDGE-1 shade levels)
      // from 0. to 1.
      if ( ( edge = (PLFLT *) malloc( NEDGE * sizeof ( PLFLT ) ) ) == NULL )
          plexit( "plgradient_soft: Insufficient memory for large polygon"
              );
      for ( i = 0; i < NEDGE; i++ )
          edge[i] = (PLFLT) i / (PLFLT) ( NEDGE - 1 );
  
      plshades( (const PLFLT * const *) z, NX, NY, gradient_defined, xmin, xmax, ymin, ymax,
          edge, NEDGE, 0, 0, 0, plfill, 1, NULL, NULL );
      free( (void *) edge );
      plFree2dGrid( z, NX, NY );
  }
  
  static PLINT
  gradient_defined( PLFLT x, PLFLT y )
  {
      return plP_pointinpolygon( m_plsc->n_polygon, m_plsc->x_polygon, m_plsc->y_polygon,
          x, y );
  }
  