summaryrefslogtreecommitdiffstats
path: root/ast/polygon.c
diff options
context:
space:
mode:
authorWilliam Joye <wjoye@cfa.harvard.edu>2019-05-10 16:18:58 (GMT)
committerWilliam Joye <wjoye@cfa.harvard.edu>2019-05-10 16:18:58 (GMT)
commit5492ad5105428df25cca70ab260229f757427278 (patch)
treee2bc900ba8c297d483518d1e86405e2e0f86f0ea /ast/polygon.c
parent9646e8d50bc1481de77459d59738826f9c256ad6 (diff)
downloadblt-5492ad5105428df25cca70ab260229f757427278.zip
blt-5492ad5105428df25cca70ab260229f757427278.tar.gz
blt-5492ad5105428df25cca70ab260229f757427278.tar.bz2
upgrade ast 8.7.1
Diffstat (limited to 'ast/polygon.c')
-rw-r--r--ast/polygon.c7086
1 files changed, 7086 insertions, 0 deletions
diff --git a/ast/polygon.c b/ast/polygon.c
new file mode 100644
index 0000000..80b42ff
--- /dev/null
+++ b/ast/polygon.c
@@ -0,0 +1,7086 @@
+/*
+*class++
+* Name:
+* Polygon
+
+* Purpose:
+* A polygonal region within a 2-dimensional Frame.
+
+* Constructor Function:
+c astPolygon
+f AST_POLYGON
+
+* Description:
+* The Polygon class implements a polygonal area, defined by a
+* collection of vertices, within a 2-dimensional Frame. The vertices
+* are connected together by geodesic curves within the encapsulated Frame.
+* For instance, if the encapsulated Frame is a simple Frame then the
+* geodesics will be straight lines, but if the Frame is a SkyFrame then
+* the geodesics will be great circles. Note, the vertices must be
+* supplied in an order such that the inside of the polygon is to the
+* left of the boundary as the vertices are traversed. Supplying them
+* in the reverse order will effectively negate the polygon.
+*
+* Within a SkyFrame, neighbouring vertices are always joined using the
+* shortest path. Thus if an edge of 180 degrees or more in length is
+* required, it should be split into section each of which is less
+* than 180 degrees. The closed path joining all the vertices in order
+* will divide the celestial sphere into two disjoint regions. The
+* inside of the polygon is the region which is circled in an
+* anti-clockwise manner (when viewed from the inside of the celestial
+* sphere) when moving through the list of vertices in the order in
+* which they were supplied when the Polygon was created (i.e. the
+* inside is to the left of the boundary when moving through the
+* vertices in the order supplied).
+
+* Inheritance:
+* The Polygon class inherits from the Region class.
+
+* Attributes:
+* In addition to those attributes common to all Regions, every
+* Polygon also has the following attributes:
+* - SimpVertices: Simplify by transforming the vertices?
+
+* Functions:
+c In addition to those functions applicable to all Regions, the
+c following functions may also be applied to all Polygons:
+f In addition to those routines applicable to all Regions, the
+f following routines may also be applied to all Polygons:
+*
+c - astDownsize: Reduce the number of vertices in a Polygon.
+f - AST_DOWNSIZE: Reduce the number of vertices in a Polygon.
+c - astConvex<X>: Create a Polygon giving the convex hull of a pixel array
+f - AST_CONVEX<X>: Create a Polygon giving the convex hull of a pixel array
+c - astOutline<X>: Create a Polygon outlining values in a pixel array
+f - AST_OUTLINE<X>: Create a Polygon outlining values in a pixel array
+
+* Copyright:
+* Copyright (C) 1997-2006 Council for the Central Laboratory of the
+* Research Councils
+* Copyright (C) 2009 Science & Technology Facilities Council.
+* All Rights Reserved.
+
+* Licence:
+* This program is free software: you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation, either
+* version 3 of the License, or (at your option) any later
+* version.
+*
+* This program 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 Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General
+* License along with this program. If not, see
+* <http://www.gnu.org/licenses/>.
+
+* Authors:
+* DSB: David S. Berry (Starlink)
+
+* History:
+* 26-OCT-2004 (DSB):
+* Original version.
+* 28-MAY-2009 (DSB):
+* Added astDownsize.
+* 29-MAY-2009 (DSB):
+* Added astOutline<X>.
+* 30-JUN-2009 (DSB):
+* Override astGetBounded.
+* 4-NOV-2013 (DSB):
+* Modify RegPins so that it can handle uncertainty regions that straddle
+* a discontinuity. Previously, such uncertainty Regions could have a huge
+* bounding box resulting in matching region being far too big.
+* 6-DEC-2013 (DSB):
+* Reverse the order of the vertices when the Polygon is created,
+* if necessary, to ensure that the unnegated Polygon is bounded.
+* The parent Region class assumes that unnegated regions are
+* bounded.
+* 6-JAN-2014 (DSB):
+* Free edges when clearing the cache, not when establishing a new
+* cache, as the number of edges may have changed.
+* 10-JAN-2014 (DSB):
+* - Remove unused parameter description in prologue of for astOutline<X>
+* 24-FEB-2014 (DSB):
+* Added astConvex<X>.
+* 25-FEB-2014 (DSB):
+* Added attribute SimpVertices.
+* 7-SEP-2015 (DSB):
+* Shrink outline polygons by a small fraction of a pixel, in order
+* to avoid placing vertices exactly on pixel edges. This is because
+* rounding errors in subsequent code may push the vertices into
+* neighbouring pixels, which may have bad WCS coords (e.g.
+* vertices on the boundary of a polar cusp in an HPX map).
+*class--
+*/
+
+/* Module Macros. */
+/* ============== */
+/* Set the name of the class we are implementing. This indicates to
+ the header files that define class interfaces that they should make
+ "protected" symbols available. */
+#define astCLASS Polygon
+
+/* Define a macro for testing if a pixel value <V> satisfies the requirements
+ specified by <Oper> and <Value>. Compiler optimisation should remove
+ all the "if" testing from this expression. */
+#define ISVALID(V,OperI,Value) ( \
+ ( OperI == AST__LT ) ? ( (V) < Value ) : ( \
+ ( OperI == AST__LE ) ? ( (V) <= Value ) : ( \
+ ( OperI == AST__EQ ) ? ( (V) == Value ) : ( \
+ ( OperI == AST__GE ) ? ( (V) >= Value ) : ( \
+ ( OperI == AST__NE ) ? ( (V) != Value ) : ( \
+ (V) > Value \
+ ) \
+ ) \
+ ) \
+ ) \
+ ) \
+)
+
+/* Macros specifying whether a point is inside, outside or on the
+ boundary of the polygon. */
+#define UNKNOWN 0
+#define IN 1
+#define OUT 2
+#define ON 3
+
+/* Size of pertubation (in pixels) used to avoid placing vertices exactly
+ on a pixel edge. */
+#define DELTA 0.01
+
+/* Include files. */
+/* ============== */
+/* Interface definitions. */
+/* ---------------------- */
+
+#include "globals.h" /* Thread-safe global data access */
+#include "error.h" /* Error reporting facilities */
+#include "memory.h" /* Memory allocation facilities */
+#include "object.h" /* Base Object class */
+#include "pointset.h" /* Sets of points/coordinates */
+#include "region.h" /* Coordinate regions (parent class) */
+#include "channel.h" /* I/O channels */
+#include "box.h" /* Box Regions */
+#include "wcsmap.h" /* Definitons of AST__DPI etc */
+#include "polygon.h" /* Interface definition for this class */
+#include "mapping.h" /* Position mappings */
+#include "unitmap.h" /* Unit Mapping */
+#include "pal.h" /* SLALIB library interface */
+#include "frame.h" /* Coordinate system description */
+
+/* Error code definitions. */
+/* ----------------------- */
+#include "ast_err.h" /* AST error codes */
+
+/* C header files. */
+/* --------------- */
+#include <float.h>
+#include <math.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+
+/* Type definitions. */
+/* ================= */
+
+/* A structure that holds information about an edge of the new Polygon
+ being created by astDownsize. The edge is a line betweeen two of the
+ vertices of the original Polygon. */
+typedef struct Segment {
+ int i1; /* Index of starting vertex within old Polygon */
+ int i2; /* Index of ending vertex within old Polygon */
+ double error; /* Max geodesic distance from any old vertex to the line */
+ int imax; /* Index of the old vertex at which max error is reached */
+ struct Segment *next;/* Pointer to next Segment in a double link list */
+ struct Segment *prev;/* Pointer to previous Segment in a double link list */
+} Segment;
+
+
+/* Module Variables. */
+/* ================= */
+
+/* Address of this static variable is used as a unique identifier for
+ member of this class. */
+static int class_check;
+
+/* Pointers to parent class methods which are extended by this class. */
+static AstPointSet *(* parent_transform)( AstMapping *, AstPointSet *, int, AstPointSet *, int * );
+static AstMapping *(* parent_simplify)( AstMapping *, int * );
+static void (* parent_setregfs)( AstRegion *, AstFrame *, int * );
+static void (* parent_resetcache)( AstRegion *, int * );
+static const char *(* parent_getattrib)( AstObject *, const char *, int * );
+static int (* parent_testattrib)( AstObject *, const char *, int * );
+static void (* parent_clearattrib)( AstObject *, const char *, int * );
+static void (* parent_setattrib)( AstObject *, const char *, int * );
+
+
+#ifdef THREAD_SAFE
+/* Define how to initialise thread-specific globals. */
+#define GLOBAL_inits \
+ globals->Class_Init = 0; \
+ globals->GetAttrib_Buff[ 0 ] = 0;
+
+/* Create the function that initialises global data for this module. */
+astMAKE_INITGLOBALS(Polygon)
+
+/* Define macros for accessing each item of thread specific global data. */
+#define class_init astGLOBAL(Polygon,Class_Init)
+#define class_vtab astGLOBAL(Polygon,Class_Vtab)
+#define getattrib_buff astGLOBAL(Polygon,GetAttrib_Buff)
+
+
+#include <pthread.h>
+
+
+#else
+
+static char getattrib_buff[ 51 ];
+
+/* Define the class virtual function table and its initialisation flag
+ as static variables. */
+static AstPolygonVtab class_vtab; /* Virtual function table */
+static int class_init = 0; /* Virtual function table initialised? */
+
+#endif
+
+/* External Interface Function Prototypes. */
+/* ======================================= */
+/* The following functions have public prototypes only (i.e. no
+ protected prototypes), so we must provide local prototypes for use
+ within this module. */
+AstPolygon *astPolygonId_( void *, int, int, const double *, void *, const char *, ... );
+
+/* Prototypes for Private Member Functions. */
+/* ======================================== */
+
+/* Define a macro that expands to a single prototype for function
+ FindInsidePoint for a given data type and operation. */
+#define FINDINSIDEPOINT_PROTO0(X,Xtype,Oper) \
+static void FindInsidePoint##Oper##X( Xtype, const Xtype *, const int[2], const int[2], int *, int *, int *, int * );
+
+/* Define a macro that expands to a set of prototypes for all operations
+ for function FindInsidePoint for a given data type. */
+#define FINDINSIDEPOINT_PROTO(X,Xtype) \
+FINDINSIDEPOINT_PROTO0(X,Xtype,LT) \
+FINDINSIDEPOINT_PROTO0(X,Xtype,LE) \
+FINDINSIDEPOINT_PROTO0(X,Xtype,EQ) \
+FINDINSIDEPOINT_PROTO0(X,Xtype,GE) \
+FINDINSIDEPOINT_PROTO0(X,Xtype,GT) \
+FINDINSIDEPOINT_PROTO0(X,Xtype,NE)
+
+/* Use the above macros to define all FindInsidePoint prototypes for all
+ data types and operations. */
+#if HAVE_LONG_DOUBLE /* Not normally implemented */
+FINDINSIDEPOINT_PROTO(LD,long double)
+#endif
+FINDINSIDEPOINT_PROTO(D,double)
+FINDINSIDEPOINT_PROTO(L,long int)
+FINDINSIDEPOINT_PROTO(UL,unsigned long int)
+FINDINSIDEPOINT_PROTO(I,int)
+FINDINSIDEPOINT_PROTO(UI,unsigned int)
+FINDINSIDEPOINT_PROTO(S,short int)
+FINDINSIDEPOINT_PROTO(US,unsigned short int)
+FINDINSIDEPOINT_PROTO(B,signed char)
+FINDINSIDEPOINT_PROTO(UB,unsigned char)
+FINDINSIDEPOINT_PROTO(F,float)
+
+/* Define a macro that expands to a single prototype for function
+ TraceEdge for a given data type and operation. */
+#define TRACEEDGE_PROTO0(X,Xtype,Oper) \
+static AstPointSet *TraceEdge##Oper##X( Xtype, const Xtype *, const int[2], const int[2], int, int, int, int, int, int * );
+
+/* Define a macro that expands to a set of prototypes for all operations
+ for function TraceEdge for a given data type. */
+#define TRACEEDGE_PROTO(X,Xtype) \
+TRACEEDGE_PROTO0(X,Xtype,LT) \
+TRACEEDGE_PROTO0(X,Xtype,LE) \
+TRACEEDGE_PROTO0(X,Xtype,EQ) \
+TRACEEDGE_PROTO0(X,Xtype,GE) \
+TRACEEDGE_PROTO0(X,Xtype,GT) \
+TRACEEDGE_PROTO0(X,Xtype,NE)
+
+/* Use the above macros to define all TraceEdge prototypes for all
+ data types and operations. */
+#if HAVE_LONG_DOUBLE /* Not normally implemented */
+TRACEEDGE_PROTO(LD,long double)
+#endif
+TRACEEDGE_PROTO(D,double)
+TRACEEDGE_PROTO(L,long int)
+TRACEEDGE_PROTO(UL,unsigned long int)
+TRACEEDGE_PROTO(I,int)
+TRACEEDGE_PROTO(UI,unsigned int)
+TRACEEDGE_PROTO(S,short int)
+TRACEEDGE_PROTO(US,unsigned short int)
+TRACEEDGE_PROTO(B,signed char)
+TRACEEDGE_PROTO(UB,unsigned char)
+TRACEEDGE_PROTO(F,float)
+
+/* Define a macro that expands to a single prototype for function
+ PartHull for a given data type and operation. */
+#define PARTHULL_PROTO0(X,Xtype,Oper) \
+static void PartHull##Oper##X( Xtype, const Xtype[], int, int, int, int, int, int, int, const int[2], double **, double **, int *, int * );
+
+/* Define a macro that expands to a set of prototypes for all operations
+ for function PartHull for a given data type. */
+#define PARTHULL_PROTO(X,Xtype) \
+PARTHULL_PROTO0(X,Xtype,LT) \
+PARTHULL_PROTO0(X,Xtype,LE) \
+PARTHULL_PROTO0(X,Xtype,EQ) \
+PARTHULL_PROTO0(X,Xtype,GE) \
+PARTHULL_PROTO0(X,Xtype,GT) \
+PARTHULL_PROTO0(X,Xtype,NE)
+
+/* Use the above macros to define all PartHull prototypes for all
+ data types and operations. */
+#if HAVE_LONG_DOUBLE /* Not normally implemented */
+PARTHULL_PROTO(LD,long double)
+#endif
+PARTHULL_PROTO(D,double)
+PARTHULL_PROTO(L,long int)
+PARTHULL_PROTO(UL,unsigned long int)
+PARTHULL_PROTO(I,int)
+PARTHULL_PROTO(UI,unsigned int)
+PARTHULL_PROTO(S,short int)
+PARTHULL_PROTO(US,unsigned short int)
+PARTHULL_PROTO(B,signed char)
+PARTHULL_PROTO(UB,unsigned char)
+PARTHULL_PROTO(F,float)
+
+/* Define a macro that expands to a single prototype for function
+ ConvexHull for a given data type and operation. */
+#define CONVEXHULL_PROTO0(X,Xtype,Oper) \
+static AstPointSet *ConvexHull##Oper##X( Xtype, const Xtype[], const int[2], int, int, int, int * );
+
+/* Define a macro that expands to a set of prototypes for all operations
+ for function ConvexHull for a given data type. */
+#define CONVEXHULL_PROTO(X,Xtype) \
+CONVEXHULL_PROTO0(X,Xtype,LT) \
+CONVEXHULL_PROTO0(X,Xtype,LE) \
+CONVEXHULL_PROTO0(X,Xtype,EQ) \
+CONVEXHULL_PROTO0(X,Xtype,GE) \
+CONVEXHULL_PROTO0(X,Xtype,GT) \
+CONVEXHULL_PROTO0(X,Xtype,NE)
+
+/* Use the above macros to define all ConvexHull prototypes for all
+ data types and operations. */
+#if HAVE_LONG_DOUBLE /* Not normally implemented */
+CONVEXHULL_PROTO(LD,long double)
+#endif
+CONVEXHULL_PROTO(D,double)
+CONVEXHULL_PROTO(L,long int)
+CONVEXHULL_PROTO(UL,unsigned long int)
+CONVEXHULL_PROTO(I,int)
+CONVEXHULL_PROTO(UI,unsigned int)
+CONVEXHULL_PROTO(S,short int)
+CONVEXHULL_PROTO(US,unsigned short int)
+CONVEXHULL_PROTO(B,signed char)
+CONVEXHULL_PROTO(UB,unsigned char)
+CONVEXHULL_PROTO(F,float)
+
+/* Define a macro that expands to a single prototype for function
+ FindBoxEdge for a given data type and operation. */
+#define FINDBOXEDGE_PROTO0(X,Xtype,Oper) \
+static void FindBoxEdge##Oper##X( Xtype, const Xtype[], int, int, int, int, int *, int *, int *, int * );
+
+/* Define a macro that expands to a set of prototypes for all operations
+ for function FindBoxEdge for a given data type. */
+#define FINDBOXEDGE_PROTO(X,Xtype) \
+FINDBOXEDGE_PROTO0(X,Xtype,LT) \
+FINDBOXEDGE_PROTO0(X,Xtype,LE) \
+FINDBOXEDGE_PROTO0(X,Xtype,EQ) \
+FINDBOXEDGE_PROTO0(X,Xtype,GE) \
+FINDBOXEDGE_PROTO0(X,Xtype,GT) \
+FINDBOXEDGE_PROTO0(X,Xtype,NE)
+
+/* Use the above macros to define all FindBoxEdge prototypes for all
+ data types and operations. */
+#if HAVE_LONG_DOUBLE /* Not normally implemented */
+FINDBOXEDGE_PROTO(LD,long double)
+#endif
+FINDBOXEDGE_PROTO(D,double)
+FINDBOXEDGE_PROTO(L,long int)
+FINDBOXEDGE_PROTO(UL,unsigned long int)
+FINDBOXEDGE_PROTO(I,int)
+FINDBOXEDGE_PROTO(UI,unsigned int)
+FINDBOXEDGE_PROTO(S,short int)
+FINDBOXEDGE_PROTO(US,unsigned short int)
+FINDBOXEDGE_PROTO(B,signed char)
+FINDBOXEDGE_PROTO(UB,unsigned char)
+FINDBOXEDGE_PROTO(F,float)
+
+
+
+
+
+
+
+
+
+/* Non-generic function prototypes. */
+static AstMapping *Simplify( AstMapping *, int * );
+static AstPointSet *DownsizePoly( AstPointSet *, double, int, AstFrame *, int * );
+static AstPointSet *RegBaseMesh( AstRegion *, int * );
+static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * );
+static AstPolygon *Downsize( AstPolygon *, double, int, int * );
+static Segment *AddToChain( Segment *, Segment *, int * );
+static Segment *NewSegment( Segment *, int, int, int, int * );
+static Segment *RemoveFromChain( Segment *, Segment *, int * );
+static double Polywidth( AstFrame *, AstLineDef **, int, int, double[ 2 ], int * );
+static int GetBounded( AstRegion *, int * );
+static int IntCmp( const void *, const void * );
+static int RegPins( AstRegion *, AstPointSet *, AstRegion *, int **, int * );
+static int RegTrace( AstRegion *, int, double *, double **, int * );
+static void Cache( AstPolygon *, int * );
+static void Copy( const AstObject *, AstObject *, int * );
+static void Delete( AstObject *, int * );
+static void Dump( AstObject *, AstChannel *, int * );
+static void EnsureInside( AstPolygon *, int * );
+static void FindMax( Segment *, AstFrame *, double *, double *, int, int, int * );
+static void RegBaseBox( AstRegion *this, double *, double *, int * );
+static void ResetCache( AstRegion *this, int * );
+static void SetPointSet( AstPolygon *, AstPointSet *, int * );
+static void SetRegFS( AstRegion *, AstFrame *, int * );
+static void SmoothPoly( AstPointSet *, int, double, int * );
+
+static const char *GetAttrib( AstObject *, const char *, int * );
+static void ClearAttrib( AstObject *, const char *, int * );
+static void SetAttrib( AstObject *, const char *, int * );
+static int TestAttrib( AstObject *, const char *, int * );
+
+static int GetSimpVertices( AstPolygon *, int * );
+static int TestSimpVertices( AstPolygon *, int * );
+static void ClearSimpVertices( AstPolygon *, int * );
+static void SetSimpVertices( AstPolygon *, int, int * );
+
+
+/* Member functions. */
+/* ================= */
+static Segment *AddToChain( Segment *head, Segment *seg, int *status ){
+/*
+* Name:
+* AddToChain
+
+* Purpose:
+* Add a Segment into the linked list of Segments, maintaining the
+* required order in the list.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "polygon.h"
+* Segment *AddToChain( Segment *head, Segment *seg, int *status )
+
+* Class Membership:
+* Polygon member function
+
+* Description:
+* The linked list of Segments maintained by astDownsize is searched
+* from the high error end (the head), until a Segment is foound which
+* has a lower error than the supplied segment. The supplied Segment
+* is then inserted into the list at that point.
+
+* Parameters:
+* head
+* The Segment structure at the head of the list (i.e. the segment
+* with maximum error).
+* seg
+* The Segment to be added into the list.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* Pointer to the link head (which will have changed if "seg" has a
+* higher error than the original head).
+
+*/
+
+/* Local Variables: */
+ Segment *tseg;
+
+/* Check the global error status. */
+ if ( !astOK ) return head;
+
+/* If the list is empty, return the supplied segment as the new list
+ head. */
+ if( !head ) {
+ head = seg;
+
+/* If the supplied segment has a higher error than the original head,
+ insert the new segment in front of the original head. */
+ } else if( seg->error > head->error ){
+ seg->next = head;
+ head->prev = seg;
+ head = seg;
+
+/* Otherwise, move down the list from the head until a segment is found
+ which has a lower error than the supplied Segment. Then insert the
+ supplied segment into the list in front of it. */
+ } else {
+ tseg = head;
+ seg->next = NULL;
+
+ while( tseg->next ) {
+ if( seg->error > tseg->next->error ) {
+ seg->next = tseg->next;
+ seg->prev = tseg;
+ tseg->next->prev = seg;
+ tseg->next = seg;
+ break;
+ }
+ tseg = tseg->next;
+ }
+
+ if( !seg->next ) {
+ tseg->next = seg;
+ seg->prev = tseg;
+ }
+ }
+
+/* Return the new head. */
+ return head;
+}
+
+static void Cache( AstPolygon *this, int *status ){
+/*
+* Name:
+* Cache
+
+* Purpose:
+* Calculate intermediate values and cache them in the Polygon structure.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "polygon.h"
+* void Cache( AstPolygon *this, int *status )
+
+* Class Membership:
+* Polygon member function
+
+* Description:
+* This function uses the PointSet stored in the parent Region to calculate
+* some intermediate values which are useful in other methods. These
+* values are stored within the Polygon structure.
+
+* Parameters:
+* this
+* Pointer to the Polygon.
+* status
+* Pointer to the inherited status variable.
+
+*/
+
+/* Local Variables: */
+ AstFrame *frm; /* Pointer to base Frame in Polygon */
+ double **ptr; /* Pointer to data in the encapsulated PointSet */
+ double end[ 2 ]; /* Start position for edge */
+ double maxwid; /* Maximum polygon width found so far */
+ double polcen[ 2 ]; /* Polygon centre perpendicular to current edge */
+ double polwid; /* Polygon width perpendicular to current edge */
+ double start[ 2 ]; /* Start position for edge */
+ int i; /* Axis index */
+ int nv; /* Number of vertices in Polygon */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Do nothing if the cached information is up to date. */
+ if( this->stale ) {
+
+/* Get a pointer to the base Frame. */
+ frm = astGetFrame( ((AstRegion *) this)->frameset, AST__BASE );
+
+/* Get the number of vertices. */
+ nv = astGetNpoint( ((AstRegion *) this)->points );
+
+/* Get pointers to the coordinate data in the parent Region structure. */
+ ptr = astGetPoints( ((AstRegion *) this)->points );
+
+/* Free any existing edge information in the Polygon structure. */
+ if( this->edges ) {
+ for( i = 0; i < nv; i++ ) {
+ this->edges[ i ] = astFree( this->edges[ i ] );
+ }
+ }
+
+/* Ensure we have enough memory to store new edge information if necessary. */
+ this->edges = astGrow( this->edges, nv, sizeof( AstLineDef *) );
+ this->startsat = astGrow( this->startsat, nv, sizeof( double ) );
+
+/* Check pointers can be used safely. */
+ if( this->edges && this->startsat ) {
+
+/* Create and store a description of each edge. Also form the total
+ distance round the polygon, and the distance from the first vertex
+ at which each edge starts. */
+ this->totlen = 0.0;
+ start[ 0 ] = ptr[ 0 ][ nv - 1 ];
+ start[ 1 ] = ptr[ 1 ][ nv - 1 ];
+
+ for( i = 0; i < nv; i++ ) {
+ end[ 0 ] = ptr[ 0 ][ i ];
+ end[ 1 ] = ptr[ 1 ][ i ];
+ this->edges[ i ] = astLineDef( frm, start, end );
+ start[ 0 ] = end[ 0 ];
+ start[ 1 ] = end[ 1 ];
+
+ this->startsat[ i ] = this->totlen;
+ this->totlen += this->edges[ i ]->length;
+ }
+
+/* We now look for a point that is inside the polygon. We want a point
+ that is well within the polygon, since points that are only just inside
+ the polygon can give numerical problems. Loop round each edge with
+ non-zero length. */
+ maxwid = -1.0;
+ for( i = 0; i < nv; i++ ) {
+ if( this->edges[ i ]->length > 0.0 ) {
+
+/* We define another line perpendicular to the current edge, passing
+ through the mid point of the edge, extending towards the inside of the
+ polygon. The following function returns the distance we can travel
+ along this line before we hit any of the other polygon edges. It also
+ puts the position corresponding to half that distance into "polcen". */
+ polwid = Polywidth( frm, this->edges, i, nv, polcen, status );
+
+/* If the width of the polygon perpendicular to the current edge is
+ greater than the width perpdeicular to any other edge, record the
+ width and also store the current polygon centre. */
+ if( polwid > maxwid && polwid != AST__BAD ) {
+ maxwid = polwid;
+ (this->in)[ 0 ] = polcen[ 0 ];
+ (this->in)[ 1 ] = polcen[ 1 ];
+ }
+ }
+ }
+
+/* If no width was found it probably means that the polygon vertices were
+ given in clockwise order, resulting in the above process probing the
+ infinite extent outside the polygonal hole. In this case any point
+ outside the hole will do, so we use the current contents of the
+ "polcen" array. Set a flag indicating if the vertices are stored in
+ anti-clockwise order. */
+ if( maxwid < 0.0 ) {
+ (this->in)[ 0 ] = polcen[ 0 ];
+ (this->in)[ 1 ] = polcen[ 1 ];
+ this->acw = 0;
+ } else {
+ this->acw = 1;
+ }
+ }
+
+/* Free resources */
+ frm = astAnnul( frm );
+
+/* Indicate cached information is up to date. */
+ this->stale = 0;
+ }
+}
+
+static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) {
+/*
+* Name:
+* ClearAttrib
+
+* Purpose:
+* Clear an attribute value for a Polygon.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "polygon.h"
+* void ClearAttrib( AstObject *this, const char *attrib, int *status )
+
+* Class Membership:
+* Polygon member function (over-rides the astClearAttrib protected
+* method inherited from the Region class).
+
+* Description:
+* This function clears the value of a specified attribute for a
+* Polygon, so that the default value will subsequently be used.
+
+* Parameters:
+* this
+* Pointer to the Polygon.
+* attrib
+* Pointer to a null terminated string specifying the attribute
+* name. This should be in lower case with no surrounding white
+* space.
+* status
+* Pointer to the inherited status variable.
+*/
+
+/* Local Variables: */
+ AstPolygon *this; /* Pointer to the Polygon structure */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Obtain a pointer to the Polygon structure. */
+ this = (AstPolygon *) this_object;
+
+/* Check the attribute name and clear the appropriate attribute. */
+
+/* SimpVertices. */
+/* ------------- */
+ if ( !strcmp( attrib, "simpvertices" ) ) {
+ astClearSimpVertices( this );
+
+/* If the attribute is not recognised, pass it on to the parent method
+ for further interpretation. */
+ } else {
+ (*parent_clearattrib)( this_object, attrib, status );
+ }
+}
+
+/*
+*++
+* Name:
+c astConvex<X>
+f AST_CONVEX<X>
+
+* Purpose:
+* Create a new Polygon representing the convex hull of a 2D data grid.
+
+* Type:
+* Public function.
+
+* Synopsis:
+c #include "polygon.h"
+c AstPolygon *astConvex<X>( <Xtype> value, int oper, const <Xtype> array[],
+c const int lbnd[2], const int ubnd[2], int starpix )
+f RESULT = AST_CONVEX<X>( VALUE, OPER, ARRAY, LBND, UBND, STARPIX, STATUS )
+
+* Class Membership:
+* Polygon method.
+
+* Description:
+* This is a set of functions that create the shortest Polygon that
+* encloses all pixels with a specified value within a gridded
+* 2-dimensional data array (e.g. an image).
+*
+* A basic 2-dimensional Frame is used to represent the pixel coordinate
+* system in the returned Polygon. The Domain attribute is set to
+* "PIXEL", the Title attribute is set to "Pixel coordinates", and the
+* Unit attribute for each axis is set to "pixel". All other
+* attributes are left unset. The nature of the pixel coordinate system
+* is determined by parameter
+c "starpix".
+f STARPIX.
+*
+* You should use a function which matches the numerical type of the
+* data you are processing by replacing <X> in the generic function
+* name
+c astConvex<X>
+f AST_CONVEX<X>
+c by an appropriate 1- or 2-character type code. For example, if you
+* are procesing data with type
+c "float", you should use the function astConvexF
+f REAL, you should use the function AST_CONVEXR
+* (see the "Data Type Codes" section below for the codes appropriate to
+* other numerical types).
+
+* Parameters:
+c value
+f VALUE = <Xtype> (Given)
+* A data value that specifies the pixels to be included within the
+* convex hull.
+c oper
+f OPER = INTEGER (Given)
+* Indicates how the
+c "value"
+f VALUE
+* parameter is used to select the required pixels. It can
+* have any of the following values:
+c - AST__LT: include pixels with value less than "value".
+c - AST__LE: include pixels with value less than or equal to "value".
+c - AST__EQ: include pixels with value equal to "value".
+c - AST__NE: include pixels with value not equal to "value".
+c - AST__GE: include pixels with value greater than or equal to "value".
+c - AST__GT: include pixels with value greater than "value".
+f - AST__LT: include pixels with value less than VALUE.
+f - AST__LE: include pixels with value less than or equal to VALUE.
+f - AST__EQ: include pixels with value equal to VALUE.
+f - AST__NE: include pixels with value not equal to VALUE.
+f - AST__GE: include pixels with value greater than or equal to VALUE.
+f - AST__GT: include pixels with value greater than VALUE.
+c array
+f ARRAY( * ) = <Xtype> (Given)
+c Pointer to a
+f A
+* 2-dimensional array containing the data to be processed. The
+* numerical type of this array should match the 1- or
+* 2-character type code appended to the function name (e.g. if
+c you are using astConvexF, the type of each array element
+c should be "float").
+f you are using AST_CONVEXR, the type of each array element
+f should be REAL).
+*
+* The storage order of data within this array should be such
+* that the index of the first grid dimension varies most
+* rapidly and that of the second dimension least rapidly
+c (i.e. Fortran array indexing is used).
+f (i.e. normal Fortran array storage order).
+c lbnd
+f LBND( 2 ) = INTEGER (Given)
+c Pointer to an array of two integers
+f An array
+* containing the coordinates of the centre of the first pixel
+* in the input grid along each dimension.
+c ubnd
+f UBND( 2) = INTEGER (Given)
+c Pointer to an array of two integers
+f An array
+* containing the coordinates of the centre of the last pixel in
+* the input grid along each dimension.
+*
+c Note that "lbnd" and "ubnd" together define the shape
+f Note that LBND and UBND together define the shape
+* and size of the input grid, its extent along a particular
+c (j'th) dimension being ubnd[j]-lbnd[j]+1 (assuming the
+c index "j" to be zero-based). They also define
+f (J'th) dimension being UBND(J)-LBND(J)+1. They also define
+* the input grid's coordinate system, each pixel having unit
+* extent along each dimension with integral coordinate values
+* at its centre or upper corner, as selected by parameter
+c "starpix".
+f STARPIX.
+c starpix
+f STARPIX = LOGICAL (Given)
+* A flag indicating the nature of the pixel coordinate system used
+* to describe the vertex positions in the returned Polygon. If
+c non-zero,
+f .TRUE.,
+* the standard Starlink definition of pixel coordinate is used in
+* which a pixel with integer index I spans a range of pixel coordinate
+* from (I-1) to I (i.e. pixel corners have integral pixel coordinates).
+c If zero,
+f If .FALSE.,
+* the definition of pixel coordinate used by other AST functions
+c such as astResample, astMask,
+f such as AST_RESAMPLE, AST_MASK,
+* etc., is used. In this definition, a pixel with integer index I
+* spans a range of pixel coordinate from (I-0.5) to (I+0.5) (i.e.
+* pixel centres have integral pixel coordinates).
+f STATUS = INTEGER (Given and Returned)
+f The global status.
+
+* Returned Value:
+c astConvex<X>()
+f AST_CONVEX<X> = INTEGER
+* A pointer to the required Polygon.
+c NULL
+f AST__NULL
+* is returned without error if the array contains no pixels that
+* satisfy the criterion specified by
+c "value" and "oper".
+f VALUE and OPER.
+
+* Notes:
+c - NULL
+f - AST__NULL
+* will be returned if this function is invoked with the global
+* error status set, or if it should fail for any reason.
+
+* Data Type Codes:
+* To select the appropriate masking function, you should
+c replace <X> in the generic function name astConvex<X> with a
+f replace <X> in the generic function name AST_CONVEX<X> with a
+* 1- or 2-character data type code, so as to match the numerical
+* type <Xtype> of the data you are processing, as follows:
+c - D: double
+c - F: float
+c - L: long int
+c - UL: unsigned long int
+c - I: int
+c - UI: unsigned int
+c - S: short int
+c - US: unsigned short int
+c - B: byte (signed char)
+c - UB: unsigned byte (unsigned char)
+f - D: DOUBLE PRECISION
+f - R: REAL
+f - I: INTEGER
+f - UI: INTEGER (treated as unsigned)
+f - S: INTEGER*2 (short integer)
+f - US: INTEGER*2 (short integer, treated as unsigned)
+f - B: BYTE (treated as signed)
+f - UB: BYTE (treated as unsigned)
+*
+c For example, astConvexD would be used to process "double"
+c data, while astConvexS would be used to process "short int"
+c data, etc.
+f For example, AST_CONVEXD would be used to process DOUBLE
+f PRECISION data, while AST_CONVEXS would be used to process
+f short integer data (stored in an INTEGER*2 array), etc.
+f
+f For compatibility with other Starlink facilities, the codes W
+f and UW are provided as synonyms for S and US respectively (but
+f only in the Fortran interface to AST).
+
+*--
+*/
+/* Define a macro to implement the function for a specific data
+ type. Note, this function cannot be a virtual function since the
+ argument list does not include a Polygon, and so no virtual function
+ table is available. */
+#define MAKE_CONVEX(X,Xtype) \
+AstPolygon *astConvex##X##_( Xtype value, int oper, const Xtype array[], \
+ const int lbnd[2], const int ubnd[2], \
+ int starpix, int *status ) { \
+\
+/* Local Variables: */ \
+ AstFrame *frm; /* Frame in which to define the Polygon */ \
+ AstPointSet *candidate; /* Candidate polygon vertices */ \
+ AstPolygon *result; /* Result value to return */ \
+ int xdim; /* Number of pixels per row */ \
+ int ydim; /* Number of rows */ \
+ static double junk[ 6 ] = {0.0, 0.0, 1.0, 1.0, 0.0, 1.0 }; /* Junk poly */ \
+\
+/* Initialise. */ \
+ result = NULL; \
+ candidate = NULL; \
+\
+/* Check the global error status. */ \
+ if ( !astOK ) return result; \
+\
+/* Get the array dimensions. */ \
+ xdim = ubnd[ 0 ] - lbnd[ 0 ] + 1; \
+ ydim = ubnd[ 1 ] - lbnd[ 1 ] + 1; \
+\
+/* Get the basic concvex hull. */ \
+ if( oper == AST__LT ) { \
+ candidate = ConvexHullLT##X( value, array, lbnd, starpix, xdim, ydim, status ); \
+\
+ } else if( oper == AST__LE ) { \
+ candidate = ConvexHullLE##X( value, array, lbnd, starpix, xdim, ydim, status ); \
+\
+ } else if( oper == AST__EQ ) { \
+ candidate = ConvexHullEQ##X( value, array, lbnd, starpix, xdim, ydim, status ); \
+\
+ } else if( oper == AST__NE ) { \
+ candidate = ConvexHullNE##X( value, array, lbnd, starpix, xdim, ydim, status ); \
+\
+ } else if( oper == AST__GE ) { \
+ candidate = ConvexHullGE##X( value, array, lbnd, starpix, xdim, ydim, status ); \
+\
+ } else if( oper == AST__GT ) { \
+ candidate = ConvexHullGT##X( value, array, lbnd, starpix, xdim, ydim, status ); \
+\
+ } else if( astOK ){ \
+ astError( AST__OPRIN, "astConvex"#X": Invalid operation code " \
+ "(%d) supplied (programming error).", status, oper ); \
+ } \
+\
+/* Check some good selected values were found. */ \
+ if( candidate ) { \
+\
+/* Create a default Polygon with 3 junk vertices. */ \
+ frm = astFrame( 2, "Domain=PIXEL,Unit(1)=pixel,Unit(2)=pixel," \
+ "Title=Pixel coordinates", status ); \
+ result = astPolygon( frm, 3, 3, junk, NULL, " ", status ); \
+\
+/* Change the PointSet within the Polygon to the one created above. */ \
+ SetPointSet( result, candidate, status ); \
+\
+/* Free resources. */ \
+ frm = astAnnul( frm ); \
+ candidate = astAnnul( candidate ); \
+ } \
+\
+/* If an error occurred, clear the returned result. */ \
+ if ( !astOK ) result = astAnnul( result ); \
+\
+/* Return the result. */ \
+ return result; \
+}
+
+
+/* Expand the above macro to generate a function for each required
+ data type. */
+#if HAVE_LONG_DOUBLE /* Not normally implemented */
+MAKE_CONVEX(LD,long double)
+#endif
+MAKE_CONVEX(D,double)
+MAKE_CONVEX(L,long int)
+MAKE_CONVEX(UL,unsigned long int)
+MAKE_CONVEX(I,int)
+MAKE_CONVEX(UI,unsigned int)
+MAKE_CONVEX(S,short int)
+MAKE_CONVEX(US,unsigned short int)
+MAKE_CONVEX(B,signed char)
+MAKE_CONVEX(UB,unsigned char)
+MAKE_CONVEX(F,float)
+
+/* Undefine the macros. */
+#undef MAKE_CONVEX
+
+/*
+* Name:
+* ConvexHull
+
+* Purpose:
+* Find the convex hull enclosing selected pixels in a 2D array.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "polygon.h"
+* AstPointSet *ConvexHull<Oper><X>( <Xtype> value, const <Xtype> array[],
+* const int lbnd[2], int starpix,
+* int xdim, int ydim, int *status )
+
+* Class Membership:
+* Polygon member function
+
+* Description:
+* This function uses an algorithm similar to "Andrew's Monotone Chain
+* Algorithm" to create a list of vertices describing the convex hull
+* enclosing the selected pixels in the supplied array. The vertices
+* are returned in a PointSet.
+
+* Parameters:
+* value
+* A data value that specifies the pixels to be selected.
+* array
+* Pointer to a 2-dimensional array containing the data to be
+* processed. The numerical type of this array should match the 1-
+* or 2-character type code appended to the function name.
+* lbnd
+* The lower pixel index bounds of the array.
+* starpix
+* If non-zero, the usual Starlink definition of pixel coordinate
+* is used (integral values at pixel corners). Otherwise, the
+* system used by other AST functions such as astResample is used
+* (integral values at pixel centres).
+* xdim
+* The number of pixels along each row of the array.
+* ydim
+* The number of rows in the array.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A PointSet holding the vertices of the convex hull, or NULL if an
+* error occurs.
+
+* Notes:
+* - The following code has been designed with a view to it being
+* multi-threaded at some point.
+
+*/
+
+/* Define a macro to implement the function for a specific data
+ type and operation. */
+#define MAKE_CONVEXHULL(X,Xtype,Oper,OperI) \
+static AstPointSet *ConvexHull##Oper##X( Xtype value, const Xtype array[], \
+ const int lbnd[2], int starpix, \
+ int xdim, int ydim, int *status ) { \
+\
+/* Local Variables: */ \
+ AstPointSet *result; \
+ double **ptr; \
+ double *xv1; \
+ double *xv2; \
+ double *xv3; \
+ double *xv4; \
+ double *xvert; \
+ double *yv1; \
+ double *yv2; \
+ double *yv3; \
+ double *yv4; \
+ double *yvert; \
+ int nv1; \
+ int nv2; \
+ int nv3; \
+ int nv4; \
+ int nv; \
+ int xhi; \
+ int xhiymax; \
+ int xhiymin; \
+ int xlo; \
+ int xloymax; \
+ int xloymin; \
+ int yhi; \
+ int yhixmax; \
+ int yhixmin; \
+ int ylo; \
+ int yloxmax; \
+ int yloxmin; \
+\
+/* Initialise */ \
+ result = NULL; \
+\
+/* Check the global error status. */ \
+ if ( !astOK ) return result; \
+\
+/* Find the lowest Y value at any selected pixel, and find the max and \
+ min X value of the selected pixels at that lowest Y value. */ \
+ FindBoxEdge##Oper##X( value, array, xdim, ydim, 1, 1, &ylo, &yloxmax, \
+ &yloxmin, status ); \
+\
+/* Skip if there are no selected values in the array. */ \
+ if( ylo > 0 ) { \
+\
+/* Find the highest Y value at any selected pixel, and find the max and \
+ min X value of the selected pixels at that highest Y value. */ \
+ FindBoxEdge##Oper##X( value, array, xdim, ydim, 1, 0, &yhi, &yhixmax, \
+ &yhixmin, status ); \
+\
+/* Find the lowest X value at any selected pixel, and find the max and \
+ min Y value of the selected pixels at that lowest X value. */ \
+ FindBoxEdge##Oper##X( value, array, xdim, ydim, 0, 1, &xlo, &xloymax, \
+ &xloymin, status ); \
+\
+/* Find the highest X value at any selected pixel, and find the max and \
+ min Y value of the selected pixels at that highest X value. */ \
+ FindBoxEdge##Oper##X( value, array, xdim, ydim, 0, 0, &xhi, &xhiymax, \
+ &xhiymin, status ); \
+\
+/* Create a list of vertices for the bottom right corner of the bounding \
+ box of the selected pixels. */ \
+ PartHull##Oper##X( value, array, xdim, ydim, yloxmax, ylo, xhi, xhiymin, \
+ starpix, lbnd, &xv1, &yv1, &nv1, status ); \
+\
+/* Create a list of vertices for the top right corner of the bounding \
+ box of the selected pixels. */ \
+ PartHull##Oper##X( value, array, xdim, ydim, xhi, xhiymax, yhixmax, yhi, \
+ starpix, lbnd, &xv2, &yv2, &nv2, status ); \
+\
+/* Create a list of vertices for the top left corner of the bounding \
+ box of the selected pixels. */ \
+ PartHull##Oper##X( value, array, xdim, ydim, yhixmin, yhi, xlo, xloymax, \
+ starpix, lbnd, &xv3, &yv3, &nv3, status ); \
+\
+/* Create a list of vertices for the bottom left corner of the bounding \
+ box of the selected pixels. */ \
+ PartHull##Oper##X( value, array, xdim, ydim, xlo, xloymin, yloxmin, ylo, \
+ starpix, lbnd, &xv4, &yv4, &nv4, status ); \
+\
+/* Concatenate the four vertex lists and store them in the returned \
+ PointSet. */ \
+ nv = nv1 + nv2 + nv3 + nv4; \
+ result = astPointSet( nv, 2, " ", status ); \
+ ptr = astGetPoints( result ); \
+ if( astOK ) { \
+ xvert = ptr[ 0 ]; \
+ yvert = ptr[ 1 ]; \
+\
+ memcpy( xvert, xv1, nv1*sizeof( double ) ); \
+ memcpy( yvert, yv1, nv1*sizeof( double ) ); \
+ xvert += nv1; \
+ yvert += nv1; \
+\
+ memcpy( xvert, xv2, nv2*sizeof( double ) ); \
+ memcpy( yvert, yv2, nv2*sizeof( double ) ); \
+ xvert += nv2; \
+ yvert += nv2; \
+\
+ memcpy( xvert, xv3, nv3*sizeof( double ) ); \
+ memcpy( yvert, yv3, nv3*sizeof( double ) ); \
+ xvert += nv3; \
+ yvert += nv3; \
+\
+ memcpy( xvert, xv4, nv4*sizeof( double ) ); \
+ memcpy( yvert, yv4, nv4*sizeof( double ) ); \
+ } \
+\
+/* Free resources. */ \
+ xv1 = astFree( xv1 ); \
+ xv2 = astFree( xv2 ); \
+ xv3 = astFree( xv3 ); \
+ xv4 = astFree( xv4 ); \
+ yv1 = astFree( yv1 ); \
+ yv2 = astFree( yv2 ); \
+ yv3 = astFree( yv3 ); \
+ yv4 = astFree( yv4 ); \
+ } \
+\
+/* Free the returned PointSet if an error occurred. */ \
+ if( result && !astOK ) result = astAnnul( result ); \
+\
+/* Return the result. */ \
+ return result; \
+}
+
+/* Define a macro that uses the above macro to to create implementations
+ of ConvexHull for all operations. */
+#define MAKEALL_CONVEXHULL(X,Xtype) \
+MAKE_CONVEXHULL(X,Xtype,LT,AST__LT) \
+MAKE_CONVEXHULL(X,Xtype,LE,AST__LE) \
+MAKE_CONVEXHULL(X,Xtype,EQ,AST__EQ) \
+MAKE_CONVEXHULL(X,Xtype,NE,AST__NE) \
+MAKE_CONVEXHULL(X,Xtype,GE,AST__GE) \
+MAKE_CONVEXHULL(X,Xtype,GT,AST__GT)
+
+/* Expand the above macro to generate a function for each required
+ data type and operation. */
+#if HAVE_LONG_DOUBLE /* Not normally implemented */
+MAKEALL_CONVEXHULL(LD,long double)
+#endif
+MAKEALL_CONVEXHULL(D,double)
+MAKEALL_CONVEXHULL(L,long int)
+MAKEALL_CONVEXHULL(UL,unsigned long int)
+MAKEALL_CONVEXHULL(I,int)
+MAKEALL_CONVEXHULL(UI,unsigned int)
+MAKEALL_CONVEXHULL(S,short int)
+MAKEALL_CONVEXHULL(US,unsigned short int)
+MAKEALL_CONVEXHULL(B,signed char)
+MAKEALL_CONVEXHULL(UB,unsigned char)
+MAKEALL_CONVEXHULL(F,float)
+
+/* Undefine the macros. */
+#undef MAKE_CONVEXHULL
+#undef MAKEALL_CONVEXHULL
+
+static AstPolygon *Downsize( AstPolygon *this, double maxerr, int maxvert,
+ int *status ) {
+/*
+*++
+* Name:
+c astDownsize
+f AST_DOWNSIZE
+
+* Purpose:
+* Reduce the number of vertices in a Polygon.
+
+* Type:
+* Public virtual function.
+
+* Synopsis:
+c #include "polygon.h"
+c AstPolygon *astDownsize( AstPolygon *this, double maxerr, int maxvert )
+f RESULT = AST_DOWNSIZE( THIS, MAXERR, MAXVERT, STATUS )
+
+* Class Membership:
+* Polygon method.
+
+* Description:
+* This function returns a pointer to a new Polygon that contains a
+* subset of the vertices in the supplied Polygon. The subset is
+* chosen so that the returned Polygon is a good approximation to
+* the supplied Polygon, within the limits specified by the supplied
+* parameter values. That is, the density of points in the returned
+* Polygon is greater at points where the curvature of the boundary of
+* the supplied Polygon is greater.
+
+* Parameters:
+c this
+f THIS = INTEGER (Given)
+* Pointer to the Polygon.
+c maxerr
+f MAXERR = DOUBLE PRECISION (Given)
+* The maximum allowed discrepancy between the supplied and
+* returned Polygons, expressed as a geodesic distance within the
+* Polygon's coordinate frame. If this is zero or less, the
+* returned Polygon will have the number of vertices specified by
+c maxvert.
+f MAXVERT.
+c maxvert
+f MAXVERT = INTEGER (Given)
+* The maximum allowed number of vertices in the returned Polygon.
+* If this is less than 3, the number of vertices in the returned
+* Polygon will be the minimum needed to achieve the maximum
+* discrepancy specified by
+c maxerr.
+f MAXERR.
+f STATUS = INTEGER (Given and Returned)
+f The global status.
+
+* Returned Value:
+c astDownsize()
+f AST_DOWNSIZE = INTEGER
+* Pointer to the new Polygon.
+
+* Notes:
+* - A null Object pointer (AST__NULL) will be returned if this
+c function is invoked with the AST error status set, or if it
+f function is invoked with STATUS set to an error value, or if it
+* should fail for any reason.
+*--
+*/
+
+/* Local Variables: */
+ AstFrame *frm; /* Base Frame from the Polygon */
+ AstPointSet *pset; /* PointSet holding vertices of downsized polygon */
+ AstPolygon *result; /* Returned pointer to new Polygon */
+
+/* Initialise. */
+ result = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Get a pointer to the base Frame of the Polygon. */
+ frm = astGetFrame( ((AstRegion *) this)->frameset, AST__BASE );
+
+/* Create a PointSet holding the vertices of the downsized polygon. */
+ pset = DownsizePoly( ((AstRegion *) this)->points, maxerr, maxvert,
+ frm, status );
+
+/* Take a deep copy of the supplied Polygon. */
+ result = astCopy( this );
+
+/* Change the PointSet within the result Polygon to the one created above. */ \
+ SetPointSet( result, pset, status ); \
+
+/* Free resources. */
+ frm = astAnnul( frm );
+ pset = astAnnul( pset );
+
+/* If an error occurred, annul the returned Polygon. */
+ if ( !astOK ) result = astAnnul( result );
+
+/* Return the result. */
+ return result;
+}
+
+static AstPointSet *DownsizePoly( AstPointSet *pset, double maxerr,
+ int maxvert, AstFrame *frm, int *status ) {
+/*
+* Name:
+* DownsizePoly
+
+* Purpose:
+* Reduce the number of vertices in a Polygon.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "polygon.h"
+* AstPointSet *DownsizePoly( AstPointSet *pset, double maxerr, int maxvert,
+* AstFrame *frm, int *status )
+
+* Class Membership:
+* Polygon member function.
+
+* Description:
+* This function returns a pointer to a new PointSet that contains a
+* subset of the vertices in the supplied PointSet. The subset is
+* chosen so that the returned polygon is a good approximation to
+* the supplied polygon, within the limits specified by the supplied
+* parameter values. That is, the density of points in the returned
+* polygon is greater at points where the curvature of the boundary of
+* the supplied polygon is greater.
+
+* Parameters:
+* pset
+* Pointer to the PointSet holding the polygon vertices.
+* maxerr
+* The maximum allowed discrepancy between the supplied and
+* returned Polygons, expressed as a geodesic distance within the
+* Polygon's coordinate frame. If this is zero or less, the
+* returned Polygon will have the number of vertices specified by
+* maxvert.
+* maxvert
+* The maximum allowed number of vertices in the returned Polygon.
+* If this is less than 3, the number of vertices in the returned
+* Polygon will be the minimum needed to achieve the maximum
+* discrepancy specified by
+* maxerr.
+* frm
+* Pointer to the Frame in which the polygon is defined.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* Pointer to the new PointSet.
+
+* Notes:
+* - A null Object pointer (AST__NULL) will be returned if this
+* function is invoked with the AST error status set, or if it
+* should fail for any reason.
+*/
+
+/* Local Variables: */
+ AstPointSet *result; /* Returned pointer to new PointSet */
+ Segment *head; /* Pointer to new polygon edge with highest error */
+ Segment *seg1; /* Pointer to new polygon edge */
+ Segment *seg2; /* Pointer to new polygon edge */
+ Segment *seg3; /* Pointer to new polygon edge */
+ double **ptr; /* Pointer to arrays of axis values */
+ double *x; /* Pointer to array of X values for old Polygon */
+ double *xnew; /* Pointer to array of X values for new Polygon */
+ double *y; /* Pointer to array of Y values for old Polygon */
+ double *ynew; /* Pointer to array of Y values for new Polygon */
+ double x1; /* Lowest X value at any vertex */
+ double x2; /* Highest X value at any vertex */
+ double y1; /* Lowest Y value at any vertex */
+ double y2; /* Highest Y value at any vertex */
+ int *newpoly; /* Holds indices of retained input vertices */
+ int i1; /* Index of first vertex added to output polygon */
+ int i1x; /* Index of vertex with lowest X */
+ int i1y; /* Index of vertex with lowest Y */
+ int i2; /* Index of second vertex added to output polygon */
+ int i2x; /* Index of vertex with highest X */
+ int i2y; /* Index of vertex with highest Y */
+ int i3; /* Index of third vertex added to output polygon */
+ int iadd; /* Normalised vertex index */
+ int iat; /* Index at which to store new vertex index */
+ int newlen; /* Number of vertices currently in new Polygon */
+ int nv; /* Number of vertices in old Polygon */
+
+/* Initialise. */
+ result = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Get the number of vertices in the supplied polygon. */
+ nv = astGetNpoint( pset );
+
+/* If the maximum error allowed is zero, and the maximum number of
+ vertices is equal to or greater than the number in the supplied
+ polygon, just return a deep copy of the supplied PointSet. */
+ if( maxerr <= 0.0 && ( maxvert < 3 || maxvert >= nv ) ) {
+ result = astCopy( pset );
+
+/* Otherwise... */
+ } else {
+
+/* Get pointers to the X and Y arrays holding the vertex coordinates in
+ the supplied polygon. */
+ ptr = astGetPoints( pset );
+ x = ptr[ 0 ];
+ y = ptr[ 1 ];
+
+/* Allocate memory for an array to hold the original indices of the vertices
+ to be used to create the returned Polygon. This array is expanded as
+ needed. */
+ newpoly = astMalloc( 10*sizeof( int ) );
+
+/* Check the pointers can be used safely. */
+ if( astOK ) {
+
+/* We first need to decide on three widely spaced vertices which form a
+ reasonable triangular approximation to the whole polygon. First find
+ the vertices with the highest and lowest Y values, and the highest and
+ lowest X values. */
+ x1 = DBL_MAX;
+ x2 = -DBL_MAX;
+ y1 = DBL_MAX;
+ y2 = -DBL_MAX;
+
+ i1y = i1x = 0;
+ i2y = i2x = nv/2;
+
+ for( i3 = 0; i3 < nv; i3++ ) {
+ if( y[ i3 ] < y1 ) {
+ y1 = y[ i3 ];
+ i1y = i3;
+ } else if( y[ i3 ] > y2 ) {
+ y2 = y[ i3 ];
+ i2y = i3;
+ }
+
+ if( x[ i3 ] < x1 ) {
+ x1 = x[ i3 ];
+ i1x = i3;
+ } else if( x[ i3 ] > x2 ) {
+ x2 = x[ i3 ];
+ i2x = i3;
+ }
+ }
+
+/* Use the axis which spans the greater range. */
+ if( y2 - y1 > x2 - x1 ) {
+ i1 = i1y;
+ i2 = i2y;
+ } else {
+ i1 = i1x;
+ i2 = i2x;
+ }
+
+/* The index with vertex i1 is definitely going to be one of our three
+ vertices. We are going to use the line from i1 to i2 to choose the two
+ other vertices to use. Create a structure describing the segment of the
+ Polygon from the lowest value on the selected axis (X or Y) to the
+ highest value. As always, the polygons is traversed in an anti-clockwise
+ direction. */
+ seg1 = NewSegment( NULL, i1, i2, nv, status );
+
+/* Find the vertex within this segment which is furthest away from the
+ line on the right hand side (as moving from vertex i1 to vertex i2). */
+ FindMax( seg1, frm, x, y, nv, 0, status );
+
+/* Likewise, create a structure describing the remained of the Polygon
+ (i.e. the segment from the highest value on the selected axis to the
+ lowest value). Then find the vertex within this segment which is
+ furthest away from the line on the right hand side. */
+ seg2 = NewSegment( NULL, i2, i1, nv, status );
+ FindMax( seg2, frm, x, y, nv, 0, status );
+
+/* Select the segment for which the found vertex is furthest from the
+ line. */
+ if( seg2->error > seg1->error ) {
+
+/* If the second segment, we will use the vertex that is farthest from
+ the line as one of our threee vertices. To ensure that movement from
+ vertex i1 to i2 to i3 is anti-clockwise, we must use this new vertex
+ as vertex i3, not i2. */
+ i3 = seg2->imax;
+
+/* Create a description of the polygon segment from vertex i1 to i3, and
+ find the vertex which is furthest to the right of the line joining the
+ two vertices. We use this as the middle vertex (i2). */
+ seg1 = NewSegment( seg1, i1, i3, nv, status );
+ FindMax( seg1, frm, x, y, nv, 0, status );
+ i2 = seg1->imax;
+
+/* Do the same if we are choosing the other segment, ordering the
+ vertices to retain anti-clockwise movement from i1 to i2 to i3. */
+ } else {
+ i2 = seg1->imax;
+ seg1 = NewSegment( seg1, i2, i1, nv, status );
+ FindMax( seg1, frm, x, y, nv, 0, status );
+ i3 = seg1->imax;
+ }
+
+/* Ensure the vertex indices are in the first cycle. */
+ if( i2 >= nv ) i2 -= nv;
+ if( i3 >= nv ) i3 -= nv;
+
+/* Create Segment structures to describe each of these three edges. */
+ seg1 = NewSegment( seg1, i1, i2, nv, status );
+ seg2 = NewSegment( seg2, i2, i3, nv, status );
+ seg3 = NewSegment( NULL, i3, i1, nv, status );
+
+/* Record these 3 vertices in an array holding the original indices of
+ the vertices to be used to create the returned Polygon. */
+ newpoly[ 0 ] = i1;
+ newpoly[ 1 ] = i2;
+ newpoly[ 2 ] = i3;
+
+/* Indicate the new polygon currently has 3 vertices. */
+ newlen = 3;
+
+/* Search the old vertices between the start and end of segment 3, looking
+ for the vertex which lies furthest from the line of segment 3. The
+ residual between this point and the line is stored in the Segment
+ structure, as is the index of the vertex at which this maximum residual
+ occurred. */
+ FindMax( seg3, frm, x, y, nv, 1, status );
+
+/* The "head" variable points to the head of a double linked list of
+ Segment structures. This list is ordered by residual, so that the
+ Segment with the maximum residual is at the head, and the Segment
+ with the minimum residual is at the tail. Initially "seg3" is at the
+ head. */
+ head = seg3;
+
+/* Search the old vertices between the start and end of segment 1, looking
+ for the vertex which lies furthest from the line of segment 1. The
+ residual between this point and the line is stored in the Segment
+ structure, as is the index of the vertex at which this maximum residual
+ occurred. */
+ FindMax( seg1, frm, x, y, nv, 1, status );
+
+/* Insert segment 1 into the linked list of Segments, at a position that
+ maintains the ordering of the segments by error. Thus the head of the
+ list will still have the max error. */
+ head = AddToChain( head, seg1, status );
+
+/* Do the same for segment 2. */
+ FindMax( seg2, frm, x, y, nv, 1, status );
+ head = AddToChain( head, seg2, status );
+
+/* If the maximum allowed number of vertices in the output Polygon is
+ less than 3, allow any number of vertices up to the number in the
+ input Polygon (termination will then be determined just by "maxerr"). */
+ if( maxvert < 3 ) maxvert = nv;
+
+/* Loop round adding an extra vertex to the returned Polygon until the
+ maximum residual between the new and old polygons is no more than
+ "maxerr". Abort early if the specified maximum number of vertices is
+ reached. */
+ while( head->error > maxerr && newlen < maxvert ) {
+
+/* The segment at the head of the list has the max error (that is, it is
+ the segment that departs most from the supplied Polygon). To make the
+ new polygon a better fit to the old polygon, we add the vertex that is
+ furthest away from this segment to the new polygon. Remember that a
+ polygon is cyclic so if the vertex has an index that is greater than the
+ number of vertices in the old polygon, reduce the index by the number
+ of vertices in the old polygon. */
+ iadd = head->imax;
+ if( iadd >= nv ) iadd -= nv;
+ iat = newlen++;
+ newpoly = astGrow( newpoly, newlen, sizeof( int ) );
+ if( !astOK ) break;
+ newpoly[ iat ] = iadd;
+
+/* We now split the segment that had the highest error into two segments.
+ The split occurs at the vertex that had the highest error. */
+ seg1 = NewSegment( NULL, head->imax, head->i2, nv, status );
+ seg2 = head;
+ seg2->i2 = head->imax;
+
+/* We do not know where these two new segments should be in the ordered
+ linked list, so remove them from the list. */
+ head = RemoveFromChain( head, seg1, status );
+ head = RemoveFromChain( head, seg2, status );
+
+/* Find the vertex that deviates most from the first of these two new
+ segments, and then add the segment into the list of vertices, using
+ the maximum deviation to determine the position of the segment within
+ the list. */
+ FindMax( seg1, frm, x, y, nv, 1, status );
+ head = AddToChain( head, seg1, status );
+
+/* Do the same for the second new segment. */
+ FindMax( seg2, frm, x, y, nv, 1, status );
+ head = AddToChain( head, seg2, status );
+ }
+
+/* Now we have reached the required accuracy, free resources. */
+ while( head ) {
+ seg1 = head;
+ head = head->next;
+ seg1 = astFree( seg1 );
+ }
+
+/* If no vertices have been left out, return a deep copy of the supplied
+ PointSet. */
+ if( newlen == nv ) {
+ result = astCopy( pset );
+
+/* Otherwise, sort the indices of the vertices to be retained so that they
+ are in the same order as they were in the supplied Polygon. */
+ } else if( astOK ){
+ qsort( newpoly, newlen, sizeof( int ), IntCmp );
+
+/* Create a new PointSet and get pointers to its axis values. */
+ result = astPointSet( newlen, 2, " ", status );
+ ptr = astGetPoints( result );
+ xnew = ptr[ 0 ];
+ ynew = ptr[ 1 ];
+
+/* Copy the axis values for the retained vertices from the old to the new
+ PointSet. */
+ if( astOK ) {
+ for( iat = 0; iat < newlen; iat++ ) {
+ *(xnew++) = x[ newpoly[ iat ] ];
+ *(ynew++) = y[ newpoly[ iat ] ];
+ }
+ }
+ }
+ }
+
+/* Free resources. */
+ newpoly = astFree( newpoly );
+ }
+
+/* If an error occurred, annul the returned PointSet. */
+ if ( !astOK ) result = astAnnul( result );
+
+/* Return the result. */
+ return result;
+}
+
+static void EnsureInside( AstPolygon *this, int *status ){
+/*
+* Name:
+* EnsureInside
+
+* Purpose:
+* Ensure the unnegated Polygon represents the inside of the polygon.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "polygon.h"
+* void EnsureInside( AstPolygon *this, int *status )
+
+* Class Membership:
+* Polygon member function
+
+* Description:
+* Reversing the order of the vertices of a Polygon is like negating
+* the Polygon. But the parent Region class assumes that an unnegated
+* region bounded by closed curves (e.g. boxes, circles, ellipses, etc)
+* is bounded. So we need to have a way to ensure that a Polygon also
+* follows this convention. So this function reverses the order of the
+* vertices in the Polygon, if necessary, to ensure that the unnegated
+* Polygon is bounded.
+
+* Parameters:
+* this
+* The Polygon.
+* status
+* Pointer to the inherited status variable.
+
+*/
+
+/* Local Variables: */
+ AstRegion *this_region;
+ double **ptr;
+ double *p;
+ double *q;
+ double tmp;
+ int bounded;
+ int i;
+ int j;
+ int jmid;
+ int negated;
+ int np;
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Is the unnegated Polygon unbounded? If so, we need to reverse the
+ vertices. */
+ bounded = astGetBounded( this );
+ negated = astGetNegated( this );
+ if( ( bounded && negated ) || ( !bounded && !negated ) ) {
+ this_region = (AstRegion *) this;
+
+/* Get a pointer to the arrays holding the coordinates at the Polygon
+ vertices. */
+ ptr = astGetPoints( this_region->points );
+
+/* Get the number of vertices. */
+ np = astGetNpoint( this_region->points );
+
+/* Store the index of the last vertex to swap. For odd "np" the central
+ vertex does not need to be swapped. */
+ jmid = np/2;
+
+/* Loop round the two axes spanned by the Polygon. */
+ for( i = 0; i < 2; i++ ) {
+
+/* Get pointers to the first pair of axis values to be swapped - i.e. the
+ first and last axis values. */
+ p = ptr[ i ];
+ q = p + np - 1;
+
+/* Loop round all pairs of axis values. */
+ for( j = 0; j < jmid; j++ ) {
+
+/* Swap the pair. */
+ tmp = *p;
+ *(p++) = *q;
+ *(q--) = tmp;
+ }
+ }
+
+/* Invert the value of the "Negated" attribute to cancel out the effect
+ of the above vertex reversal. */
+ astNegate( this );
+
+/* Indicate the cached information in the Polygon structure is stale. */
+ this->stale = 1;
+ }
+}
+
+/*
+* Name:
+* FindBoxEdge
+
+* Purpose:
+* Find an edge of the bounding box containing the selected pixels.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "polygon.h"
+* void FindBoxEdge<Oper><X>( <Xtype> value, const <Xtype> array[],
+* int xdim, int ydim, int axis, int low,
+* int *val, int *valmax, int *valmin,
+* int *status )
+
+* Class Membership:
+* Polygon member function
+
+* Description:
+* This function search for an edge of the bounding box containing the
+* selected pixels in the supplied array.
+
+* Parameters:
+* value
+* A data value that specifies the pixels to be selected.
+* array
+* Pointer to a 2-dimensional array containing the data to be
+* processed. The numerical type of this array should match the 1-
+* or 2-character type code appended to the function name.
+* xdim
+* The number of pixels along each row of the array.
+* ydim
+* The number of rows in the array.
+* axis
+* The index (0 or 1) of the pixel axis perpendicular to the edge of the
+* bounding box being found.
+* low
+* If non-zero, the lower edge of the bounding box on the axis
+* specified by "axis" is found and returned. Otherwise, the higher
+* edge of the bounding box on the axis specified by "axis" is
+* found and returned.
+* val
+* Address of an int in which to return the value on axis "axis" of
+* the higher or lower (as specified by "low") edge of the bounding
+* box.
+* valmax
+* Address of an int in which to return the highest value on axis
+* "1-axis" of the selected pixels on the returned edge.
+* valmin
+* Address of an int in which to return the lowest value on axis
+* "1-axis" of the selected pixels on the returned edge.
+* status
+* Pointer to the inherited status variable.
+
+* Notes;
+* - Zero is returned for "*val" if no good values are found, or if
+* an error occurs.
+
+*/
+
+/* Define a macro to implement the function for a specific data
+ type and operation. */
+#define MAKE_FINDBOXEDGE(X,Xtype,Oper,OperI) \
+static void FindBoxEdge##Oper##X( Xtype value, const Xtype array[], int xdim, \
+ int ydim, int axis, int low, int *val, \
+ int *valmax, int *valmin, int *status ) { \
+\
+/* Local Variables: */ \
+ int astep; \
+ int bstep; \
+ int a0; \
+ int a1; \
+ int b0; \
+ int b1; \
+ int inc; \
+ int a; \
+ int b; \
+ const Xtype *pc; \
+\
+/* Initialise. */ \
+ *val = 0; \
+ *valmin = 0; \
+ *valmax = 0; \
+\
+/* Check the global error status. */ \
+ if ( !astOK ) return; \
+\
+/* If we are finding an edge that is parallel to the X axis... */ \
+ if( axis ) { \
+\
+/* Get the vector step between adjacent pixels on the selected axis, and \
+ on the other axis. */ \
+ astep = xdim; \
+ bstep = 1; \
+\
+/* Get the first and last value to check on the selected axis, and the \
+ increment between checks. */ \
+ if( low ) { \
+ a0 = 1; \
+ a1 = ydim; \
+ inc = 1; \
+ } else { \
+ a0 = ydim; \
+ a1 = 1; \
+ inc = -1; \
+ } \
+\
+/* The first and last value to check on the other axis. */ \
+ b0 = 1; \
+ b1 = xdim; \
+\
+/* Do the same if we are finding an edge that is parallel to the Y axis. */ \
+ } else { \
+ astep = 1; \
+ bstep = xdim; \
+\
+ if( low ) { \
+ a0 = 1; \
+ a1 = xdim; \
+ inc = 1; \
+ } else { \
+ a0 = xdim; \
+ a1 = 1; \
+ inc = -1; \
+ } \
+\
+ b0 = 1; \
+ b1 = ydim; \
+ } \
+\
+/* Loop round the axis values. */ \
+ a = a0; \
+ while( 1 ) { \
+\
+/* Get a pointer to the first value to be checked at this axis value. */ \
+ pc = array + (a - 1)*astep + (b0 - 1)*bstep; \
+\
+/* Scan the other axis to find the first and last selected pixel. */ \
+ for( b = b0; b <= b1; b++ ) { \
+ if( ISVALID(*pc,OperI,value) ) { \
+ if( *valmin == 0 ) *valmin = b; \
+ *valmax = b; \
+ } \
+ pc += bstep; \
+ } \
+\
+/* If any selected pixels were found, store the axis value and exit. */ \
+ if( *valmax ) { \
+ *val = a; \
+ break; \
+ } \
+\
+/* Move on to the next axis value. */ \
+ if( a != a1 ) { \
+ a += inc; \
+ } else { \
+ break; \
+ } \
+\
+ } \
+}
+
+/* Define a macro that uses the above macro to to create implementations
+ of FindBoxEdge for all operations. */
+#define MAKEALL_FINDBOXEDGE(X,Xtype) \
+MAKE_FINDBOXEDGE(X,Xtype,LT,AST__LT) \
+MAKE_FINDBOXEDGE(X,Xtype,LE,AST__LE) \
+MAKE_FINDBOXEDGE(X,Xtype,EQ,AST__EQ) \
+MAKE_FINDBOXEDGE(X,Xtype,NE,AST__NE) \
+MAKE_FINDBOXEDGE(X,Xtype,GE,AST__GE) \
+MAKE_FINDBOXEDGE(X,Xtype,GT,AST__GT)
+
+/* Expand the above macro to generate a function for each required
+ data type and operation. */
+#if HAVE_LONG_DOUBLE /* Not normally implemented */
+MAKEALL_FINDBOXEDGE(LD,long double)
+#endif
+MAKEALL_FINDBOXEDGE(D,double)
+MAKEALL_FINDBOXEDGE(L,long int)
+MAKEALL_FINDBOXEDGE(UL,unsigned long int)
+MAKEALL_FINDBOXEDGE(I,int)
+MAKEALL_FINDBOXEDGE(UI,unsigned int)
+MAKEALL_FINDBOXEDGE(S,short int)
+MAKEALL_FINDBOXEDGE(US,unsigned short int)
+MAKEALL_FINDBOXEDGE(B,signed char)
+MAKEALL_FINDBOXEDGE(UB,unsigned char)
+MAKEALL_FINDBOXEDGE(F,float)
+
+/* Undefine the macros. */
+#undef MAKE_FINDBOXEDGE
+#undef MAKEALL_FINDBOXEDGE
+
+/*
+* Name:
+* FindInsidePoint
+
+* Purpose:
+* Find a point that is inside the required outline.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "polygon.h"
+* void FindInsidePoint<Oper><X>( <Xtype> value, const <Xtype> array[],
+* const int lbnd[ 2 ], const int ubnd[ 2 ],
+* int *inx, int *iny, int *iv,
+* int *status );
+
+* Class Membership:
+* Polygon member function
+
+* Description:
+* The central pixel in the array is checked to see if its value meets
+* the requirements implied by <Oper> and "value". If so, its pixel
+* indices and vector index are returned> if not, a spiral search is
+* made until such a pixel value is found.
+
+* Parameters:
+* value
+* The data value defining valid pixels.
+* array
+* The data array.
+* lbnd
+* The lower pixel index bounds of the array.
+* ubnd
+* The upper pixel index bounds of the array.
+* inx
+* Pointer to an int in which to return the X pixel index of the
+* first point that meets the requirements implied by <oper> and
+* "value".
+* iny
+* Pointer to an int in which to return the Y pixel index of the
+* first point that meets the requirements implied by <oper> and
+* "value".
+* iv
+* Pointer to an int in which to return the vector index of the
+* first point that meets the requirements implied by <oper> and
+* "value".
+* status
+* Pointer to the inherited status variable.
+
+* Notes:
+* - <Oper> must be one of LT, LE, EQ, NE, GE, GT.
+
+
+*/
+
+/* Define a macro to implement the function for a specific data
+ type and operation. */
+#define MAKE_FINDINSIDEPOINT(X,Xtype,Oper,OperI) \
+static void FindInsidePoint##Oper##X( Xtype value, const Xtype array[], \
+ const int lbnd[ 2 ], const int ubnd[ 2 ], \
+ int *inx, int *iny, int *iv, \
+ int *status ){ \
+\
+/* Local Variables: */ \
+ const Xtype *pv; /* Pointer to next data value to test */ \
+ const char *text; /* Pointer to text describing oper */ \
+ int cy; /* Central row index */ \
+ int iskin; /* Index of spiral layer being searched */ \
+ int nskin; /* Number of spiral layers to search */ \
+ int nx; /* Pixel per row */ \
+ int tmp; /* Temporary storage */ \
+ int xhi; /* High X pixel index bound of current skin */ \
+ int xlo; /* Low X pixel index bound of current skin */ \
+ int yhi; /* High X pixel index bound of current skin */ \
+ int ylo; /* Low X pixel index bound of current skin */ \
+\
+/* Check the global error status. */ \
+ if ( !astOK ) return; \
+\
+/* Find number of pixels in one row of the array. */ \
+ nx = ( ubnd[ 0 ] - lbnd[ 0 ] + 1 ); \
+\
+/* Find the pixel indices of the central pixel */ \
+ *inx = ( ubnd[ 0 ] + lbnd[ 0 ] )/2; \
+ *iny = ( ubnd[ 1 ] + lbnd[ 1 ] )/2; \
+\
+/* Initialise the vector index and pointer to refer to the central pixel. */ \
+ *iv = ( *inx - lbnd[ 0 ] ) + nx*( *iny - lbnd[ 1 ] ) ; \
+ pv = array + (*iv); \
+\
+/* Test the pixel value, returning if it is valid. This \
+ relies on the compiler optimisation to remove the "if" statements \
+ for all but the operation appropriate to the function being defined. */ \
+ if( OperI == AST__LT ) { \
+ if( *pv < value ) return; \
+\
+ } else if( OperI == AST__LE ) { \
+ if( *pv <= value ) return; \
+\
+ } else if( OperI == AST__EQ ) { \
+ if( *pv == value ) return; \
+\
+ } else if( OperI == AST__NE ) { \
+ if( *pv != value ) return; \
+\
+ } else if( OperI == AST__GE ) { \
+ if( *pv >= value ) return; \
+\
+ } else { \
+ if( *pv > value ) return; \
+ } \
+\
+/* The central pixel is invalid if we arrive here. So we need to do a \
+ spiral search out from the centre looking for a valid pixel. Record \
+ the central row index (this is the row at which we jump to the next \
+ outer skin when doing the spiral search below). */ \
+ cy = *iny; \
+\
+/* Find how many skins can be searched as part of the spiral search \
+ before the edge of the array is encountered. */ \
+ nskin = ubnd[ 0 ] - *inx; \
+ tmp = *inx - lbnd[ 0 ]; \
+ if( tmp < nskin ) nskin = tmp; \
+ tmp = ubnd[ 1 ] - *iny; \
+ if( tmp < nskin ) nskin = tmp; \
+ tmp = *iny - lbnd[ 1 ]; \
+ if( tmp < nskin ) nskin = tmp; \
+\
+/* Initialise the skin box bounds to be just the central pixel. */ \
+ xlo = xhi = *inx; \
+ ylo = yhi = *iny; \
+\
+/* Loop round each skin looking for a valid test pixel. */ \
+ for( iskin = 0; iskin < nskin; iskin++ ) { \
+\
+/* Increment the upper and lower bounds of the box forming the next \
+ skin. */ \
+ xhi++; \
+ xlo--; \
+ yhi++; \
+ ylo--; \
+\
+/* Initialise things for the first pixel in the new skin by moving one \
+ pixel to the right. */ \
+ pv++; \
+ (*iv)++; \
+ (*inx)++; \
+\
+/* Move up the right hand edge of the box corresponding to the current \
+ skin. We start at the middle of the right hand edge. */ \
+ for( *iny = cy; *iny <= yhi; (*iny)++ ) { \
+\
+/* Test the pixel value, returning if it is valid. This relies on the \
+ compiler optimisation to remove the "if" statements for all but the \
+ operation appropriate to the function being defined. */ \
+ if( OperI == AST__LT ) { \
+ if( *pv < value ) return; \
+\
+ } else if( OperI == AST__LE ) { \
+ if( *pv <= value ) return; \
+\
+ } else if( OperI == AST__EQ ) { \
+ if( *pv == value ) return; \
+\
+ } else if( OperI == AST__NE ) { \
+ if( *pv != value ) return; \
+\
+ } else if( OperI == AST__GE ) { \
+ if( *pv >= value ) return; \
+\
+ } else { \
+ if( *pv > value ) return; \
+ } \
+\
+/* Move up a pixel. */ \
+ pv += nx; \
+ *iv += nx; \
+ } \
+\
+/* Move down a pixel so that *iny == yhi. */ \
+ pv -= nx; \
+ *iv -= nx; \
+ (*iny)--; \
+\
+/* Move left along the top edge of the box corresponding to the current \
+ skin. */ \
+ for( *inx = xhi; *inx >= xlo; (*inx)-- ) { \
+\
+/* Test the pixel value, returning if it is valid. */ \
+ if( OperI == AST__LT ) { \
+ if( *pv < value ) return; \
+\
+ } else if( OperI == AST__LE ) { \
+ if( *pv <= value ) return; \
+\
+ } else if( OperI == AST__EQ ) { \
+ if( *pv == value ) return; \
+\
+ } else if( OperI == AST__NE ) { \
+ if( *pv != value ) return; \
+\
+ } else if( OperI == AST__GE ) { \
+ if( *pv >= value ) return; \
+\
+ } else { \
+ if( *pv > value ) return; \
+ } \
+\
+/* Move left a pixel. */ \
+ pv--; \
+ (*iv)--; \
+ } \
+\
+/* Move right a pixel so that *inx == xlo. */ \
+ pv++; \
+ (*iv)++; \
+ (*inx)++; \
+\
+/* Move down along the left hand edge of the box corresponding to the current \
+ skin. */ \
+ for( *iny = yhi; *iny >= ylo; (*iny)-- ) { \
+\
+/* Test the pixel value, returning if it is valid. */ \
+ if( OperI == AST__LT ) { \
+ if( *pv < value ) return; \
+\
+ } else if( OperI == AST__LE ) { \
+ if( *pv <= value ) return; \
+\
+ } else if( OperI == AST__EQ ) { \
+ if( *pv == value ) return; \
+\
+ } else if( OperI == AST__NE ) { \
+ if( *pv != value ) return; \
+\
+ } else if( OperI == AST__GE ) { \
+ if( *pv >= value ) return; \
+\
+ } else { \
+ if( *pv > value ) return; \
+ } \
+\
+/* Move down a pixel. */ \
+ pv -= nx; \
+ *iv -= nx; \
+ } \
+\
+/* Move up a pixel so that *iny == ylo. */ \
+ pv += nx; \
+ *iv += nx; \
+ (*iny)++; \
+\
+/* Move right along the bottom edge of the box corresponding to the current \
+ skin. */ \
+ for( *inx = xlo; *inx <= xhi; (*inx)++ ) { \
+\
+/* Test the pixel value, returning if it is valid. */ \
+ if( OperI == AST__LT ) { \
+ if( *pv < value ) return; \
+\
+ } else if( OperI == AST__LE ) { \
+ if( *pv <= value ) return; \
+\
+ } else if( OperI == AST__EQ ) { \
+ if( *pv == value ) return; \
+\
+ } else if( OperI == AST__NE ) { \
+ if( *pv != value ) return; \
+\
+ } else if( OperI == AST__GE ) { \
+ if( *pv >= value ) return; \
+\
+ } else { \
+ if( *pv > value ) return; \
+ } \
+\
+/* Move right a pixel. */ \
+ pv++; \
+ (*iv)++; \
+ } \
+\
+/* Move left a pixel so that *inx == xhi. */ \
+ pv--; \
+ (*iv)--; \
+ (*inx)--; \
+\
+/* Move up the right hand edge of the box correspionding to the current \
+ skin. We stop just below the middle of the right hand edge. */ \
+ for( *iny = ylo; *iny < cy; (*iny)++ ) { \
+\
+/* Test the pixel value, returning if it is valid. This relies on the \
+ compiler optimisation to remove the "if" statements for all but the \
+ operation appropriate to the function being defined. */ \
+ if( OperI == AST__LT ) { \
+ if( *pv < value ) return; \
+\
+ } else if( OperI == AST__LE ) { \
+ if( *pv <= value ) return; \
+\
+ } else if( OperI == AST__EQ ) { \
+ if( *pv == value ) return; \
+\
+ } else if( OperI == AST__NE ) { \
+ if( *pv != value ) return; \
+\
+ } else if( OperI == AST__GE ) { \
+ if( *pv >= value ) return; \
+\
+ } else { \
+ if( *pv > value ) return; \
+ } \
+\
+/* Move up a pixel. */ \
+ pv += nx; \
+ *iv += nx; \
+ } \
+ } \
+\
+/* Report an error if no inside pooint could be found. */ \
+ if( OperI == AST__LT ) { \
+ text = "less than"; \
+ } else if( OperI == AST__LE ) { \
+ text = "less than or equal to"; \
+ } else if( OperI == AST__EQ ) { \
+ text = "equal to"; \
+ } else if( OperI == AST__NE ) { \
+ text = "not equal to"; \
+ } else if( OperI == AST__GE ) { \
+ text = "greater than or equal to"; \
+ } else { \
+ text = "greater than"; \
+ } \
+ astError( AST__NONIN, "astOutline"#X": Could not find a pixel value %s " \
+ "%g in the supplied array.", status, text, (double) value ); \
+}
+
+/* Define a macro that uses the above macro to to create implementations
+ of FindInsidePoint for all operations. */
+#define MAKEALL_FINDINSIDEPOINT(X,Xtype) \
+MAKE_FINDINSIDEPOINT(X,Xtype,LT,AST__LT) \
+MAKE_FINDINSIDEPOINT(X,Xtype,LE,AST__LE) \
+MAKE_FINDINSIDEPOINT(X,Xtype,EQ,AST__EQ) \
+MAKE_FINDINSIDEPOINT(X,Xtype,GE,AST__GE) \
+MAKE_FINDINSIDEPOINT(X,Xtype,GT,AST__GT) \
+MAKE_FINDINSIDEPOINT(X,Xtype,NE,AST__NE)
+
+/* Expand the above macro to generate a function for each required
+ data type and operation. */
+#if HAVE_LONG_DOUBLE /* Not normally implemented */
+MAKEALL_FINDINSIDEPOINT(LD,long double)
+#endif
+MAKEALL_FINDINSIDEPOINT(D,double)
+MAKEALL_FINDINSIDEPOINT(L,long int)
+MAKEALL_FINDINSIDEPOINT(UL,unsigned long int)
+MAKEALL_FINDINSIDEPOINT(I,int)
+MAKEALL_FINDINSIDEPOINT(UI,unsigned int)
+MAKEALL_FINDINSIDEPOINT(S,short int)
+MAKEALL_FINDINSIDEPOINT(US,unsigned short int)
+MAKEALL_FINDINSIDEPOINT(B,signed char)
+MAKEALL_FINDINSIDEPOINT(UB,unsigned char)
+MAKEALL_FINDINSIDEPOINT(F,float)
+
+/* Undefine the macros. */
+#undef MAKE_FINDINSIDEPOINT
+#undef MAKEALL_FINDINSIDEPOINT
+
+static void FindMax( Segment *seg, AstFrame *frm, double *x, double *y,
+ int nv, int abs, int *status ){
+/*
+* Name:
+* FindMax
+
+* Purpose:
+* Find the maximum discrepancy between a given line segment and the
+* Polygon being downsized.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "polygon.h"
+* void FindMax( Segment *seg, AstFrame *frm, double *x, double *y,
+* int nv, int abs, int *status )
+
+* Class Membership:
+* Polygon member function
+
+* Description:
+* The supplied Segment structure describes a range of vertices in
+* the polygon being downsized. This function checks each of these
+* vertices to find the one that lies furthest from the line joining the
+* first and last vertices in the segment. The maximum error, and the
+* vertex index at which this maximum error is found, is stored in the
+* Segment structure.
+
+* Parameters:
+* seg
+* The structure describing the range of vertices to check. It
+* corresponds to a candidate edge in the downsized polygon.
+* frm
+* The Frame in which the positions are defined.
+* x
+* Pointer to the X axis values in the original Polygon.
+* y
+* Pointer to the Y axis values in the original Polygon.
+* nv
+* Total number of vertics in the old Polygon..
+* abs
+* If non-zero, then the stored maximum is the position with
+* maximum absolute error. Otherwise, the stored maximum is the
+* position with maximum positive error (positive errors are to the
+* right when travelling from start to end of the segment).
+* status
+* Pointer to the inherited status variable.
+
+*/
+
+/* Local Variables: */
+ AstPointSet *pset1; /* PointSet holding vertex positions */
+ AstPointSet *pset2; /* PointSet holding offset par/perp components */
+ double **ptr1; /* Pointers to "pset1" data arrays */
+ double **ptr2; /* Pointers to "pset2" data arrays */
+ double *px; /* Pointer to next X value */
+ double *py; /* Pointer to next Y value */
+ double ax; /* X value at start */
+ double ay; /* Y value at start */
+ double ba; /* Distance between a and b */
+ double bax; /* X increment from a to b */
+ double bay; /* Y increment from a to b */
+ double cax; /* X increment from a to c */
+ double cay; /* Y increment from a to c */
+ double end[ 2 ]; /* Position of starting vertex */
+ double error; /* Error value for current vertex */
+ double start[ 2 ]; /* Position of starting vertex */
+ int i1; /* Starting index (always in first cycle) */
+ int i2; /* Ending index converted to first cycle */
+ int i2b; /* Upper vertex limit in first cycle */
+ int i; /* Loop count */
+ int n; /* Number of vertices to check */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Stuff needed for handling cyclic redundancy of vertex indices. */
+ i1 = seg->i1;
+ i2 = seg->i2;
+ n = i2 - i1 - 1;
+ i2b = i2;
+ if( i2 >= nv ) {
+ i2 -= nv;
+ i2b = nv;
+ }
+
+/* If the segment has no intermediate vertices, set the segment error to
+ zero and return. */
+ if( n < 1 ) {
+ seg->error = 0.0;
+ seg->imax = i1;
+
+/* For speed, we use simple plane geometry if the Polygon is defined in a
+ simple Frame. */
+ } else if( !strcmp( astGetClass( frm ), "Frame" ) ) {
+
+/* Point "a" is the vertex that marks the start of the segment. Point "b"
+ is the vertex that marks the end of the segment. */
+ ax = x[ i1 ];
+ ay = y[ i1 ];
+ bax = x[ i2 ] - ax;
+ bay = y[ i2 ] - ay;
+ ba = sqrt( bax*bax + bay*bay );
+
+/* Initialise the largest error found so far. */
+ seg->error = -1.0;
+
+/* Check the vertices from the start (plus one) up to the end (minus one)
+ or the last vertex (which ever comes first). */
+ for( i = i1 + 1; i < i2b; i++ ) {
+
+/* Position "c" is the vertex being tested. Find the squared distance from
+ "c" to the line joining "a" and "b". */
+ cax = x[ i ] - ax;
+ cay = y[ i ] - ay;
+ error = ( bay*cax - cay*bax )/ba;
+ if( abs ) error = fabs( error );
+
+/* If this is the largest value found so far, record it. Note the error
+ here is a squared distance. */
+ if( error > seg->error ) {
+ seg->error = error;
+ seg->imax = i;
+ }
+ }
+
+/* If the end vertex is in the next cycle, check the remaining vertex
+ posI would have thought a telentitions in the same way. */
+ if( i2b != i2 ) {
+
+ for( i = 0; i < i2; i++ ) {
+
+ cax = x[ i ] - ax;
+ cay = y[ i ] - ay;
+ error = ( bay*cax - cay*bax )/ba;
+ if( abs ) error = fabs( error );
+
+ if( error > seg->error ) {
+ seg->error = error;
+ seg->imax = i + i2b;
+ }
+
+ }
+ }
+
+/* If the polygon is not defined in a simple Frame, we use the overloaded
+ Frame methods to do the geometry. */
+ } else {
+
+/* Create a PointSet to hold the positions of the vertices to test. We do
+ not need to test the start or end vertex. */
+ pset1 = astPointSet( n, 2, " ", status );
+ ptr1 = astGetPoints( pset1 );
+ if( astOK ) {
+
+/* Copy the vertex axis values form the start (plus one) up to the end
+ (minus one) vertex or the last vertex (which ever comes first). */
+ px = ptr1[ 0 ];
+ py = ptr1[ 1 ];
+
+ for( i = i1 + 1; i < i2b; i++ ){
+ *(px++) = x[ i ];
+ *(py++) = y[ i ];
+ }
+
+/* If the end vertex is in the next cycle, copy the remaining vertex
+ positions into the PointSet. */
+ if( i2b != i2 ) {
+ for( i = 0; i < i2; i++ ) {
+ *(px++) = x[ i ];
+ *(py++) = y[ i ];
+ }
+ }
+
+/* Record the start and end vertex positions. */
+ start[ 0 ] = x[ i1 ];
+ start[ 1 ] = y[ i1 ];
+ end[ 0 ] = x[ i2 ];
+ end[ 1 ] = y[ i2 ];
+
+/* Resolve the vector from the start to each vertex into two components,
+ parallel and perpendicular to the start->end vector. */
+ pset2 = astResolvePoints( frm, start, end, pset1, NULL );
+ ptr2 = astGetPoints( pset2 );
+ if( astOK ) {
+
+/* Find the vertex with largest perpendicular component. */
+ seg->error = -1.0;
+ py = ptr2[ 1 ];
+ for( i = 1; i <= n; i++ ) {
+
+ error = *(py++);
+ if( abs ) error = fabs( error );
+
+ if( error > seg->error ) {
+ seg->error = error;
+ seg->imax = i + i1;
+ }
+
+ }
+ }
+
+/* Free resources. */
+ pset2 = astAnnul( pset2 );
+ }
+ pset1 = astAnnul( pset1 );
+ }
+}
+
+static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) {
+/*
+* Name:
+* GetAttrib
+
+* Purpose:
+* Get the value of a specified attribute for a Polygon.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "polygon.h"
+* const char *GetAttrib( AstObject *this, const char *attrib, int *status )
+
+* Class Membership:
+* Polygon member function (over-rides the protected astGetAttrib
+* method inherited from the Region class).
+
+* Description:
+* This function returns a pointer to the value of a specified
+* attribute for a Polygon, formatted as a character string.
+
+* Parameters:
+* this
+* Pointer to the Polygon.
+* attrib
+* Pointer to a null-terminated string containing the name of
+* the attribute whose value is required. This name should be in
+* lower case, with all white space removed.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* - Pointer to a null-terminated string containing the attribute
+* value.
+
+* Notes:
+* - The returned string pointer may point at memory allocated
+* within the Polygon, or at static memory. The contents of the
+* string may be over-written or the pointer may become invalid
+* following a further invocation of the same function or any
+* modification of the Polygon. A copy of the string should
+* therefore be made if necessary.
+* - A NULL pointer will be returned if this function is invoked
+* with the global error status set, or if it should fail for any
+* reason.
+*/
+
+/* Local Variables: */
+ astDECLARE_GLOBALS /* Pointer to thread-specific global data */
+ AstPolygon *this; /* Pointer to the Polygon structure */
+ const char *result; /* Pointer value to return */
+ int ival; /* Integer attribute value */
+
+/* Initialise. */
+ result = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Get a pointer to the thread specific global data structure. */
+ astGET_GLOBALS(this_object);
+
+/* Obtain a pointer to the Polygon structure. */
+ this = (AstPolygon *) this_object;
+
+/* Compare "attrib" with each recognised attribute name in turn,
+ obtaining the value of the required attribute. If necessary, write
+ the value into "getattrib_buff" as a null-terminated string in an appropriate
+ format. Set "result" to point at the result string. */
+
+/* SimpVertices. */
+/* ------------- */
+ if ( !strcmp( attrib, "simpvertices" ) ) {
+ ival = astGetSimpVertices( this );
+ if ( astOK ) {
+ (void) sprintf( getattrib_buff, "%d", ival );
+ result = getattrib_buff;
+ }
+
+/* If the attribute name was not recognised, pass it on to the parent
+ method for further interpretation. */
+ } else {
+ result = (*parent_getattrib)( this_object, attrib, status );
+ }
+
+/* Return the result. */
+ return result;
+
+}
+
+static int GetBounded( AstRegion *this, int *status ) {
+/*
+* Name:
+* GetBounded
+
+* Purpose:
+* Is the Region bounded?
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "polygon.h"
+* int GetBounded( AstRegion *this, int *status )
+
+* Class Membership:
+* Polygon method (over-rides the astGetBounded method inherited from
+* the Region class).
+
+* Description:
+* This function returns a flag indicating if the Region is bounded.
+* The implementation provided by the base Region class is suitable
+* for Region sub-classes representing the inside of a single closed
+* curve (e.g. Circle, Interval, Box, etc). Other sub-classes (such as
+* CmpRegion, PointList, etc ) may need to provide their own
+* implementations.
+
+* Parameters:
+* this
+* Pointer to the Region.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* Non-zero if the Region is bounded. Zero otherwise.
+
+*/
+
+/* Local Variables: */
+ int neg; /* Has the Polygon been negated? */
+ int result; /* Returned result */
+
+/* Initialise */
+ result = 0;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Ensure cached information is available. */
+ Cache( (AstPolygon *) this, status );
+
+/* See if the Polygon has been negated. */
+ neg = astGetNegated( this );
+
+/* If the polygon vertices are stored in anti-clockwise order, then the
+ polygon is bounded if it has not been negated. */
+ if( ( (AstPolygon *) this)->acw ) {
+ result = (! neg );
+
+/* If the polygon vertices are stored in clockwise order, then the
+ polygon is bounded if it has been negated. */
+ } else {
+ result = neg;
+ }
+
+/* Return the result. */
+ return result;
+}
+
+void astInitPolygonVtab_( AstPolygonVtab *vtab, const char *name, int *status ) {
+/*
+*+
+* Name:
+* astInitPolygonVtab
+
+* Purpose:
+* Initialise a virtual function table for a Polygon.
+
+* Type:
+* Protected function.
+
+* Synopsis:
+* #include "polygon.h"
+* void astInitPolygonVtab( AstPolygonVtab *vtab, const char *name )
+
+* Class Membership:
+* Polygon vtab initialiser.
+
+* Description:
+* This function initialises the component of a virtual function
+* table which is used by the Polygon class.
+
+* Parameters:
+* vtab
+* Pointer to the virtual function table. The components used by
+* all ancestral classes will be initialised if they have not already
+* been initialised.
+* name
+* Pointer to a constant null-terminated character string which contains
+* the name of the class to which the virtual function table belongs (it
+* is this pointer value that will subsequently be returned by the Object
+* astClass function).
+*-
+*/
+
+/* Local Variables: */
+ astDECLARE_GLOBALS /* Pointer to thread-specific global data */
+ AstMappingVtab *mapping; /* Pointer to Mapping component of Vtab */
+ AstRegionVtab *region; /* Pointer to Region component of Vtab */
+ AstObjectVtab *object; /* Pointer to Object component of Vtab */
+
+/* Check the local error status. */
+ if ( !astOK ) return;
+
+/* Get a pointer to the thread specific global data structure. */
+ astGET_GLOBALS(NULL);
+
+/* Initialize the component of the virtual function table used by the
+ parent class. */
+ astInitRegionVtab( (AstRegionVtab *) vtab, name );
+
+/* Store a unique "magic" value in the virtual function table. This
+ will be used (by astIsAPolygon) to determine if an object belongs
+ to this class. We can conveniently use the address of the (static)
+ class_check variable to generate this unique value. */
+ vtab->id.check = &class_check;
+ vtab->id.parent = &(((AstRegionVtab *) vtab)->id);
+
+/* Initialise member function pointers. */
+/* ------------------------------------ */
+/* Store pointers to the member functions (implemented here) that provide
+ virtual methods for this class. */
+ vtab->Downsize = Downsize;
+
+/* Save the inherited pointers to methods that will be extended, and
+ replace them with pointers to the new member functions. */
+ object = (AstObjectVtab *) vtab;
+ mapping = (AstMappingVtab *) vtab;
+ region = (AstRegionVtab *) vtab;
+
+ parent_transform = mapping->Transform;
+ mapping->Transform = Transform;
+
+ parent_simplify = mapping->Simplify;
+ mapping->Simplify = Simplify;
+
+ parent_setregfs = region->SetRegFS;
+ region->SetRegFS = SetRegFS;
+
+ parent_resetcache = region->ResetCache;
+ region->ResetCache = ResetCache;
+
+ parent_clearattrib = object->ClearAttrib;
+ object->ClearAttrib = ClearAttrib;
+ parent_getattrib = object->GetAttrib;
+ object->GetAttrib = GetAttrib;
+ parent_setattrib = object->SetAttrib;
+ object->SetAttrib = SetAttrib;
+ parent_testattrib = object->TestAttrib;
+ object->TestAttrib = TestAttrib;
+
+ region->RegPins = RegPins;
+ region->RegBaseMesh = RegBaseMesh;
+ region->RegBaseBox = RegBaseBox;
+ region->RegTrace = RegTrace;
+ region->GetBounded = GetBounded;
+
+ vtab->ClearSimpVertices = ClearSimpVertices;
+ vtab->GetSimpVertices = GetSimpVertices;
+ vtab->SetSimpVertices = SetSimpVertices;
+ vtab->TestSimpVertices = TestSimpVertices;
+
+/* Store replacement pointers for methods which will be over-ridden by
+ new member functions implemented here. */
+
+/* Declare the copy constructor, destructor and class dump
+ functions. */
+ astSetDump( vtab, Dump, "Polygon", "Polygonal region" );
+ astSetDelete( vtab, Delete );
+ astSetCopy( vtab, Copy );
+
+/* If we have just initialised the vtab for the current class, indicate
+ that the vtab is now initialised, and store a pointer to the class
+ identifier in the base "object" level of the vtab. */
+ if( vtab == &class_vtab ) {
+ class_init = 1;
+ astSetVtabClassIdentifier( vtab, &(vtab->id) );
+ }
+}
+
+static int IntCmp( const void *a, const void *b ){
+/*
+* Name:
+* IntCmp
+
+* Purpose:
+* An integer comparison function for the "qsort" function.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "polygon.h"
+* int IntCmp( const void *a, const void *b )
+
+* Class Membership:
+* Polygon member function
+
+* Description:
+* See the docs for "qsort".
+
+* Parameters:
+* a
+* Pointer to the first int
+* b
+* Pointer to the second int
+
+* Returnd Value:
+* Positive, negative or zero, depending on whether a is larger than,
+* equal to, or less than b.
+
+*/
+
+ return *((int*)a) - *((int*)b);
+}
+
+static Segment *NewSegment( Segment *seg, int i1, int i2, int nvert,
+ int *status ){
+/*
+* Name:
+* NewSegment
+
+* Purpose:
+* Initialise a structure describing a segment of the new Polygon
+* created by astDownsize.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "polygon.h"
+* Segment *NewSegment( Segment *seg, int i1, int i2, int nvert,
+* int *status )
+
+* Class Membership:
+* Polygon member function
+
+* Description:
+* This function initialises the contents of a structure describing
+* the specified range of vertices within a Polygon. The cyclic nature
+* of vertex indices is taken into account.
+*
+* If no structure is supplied, memory is allocated to hold a new
+* structure.
+
+* Parameters:
+* seg
+* Pointer to a structure to initialise, or NULL if a new structure
+* is to be allocated.
+* i1
+* The index of a vertex within the old Polygon (supplied to
+* astDownsize) that marks the start of the new line segment in
+* the downsized polygon.
+* i2
+* The index of a vertex within the old Polygon (supplied to
+* astDownsize) that marks the end of the new line segment in
+* the downsized polygon.
+* nvert
+* Total number of vertics in the old Polygon..
+* status
+* Pointer to the inherited status variable.
+
+* Returnd Value:
+* Pointer to the initialised Segment structure. It should be freed using
+* astFree when no longer needed.
+
+*/
+
+/* Local Variables: */
+ Segment *result;
+
+/* Check the global error status. */
+ if ( !astOK ) return NULL;
+
+/* Get a pointer to the structure to be initialised, allocating memory
+ for a new structure if none was supplied. */
+ result = seg ? seg : astMalloc( sizeof( Segment ) );
+
+/* Check the pointer can be used safely. */
+ if( result ) {
+
+/* If the supplied ending index is less than the starting index, the
+ ending index must have gone all the way round the polygon and started
+ round again. Increase the ending index by the number of vertices to
+ put it in the same cycle as the starting index. */
+ if( i2 < i1 ) i2 += nvert;
+
+/* If the supplied starting index is within the first cycle (i.e. zero ->
+ nvert-1 ), use the indices as they are (which may mean that the ending
+ index is greater than nvert, but this is handled correctly by other
+ functions). */
+ if( i1 < nvert ) {
+ result->i1 = i1;
+ result->i2 = i2;
+
+/* If the supplied starting index is within the second cycle (i.e. nvert
+ or greater) the ending index will be even greater, so we can reduce
+ both by "nvert" to put them both in the first cycle. The goal is that
+ the starting index should always be in the first cycle, but the ending
+ index may possibly be in the second cycle. */
+ } else {
+ result->i1 = i1 - nvert;
+ result->i2 = i2 - nvert;
+ }
+
+/* Nullify the links to other Segments */
+ result->next = NULL;
+ result->prev = NULL;
+ }
+
+/* Return the pointer to the new Segment structure. */
+ return result;
+}
+
+/*
+*++
+* Name:
+c astOutline<X>
+f AST_OUTLINE<X>
+
+* Purpose:
+* Create a new Polygon outling values in a 2D data grid.
+
+* Type:
+* Public function.
+
+* Synopsis:
+c #include "polygon.h"
+c AstPolygon *astOutline<X>( <Xtype> value, int oper, const <Xtype> array[],
+c const int lbnd[2], const int ubnd[2], double maxerr,
+c int maxvert, const int inside[2], int starpix )
+f RESULT = AST_OUTLINE<X>( VALUE, OPER, ARRAY, LBND, UBND, MAXERR,
+f MAXVERT, INSIDE, STARPIX, STATUS )
+
+* Class Membership:
+* Polygon method.
+
+* Description:
+* This is a set of functions that create a Polygon enclosing a single
+* contiguous set of pixels that have a specified value within a gridded
+* 2-dimensional data array (e.g. an image).
+*
+* A basic 2-dimensional Frame is used to represent the pixel coordinate
+* system in the returned Polygon. The Domain attribute is set to
+* "PIXEL", the Title attribute is set to "Pixel coordinates", and the
+* Unit attribute for each axis is set to "pixel". All other
+* attributes are left unset. The nature of the pixel coordinate system
+* is determined by parameter
+c "starpix".
+f STARPIX.
+*
+* The
+c "maxerr" and "maxvert"
+f MAXERR and MAXVERT
+* parameters can be used to control how accurately the returned
+* Polygon represents the required region in the data array. The
+* number of vertices in the returned Polygon will be the minimum
+* needed to achieve the required accuracy.
+*
+* You should use a function which matches the numerical type of the
+* data you are processing by replacing <X> in the generic function
+* name
+c astOutline<X>
+f AST_OUTLINE<X>
+c by an appropriate 1- or 2-character type code. For example, if you
+* are procesing data with type
+c "float", you should use the function astOutlineF
+f REAL, you should use the function AST_OUTLINER
+* (see the "Data Type Codes" section below for the codes appropriate to
+* other numerical types).
+
+* Parameters:
+c value
+f VALUE = <Xtype> (Given)
+* A data value that specifies the pixels to be outlined.
+c oper
+f OPER = INTEGER (Given)
+* Indicates how the
+c "value"
+f VALUE
+* parameter is used to select the outlined pixels. It can
+* have any of the following values:
+c - AST__LT: outline pixels with value less than "value".
+c - AST__LE: outline pixels with value less than or equal to "value".
+c - AST__EQ: outline pixels with value equal to "value".
+c - AST__NE: outline pixels with value not equal to "value".
+c - AST__GE: outline pixels with value greater than or equal to "value".
+c - AST__GT: outline pixels with value greater than "value".
+f - AST__LT: outline pixels with value less than VALUE.
+f - AST__LE: outline pixels with value less than or equal to VALUE.
+f - AST__EQ: outline pixels with value equal to VALUE.
+f - AST__NE: outline pixels with value not equal to VALUE.
+f - AST__GE: outline pixels with value greater than or equal to VALUE.
+f - AST__GT: outline pixels with value greater than VALUE.
+c array
+f ARRAY( * ) = <Xtype> (Given)
+c Pointer to a
+f A
+* 2-dimensional array containing the data to be processed. The
+* numerical type of this array should match the 1- or
+* 2-character type code appended to the function name (e.g. if
+c you are using astOutlineF, the type of each array element
+c should be "float").
+f you are using AST_OUTLINER, the type of each array element
+f should be REAL).
+*
+* The storage order of data within this array should be such
+* that the index of the first grid dimension varies most
+* rapidly and that of the second dimension least rapidly
+c (i.e. Fortran array indexing is used).
+f (i.e. normal Fortran array storage order).
+c lbnd
+f LBND( 2 ) = INTEGER (Given)
+c Pointer to an array of two integers
+f An array
+* containing the pixel index of the first pixel in the input grid
+* along each dimension.
+c ubnd
+f UBND( 2) = INTEGER (Given)
+c Pointer to an array of two integers
+f An array
+* containing the pixel index of the last pixel in the input grid
+* along each dimension.
+*
+c Note that "lbnd" and "ubnd" together define the shape
+f Note that LBND and UBND together define the shape
+* and size of the input pixel grid, its extent along a particular
+c (j'th) dimension being ubnd[j]-lbnd[j]+1 pixels.
+f (J'th) dimension being UBND(J)-LBND(J)+1 pixels.
+* For FITS images,
+c the lbnd values will be 1 and the ubnd
+f the LBND values will be 1 and the UBND
+* values will be equal to the NAXISi header values. Other
+* data systems, such as the Starlink NDF system, allow an
+c arbitrary pixel origin to be used (i.e. lbnd
+f arbitrary pixel origin to be used (i.e. LBND
+* is not necessarily 1).
+*
+* These bounds also define the input grid's floating point coordinate
+* system, each pixel having unit extent along each dimension with
+* integral coordinate values at its centre or upper corner, as selected
+* by parameter
+c "starpix".
+f STARPIX.
+c maxerr
+f MAXERR = DOUBLE PRECISION (Given)
+* Together with
+c "maxvert",
+f MAXVERT,
+* this determines how accurately the returned Polygon represents
+* the required region of the data array. It gives the target
+* discrepancy between the returned Polygon and the accurate outline
+* in the data array, expressed as a number of pixels. Insignificant
+* vertices are removed from the accurate outline, one by one, until
+* the number of vertices remaining in the returned Polygon equals
+c "maxvert",
+f MAXVERT,
+* or the largest discrepancy between the accurate outline and the
+* returned Polygon is greater than
+c "maxerr". If "maxerr"
+f MAXERR. If MAXERR
+* is zero or less, its value is ignored and the returned Polygon will
+* have the number of vertices specified by
+c "maxvert".
+f MAXVERT.
+c maxvert
+f MAXVERT = INTEGER (Given)
+* Together with
+c "maxerr",
+f MAXERR,
+* this determines how accurately the returned Polygon represents
+* the required region of the data array. It gives the maximum
+* allowed number of vertices in the returned Polygon. Insignificant
+* vertices are removed from the accurate outline, one by one, until
+* the number of vertices remaining in the returned Polygon equals
+c "maxvert",
+f MAXVERT,
+* or the largest discrepancy between the accurate outline and the
+* returned Polygon is greater than
+c "maxerr". If "maxvert"
+f MAXERR. If MAXVERT
+* is less than 3, its value is ignored and the number of vertices in
+* the returned Polygon will be the minimum needed to ensure that the
+* discrepancy between the accurate outline and the returned
+* Polygon is less than
+c "maxerr".
+f MAXERR.
+c inside
+f INSIDE( 2 ) = INTEGER (Given)
+c Pointer to an array of two integers
+f An array
+* containing the pixel indices of a pixel known to be inside the
+* required region. This is needed because the supplied data
+* array may contain several disjoint areas of pixels that satisfy
+* the criterion specified by
+c "value" and "oper".
+f VALUE and OPER.
+* In such cases, the area described by the returned Polygon will
+* be the one that contains the pixel specified by
+c "inside".
+f INSIDE.
+* If the specified pixel is outside the bounds given by
+c "lbnd" and "ubnd",
+f LBND and UBND,
+* or has a value that does not meet the criterion specified by
+c "value" and "oper",
+f VALUE and OPER,
+* then this function will search for a suitable pixel. The search
+* starts at the central pixel and proceeds in a spiral manner until
+* a pixel is found that meets the specified crierion.
+c starpix
+f STARPIX = LOGICAL (Given)
+* A flag indicating the nature of the pixel coordinate system used
+* to describe the vertex positions in the returned Polygon. If
+c non-zero,
+f .TRUE.,
+* the standard Starlink definition of pixel coordinate is used in
+* which a pixel with integer index I spans a range of pixel coordinate
+* from (I-1) to I (i.e. pixel corners have integral pixel coordinates).
+c If zero,
+f If .FALSE.,
+* the definition of pixel coordinate used by other AST functions
+c such as astResample, astMask,
+f such as AST_RESAMPLE, AST_MASK,
+* etc., is used. In this definition, a pixel with integer index I
+* spans a range of pixel coordinate from (I-0.5) to (I+0.5) (i.e.
+* pixel centres have integral pixel coordinates).
+f STATUS = INTEGER (Given and Returned)
+f The global status.
+
+* Returned Value:
+c astOutline<X>()
+f AST_OUTLINE<X> = INTEGER
+* A pointer to the required Polygon.
+
+* Notes:
+* - This function proceeds by first finding a very accurate polygon,
+* and then removing insignificant vertices from this fine polygon
+* using
+c astDownsize.
+f AST_DOWNSIZE.
+* - The returned Polygon is the outer boundary of the contiguous set
+* of pixels that includes ths specified "inside" point, and satisfy
+* the specified value requirement. This set of pixels may potentially
+* include "holes" where the pixel values fail to meet the specified
+* value requirement. Such holes will be ignored by this function.
+c - NULL
+f - AST__NULL
+* will be returned if this function is invoked with the global
+* error status set, or if it should fail for any reason.
+
+* Data Type Codes:
+* To select the appropriate masking function, you should
+c replace <X> in the generic function name astOutline<X> with a
+f replace <X> in the generic function name AST_OUTLINE<X> with a
+* 1- or 2-character data type code, so as to match the numerical
+* type <Xtype> of the data you are processing, as follows:
+c - D: double
+c - F: float
+c - L: long int
+c - UL: unsigned long int
+c - I: int
+c - UI: unsigned int
+c - S: short int
+c - US: unsigned short int
+c - B: byte (signed char)
+c - UB: unsigned byte (unsigned char)
+f - D: DOUBLE PRECISION
+f - R: REAL
+f - I: INTEGER
+f - UI: INTEGER (treated as unsigned)
+f - S: INTEGER*2 (short integer)
+f - US: INTEGER*2 (short integer, treated as unsigned)
+f - B: BYTE (treated as signed)
+f - UB: BYTE (treated as unsigned)
+*
+c For example, astOutlineD would be used to process "double"
+c data, while astOutlineS would be used to process "short int"
+c data, etc.
+f For example, AST_OUTLINED would be used to process DOUBLE
+f PRECISION data, while AST_OUTLINES would be used to process
+f short integer data (stored in an INTEGER*2 array), etc.
+f
+f For compatibility with other Starlink facilities, the codes W
+f and UW are provided as synonyms for S and US respectively (but
+f only in the Fortran interface to AST).
+
+*--
+*/
+/* Define a macro to implement the function for a specific data
+ type. Note, this function cannot be a virtual function since the
+ argument list does not include a Polygon, and so no virtual function
+ table is available. */
+#define MAKE_OUTLINE(X,Xtype) \
+AstPolygon *astOutline##X##_( Xtype value, int oper, const Xtype array[], \
+ const int lbnd[2], const int ubnd[2], double maxerr, \
+ int maxvert, const int inside[2], int starpix, \
+ int *status ) { \
+\
+/* Local Variables: */ \
+ AstFrame *frm; /* Frame in which to define the Polygon */ \
+ AstPointSet *candidate; /* Candidate polygon vertices */ \
+ AstPointSet *pset; /* PointSet holding downsized polygon vertices */ \
+ AstPolygon *result; /* Result value to return */ \
+ const Xtype *pv; /* Pointer to next test point */ \
+ Xtype v; /* Value of current pixel */ \
+ double **ptr; /* Pointers to PointSet arrays */ \
+ int boxsize; /* Half width of smoothign box in vertices */ \
+ int inx; /* X index of inside point */ \
+ int iny; /* Y index of inside point */ \
+ int iv; /* Vector index of next test pixel */ \
+ int ixv; /* X pixel index of next test point */ \
+ int nv0; /* Number of vertices in accurate outline */ \
+ int nx; /* Length of array x axis */ \
+ int smooth; /* Do we need to smooth the polygon? */ \
+ int stop_at_invalid; /* Indicates when to stop rightwards search */ \
+ int tmp; /* Alternative boxsize */ \
+ int valid; /* Does current pixel meet requirements? */ \
+ static double junk[ 6 ] = {0.0, 0.0, 1.0, 1.0, 0.0, 1.0 }; /* Junk poly */ \
+\
+/* Initialise. */ \
+ result = NULL; \
+\
+/* Check the global error status. */ \
+ if ( !astOK ) return result; \
+\
+/* Avoid compiler warnings. */ \
+ iv = 0; \
+\
+/* If we are going to be smoothing the polygon before downsizing it, we \
+ need to ensure that the full polygon is retained within TraceEdge. if \
+ this is not the case, TraceEdge can remove all vertices from straight \
+ lines, except for the vertices that mark the beinning and end of the \
+ straight line. */ \
+ smooth = ( maxerr > 0.0 || maxvert > 2 ); \
+\
+/* Store the X dimension of the array. */ \
+ nx = ubnd[ 0 ] - lbnd[ 0 ] + 1; \
+\
+/* See if a valid inside point was supplied. It must be inside the bounds \
+ of the array, and must have a pixel value that meets the specified \
+ requirement. */ \
+ inx = inside[ 0 ]; \
+ iny = inside[ 1 ]; \
+ valid = ( inx >= lbnd[ 0 ] && inx <= ubnd[ 0 ] && \
+ iny >= lbnd[ 1 ] && iny <= ubnd[ 1 ] ); \
+\
+ if( valid ) { \
+ iv = ( inx - lbnd[ 0 ] ) + (iny - lbnd[ 1 ] )*nx; \
+ v = array[ iv ]; \
+\
+ if( oper == AST__LT ) { \
+ valid = ( v < value ); \
+\
+ } else if( oper == AST__LE ) { \
+ valid = ( v <= value ); \
+\
+ } else if( oper == AST__EQ ) { \
+ valid = ( v == value ); \
+\
+ } else if( oper == AST__NE ) { \
+ valid = ( v != value ); \
+\
+ } else if( oper == AST__GE ) { \
+ valid = ( v >= value ); \
+\
+ } else if( oper == AST__GT ) { \
+ valid = ( v > value ); \
+\
+ } else if( astOK ){ \
+ astError( AST__OPRIN, "astOutline"#X": Invalid operation code " \
+ "(%d) supplied (programming error).", status, oper ); \
+ } \
+ } \
+\
+/* If no valid inside point was supplied, find one now. */ \
+ if( !valid ) { \
+\
+ if( oper == AST__LT ) { \
+ FindInsidePointLT##X( value, array, lbnd, ubnd, &inx, &iny, &iv, status ); \
+\
+ } else if( oper == AST__LE ) { \
+ FindInsidePointLE##X( value, array, lbnd, ubnd, &inx, &iny, &iv, status ); \
+\
+ } else if( oper == AST__EQ ) { \
+ FindInsidePointEQ##X( value, array, lbnd, ubnd, &inx, &iny, &iv, status ); \
+\
+ } else if( oper == AST__NE ) { \
+ FindInsidePointNE##X( value, array, lbnd, ubnd, &inx, &iny, &iv, status ); \
+\
+ } else if( oper == AST__GE ) { \
+ FindInsidePointGE##X( value, array, lbnd, ubnd, &inx, &iny, &iv, status ); \
+\
+ } else if( oper == AST__GT ) { \
+ FindInsidePointGT##X( value, array, lbnd, ubnd, &inx, &iny, &iv, status ); \
+\
+ } else if( astOK ){ \
+ astError( AST__OPRIN, "astOutline"#X": Invalid operation code " \
+ "(%d) supplied (programming error).", status, oper ); \
+ } \
+ } \
+\
+/* We now need to find a point on the boundary of the region containing \
+ the inside point. Starting at the inside point, move to the right \
+ through the array until a pixel is found which fails to meet the value \
+ requirement or the edge of the array is reached. */ \
+\
+ candidate = NULL; \
+ pv = array + iv; \
+ ixv = inx; \
+ stop_at_invalid = 1; \
+\
+ while( ++ixv <= ubnd[ 0 ] ) { \
+\
+/* Get the next pixel value. */ \
+ v = *(++pv); \
+\
+/* See if it meets the value requirement. */ \
+ if( oper == AST__LT ) { \
+ valid = ( v < value ); \
+\
+ } else if( oper == AST__LE ) { \
+ valid = ( v <= value ); \
+\
+ } else if( oper == AST__EQ ) { \
+ valid = ( v == value ); \
+\
+ } else if( oper == AST__NE ) { \
+ valid = ( v != value ); \
+\
+ } else if( oper == AST__GE ) { \
+ valid = ( v >= value ); \
+\
+ } else if( oper == AST__GT ) { \
+ valid = ( v > value ); \
+\
+ } else if( astOK ){ \
+ astError( AST__OPRIN, "astOutline"#X": Invalid operation code " \
+ "(%d) supplied (programming error).", status, oper ); \
+ break; \
+ } \
+\
+/* If we are currently looking for the next invalid pixel, and this pixel \
+ is invalid... */ \
+ if( stop_at_invalid ) { \
+ if( ! valid ) { \
+\
+/* The current pixel may be on the required polygon, or it may be on the \
+ edge of a hole contained within the region being outlined. We would \
+ like to jump over such holes so that we can continue to look for the \
+ real edge of the region being outlined. In order to determine if we \
+ have reached a hole, we trace the edge that passes through the current \
+ pixel, forming a candidate polygon in the process. In the process, We \
+ see if the inside point falls within this candidate polygon. If it does \
+ then the polygon is accepted as the required polygon. Otherwise, it is \
+ rejected as a mere hole, and we continue moving away from the inside \
+ point, looking for a new edge. */ \
+ if( oper == AST__LT ) { \
+ candidate = TraceEdgeLT##X( value, array, lbnd, ubnd, iv - 1, \
+ ixv - 1, iny, starpix, smooth, status ); \
+\
+ } else if( oper == AST__LE ) { \
+ candidate = TraceEdgeLE##X( value, array, lbnd, ubnd, iv - 1, \
+ ixv - 1, iny, starpix, smooth, status ); \
+\
+ } else if( oper == AST__EQ ) { \
+ candidate = TraceEdgeEQ##X( value, array, lbnd, ubnd, iv - 1, \
+ ixv - 1, iny, starpix, smooth, status ); \
+\
+ } else if( oper == AST__NE ) { \
+ candidate = TraceEdgeNE##X( value, array, lbnd, ubnd, iv - 1, \
+ ixv - 1, iny, starpix, smooth, status ); \
+\
+ } else if( oper == AST__GE ) { \
+ candidate = TraceEdgeGE##X( value, array, lbnd, ubnd, iv - 1, \
+ ixv - 1, iny, starpix, smooth, status ); \
+\
+ } else if( oper == AST__GT ) { \
+ candidate = TraceEdgeGT##X( value, array, lbnd, ubnd, iv - 1, \
+ ixv - 1, iny, starpix, smooth, status ); \
+\
+ } else if( astOK ){ \
+ astError( AST__OPRIN, "astOutline"#X": Invalid operation code " \
+ "(%d) supplied (programming error).", status, oper ); \
+ } \
+\
+/* If the candidate polygon is the required polygon, break out of the \
+ loop. Otherwise, indicate that we want to continue moving right, \
+ across the hole, until we reach the far side of the hole (i.e. find \
+ the next valid pixel). */ \
+ if( candidate ) { \
+ break; \
+ } else { \
+ stop_at_invalid = 0; \
+ } \
+ } \
+\
+/* If we are currently looking for the next valid pixel, and the current \
+ pixel is valid... */ \
+ } else if( valid ) { \
+\
+/* We have reached the far side of a hole. Continue moving right, looking \
+ now for the next invalid pixel (which may be on the required polygon). */ \
+ stop_at_invalid = 1; \
+ } \
+ } \
+\
+/* If we have not yet found the required polygon, we must have reached \
+ the right hand edge of the array. So we now follow the edge of the \
+ array round until we meet the boundary of the required region. */ \
+ if( !candidate ) { \
+ if( oper == AST__LT ) { \
+ candidate = TraceEdgeLT##X( value, array, lbnd, ubnd, iv - 1, \
+ ixv - 1, iny, starpix, smooth, status ); \
+\
+ } else if( oper == AST__LE ) { \
+ candidate = TraceEdgeLE##X( value, array, lbnd, ubnd, iv - 1, \
+ ixv - 1, iny, starpix, smooth, status ); \
+\
+ } else if( oper == AST__EQ ) { \
+ candidate = TraceEdgeEQ##X( value, array, lbnd, ubnd, iv - 1, \
+ ixv - 1, iny, starpix, smooth, status ); \
+\
+ } else if( oper == AST__NE ) { \
+ candidate = TraceEdgeNE##X( value, array, lbnd, ubnd, iv - 1, \
+ ixv - 1, iny, starpix, smooth, status ); \
+\
+ } else if( oper == AST__GE ) { \
+ candidate = TraceEdgeGE##X( value, array, lbnd, ubnd, iv - 1, \
+ ixv - 1, iny, starpix, smooth, status ); \
+\
+ } else if( oper == AST__GT ) { \
+ candidate = TraceEdgeGT##X( value, array, lbnd, ubnd, iv - 1, \
+ ixv - 1, iny, starpix, smooth, status ); \
+\
+ } else if( astOK ){ \
+ astError( AST__OPRIN, "astOutline"#X": Invalid operation code " \
+ "(%d) supplied (programming error).", status, oper ); \
+ } \
+ } \
+\
+/* If required smooth the full resolution polygon before downsizing it. */ \
+ if( smooth ) { \
+\
+/* Initially, set the boxsize to be equal to the required accouracy. */ \
+ if( maxerr > 0 ) { \
+ boxsize = (int) maxerr; \
+ } else { \
+ boxsize = INT_MAX; \
+ } \
+\
+/* Determine a second box size equal to the average number of vertices in \
+ the accurate outline, per vertex in the returned Polygon. */ \
+ nv0 = astGetNpoint( candidate ); \
+ if( maxvert > 2 ) { \
+ tmp = nv0/(2*maxvert); \
+ } else { \
+ tmp = INT_MAX; \
+ } \
+\
+/* Use the minimum of the two box sizes. */ \
+ if( tmp < boxsize ) boxsize = tmp; \
+\
+/* Ensure the box is sufficiently small to allow at least 10 full boxes \
+ (=20 half boxes) around the polygon. */ \
+ tmp = nv0/20; \
+ if( tmp < boxsize ) boxsize = tmp; \
+ if( boxsize == 0 ) boxsize = 1; \
+\
+/* Smooth the polygon. */ \
+ SmoothPoly( candidate, boxsize, 1.0, status ); \
+ } \
+\
+/* Reduce the number of vertices in the outline. */ \
+ frm = astFrame( 2, "Domain=PIXEL,Unit(1)=pixel,Unit(2)=pixel," \
+ "Title=Pixel coordinates", status ); \
+ pset = DownsizePoly( candidate, maxerr, maxvert, frm, status ); \
+\
+/* Create a default Polygon with 3 junk vertices. */ \
+ result = astPolygon( frm, 3, 3, junk, NULL, " ", status ); \
+\
+/* Change the PointSet within the Polygon to the one created above. */ \
+ SetPointSet( result, pset, status ); \
+\
+/* Free resources. Note, we need to free the arrays within the candidate \
+ PointSet explicitly, since they were not created as part of the \
+ construction of the PointSet (see TraceEdge). */ \
+ pset = astAnnul( pset ); \
+ frm = astAnnul( frm ); \
+ ptr = astGetPoints( candidate ); \
+ if( astOK ) { \
+ astFree( ptr[ 0 ] ); \
+ astFree( ptr[ 1 ] ); \
+ } \
+ candidate = astAnnul( candidate ); \
+\
+/* If an error occurred, clear the returned result. */ \
+ if ( !astOK ) result = astAnnul( result ); \
+\
+/* Return the result. */ \
+ return result; \
+}
+
+
+/* Expand the above macro to generate a function for each required
+ data type. */
+#if HAVE_LONG_DOUBLE /* Not normally implemented */
+MAKE_OUTLINE(LD,long double)
+#endif
+MAKE_OUTLINE(D,double)
+MAKE_OUTLINE(L,long int)
+MAKE_OUTLINE(UL,unsigned long int)
+MAKE_OUTLINE(I,int)
+MAKE_OUTLINE(UI,unsigned int)
+MAKE_OUTLINE(S,short int)
+MAKE_OUTLINE(US,unsigned short int)
+MAKE_OUTLINE(B,signed char)
+MAKE_OUTLINE(UB,unsigned char)
+MAKE_OUTLINE(F,float)
+
+/* Undefine the macros. */
+#undef MAKE_OUTLINE
+
+/*
+* Name:
+* PartHull
+
+* Purpose:
+* Find the convex hull enclosing selected pixels in one corner of a 2D array.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "polygon.h"
+* void PartHull<Oper><X>( <Xtype> value, const <Xtype> array[], int xdim,
+* int ydim, int xs, int ys, int xe, int ye,
+* int starpix, const int lbnd[2], double **xvert,
+* double **yvert, int *nvert, int *status )
+
+* Class Membership:
+* Polygon member function
+
+* Description:
+* This function uses an algorithm similar to "Andrew's Monotone Chain
+* Algorithm" to create a list of vertices describing one corner of the
+* convex hull enclosing the selected pixels in the supplied array.
+* The corner is defined to be the area of the array to the right of
+* the line from (xs,ys) to (xe,ye).
+
+* Parameters:
+* value
+* A data value that specifies the pixels to be selected.
+* array
+* Pointer to a 2-dimensional array containing the data to be
+* processed. The numerical type of this array should match the 1-
+* or 2-character type code appended to the function name.
+* xdim
+* The number of pixels along each row of the array.
+* ydim
+* The number of rows in the array.
+* xs
+* The X GRID index of the first pixel on the line to be checked.
+* ys
+* The Y GRID index of the first pixel on the line to be checked.
+* xe
+* The X GRID index of the last pixel on the line to be checked.
+* ye
+* The Y GRID index of the last pixel on the line to be checked.
+* starpix
+* If non-zero, the usual Starlink definition of pixel coordinate
+* is used (integral values at pixel corners). Otherwise, the
+* system used by other AST functions such as astResample is used
+* (integral values at pixel centres).
+* lbnd
+* The lower pixel index bounds of the array.
+* xvert
+* Address of a pointer in which to return a pointer to the list
+* of GRID x values on the hull.
+* yvert
+* Address of a pointer in which to return a pointer to the list
+* of GRID y values on the hull.
+* nvert
+* Address of a pointer in which to return the number of points in
+* the returned xvert and yvert arrays.
+* status
+* Pointer to the inherited status variable.
+
+*/
+
+/* Define a macro to implement the function for a specific data
+ type and operation. */
+#define MAKE_PARTHULL(X,Xtype,Oper,OperI) \
+static void PartHull##Oper##X( Xtype value, const Xtype array[], int xdim, \
+ int ydim, int xs, int ys, int xe, int ye, \
+ int starpix, const int lbnd[2], \
+ double **xvert, double **yvert, int *nvert, \
+ int *status ) { \
+\
+/* Local Variables: */ \
+ const Xtype *pc; \
+ double *pxy; \
+ double dx2; \
+ double dx1; \
+ double dy1; \
+ double dy2; \
+ double off; \
+ double xdelta; \
+ int ivert; \
+ int ix; \
+ int iy; \
+ int x0; \
+ int x1; \
+ int xl; \
+ int xlim; \
+ int xr; \
+ int yinc; \
+\
+/* Initialise */ \
+ *yvert = NULL; \
+ *xvert = NULL; \
+ *nvert = 0; \
+\
+/* Check the global error status. */ \
+ if ( !astOK ) return; \
+\
+/* If the line has zero length. just return a single vertex. */ \
+ if( xs == xe && ys == ye ) { \
+ *xvert = astMalloc( sizeof( double ) ); \
+ *yvert = astMalloc( sizeof( double ) ); \
+ if( astOK ) { \
+ if( starpix ) { \
+ (*xvert)[ 0 ] = xs + lbnd[ 0 ] - 1.5; \
+ (*yvert)[ 0 ] = ys + lbnd[ 1 ] - 1.5; \
+ } else { \
+ (*xvert)[ 0 ] = xs + lbnd[ 0 ] - 1.0; \
+ (*yvert)[ 0 ] = ys + lbnd[ 1 ] - 1.0; \
+ } \
+ *nvert = 1; \
+ } \
+ return; \
+ } \
+\
+/* Otherwise check the line is sloping. */ \
+ if( xs == xe ) { \
+ astError( AST__INTER, "astOutline(Polygon): Bounding box " \
+ "has zero width (internal AST programming error).", \
+ status ); \
+ return; \
+ } else if( ys == ye ) { \
+ astError( AST__INTER, "astOutline(Polygon): Bounding box " \
+ "has zero height (internal AST programming error).", \
+ status ); \
+ return; \
+ } \
+\
+/* Calculate the difference in length between adjacent rows of the area \
+ to be tested. */ \
+ xdelta = ((double)( xe - xs ))/((double)( ye - ys )); \
+\
+/* The left and right X limits */ \
+ if( xe > xs ) { \
+ xl = xs; \
+ xr = xe; \
+ } else { \
+ xl = xe; \
+ xr = xs; \
+ } \
+\
+/* Get the increment in row number as we move from the start to the end \
+ of the line. */ \
+ yinc = ( ye > ys ) ? 1 : -1; \
+\
+/* Loop round all rows that cross the region to be tested, from start to \
+ end of the supplied line. */ \
+ iy = ys; \
+ while( astOK ) { \
+\
+/* Get the GRID X coord where the line crosses this row. */ \
+ xlim = (int)( 0.5 + xs + xdelta*( iy - ys ) ); \
+\
+/* Get the index of the first and last columns to be tested on this row. */ \
+ if( yinc < 0 ) { \
+ x0 = xl; \
+ x1 = xlim; \
+ } else { \
+ x0 = xlim; \
+ x1 = xr; \
+ } \
+\
+/* Get a pointer to the first pixel to be tested at this row. */ \
+ pc = array + ( iy - 1 )*xdim + x0 - 1; \
+\
+/* Loop round all columns to be tested in this row. */ \
+ for( ix = x0; ix <= x1 && astOK; ix++,pc++ ) { \
+\
+/* Ignore pixels that are not selected. */ \
+ if( ISVALID(*pc,OperI,value) ) { \
+\
+/* If this is the very first pixel, initialise the hull to contain just \
+ the first pixel. */ \
+ if( *nvert == 0 ){ \
+ *xvert = astMalloc( 200*sizeof( double ) ); \
+ *yvert = astMalloc( 200*sizeof( double ) ); \
+ if( astOK ) { \
+ (*xvert)[ 0 ] = ix; \
+ (*yvert)[ 0 ] = iy; \
+ *nvert = 1; \
+ } else { \
+ break; \
+ } \
+\
+/* Otherwise.... */ \
+ } else { \
+\
+/* Loop until the hull has been corrected to include the current pixel. */ \
+ while( 1 ) { \
+\
+/* If the hull currently contains only one pixel, add the current pixel to \
+ the end of the hull. */ \
+ if( *nvert == 1 ){ \
+ (*xvert)[ 1 ] = ix; \
+ (*yvert)[ 1 ] = iy; \
+ *nvert = 2; \
+ break; \
+\
+/* Otherwise... */ \
+ } else { \
+\
+/* Extend the line from the last-but-one pixel on the hull to the last \
+ pixel on the hull, and see if the current pixel is to the left of \
+ this line. If it is, it too is on the hull and so push it onto the end \
+ of the list of vertices. */ \
+ dx1 = (*xvert)[ *nvert - 1 ] - (*xvert)[ *nvert - 2 ]; \
+ dy1 = (*yvert)[ *nvert - 1 ] - (*yvert)[ *nvert - 2 ]; \
+ dx2 = ix - (*xvert)[ *nvert - 2 ]; \
+ dy2 = iy - (*yvert)[ *nvert - 2 ]; \
+\
+ if( dx1*dy2 > dx2*dy1 ) { \
+ ivert = (*nvert)++; \
+ *xvert = astGrow( *xvert, *nvert, sizeof( double ) ); \
+ *yvert = astGrow( *yvert, *nvert, sizeof( double ) ); \
+ if( astOK ) { \
+ (*xvert)[ ivert ] = ix; \
+ (*yvert)[ ivert ] = iy; \
+ } \
+\
+/* Leave the loop now that the new point is on the hull. */ \
+ break; \
+\
+/* If the new point is to the left of the line, then the last point \
+ previously thought to be on hull is in fact not on the hull, so remove \
+ it. We then loop again to compare the new pixel with modified hull. */ \
+ } else { \
+ (*nvert)--; \
+ } \
+ } \
+ } \
+ } \
+ } \
+ } \
+\
+ if( iy == ye ) { \
+ break; \
+ } else { \
+ iy += yinc; \
+ } \
+\
+ } \
+\
+/* Convert GRID coords to PIXEL coords. */ \
+ if( astOK ) { \
+ pxy = *xvert; \
+ off = starpix ? lbnd[ 0 ] - 1.5 : lbnd[ 0 ] - 1.0; \
+ for( ivert = 0; ivert < *nvert; ivert++ ) *(pxy++) += off; \
+\
+ pxy = *yvert; \
+ off = starpix ? lbnd[ 1 ] - 1.5 : lbnd[ 1 ] - 1.0; \
+ for( ivert = 0; ivert < *nvert; ivert++ ) *(pxy++) += off; \
+\
+/* Free lists if an error has occurred. */ \
+ } else { \
+ *xvert = astFree( *xvert ); \
+ *yvert = astFree( *yvert ); \
+ *nvert = 0; \
+ } \
+}
+
+/* Define a macro that uses the above macro to to create implementations
+ of PartHull for all operations. */
+#define MAKEALL_PARTHULL(X,Xtype) \
+MAKE_PARTHULL(X,Xtype,LT,AST__LT) \
+MAKE_PARTHULL(X,Xtype,LE,AST__LE) \
+MAKE_PARTHULL(X,Xtype,EQ,AST__EQ) \
+MAKE_PARTHULL(X,Xtype,NE,AST__NE) \
+MAKE_PARTHULL(X,Xtype,GE,AST__GE) \
+MAKE_PARTHULL(X,Xtype,GT,AST__GT)
+
+/* Expand the above macro to generate a function for each required
+ data type and operation. */
+#if HAVE_LONG_DOUBLE /* Not normally implemented */
+MAKEALL_PARTHULL(LD,long double)
+#endif
+MAKEALL_PARTHULL(D,double)
+MAKEALL_PARTHULL(L,long int)
+MAKEALL_PARTHULL(UL,unsigned long int)
+MAKEALL_PARTHULL(I,int)
+MAKEALL_PARTHULL(UI,unsigned int)
+MAKEALL_PARTHULL(S,short int)
+MAKEALL_PARTHULL(US,unsigned short int)
+MAKEALL_PARTHULL(B,signed char)
+MAKEALL_PARTHULL(UB,unsigned char)
+MAKEALL_PARTHULL(F,float)
+
+/* Undefine the macros. */
+#undef MAKE_PARTHULL
+#undef MAKEALL_PARTHULL
+
+static double Polywidth( AstFrame *frm, AstLineDef **edges, int i, int nv,
+ double cen[ 2 ], int *status ){
+/*
+* Name:
+* Polywidth
+
+* Purpose:
+* Find the width of a polygon perpendicular to a given edge.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "polygon.h"
+* double Polywidth( AstFrame *frm, AstLineDef **edges, int i, int nv,
+* double cen[ 2 ], int *status )
+
+* Class Membership:
+* Polygon member function
+
+* Description:
+* This function defines a line perpendicular to a given polygon edge,
+* passing through the mid point of the edge, extending towards the
+* inside of the polygon. It returns the distance that can be travelled
+* along this line before any of the other polygon edges are hit (the
+* "width" of the polygon perpendicular to the given edge). It also
+* puts the position corresponding to half that distance into "cen".
+
+* Parameters:
+* frm
+* The Frame in which the lines are defined.
+* edges
+* Array of "nv" pointers to AstLineDef structures, each defining an
+* edge of the polygon.
+* i
+* The index of the edge that is to define the polygon width.
+* nv
+* Total number of edges.
+* cen
+* An array into which are put the coords of the point half way
+* along the polygon width line.
+* status
+* Pointer to the inherited status variable.
+
+* Returnd Value:
+* The width of the polygon perpendicular to the given edge, or
+* AST__BAD if the width cannot be determined (usually because the
+* vertices been supplied in a clockwise direction, effectively
+* negating the Polygon).
+
+*/
+
+/* Local Variables: */
+ AstLineDef *line;
+ double *cross;
+ double d;
+ double end[ 2 ];
+ double l1;
+ double l2;
+ double result;
+ double start[ 2 ];
+ int j;
+
+/* Check the global error status. */
+ result = AST__BAD;
+ if ( !astOK ) return result;
+
+/* Create a Line description for a line perpendicular to the specified
+ edge, passing through the mid point of the edge, and extending towards
+ the inside of the polygon. First move away from the start along the
+ line to the mid point. This gives the start of the new line. */
+ l1 = 0.5*( edges[ i ]->length );
+ astLineOffset( frm, edges[ i ], l1, 0.0, start );
+
+/* We now move away from this position at right angles to the line. We
+ start off by moving 5 times the length of the specified edge. For
+ some Frames (e.g. SkyFrames) this may result in a position that is
+ much too close (i.e. if it goes all the way round the great circle
+ and comes back to the beginning). Therefore, we check that the end
+ point is the requested distance from the start point, and if not, we
+ halve the length of the line and try again. */
+ l2 = 10.0*l1;
+ while( 1 ) {
+ astLineOffset( frm, edges[ i ], l1, l2, end );
+ d = astDistance( frm, start, end );
+ if( d != AST__BAD && fabs( d - l2 ) < 1.0E-6*l2 ) break;
+ l2 *= 0.5;
+ }
+
+/* Create a description of the required line. */
+ line = astLineDef( frm, start, end );
+
+/* Loop round every edge, except for the supplied edge. */
+ for( j = 0; j < nv; j++ ) {
+ if( j != i ) {
+
+/* Find the position at which the line created above crosses the current
+ edge. Skip to the next edge if the line does not intersect the edge
+ within the length of the edge. */
+ if( astLineCrossing( frm, line, edges[ j ], &cross ) ) {
+
+/* Find the distance between the crossing point and the line start. */
+ d = astDistance( frm, start, cross );
+
+/* If this is less than the smallest found so far, record it. */
+ if( d != AST__BAD && ( d < result || result == AST__BAD ) ) {
+ result = d;
+ }
+ }
+
+/* Free resources */
+ cross = astFree( cross );
+ }
+ }
+ line = astFree( line );
+
+/* If a width was found, return the point half way across the polygon. */
+ if( result != AST__BAD ) {
+ astOffset( frm, start, end, 0.5*result, cen );
+
+/* The usual reason for not finding a width is if the polygon vertices
+ are supplied in clockwise order, effectively negating the polygon, and
+ resulting in the "inside" of the polygon being the infinite region
+ outside a polygonal hole. In this case, the end point of the line
+ perpendicular to the initial edge can be returned as a representative
+ "inside" point. */
+ } else {
+ cen[ 0 ] = end[ 0 ];
+ cen[ 1 ] = end[ 1 ];
+ }
+
+/* Return the width. */
+ return result;
+
+}
+
+static void RegBaseBox( AstRegion *this_region, double *lbnd, double *ubnd, int *status ){
+/*
+* Name:
+* RegBaseBox
+
+* Purpose:
+* Returns the bounding box of an un-negated Region in the base Frame of
+* the encapsulated FrameSet.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "polygon.h"
+* void RegBaseBox( AstRegion *this, double *lbnd, double *ubnd, int *status )
+
+* Class Membership:
+* Polygon member function (over-rides the astRegBaseBox protected
+* method inherited from the Region class).
+
+* Description:
+* This function returns the upper and lower axis bounds of a Region in
+* the base Frame of the encapsulated FrameSet, assuming the Region
+* has not been negated. That is, the value of the Negated attribute
+* is ignored.
+
+* Parameters:
+* this
+* Pointer to the Region.
+* lbnd
+* Pointer to an array in which to return the lower axis bounds
+* covered by the Region in the base Frame of the encapsulated
+* FrameSet. It should have at least as many elements as there are
+* axes in the base Frame.
+* ubnd
+* Pointer to an array in which to return the upper axis bounds
+* covered by the Region in the base Frame of the encapsulated
+* FrameSet. It should have at least as many elements as there are
+* axes in the base Frame.
+* status
+* Pointer to the inherited status variable.
+
+*/
+
+/* Local Variables: */
+ AstFrame *frm; /* Pointer to encapsulated Frame */
+ AstPointSet *pset; /* Pointer to PointSet defining the Region */
+ AstPolygon *this; /* Pointer to Polygon structure */
+ AstRegion *reg; /* Base Frame equivalent of supplied Polygon */
+ double **ptr; /* Pointer to PointSet data */
+ double *x; /* Pointer to next X axis value */
+ double *y; /* Pointer to next Y axis value */
+ double dist; /* Offset along an axis */
+ double x0; /* The first X axis value */
+ double y0; /* The first Y axis value */
+ int ip; /* Point index */
+ int np; /* No. of points in PointSet */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Get a pointer to the Polygon structure. */
+ this = (AstPolygon *) this_region;
+
+/* If the base Frame bounding box has already been found, return the
+ values stored in the Polygon structure. */
+ if( this->lbnd[ 0 ] != AST__BAD ) {
+ lbnd[ 0 ] = this->lbnd[ 0 ];
+ lbnd[ 1 ] = this->lbnd[ 1 ];
+ ubnd[ 0 ] = this->ubnd[ 0 ];
+ ubnd[ 1 ] = this->ubnd[ 1 ];
+
+/* If the base Frame bounding box has not yet been found, find it now and
+ store it in the Polygon structure. */
+ } else {
+
+/* Get the axis values for the PointSet which defines the location and
+ extent of the region in the base Frame of the encapsulated FrameSet. */
+ pset = this_region->points;
+ ptr = astGetPoints( pset );
+ np = astGetNpoint( pset );
+
+/* Get a pointer to the base Frame in the frameset encapsulated by the
+ parent Region structure. */
+ frm = astGetFrame( this_region->frameset, AST__BASE );
+
+/* Find the upper and lower bounds of the box enclosing all the vertices.
+ The box is expressed in terms of axis offsets from the first vertex, in
+ order to avoid problems with boxes that cross RA=0 or RA=12h */
+ lbnd[ 0 ] = 0.0;
+ lbnd[ 1 ] = 0.0;
+ ubnd[ 0 ] = 0.0;
+ ubnd[ 1 ] = 0.0;
+
+ x = ptr[ 0 ];
+ y = ptr[ 1 ];
+
+ x0 = *x;
+ y0 = *y;
+
+ for( ip = 0; ip < np; ip++, x++, y++ ) {
+
+ dist = astAxDistance( frm, 1, x0, *x );
+ if( dist < lbnd[ 0 ] ) {
+ lbnd[ 0 ] = dist;
+ } else if( dist > ubnd[ 0 ] ) {
+ ubnd[ 0 ] = dist;
+ }
+
+ dist = astAxDistance( frm, 2, y0, *y );
+ if( dist < lbnd[ 1 ] ) {
+ lbnd[ 1 ] = dist;
+ } else if( dist > ubnd[ 1 ] ) {
+ ubnd[ 1 ] = dist;
+ }
+
+ }
+
+/* Convert the box bounds to absolute values, rather than values relative
+ to the first vertex. */
+ lbnd[ 0 ] += x0;
+ lbnd[ 1 ] += y0;
+ ubnd[ 0 ] += x0;
+ ubnd[ 1 ] += y0;
+
+/* The astNormBox requires a Mapping which can be used to test points in
+ this base Frame. Create a copy of the Polygon and then set its
+ FrameSet so that the current Frame in the copy is the same as the base
+ Frame in the original. */
+ reg = astCopy( this );
+ astSetRegFS( reg, frm );
+ astSetNegated( reg, 0 );
+
+/* Normalise this box. */
+ astNormBox( frm, lbnd, ubnd, reg );
+
+/* Free resources */
+ reg = astAnnul( reg );
+ frm = astAnnul( frm );
+
+/* Store it in the olygon structure for future use. */
+ this->lbnd[ 0 ] = lbnd[ 0 ];
+ this->lbnd[ 1 ] = lbnd[ 1 ];
+ this->ubnd[ 0 ] = ubnd[ 0 ];
+ this->ubnd[ 1 ] = ubnd[ 1 ];
+
+ }
+}
+
+static AstPointSet *RegBaseMesh( AstRegion *this_region, int *status ){
+/*
+* Name:
+* RegBaseMesh
+
+* Purpose:
+* Return a PointSet containing a mesh of points on the boundary of a
+* Region in its base Frame.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "polygon.h"
+* AstPointSet *astRegBaseMesh( AstRegion *this, int *status )
+
+* Class Membership:
+* Polygon member function (over-rides the astRegBaseMesh protected
+* method inherited from the Region class).
+
+* Description:
+* This function returns a PointSet containing a mesh of points on the
+* boundary of the Region. The points refer to the base Frame of
+* the encapsulated FrameSet.
+
+* Parameters:
+* this
+* Pointer to the Region.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* Pointer to the PointSet. Annul the pointer using astAnnul when it
+* is no longer needed.
+
+* Notes:
+* - A NULL pointer is returned if an error has already occurred, or if
+* this function should fail for any reason.
+
+*/
+
+/* Local Variables: */
+ AstFrame *frm; /* Base Frame in encapsulated FrameSet */
+ AstPointSet *result; /* Returned pointer */
+ AstPolygon *this; /* The Polygon structure */
+ double **rptr; /* Pointers to returned mesh data */
+ double **vptr; /* Pointers to vertex data */
+ double *lens; /* Pointer to work space holding edge lengths */
+ double d; /* Length of this edge */
+ double delta; /* Angular separation of points */
+ double end[ 2 ]; /* End position */
+ double mp; /* No. of mesh points per unit distance */
+ double p[ 2 ]; /* Position in 2D Frame */
+ double start[ 2 ]; /* Start position */
+ double total; /* Total length of polygon */
+ int ip; /* Point index */
+ int iv; /* Vertex index */
+ int n; /* No. of points on this edge */
+ int next; /* Index of next point in returned PointSet */
+ int np; /* No. of points in returned PointSet */
+ int nv; /* No. of polygon vertices */
+
+/* Initialise */
+ result= NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* If the Region structure contains a pointer to a PointSet holding
+ a previously created mesh, return it. */
+ if( this_region->basemesh ) {
+ result = astClone( this_region->basemesh );
+
+/* Otherwise, create a new mesh. */
+ } else {
+
+/* Get a pointer to the Polygon structure. */
+ this = (AstPolygon *) this_region;
+
+/* Get a pointer to the base Frame in the encapsulated FrameSet. */
+ frm = astGetFrame( this_region->frameset, AST__BASE );
+
+/* Get the number of vertices and pointers to the vertex axis values. */
+ nv = astGetNpoint( this_region->points );
+ vptr = astGetPoints( this_region->points );
+
+/* Allocate memory to hold the geodesic length of each edge. */
+ lens = astMalloc( sizeof( double )*(size_t) nv );
+
+ if( astOK ) {
+
+/* Find the total geodesic distance around the boundary. */
+ total = 0.0;
+
+ start[ 0 ] = vptr[ 0 ][ 0 ];
+ start[ 1 ] = vptr[ 1 ][ 0 ];
+
+ for( iv = 1; iv < nv; iv++ ) {
+ end[ 0 ] = vptr[ 0 ][ iv ];
+ end[ 1 ] = vptr[ 1 ][ iv ];
+
+ d = astDistance( frm, start, end );
+ if( d != AST__BAD ) total += fabs( d );
+ lens[ iv ] = d;
+ start[ 0 ] = end[ 0 ];
+ start[ 1 ] = end[ 1 ];
+ }
+
+ end[ 0 ] = vptr[ 0 ][ 0 ];
+ end[ 1 ] = vptr[ 1 ][ 0 ];
+
+ d = astDistance( frm, start, end );
+ if( d != AST__BAD ) total += fabs( d );
+ lens[ 0 ] = d;
+
+/* Find the number of mesh points per unit geodesic distance. */
+ if( total > 0.0 ){
+ mp = astGetMeshSize( this )/total;
+
+/* Find the total number of mesh points required. */
+ np = 0;
+ for( iv = 0; iv < nv; iv++ ) {
+ if( lens[ iv ] != AST__BAD ) np += 1 + (int)( lens[ iv ]*mp );
+ }
+
+/* Create a suitable PointSet to hold the returned positions. */
+ result = astPointSet( np, 2, "", status );
+ rptr = astGetPoints( result );
+ if( astOK ) {
+
+/* Initialise the index of the next point to be added to the returned
+ PointSet. */
+ next = 0;
+
+/* Loop round each good edge of the polygon. The edge ends at vertex "iv". */
+ start[ 0 ] = vptr[ 0 ][ 0 ];
+ start[ 1 ] = vptr[ 1 ][ 0 ];
+
+ for( iv = 1; iv < nv; iv++ ) {
+ end[ 0 ] = vptr[ 0 ][ iv ];
+ end[ 1 ] = vptr[ 1 ][ iv ];
+ if( lens[ iv ] != AST__BAD ) {
+
+/* Add the position of the starting vertex to the returned mesh. */
+ rptr[ 0 ][ next ] = start[ 0 ];
+ rptr[ 1 ][ next ] = start[ 1 ];
+ next++;
+
+/* Find the number of points on this edge, and the geodesic distance
+ between them. */
+ n = 1 + (int) ( lens[ iv ]*mp );
+
+/* If more than one point, find the distance between points. */
+ if( n > 1 ) {
+ delta = lens[ iv ]/n;
+
+/* Loop round the extra points. */
+ for( ip = 1; ip < n; ip++ ) {
+
+/* Find the position of the next mesh point. */
+ astOffset( frm, start, end, delta*ip, p );
+
+/* Add it to the mesh. */
+ rptr[ 0 ][ next ] = p[ 0 ];
+ rptr[ 1 ][ next ] = p[ 1 ];
+ next++;
+
+ }
+ }
+ }
+
+/* The end of this edge becomes the start of the next. */
+ start[ 0 ] = end[ 0 ];
+ start[ 1 ] = end[ 1 ];
+ }
+
+/* Now do the edge which ends at the first vertex. */
+ end[ 0 ] = vptr[ 0 ][ 0 ];
+ end[ 1 ] = vptr[ 1 ][ 0 ];
+ if( lens[ 0 ] != AST__BAD ) {
+ rptr[ 0 ][ next ] = start[ 0 ];
+ rptr[ 1 ][ next ] = start[ 1 ];
+ next++;
+
+ n = 1 + (int)( lens[ 0 ]*mp );
+ if( n > 1 ) {
+ delta = lens[ 0 ]/n;
+ for( ip = 1; ip < n; ip++ ) {
+ astOffset( frm, start, end, delta*ip, p );
+ rptr[ 0 ][ next ] = p[ 0 ];
+ rptr[ 1 ][ next ] = p[ 1 ];
+ next++;
+ }
+ }
+ }
+
+/* Check the PointSet size was correct. */
+ if( next != np && astOK ) {
+ astError( AST__INTER, "astRegBaseMesh(%s): Error in the "
+ "allocated PointSet size (%d) - should have "
+ "been %d (internal AST programming error).", status,
+ astGetClass( this ), np, next );
+ }
+
+/* Save the returned pointer in the Region structure so that it does not
+ need to be created again next time this function is called. */
+ if( astOK ) this_region->basemesh = astClone( result );
+
+ }
+
+ } else if( astOK ) {
+ astError( AST__BADIN, "astRegBaseMesh(%s): The boundary of "
+ "the supplied %s has an undefined length.", status,
+ astGetClass( this ), astGetClass( this ) );
+ }
+
+ }
+
+/* Free resources. */
+ frm = astAnnul( frm );
+ lens = astFree( lens );
+ }
+
+/* Annul the result if an error has occurred. */
+ if( !astOK ) result = astAnnul( result );
+
+/* Return a pointer to the output PointSet. */
+ return result;
+}
+
+static int RegPins( AstRegion *this_region, AstPointSet *pset, AstRegion *unc,
+ int **mask, int *status ){
+/*
+* Name:
+* RegPins
+
+* Purpose:
+* Check if a set of points fall on the boundary of a given Polygon.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "polygon.h"
+* int RegPins( AstRegion *this, AstPointSet *pset, AstRegion *unc,
+* int **mask, int *status )
+
+* Class Membership:
+* Polygon member function (over-rides the astRegPins protected
+* method inherited from the Region class).
+
+* Description:
+* This function returns a flag indicating if the supplied set of
+* points all fall on the boundary of the given Polygon.
+*
+* Some tolerance is allowed, as specified by the uncertainty Region
+* stored in the supplied Polygon "this", and the supplied uncertainty
+* Region "unc" which describes the uncertainty of the supplied points.
+
+* Parameters:
+* this
+* Pointer to the Polygon.
+* pset
+* Pointer to the PointSet. The points are assumed to refer to the
+* base Frame of the FrameSet encapsulated by "this".
+* unc
+* Pointer to a Region representing the uncertainties in the points
+* given by "pset". The Region is assumed to represent the base Frame
+* of the FrameSet encapsulated by "this". Zero uncertainity is assumed
+* if NULL is supplied.
+* mask
+* Pointer to location at which to return a pointer to a newly
+* allocated dynamic array of ints. The number of elements in this
+* array is equal to the value of the Npoint attribute of "pset".
+* Each element in the returned array is set to 1 if the
+* corresponding position in "pset" is on the boundary of the Region
+* and is set to zero otherwise. A NULL value may be supplied
+* in which case no array is created. If created, the array should
+* be freed using astFree when no longer needed.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* Non-zero if the points all fall on the boundary of the given
+* Region, to within the tolerance specified. Zero otherwise.
+
+*/
+
+/* Local variables: */
+ AstFrame *frm; /* Base Frame in supplied Polygon */
+ AstPointSet *pset1; /* Pointer to copy of supplied PointSet */
+ AstPointSet *pset2; /* Pointer to PointSet holding resolved components */
+ AstPolygon *this; /* Pointer to the Polygon structure. */
+ AstRegion *tunc; /* Uncertainity Region from "this" */
+ double **ptr1; /* Pointer to axis values in "pset1" */
+ double **ptr2; /* Pointer to axis values in "pset2" */
+ double **vptr; /* Pointer to axis values at vertices */
+ double *safe; /* An interior point in "this" */
+ double edge_len; /* Length of current edge */
+ double end[2]; /* Position of end of current edge */
+ double l1; /* Length of bounding box diagonal */
+ double l2; /* Length of bounding box diagonal */
+ double lbnd_tunc[2]; /* Lower bounds of "this" uncertainty Region */
+ double lbnd_unc[2]; /* Lower bounds of supplied uncertainty Region */
+ double par; /* Parallel component */
+ double parmax; /* Max acceptable parallel component */
+ double prp; /* Perpendicular component */
+ double start[2]; /* Position of start of current edge */
+ double ubnd_tunc[2]; /* Upper bounds of "this" uncertainty Region */
+ double ubnd_unc[2]; /* Upper bounds of supplied uncertainty Region */
+ double wid; /* Width of acceptable margin around polygon */
+ int *m; /* Pointer to next mask value */
+ int i; /* Edge index */
+ int ip; /* Point index */
+ int np; /* No. of supplied points */
+ int nv; /* No. of vertices */
+ int result; /* Returned flag */
+
+/* Initialise */
+ result = 0;
+ if( mask ) *mask = NULL;
+
+/* Check the inherited status. */
+ if( !astOK ) return result;
+
+/* Get a pointer to the Polygon structure. */
+ this = (AstPolygon *) this_region;
+
+/* Check the supplied PointSet has 2 axis values per point. */
+ if( astGetNcoord( pset ) != 2 && astOK ) {
+ astError( AST__INTER, "astRegPins(%s): Illegal number of axis "
+ "values per point (%d) in the supplied PointSet - should be "
+ "2 (internal AST programming error).", status, astGetClass( this ),
+ astGetNcoord( pset ) );
+ }
+
+/* Get the number of axes in the uncertainty Region and check it is also 2. */
+ if( unc && astGetNaxes( unc ) != 2 && astOK ) {
+ astError( AST__INTER, "astRegPins(%s): Illegal number of axes (%d) "
+ "in the supplied uncertainty Region - should be 2 "
+ "(internal AST programming error).", status, astGetClass( this ),
+ astGetNaxes( unc ) );
+ }
+
+/* Get pointers to the axis values at the polygon vertices. */
+ vptr = astGetPoints( this_region->points );
+
+/* Get the number of vertices. */
+ nv = astGetNpoint( this_region->points );
+
+/* Take a copy of the supplied PointSet and get pointers to its axis
+ values,and its size */
+ pset1 = astCopy( pset );
+ ptr1 = astGetPoints( pset1 );
+ np = astGetNpoint( pset1 );
+
+/* Create a PointSet to hold the resolved components and get pointers to its
+ axis data. */
+ pset2 = astPointSet( np, 2, "", status );
+ ptr2 = astGetPoints( pset2 );
+
+/* Create a mask array if required. */
+ if( mask ) *mask = astMalloc( sizeof(int)*(size_t) np );
+
+/* Get the centre of the region in the base Frame. We use this as a "safe"
+ interior point within the region. */
+ safe = astRegCentre( this, NULL, NULL, 0, AST__BASE );
+
+/* We now find the maximum distance on each axis that a point can be from the
+ boundary of the Polygon for it still to be considered to be on the boundary.
+ First get the Region which defines the uncertainty within the Polygon
+ being checked (in its base Frame), re-centre it on the interior point
+ found above (to avoid problems if the uncertainty region straddles a
+ discontinuity), and get its bounding box. The current Frame of the
+ uncertainty Region is the same as the base Frame of the Polygon. */
+ tunc = astGetUncFrm( this, AST__BASE );
+ if( safe ) astRegCentre( tunc, safe, NULL, 0, AST__CURRENT );
+ astGetRegionBounds( tunc, lbnd_tunc, ubnd_tunc );
+
+/* Find the geodesic length within the base Frame of "this" of the diagonal of
+ the bounding box. */
+ frm = astGetFrame( this_region->frameset, AST__BASE );
+ l1 = astDistance( frm, lbnd_tunc, ubnd_tunc );
+
+/* Also get the Region which defines the uncertainty of the supplied
+ points and get its bounding box. First re-centre the uncertainty at the
+ interior position to avoid problems from uncertainties that straddle a
+ discontinuity. */
+ if( unc ) {
+ if( safe ) astRegCentre( unc, safe, NULL, 0, AST__CURRENT );
+ astGetRegionBounds( unc, lbnd_unc, ubnd_unc );
+
+/* Find the geodesic length of the diagonal of this bounding box. */
+ l2 = astDistance( frm, lbnd_unc, ubnd_unc );
+
+/* Assume zero uncertainty if no "unc" Region was supplied. */
+ } else {
+ l2 = 0.0;
+ }
+
+/* The required border width is half of the total diagonal of the two bounding
+ boxes. */
+ if( astOK ) {
+ wid = 0.5*( l1 + l2 );
+
+/* Loop round each edge of the polygon. Edge "i" starts at vertex "i-1"
+ and ends at vertex "i". Edge zero starts at vertex "nv-1" and ends at
+ vertex zero. */
+ start[ 0 ] = vptr[ 0 ][ nv - 1 ];
+ start[ 1 ] = vptr[ 1 ][ nv - 1 ];
+ for( i = 0; i < nv; i++ ) {
+ end[ 0 ] = vptr[ 0 ][ i ];
+ end[ 1 ] = vptr[ 1 ][ i ];
+
+/* Find the length of this edge. */
+ edge_len = astDistance( frm, start, end );
+
+/* Resolve all the supplied mesh points into components parallel and
+ perpendicular to this edge. */
+ (void) astResolvePoints( frm, start, end, pset1, pset2 );
+
+/* A point is effectively on this edge if the parallel component is
+ greater than (-wid) and less than (edge_len+wid) AND the perpendicular
+ component has an absolute value less than wid. Identify such positions
+ and set them bad in pset1. */
+ parmax = edge_len + wid;
+ for( ip = 0; ip < np; ip++ ) {
+ par = ptr2[ 0 ][ ip ];
+ prp = ptr2[ 1 ][ ip ];
+
+ if( par != AST__BAD && prp != AST__BAD ) {
+ if( par > -wid && par < parmax && prp > -wid && prp < wid ) {
+ ptr1[ 0 ][ ip ] = AST__BAD;
+ ptr1[ 1 ][ ip ] = AST__BAD;
+ }
+ }
+ }
+
+/* The end of the current edge becomes the start of the next. */
+ start[ 0 ] = end[ 0 ];
+ start[ 1 ] = end[ 1 ];
+ }
+
+/* See if any good points are left in pset1. If so, it means that those
+ points were not on any edge of hte Polygon. We use two alogorithms
+ here depending on whether we are creating a mask array, since we can
+ abort the check upon finding the first good point if we are not
+ producing a mask. */
+ result = 1;
+ if( mask ) {
+ m = *mask;
+ for( ip = 0; ip < np; ip++, m++ ) {
+ if( ptr1[ 0 ][ ip ] != AST__BAD &&
+ ptr1[ 1 ][ ip ] != AST__BAD ) {
+ *m = 0;
+ result = 0;
+ } else {
+ *m = 1;
+ }
+ }
+ } else {
+ for( ip = 0; ip < np; ip++ ) {
+ if( ptr1[ 0 ][ ip ] != AST__BAD &&
+ ptr1[ 1 ][ ip ] != AST__BAD ) {
+ result = 0;
+ break;
+ }
+ }
+ }
+ }
+
+/* Free resources. */
+ tunc = astAnnul( tunc );
+ frm = astAnnul( frm );
+ safe = astFree( safe );
+ pset1 = astAnnul( pset1 );
+ pset2 = astAnnul( pset2 );
+
+/* If an error has occurred, return zero. */
+ if( !astOK ) {
+ result = 0;
+ if( mask ) *mask = astFree( *mask );
+ }
+
+/* Return the result. */
+ return result;
+}
+
+static int RegTrace( AstRegion *this_region, int n, double *dist, double **ptr,
+ int *status ){
+/*
+*+
+* Name:
+* RegTrace
+
+* Purpose:
+* Return requested positions on the boundary of a 2D Region.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "polygon.h"
+* int astTraceRegion( AstRegion *this, int n, double *dist, double **ptr );
+
+* Class Membership:
+* Polygon member function (overrides the astTraceRegion method
+* inherited from the parent Region class).
+
+* Description:
+* This function returns positions on the boundary of the supplied
+* Region, if possible. The required positions are indicated by a
+* supplied list of scalar parameter values in the range zero to one.
+* Zero corresponds to some arbitrary starting point on the boundary,
+* and one corresponds to the end (which for a closed region will be
+* the same place as the start).
+
+* Parameters:
+* this
+* Pointer to the Region.
+* n
+* The number of positions to return. If this is zero, the function
+* returns without action (but the returned function value still
+* indicates if the method is supported or not).
+* dist
+* Pointer to an array of "n" scalar parameter values in the range
+* 0 to 1.0.
+* ptr
+* A pointer to an array of pointers. The number of elements in
+* this array should equal tthe number of axes in the Frame spanned
+* by the Region. Each element of the array should be a pointer to
+* an array of "n" doubles, in which to return the "n" values for
+* the corresponding axis. The contents of the arrays are unchanged
+* if the supplied Region belongs to a class that does not
+* implement this method.
+
+* Returned Value:
+* Non-zero if the astTraceRegion method is implemented by the class
+* of Region supplied, and zero if not.
+
+*-
+*/
+
+/* Local Variables; */
+ AstFrame *frm;
+ AstMapping *map;
+ AstPointSet *bpset;
+ AstPointSet *cpset;
+ AstPolygon *this;
+ double **bptr;
+ double d;
+ double p[ 2 ];
+ int i;
+ int j0;
+ int j;
+ int ncur;
+ int nv;
+ int monotonic;
+
+/* Check inherited status, and the number of points to return, returning
+ a non-zero value to indicate that this class supports the astRegTrace
+ method. */
+ if( ! astOK || n == 0 ) return 1;
+
+/* Get a pointer to the Polygon structure. */
+ this = (AstPolygon *) this_region;
+
+/* Ensure cached information is available. */
+ Cache( this, status );
+
+/* Get a pointer to the base Frame in the encapsulated FrameSet. */
+ frm = astGetFrame( this_region->frameset, AST__BASE );
+
+/* We first determine the required positions in the base Frame of the
+ Region, and then transform them into the current Frame. Get the
+ base->current Mapping, and the number of current Frame axes. */
+ map = astGetMapping( this_region->frameset, AST__BASE, AST__CURRENT );
+
+/* If it's a UnitMap we do not need to do the transformation, so put the
+ base Frame positions directly into the supplied arrays. */
+ if( astIsAUnitMap( map ) ) {
+ bpset = NULL;
+ bptr = ptr;
+ ncur = 2;
+
+/* Otherwise, create a PointSet to hold the base Frame positions (known
+ to be 2D since this is an polygon). */
+ } else {
+ bpset = astPointSet( n, 2, " ", status );
+ bptr = astGetPoints( bpset );
+ ncur = astGetNout( map );
+ }
+
+/* Check the pointers can be used safely. */
+ if( astOK ) {
+
+/* Get the number of vertices. */
+ nv = astGetNpoint( this_region->points );
+
+/* If we have a reasonable number of pointsand there are a reasonable
+ number of vertices, we can be quicker if we know if the parameteric
+ distances are monotonic increasing. Find out now. */
+ if( n > 5 && nv > 5 ) {
+
+ monotonic = 1;
+ for( i = 1; i < n; i++ ) {
+ if( dist[ i ] < dist[ i - 1 ] ) {
+ monotonic = 0;
+ break;
+ }
+ }
+
+ } else {
+ monotonic = 0;
+ }
+
+/* Loop round each point. */
+ j0 = 1;
+ for( i = 0; i < n; i++ ) {
+
+/* Get the required round the polygon, starting from vertex zero. */
+ d = dist[ i ]*this->totlen;
+
+/* Loop round each vertex until we find one which is beyond the required
+ point. If the supplied distances are monotonic increasing, we can
+ start the checks at the same vertex that was used for the previous
+ since we know there will never be a backward step. */
+ for( j = j0; j < nv; j++ ) {
+ if( this->startsat[ j ] > d ) break;
+ }
+
+/* If the distances are monotonic increasing, record the vertex that we
+ have reached so far. */
+ if( monotonic ) j0 = j;
+
+/* Find the distance to travel beyond the previous vertex. */
+ d -= this->startsat[ j - 1 ];
+
+/* Find the position, that is the required distance from the previous
+ vertex towards the next vertex. */
+ astLineOffset( frm, this->edges[ j - 1 ], d, 0.0, p );
+
+/* Store the resulting axis values. */
+ bptr[ 0 ][ i ] = p[ 0 ];
+ bptr[ 1 ][ i ] = p[ 1 ];
+ }
+ }
+
+/* If required, transform the base frame positions into the current
+ Frame, storing them in the supplied array. Then free resources. */
+ if( bpset ) {
+ cpset = astPointSet( n, ncur, " ", status );
+ astSetPoints( cpset, ptr );
+
+ (void) astTransform( map, bpset, 1, cpset );
+
+ cpset = astAnnul( cpset );
+ bpset = astAnnul( bpset );
+ }
+
+/* Free remaining resources. */
+ map = astAnnul( map );
+ frm = astAnnul( frm );
+
+/* Return a non-zero value to indicate that this class supports the
+ astRegTrace method. */
+ return 1;
+}
+
+static Segment *RemoveFromChain( Segment *head, Segment *seg, int *status ){
+/*
+* Name:
+* RemoveFromChain
+
+* Purpose:
+* Remove a Segment from the link list maintained by astDownsize.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "polygon.h"
+* Segment *RemoveFromChain( Segment *head, Segment *seg, int *status )
+
+* Class Membership:
+* Polygon member function
+
+* Description:
+* The supplied Segment is removed form the list, and the gap is
+* closed up.
+
+* Parameters:
+* head
+* The Segment structure at the head of the list.
+* seg
+* The Segment to be removed from the list.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* Pointer to the link head (which will have changed if "seg" was the
+* original head).
+
+*/
+
+/* Check the global error status. */
+ if ( !astOK ) return head;
+
+/* If the Segment was the original head, make the next segment the new
+ head. */
+ if( head == seg ) head = seg->next;
+
+/* Close up the links between the Segments on either side of the segment
+ being removed. */
+ if( seg->prev ) seg->prev->next = seg->next;
+ if( seg->next ) seg->next->prev = seg->prev;
+
+/* Nullify the links in the segment being removed. */
+ seg->next = NULL;
+ seg->prev = NULL;
+
+/* Return the new head. */
+ return head;
+}
+
+static void ResetCache( AstRegion *this_region, int *status ){
+/*
+* Name:
+* ResetCache
+
+* Purpose:
+* Clear cached information within the supplied Region.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "polygon.h"
+* void ResetCache( AstRegion *this, int *status )
+
+* Class Membership:
+* Region member function (overrides the astResetCache method
+* inherited from the parent Region class).
+
+* Description:
+* This function clears cached information from the supplied Region
+* structure.
+
+* Parameters:
+* this
+* Pointer to the Region.
+* status
+* Pointer to the inherited status variable.
+*/
+
+/* Local Variables: */
+ AstPolygon *this;
+ int i;
+ int nv;
+
+/* Get a pointer to the Polygon structure. */
+ this = (AstPolygon *) this_region;
+
+/* If a Polygon was supplied, indicate cached information needs to be
+ recalculated. */
+ if( this ) {
+ this->stale = 1;
+ this->lbnd[ 0 ] = AST__BAD;
+
+/* Free any edge structures (number of vertices may be about to change so
+ this cannot be left until the next call to "Cache()". */
+ if( this->edges ) {
+ nv = astGetNpoint( this_region->points );
+ for( i = 0; i < nv; i++ ) {
+ this->edges[ i ] = astFree( this->edges[ i ] );
+ }
+ this->edges = astFree( this->edges );
+ }
+
+/* Clear the cache of the parent class. */
+ (*parent_resetcache)( this_region, status );
+ }
+}
+
+static void SetAttrib( AstObject *this_object, const char *setting, int *status ) {
+/*
+* Name:
+* SetAttrib
+
+* Purpose:
+* Set an attribute value for a Polygon.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "polygon.h"
+* void SetAttrib( AstObject *this, const char *setting, int *status )
+
+* Class Membership:
+* Polygon member function (extends the astSetAttrib method inherited from
+* the Region class).
+
+* Description:
+* This function assigns an attribute value for a Polygon, the attribute
+* and its value being specified by means of a string of the form:
+*
+* "attribute= value "
+*
+* Here, "attribute" specifies the attribute name and should be in lower
+* case with no white space present. The value to the right of the "="
+* should be a suitable textual representation of the value to be assigned
+* and this will be interpreted according to the attribute's data type.
+* White space surrounding the value is only significant for string
+* attributes.
+
+* Parameters:
+* this
+* Pointer to the Polygon.
+* setting
+* Pointer to a null terminated string specifying the new attribute
+* value.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* void
+*/
+
+/* Local Vaiables: */
+ AstPolygon *this; /* Pointer to the Polygon structure */
+ int ival; /* Integer attribute value */
+ int len; /* Length of setting string */
+ int nc; /* Number of characters read by astSscanf */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Obtain a pointer to the Polygon structure. */
+ this = (AstPolygon *) this_object;
+
+/* Obtain the length of the setting string. */
+ len = strlen( setting );
+
+/* Test for each recognised attribute in turn, using "astSscanf" to parse the
+ setting string and extract the attribute value (or an offset to it in the
+ case of string values). In each case, use the value set in "nc" to check
+ that the entire string was matched. Once a value has been obtained, use the
+ appropriate method to set it. */
+
+/* SimpVertices. */
+/* ------------- */
+ if ( nc = 0,
+ ( 1 == astSscanf( setting, "simpvertices= %d %n", &ival, &nc ) )
+ && ( nc >= len ) ) {
+ astSetSimpVertices( this, ival );
+
+/* Pass any unrecognised setting to the parent method for further
+ interpretation. */
+ } else {
+ (*parent_setattrib)( this_object, setting, status );
+ }
+}
+
+static void SetPointSet( AstPolygon *this, AstPointSet *pset, int *status ){
+/*
+* Name:
+* SetPointSet
+
+* Purpose:
+* Store a new set of vertices in an existing Polygon.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "polygon.h"
+* void SetPointSet( AstPolygon *this, AstPointSet *pset, int *status )
+
+* Class Membership:
+* Polygon member function
+
+* Description:
+* The PointSet in the supplied Polygon is annulled, and replaced by a
+* clone of the supplied PointSet pointer.
+
+* Parameters:
+* this
+* Pointer to the Polygon to be changed.
+* pset
+* The PointSet containing the new vertex information.
+* status
+* Pointer to the inherited status variable.
+
+*/
+
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Indicate the cached information in the polygon will need to be
+ re-calculated when needed. */
+ astResetCache( this );
+
+/* Annul the pointer to the PointSet already in the supplied Polygon. */
+ (void) astAnnul( ((AstRegion *) this)->points );
+
+/* Store a clone of the supplied new PointSet pointer. */
+ ((AstRegion *) this)->points = astClone( pset );
+
+}
+
+static void SetRegFS( AstRegion *this_region, AstFrame *frm, int *status ) {
+/*
+* Name:
+* SetRegFS
+
+* Purpose:
+* Stores a new FrameSet in a Region
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "polygon.h"
+* void SetRegFS( AstRegion *this_region, AstFrame *frm, int *status )
+
+* Class Membership:
+* Polygon method (over-rides the astSetRegFS method inherited from
+* the Region class).
+
+* Description:
+* This function creates a new FrameSet and stores it in the supplied
+* Region. The new FrameSet contains two copies of the supplied
+* Frame, connected by a UnitMap.
+
+* Parameters:
+* this
+* Pointer to the Region.
+* frm
+* The Frame to use.
+* status
+* Pointer to the inherited status variable.
+
+*/
+
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Indicate cached information eeds re-calculating. */
+ astResetCache( this_region );
+
+/* Invoke the parent method to store the FrameSet in the parent Region
+ structure. */
+ (* parent_setregfs)( this_region, frm, status );
+
+}
+
+static AstMapping *Simplify( AstMapping *this_mapping, int *status ) {
+/*
+* Name:
+* Simplify
+
+* Purpose:
+* Simplify the Mapping represented by a Region.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "polygon.h"
+* AstMapping *Simplify( AstMapping *this, int *status )
+
+* Class Membership:
+* Polygon method (over-rides the astSimplify method inherited
+* from the Region class).
+
+* Description:
+* This function invokes the parent Region Simplify method, and then
+* performs any further region-specific simplification.
+*
+* If the Mapping from base to current Frame is not a UnitMap, this
+* will include attempting to fit a new Region to the boundary defined
+* in the current Frame.
+
+* Parameters:
+* this
+* Pointer to the original Region.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to the simplified Region. A cloned pointer to the
+* supplied Region will be returned if no simplication could be
+* performed.
+
+* Notes:
+* - A NULL pointer value will be returned if this function is
+* invoked with the AST error status set, or if it should fail for
+* any reason.
+*/
+
+/* Local Variables: */
+ AstFrame *frm; /* Current Frame */
+ AstMapping *map; /* Base -> current Mapping */
+ AstMapping *result; /* Result pointer to return */
+ AstPointSet *mesh; /* Mesh of current Frame positions */
+ AstPointSet *ps2; /* Polygon PointSet in current Frame */
+ AstPolygon *newpol; /* New Polygon */
+ AstRegion *new; /* Pointer to simplified Region */
+ AstRegion *this; /* Pointer to supplied Region structure */
+ AstRegion *unc; /* Pointer to uncertainty Region */
+ double **ptr2; /* Pointer axis values in "ps2" */
+ double *mem; /* Pointer to array holding new vertex coords */
+ double *p; /* Pointer to next vertex coords */
+ double *q; /* Pointer to next vertex coords */
+ int iv; /* Vertex index */
+ int nv; /* Number of vertices in polygon */
+ int ok; /* Are the new polygon vertices good? */
+ int simpler; /* Has some simplication taken place? */
+
+/* Initialise. */
+ result = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Get a pointer to the supplied Region structure. */
+ this = (AstRegion *) this_mapping;
+
+/* Invoke the parent Simplify method inherited from the Region class. This
+ will simplify the encapsulated FrameSet and uncertainty Region. */
+ new = (AstRegion *) (*parent_simplify)( this_mapping, status );
+
+/* Note if any simplification took place. This is assumed to be the case
+ if the pointer returned by the above call is different to the supplied
+ pointer. */
+ simpler = ( new != this );
+
+/* We attempt to simplify the Polygon by re-defining it within its current
+ Frame. Transforming the Polygon from its base to its current Frame may
+ result in the region no having the same edges. If required, we test this
+ by transforming a set of bounds on the Polygon boundary. This can only
+ be done if the current Frame is 2-dimensional. Also, there is only any
+ point in doing it if the Mapping from base to current Frame in the
+ Polygon is not a UnitMap. */
+ map = astGetMapping( new->frameset, AST__BASE, AST__CURRENT );
+ if( !astIsAUnitMap( map ) && astGetNout( map ) == 2 ) {
+
+/* Get a pointer to the Frame represented by the Polgon. */
+ frm = astGetFrame( new->frameset, AST__CURRENT );
+
+/* Get the Region describing the positional uncertainty in this Frame. */
+ unc = astGetUncFrm( new, AST__CURRENT );
+
+/* Get the positions of the vertices within this Frame. */
+ ps2 = astRegTransform( this, this->points, 1, NULL, NULL );
+ ptr2 = astGetPoints( ps2 );
+
+/* Get the number of vertices. */
+ nv = astGetNpoint( ps2 );
+
+/* Re-organise the vertex axis values into the form required by the
+ Polygon constructor function. */
+ mem = astMalloc( sizeof( double)*(size_t)( 2*nv ) );
+ if( astOK ) {
+ ok = 1;
+ p = mem;
+ q = ptr2[ 0 ];
+ for( iv = 0; iv < nv; iv++ ) {
+ if( ( *(p++) = *(q++) ) == AST__BAD ) ok = 0;
+ }
+ q = ptr2[ 1 ];
+ for( iv = 0; iv < nv; iv++ ) *(p++) = *(q++);
+
+/* Create a new Polygon using these transformed vertices. */
+ if( ok ) {
+ newpol = astPolygon( frm, nv, nv, mem, unc, "", status );
+
+/* If the SimpVertices attribute is zero, we now check that the
+ transformation has not bent the edges of the polygon significantly.
+ If it has, we annul the new Polygon. */
+ if( !astGetSimpVertices( this ) ) {
+
+/* Get a mesh of points covering the Polygon in this Frame. */
+ mesh = astRegMesh( new );
+
+/* See if all points within the mesh created from the original Polygon fall
+ on the boundary of the new Polygon, to within the uncertainty of the
+ Region. If not, annul the new Polgon. */
+ if( !astRegPins( newpol, mesh, NULL, NULL ) ) {
+ newpol = astAnnul( newpol );
+ }
+
+/* Free the mesh. */
+ mesh = astAnnul( mesh );
+ }
+
+/* If we still have a new polygon, use the new Polygon in place of the
+ original Region. */
+ if( newpol ) {
+ (void) astAnnul( new );
+ new = (AstRegion *) newpol;
+ simpler = 1;
+ }
+ }
+ }
+
+/* Free other resources. */
+ frm = astAnnul( frm );
+ unc = astAnnul( unc );
+ ps2 = astAnnul( ps2 );
+ mem = astFree( mem );
+ }
+ map = astAnnul( map );
+
+/* If any simplification could be performed, copy Region attributes from
+ the supplied Region to the returned Region, and return a pointer to it.
+ If the supplied Region had no uncertainty, ensure the returned Region
+ has no uncertainty. Otherwise, return a clone of the supplied pointer. */
+ if( simpler ){
+ astRegOverlay( new, this, 1 );
+ result = (AstMapping *) new;
+
+ } else {
+ new = astAnnul( new );
+ result = astClone( this );
+ }
+
+/* If an error occurred, annul the returned pointer. */
+ if ( !astOK ) result = astAnnul( result );
+
+/* Return the result. */
+ return result;
+}
+
+static void SmoothPoly( AstPointSet *pset, int boxsize, double strength,
+ int *status ) {
+/*
+* Name:
+* SmoothPoly
+
+* Purpose:
+* Smoooth a polygon assuming plane geometry.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "polygon.h"
+* void SmoothPoly( AstPointSet *pset, int boxsize, double strength,
+* int *status )
+
+* Class Membership:
+* Polygon member function
+
+* Description:
+* This function smooths a polygon, without changing the number of
+* vertices. It assumes plane geometry, so should not be used to
+* smooth polygons defined within a SkyFrame or CmpFrame.
+*
+* Each vertex is replaced by a new vertex determined as follows: the
+* mean X and Y axis value of the vertices in a section of the polygon
+* centred on the vertex being replaced are found (the length of the
+* section is given by parameter "boxsize"). The new vertex position
+* is then the weighted mean of this mean (X,Y) position and the old
+* vertex position. The weight for the mean (X,Y) position is given
+* by parameter "strength", and the weight for the old vertex
+* position is (1.0 - strength)
+
+* Parameters:
+* pset
+* A PointSet holding the polygon vertices.
+* boxsize
+* Half width of the box filter, given as a number of vertices.
+* strength
+* The weight to use for the mean (X,Y) position when finding each
+* new vertex position. Should be in the range 0.0 to 1.0. A value
+* of zero results in no change being made to the polygon. A value
+* of 1.0 results in the returned polygon being fully smoothed.
+* status
+* Pointer to the inherited status variable.
+
+*/
+
+/* Local Variables: */
+ double **ptr;
+ double *newx;
+ double *newy;
+ double *nx;
+ double *ny;
+ double *oldx;
+ double *oldy;
+ double *ox;
+ double *oy;
+ double *px;
+ double *py;
+ double *qx;
+ double *qy;
+ double a;
+ double b;
+ double sx;
+ double sy;
+ int half_width;
+ int i;
+ int nv;
+ int top;
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Get the number of vertices. */
+ nv = astGetNpoint( pset );
+
+/* Get pointers to arrays holding the supplied vertex positions. */
+ ptr = astGetPoints( pset );
+ oldx = ptr[ 0 ];
+ oldy = ptr[ 1 ];
+
+/* Allocate arrays to hold the returned vertex positions. */
+ newx = astMalloc( nv*sizeof( double ) );
+ newy = astMalloc( nv*sizeof( double ) );
+
+/* Check these pointers can be used safely. */
+ if( astOK ) {
+
+/* Get weighting factors for the fully smoothed and unsmoothed positions. */
+ a = strength;
+ b = 1.0 - a;
+
+/* Ensure the box size is sufficiently small for there to be room for
+ two boxes along the polygon. */
+ half_width = nv/4 - 1;
+ if( boxsize < half_width ) half_width = boxsize;
+ if( half_width < 1 ) half_width = 1;
+
+/* Modify the weight for the fully smoothed position to include the
+ normalisation factor needed to account for the box width. */
+ a /= 2*half_width + 1;
+
+/* Find the sum of the x and y coordinates within a box centred on the
+ first vertex. This includes vertices from the end of the polygon. */
+ px = oldx + 1;
+ qx = oldx + nv;
+ sx = (oldx)[ 0 ];
+
+ py = oldy + 1;
+ qy = oldy + nv;
+ sy = (oldy)[ 0 ];
+
+ for( i = 0; i < half_width; i++ ) {
+ sx += *(px++) + *(--qx);
+ sy += *(py++) + *(--qy);
+ }
+
+/* Replacing vertices within the first half box will include vertices at
+ both ends of the polygon. Set up the pointers accordingly, and then
+ find replacements for each vertex in the first half box.*/
+ ox = oldx;
+ oy = oldy;
+ nx = newx;
+ ny = newy;
+ for( i = 0; i < half_width; i++ ) {
+
+/* Form the new vertex (x,y) values as the weighted mean of the mean
+ (x,y) values in the box, and the old (x,y) values. */
+ *(nx++) = a*sx + b*( *(ox++) );
+ *(ny++) = a*sy + b*( *(oy++) );
+
+/* Add in the next vertex X and Y axis values to the running sums, and
+ remove the position that has now passed out of the box. */
+ sx += *(px++) - *(qx++);
+ sy += *(py++) - *(qy++);
+ }
+
+/* Adjust the pointer for the rest of the polygon, up to one half box away
+ from the end. In this section, the smoothing box does not touch either
+ end of the polygon. */
+ top = nv - half_width - 1;
+ qx = oldx;
+ qy = oldy;
+ for( ; i < top; i++ ){
+
+/* Form the new vertex (x,y) values as the weighted mean of the mean
+ (x,y) values in the box, and the old (x,y) values. */
+ *(nx++) = a*sx + b*( *(ox++) );
+ *(ny++) = a*sy + b*( *(oy++) );
+
+/* Add in the next vertex X and Y axis values to the running sums, and
+ remove the position that has now passed out of the box. */
+ sx += *(px++) - *(qx++);
+ sy += *(py++) - *(qy++);
+ }
+
+/* Now do the last half box (which includes vertices from the start of
+ the polygon). */
+ top = nv;
+ px = oldx;
+ py = oldy;
+ for( ; i < top; i++ ){
+ *(nx++) = a*sx + b*( *(ox++) );
+ *(ny++) = a*sy + b*( *(oy++) );
+ sx += *(px++) - *(qx++);
+ sy += *(py++) - *(qy++);
+ }
+
+/* Replace the data points in the PointSet. */
+ ptr[ 0 ] = newx;
+ ptr[ 1 ] = newy;
+ oldx = astFree( oldx );
+ oldy = astFree( oldy );
+ }
+}
+
+static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) {
+/*
+* Name:
+* TestAttrib
+
+* Purpose:
+* Test if a specified attribute value is set for a Polygon.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "polygon.h"
+* int TestAttrib( AstObject *this, const char *attrib, int *status )
+
+* Class Membership:
+* Polygon member function (over-rides the astTestAttrib protected
+* method inherited from the Region class).
+
+* Description:
+* This function returns a boolean result (0 or 1) to indicate whether
+* a value has been set for one of a Polygon's attributes.
+
+* Parameters:
+* this
+* Pointer to the Polygon.
+* attrib
+* Pointer to a null terminated string specifying the attribute
+* name. This should be in lower case with no surrounding white
+* space.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* One if a value has been set, otherwise zero.
+
+* Notes:
+* - A value of zero will be returned if this function is invoked
+* with the global status set, or if it should fail for any reason.
+*/
+
+/* Local Variables: */
+ AstPolygon *this; /* Pointer to the Polygon structure */
+ int result; /* Result value to return */
+
+/* Initialise. */
+ result = 0;
+
+/* Check the global error status. */
+ if ( !astOK ) return result;
+
+/* Obtain a pointer to the Polygon structure. */
+ this = (AstPolygon *) this_object;
+
+/* Check the attribute name and test the appropriate attribute. */
+
+/* SimpVertices. */
+/* ------------- */
+ if ( !strcmp( attrib, "simpvertices" ) ) {
+ result = astTestSimpVertices( this );
+
+/* If the attribute is not recognised, pass it on to the parent method
+ for further interpretation. */
+ } else {
+ result = (*parent_testattrib)( this_object, attrib, status );
+ }
+
+/* Return the result, */
+ return result;
+}
+
+/*
+* Name:
+* TraceEdge
+
+* Purpose:
+* Find a point that is inside the required outline.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "polygon.h"
+* void TraceEdge<Oper><X>( <Xtype> value, const <Xtype> array[],
+* const int lbnd[ 2 ], const int ubnd[ 2 ],
+* int iv0, int ix0, int iy0, int starpix,
+* int full, int *status );
+
+* Class Membership:
+* Polygon member function
+
+* Description:
+* This function forms a polygon enclosing the region of the data
+* array specified by <Oper> and "value". If this polygon contains
+* the point "(inx,iny)", then a PointSet is returned holding the
+* pixel coordinates of the Polygon vertices. If the polygon
+* does not contain "(inx,iny)", a NULL pointer is returned.
+*
+* Each vertex in the polygon corresponds to a corner of a pixel in
+* the data array.
+
+* Parameters:
+* value
+* The data value defining valid pixels.
+* array
+* The data array.
+* lbnd
+* The lower pixel index bounds of the array.
+* ubnd
+* The upper pixel index bounds of the array.
+* iv0
+* The vector index of a pixel inside the region such that the
+* pixel to the right is NOT inside the region. This defines the
+* start of the polygon.
+* ix0
+* The X pixel index of the pixel specified by "iv0".
+* inx
+* The X pixel index of a point which must be inside the polygon
+* for the polygon to be acceptable.
+* iny
+* The Y pixel index of a point which must be inside the polygon
+* for the polygon to be acceptable.
+* starpix
+* If non-zero, the usual Starlink definition of pixel coordinate
+* is used (integral values at pixel corners). Otherwise, the
+* system used by other AST functions such as astResample is used
+* (integral values at pixel centres).
+* full
+* If non-zero, the full polygon is stored. If zero, vertices in the
+* middle of straight sections of the Polygon are omitted.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* A pointer to a PointSet holding the vertices of the polygon, or
+* NULL if the polygon did not contain "(inx,iny)".
+
+* Notes:
+* - <Oper> must be one of LT, LE, EQ, GE, GT, NE.
+
+
+*/
+
+/* Define a macro to implement the function for a specific data
+ type and operation. */
+#define MAKE_TRACEEDGE(X,Xtype,Oper,OperI) \
+static AstPointSet *TraceEdge##Oper##X( Xtype value, const Xtype array[], \
+ const int lbnd[ 2 ], const int ubnd[ 2 ], \
+ int iv0, int ix0, int iy0, \
+ int starpix, int full, \
+ int *status ){ \
+\
+/* Local Variables: */ \
+ AstPointSet *result; /* Pointer to text describing oper */ \
+ const Xtype *pa; /* Pointer to current valid pixel value */ \
+ const Xtype *pb; /* Pointer to neigbouring valid pixel value */ \
+ const Xtype *pc; /* Pointer to neigbouring valid pixel value */ \
+ double *ptr[ 2 ]; /* PointSet data pointers */ \
+ double *xvert; /* Pointer to array holding vertex X axis values */ \
+ double *yvert; /* Pointer to array holding vertex Y axis values */ \
+ double dx; /* Pertubation in X (pixels) to avoid the pixel edge */ \
+ double dy; /* Pertubation in Y (pixels) to avoid the pixel edge */ \
+ double xx; /* Pixel X coord at corner */ \
+ double yy; /* Pixel Y coord at corner */ \
+ int at; /* The pixel corner to draw to */ \
+ int done; /* Have we arrived back at the start of the polygon? */ \
+ int ii; /* Index of new vertex */ \
+ int ix; /* X pixel index of current valid pixel */ \
+ int iy; /* Y pixel index of current valid pixel */ \
+ int nright; /* Overall number of right hand turns along polygon */ \
+ int nvert; /* Number of vertices */ \
+ int nx; /* Pixels per row */ \
+\
+/* Initialise */ \
+ result = NULL; \
+\
+/* Check the global error status. */ \
+ if ( !astOK ) return result; \
+\
+\
+/* Initialise pointers to arrays holding the X and Y pixel coordinates at \
+ the vertices of the polygon. */ \
+ xvert = NULL; \
+ yvert = NULL; \
+ nvert = 0; \
+\
+/* Find number of pixels in one row of the array. */ \
+ nx = ( ubnd[ 0 ] - lbnd[ 0 ] + 1 ); \
+\
+/* The four corners of a pixel are numbered as follows: 0=bottom left, \
+ 1=top left, 2=top right, 3=bottom right. The following algorithm moves \
+ along pixel edges, from corner to corner, using the above numbering \
+ scheme to identify the corners. We start the polygon by moving from the \
+ bottom right to the top right corner of pixel "(ix0,iy0)". */ \
+ ix = ix0; \
+ iy = iy0; \
+ at = 2; \
+\
+/* Store a pointer to the first good pixel value. */ \
+ pa = array + ( ix - lbnd[ 0 ] ) + nx*( iy - lbnd[ 1 ] ) ; \
+\
+/* We count the number of times the polygon turns to the right compared \
+ to the left. Initialise it to zero. */ \
+ nright = 0; \
+\
+/* Loop round tracing out the polygon until we arrive back at the \
+ beginning. The Polygon class requires that the inside of the polygon \
+ is to the left as we move round the polygon in an anti-clockwise \
+ direction. So at each corner, we attempt to move to the next \
+ anti-clockwise good pixel corner. */ \
+ done = 0; \
+ while( !done ) { \
+\
+/* If we have arrived at the bottom left corner of the good pixel, we must \
+ have come from the top left corner since all movements around a pixel \
+ must be anti-clockwise. */ \
+ if( at == 0 ) { \
+\
+/* Note the pixel coordinates at the bottom left corner of the current \
+ pixel. */ \
+ if( starpix ) { \
+ xx = ix - 1.0; \
+ yy = iy - 1.0; \
+ } else { \
+ xx = ix - 0.5; \
+ yy = iy - 0.5; \
+ } \
+\
+/* Get a pointer to lower left pixel value */ \
+ pb = pa - nx - 1; \
+\
+/* Get a pointer to lower mid pixel value. */ \
+ pc = pb + 1; \
+\
+/* If the lower left pixel is within the array and meets the validity \
+ requirements, move to the left along its top edge. */ \
+ if( iy > lbnd[ 1 ] && ix > lbnd[ 0 ] && ISVALID(*pb,OperI,value) ) { \
+ nright++; \
+ pa = pb; \
+ at = 1; \
+ ix--; \
+ iy--; \
+ dx = DELTA; \
+ dy = -DELTA; \
+\
+/* Otherwise, if lower mid pixel is good, move down its left edge. */ \
+ } else if( iy > lbnd[ 1 ] && ISVALID(*pc,OperI,value) ) { \
+ pa = pc; \
+ at = 0; \
+ iy--; \
+ dx = DELTA; \
+ dy = 0.0; \
+\
+/* Otherwise, move to the right along the bottom edge of the current pixel. */ \
+ } else { \
+ nright--; \
+ at = 3; \
+ dx = DELTA; \
+ dy = DELTA; \
+ } \
+\
+/* If the polygon bends at this point, or if we will be smoothing the \
+ polygon, append the pixel coordinates at this pixel corner to the \
+ polygon. */ \
+ if( full || pa != pc ) ADD( xx, yy ); \
+\
+/* If we have arrived at the top left corner of the good pixel, we must \
+ have come from the top right corner. */ \
+ } else if( at == 1 ) { \
+\
+/* Note the pixel coordinates at the top left corner of the current \
+ pixel. */ \
+ if( starpix ) { \
+ xx = ix - 1.0; \
+ yy = iy; \
+ } else { \
+ xx = ix - 0.5; \
+ yy = iy + 0.5; \
+ } \
+\
+/* Get a pointer to upper left pixel value */ \
+ pb = pa + nx - 1; \
+\
+/* Get a pointer to mid left pixel value. */ \
+ pc = pa - 1; \
+\
+/* If upper left pixel is good, move up its left edge. */ \
+ if( iy < ubnd[ 1 ] && ix > lbnd[ 0 ] && ISVALID(*pb,OperI,value) ) { \
+ nright++; \
+ pa = pb; \
+ at = 2; \
+ ix--; \
+ iy++; \
+ dx = -DELTA; \
+ dy = -DELTA; \
+\
+/* Otherwise, if left mid pixel is good, move left along its top edge. */ \
+ } else if( ix > lbnd[ 0 ] && ISVALID(*pc,OperI,value) ) { \
+ pa = pc; \
+ at = 1; \
+ ix--; \
+ dx = 0.0; \
+ dy = -DELTA; \
+\
+/* Otherwise, move down the left edge of the current pixel. */ \
+ } else { \
+ nright--; \
+ at = 0; \
+ dx = DELTA; \
+ dy = -DELTA; \
+ } \
+\
+/* If the polygon bends at this point, or if we will be smoothing the \
+ polygon, append the pixel coordinates at this pixel corner to the \
+ polygon. */ \
+ if( full || pa != pc ) ADD( xx, yy ); \
+\
+/* If we have arrived at the top right corner of the good pixel, we must \
+ have come from the bottom right corner. */ \
+ } else if( at == 2 ) { \
+\
+/* Note the pixel coordinates at the top right corner of the current \
+ pixel. */ \
+ if( starpix ) { \
+ xx = ix; \
+ yy = iy; \
+ } else { \
+ xx = ix + 0.5; \
+ yy = iy + 0.5; \
+ } \
+\
+/* Pointer to upper right pixel value */ \
+ pb = pa + nx + 1; \
+\
+/* Pointer to top mid pixel value. */ \
+ pc = pa + nx; \
+\
+/* If upper right pixel is good, move right along its bottom edge. */ \
+ if( iy < ubnd[ 1 ] && ix < ubnd[ 0 ] && ISVALID(*pb,OperI,value) ){ \
+ nright++; \
+ pa = pb; \
+ at = 3; \
+ ix++; \
+ iy++; \
+ dx = -DELTA; \
+ dy = DELTA; \
+\
+/* Otherwise, if upper mid pixel is good, move up its right edge. */ \
+ } else if( iy < ubnd[ 1 ] && ISVALID(*pc,OperI,value) ) { \
+ pa = pc; \
+ at = 2; \
+ iy++; \
+ dx = -DELTA; \
+ dy = 0.0; \
+\
+/* Otherwise, move left along the top edge of the current pixel. */ \
+ } else { \
+ nright--; \
+ at = 1; \
+ dx = -DELTA; \
+ dy = -DELTA; \
+ } \
+\
+/* If the polygon bends at this point, or if we will be smoothing the \
+ polygon, append the pixel coordinates at this pixel corner to the \
+ polygon. */ \
+ if( full || pa != pc ) ADD( xx, yy ); \
+\
+/* Arrived at bottom right corner of good pixel from lower left. */ \
+ } else { \
+\
+/* Note the pixel coordinates at the bottom right corner of the current \
+ pixel. */ \
+ if( starpix ) { \
+ xx = ix; \
+ yy = iy - 1.0; \
+ } else { \
+ xx = ix + 0.5; \
+ yy = iy - 0.5; \
+ } \
+\
+/* Pointer to lower right pixel value */ \
+ pb = pa - ( nx - 1 ); \
+\
+/* Pointer to mid right pixel value. */ \
+ pc = pa + 1; \
+\
+/* If lower right pixel is good, move down its left edge. */ \
+ if( iy > lbnd[ 1 ] && ix < ubnd[ 0 ] && ISVALID(*pb,OperI,value) ) { \
+ nright++; \
+ pa = pb; \
+ at = 0; \
+ ix++; \
+ iy--; \
+ dx = DELTA; \
+ dy = DELTA; \
+\
+/* Otherwise, if right mid pixel is good, move right along its lower edge. */ \
+ } else if( ix < ubnd[ 0 ] && ISVALID(*pc,OperI,value) ) { \
+ pa = pc; \
+ at = 3; \
+ ix++; \
+ dx = 0.0; \
+ dy = DELTA; \
+\
+/* Otherwise, move up the right edge of the current pixel. */ \
+ } else { \
+ nright--; \
+ at = 2; \
+ dx = -DELTA; \
+ dy = DELTA; \
+ } \
+\
+/* If the polygon bends at this point, or if we will be smoothing the \
+ polygon, append the pixel coordinates at this pixel corner to the \
+ polygon. */ \
+ if( full || pa != pc ) ADD( xx, yy ); \
+ } \
+\
+/* If we have arrived back at the start, break out of the loop. */ \
+ if( ix == ix0 && iy == iy0 && at == 2 ) done = 1; \
+ } \
+\
+/* If we have circled round to the right, the polygon will not enclosed \
+ the specified position, so free resources and return a NULL pointer. */ \
+ if( nright > 0 ) { \
+ xvert = astFree( xvert ); \
+ yvert = astFree( yvert ); \
+\
+/* If we have circled round to the left, the polygon will enclose \
+ the specified position, so create and return a PointSet. */ \
+ } else { \
+ result = astPointSet( nvert, 2, " ", status ); \
+ ptr[ 0 ] = xvert; \
+ ptr[ 1 ] = yvert; \
+ astSetPoints( result, ptr ); \
+ } \
+\
+/* Annul the returned PointSet if anythign went wrong. */ \
+ if( !astOK && result ) result = astAnnul( result ); \
+\
+/* Return the PointSet pointer. */ \
+ return result; \
+}
+
+/* Define a macro to add a vertex position to dynamically allocated
+ arrays of X and Y positions. We offset the supplied position by
+ a small fraction of a pixel towards the centre of hte polygon to
+ avoid placing vertices exactly on the edge, which may cause problems
+ later for pixels that are on the edge of an area of bad pixel. */
+#define ADD(X,Y) {\
+ ii = nvert++; \
+ xvert = (double *) astGrow( xvert, nvert, sizeof( double ) ); \
+ yvert = (double *) astGrow( yvert, nvert, sizeof( double ) ); \
+ if( astOK ) { \
+ xvert[ ii ] = (X+dx); \
+ yvert[ ii ] = (Y+dy); \
+ } \
+}
+
+/* Define a macro that uses the above macro to to create implementations
+ of TraceEdge for all operations. */
+#define MAKEALL_TRACEEDGE(X,Xtype) \
+MAKE_TRACEEDGE(X,Xtype,LT,AST__LT) \
+MAKE_TRACEEDGE(X,Xtype,LE,AST__LE) \
+MAKE_TRACEEDGE(X,Xtype,EQ,AST__EQ) \
+MAKE_TRACEEDGE(X,Xtype,NE,AST__NE) \
+MAKE_TRACEEDGE(X,Xtype,GE,AST__GE) \
+MAKE_TRACEEDGE(X,Xtype,GT,AST__GT)
+
+/* Expand the above macro to generate a function for each required
+ data type and operation. */
+#if HAVE_LONG_DOUBLE /* Not normally implemented */
+MAKEALL_TRACEEDGE(LD,long double)
+#endif
+MAKEALL_TRACEEDGE(D,double)
+MAKEALL_TRACEEDGE(L,long int)
+MAKEALL_TRACEEDGE(UL,unsigned long int)
+MAKEALL_TRACEEDGE(I,int)
+MAKEALL_TRACEEDGE(UI,unsigned int)
+MAKEALL_TRACEEDGE(S,short int)
+MAKEALL_TRACEEDGE(US,unsigned short int)
+MAKEALL_TRACEEDGE(B,signed char)
+MAKEALL_TRACEEDGE(UB,unsigned char)
+MAKEALL_TRACEEDGE(F,float)
+
+/* Undefine the macros. */
+#undef MAKE_TRACEEDGE
+#undef MAKEALL_TRACEEDGE
+
+static AstPointSet *Transform( AstMapping *this_mapping, AstPointSet *in,
+ int forward, AstPointSet *out, int *status ) {
+/*
+* Name:
+* Transform
+
+* Purpose:
+* Apply a Polygon to transform a set of points.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "polygon.h"
+* AstPointSet *Transform( AstMapping *this, AstPointSet *in,
+* int forward, AstPointSet *out, int *status )
+
+* Class Membership:
+* Polygon member function (over-rides the astTransform protected
+* method inherited from the Region class).
+
+* Description:
+* This function takes a Polygon and a set of points encapsulated in a
+* PointSet and transforms the points by setting axis values to
+* AST__BAD for all points which are outside the region. Points inside
+* the region are copied unchanged from input to output.
+
+* Parameters:
+* this
+* Pointer to the Polygon.
+* in
+* Pointer to the PointSet holding the input coordinate data.
+* forward
+* A non-zero value indicates that the forward coordinate transformation
+* should be applied, while a zero value requests the inverse
+* transformation.
+* out
+* Pointer to a PointSet which will hold the transformed (output)
+* coordinate values. A NULL value may also be given, in which case a
+* new PointSet will be created by this function.
+* status
+* Pointer to the inherited status variable.
+
+* Returned Value:
+* Pointer to the output (possibly new) PointSet.
+
+* Notes:
+* - The forward and inverse transformations are identical for a
+* Region.
+* - A null pointer will be returned if this function is invoked with the
+* global error status set, or if it should fail for any reason.
+* - The number of coordinate values per point in the input PointSet must
+* match the number of axes in the Frame represented by the Polygon.
+* - If an output PointSet is supplied, it must have space for sufficient
+* number of points and coordinate values per point to accommodate the
+* result. Any excess space will be ignored.
+*/
+
+/* Local Variables: */
+ AstFrame *frm; /* Pointer to base Frame in FrameSet */
+ AstLineDef *a; /* Line from inside point to test point */
+ AstLineDef *b; /* Polygon edge */
+ AstPointSet *in_base; /* PointSet holding base Frame input positions*/
+ AstPointSet *result; /* Pointer to output PointSet */
+ AstPolygon *this; /* Pointer to Polygon */
+ double **ptr_in; /* Pointer to input base Frame coordinate data */
+ double **ptr_out; /* Pointer to output current Frame coordinate data */
+ double *px; /* Pointer to array of first axis values */
+ double *py; /* Pointer to array of second axis values */
+ double p[ 2 ]; /* Current test position */
+ int closed; /* Is the boundary part of the Region? */
+ int i; /* Edge index */
+ int icoord; /* Coordinate index */
+ int in_region; /* Is the point inside the Region? */
+ int ncoord_out; /* No. of current Frame axes */
+ int ncross; /* Number of crossings */
+ int neg; /* Has the Region been negated? */
+ int npoint; /* No. of input points */
+ int nv; /* No. of vertices */
+ int point; /* Loop counter for input points */
+ int pos; /* Is test position in, on, or outside boundary? */
+
+/* Check the global error status. */
+ if ( !astOK ) return NULL;
+
+/* Obtain a pointer to the Polygon structure. */
+ this = (AstPolygon *) this_mapping;
+
+/* Apply the parent mapping using the stored pointer to the Transform member
+ function inherited from the parent Region class. This function validates
+ all arguments and generates an output PointSet if necessary,
+ containing a copy of the input PointSet. */
+ result = (*parent_transform)( this_mapping, in, forward, out, status );
+
+/* Get the number of points to be transformed. */
+ npoint = astGetNpoint( result );
+
+/* Get a pointer to the output axis values. */
+ ptr_out = astGetPoints( result );
+
+/* Find the number of axes in the current Frame. This need not be 2 (the
+ number of axes in the *base* Frame must be 2 however). */
+ ncoord_out = astGetNcoord( result );
+
+/* We will now extend the parent astTransform method by performing the
+ calculations needed to generate the output coordinate values. */
+
+/* First use the encapsulated FrameSet to transform the supplied positions
+ from the current Frame in the encapsulated FrameSet (the Frame
+ represented by the Region), to the base Frame (the Frame in which the
+ Region is defined). This call also returns a pointer to the base Frame
+ of the encapsulated FrameSet. Note, the returned pointer may be a
+ clone of the "in" pointer, and so we must be carefull not to modify the
+ contents of the returned PointSet. */
+ in_base = astRegTransform( this, in, 0, NULL, &frm );
+ ptr_in = astGetPoints( in_base );
+
+/* Get the number of vertices in the polygon. */
+ nv = astGetNpoint( ((AstRegion *) this)->points );
+
+/* See if the boundary is part of the Region. */
+ closed = astGetClosed( this );
+
+/* See if the Region has been negated. */
+ neg = astGetNegated( this );
+
+/* Perform coordinate arithmetic. */
+/* ------------------------------ */
+ if ( astOK ) {
+ px = ptr_in[ 0 ];
+ py = ptr_in[ 1 ];
+
+/* Loop round each supplied point in the base Frame of the polygon. */
+ for ( point = 0; point < npoint; point++, px++, py++ ) {
+
+/* If the input point is bad, indicate that bad output values should be
+ returned. */
+ if( *px == AST__BAD || *py == AST__BAD ) {
+ in_region = 0;
+
+/* Otherwise, we first determine if the point is inside, outside, or on,
+ the Polygon boundary. Initialially it is unknown. */
+ } else {
+
+/* Ensure cached information is available.*/
+ Cache( this, status );
+
+/* Create a definition of the line from a point which is inside the
+ polygon to the supplied point. This is a structure which includes
+ cached intermediate information which can be used to speed up
+ subsequent calculations. */
+ p[ 0 ] = *px;
+ p[ 1 ] = *py;
+ a = astLineDef( frm, this->in, p );
+
+/* We now determine the number of times this line crosses the polygon
+ boundary. Initialise the number of crossings to zero. */
+ ncross = 0;
+ pos = UNKNOWN;
+
+/* Loop rouind all edges of the polygon. */
+ for( i = 0; i < nv; i++ ) {
+ b = this->edges[ i ];
+
+/* If this point is on the current edge, then we need do no more checks
+ since we know it is either inside or outside the polygon (depending on
+ whether the polygon is closed or not). */
+ if( astLineContains( frm, b, 0, p ) ) {
+ pos = ON;
+ break;
+
+/* Otherwise, see if the two lines cross within their extent. If so,
+ increment the number of crossings. */
+ } else if( astLineCrossing( frm, b, a, NULL ) ) {
+ ncross++;
+ }
+ }
+
+/* Free resources */
+ a = astFree( a );
+
+/* If the position is not on the boundary, it is inside the boundary if
+ the number of crossings is even, and outside otherwise. */
+ if( pos == UNKNOWN ) pos = ( ncross % 2 == 0 )? IN : OUT;
+
+/* Whether the point is in the Region depends on whether the point is
+ inside the polygon boundary, whether the Polygon has been negated, and
+ whether the polygon is closed. */
+ if( neg ) {
+ if( pos == IN ) {
+ in_region = 0;
+ } else if( pos == OUT ) {
+ in_region = 1;
+ } else if( closed ) {
+ in_region = 1;
+ } else {
+ in_region = 0;
+ }
+
+ } else {
+ if( pos == IN ) {
+ in_region = 1;
+ } else if( pos == OUT ) {
+ in_region = 0;
+ } else if( closed ) {
+ in_region = 1;
+ } else {
+ in_region = 0;
+ }
+ }
+ }
+
+/* If the point is not inside the Region, store bad output values. */
+ if( !in_region ) {
+ for ( icoord = 0; icoord < ncoord_out; icoord++ ) {
+ ptr_out[ icoord ][ point ] = AST__BAD;
+ }
+ }
+ }
+ }
+
+/* Free resources */
+ in_base = astAnnul( in_base );
+ frm = astAnnul( frm );
+
+/* Annul the result if an error has occurred. */
+ if( !astOK ) result = astAnnul( result );
+
+/* Return a pointer to the output PointSet. */
+ return result;
+}
+
+/* Functions which access class attributes. */
+/* ---------------------------------------- */
+/* Implement member functions to access the attributes associated with
+ this class using the macros defined for this purpose in the
+ "object.h" file. For a description of each attribute, see the class
+ interface (in the associated .h file). */
+
+/*
+*att++
+* Name:
+* SimpVertices
+
+* Purpose:
+* Simplify a Polygon by transforming its vertices?
+
+* Type:
+* Public attribute.
+
+* Synopsis:
+* Integer (boolean).
+
+* Description:
+* This attribute controls the behaviour of the
+c astSimplify
+f AST_SIMPLIFY
+* method when applied to a Polygon. The simplified Polygon is created
+* by transforming the vertices from the Frame in which the Polygon
+* was originally defined into the Frame currently represented by the
+* Polygon. If SimpVertices is non-zero (the default) then this
+* simplified Polygon is returned without further checks. If SimpVertices
+* is zero, a check is made that the edges of the new Polygon do not
+* depart significantly from the edges of the original Polygon (as
+* determined by the uncertainty associated with the Polygon). This
+* could occur, for instance, if the Mapping frrm the original to the
+* current Frame is highly non-linear. If this check fails, the
+* original unsimplified Polygon is returned without change.
+
+* Applicability:
+* Polygon
+* All Polygons have this attribute.
+
+*att--
+*/
+astMAKE_CLEAR(Polygon,SimpVertices,simp_vertices,-INT_MAX)
+astMAKE_GET(Polygon,SimpVertices,int,0,( ( this->simp_vertices != -INT_MAX ) ?
+ this->simp_vertices : 1 ))
+astMAKE_SET(Polygon,SimpVertices,int,simp_vertices,( value != 0 ))
+astMAKE_TEST(Polygon,SimpVertices,( this->simp_vertices != -INT_MAX ))
+
+/* Copy constructor. */
+/* ----------------- */
+static void Copy( const AstObject *objin, AstObject *objout, int *status ) {
+/*
+* Name:
+* Copy
+
+* Purpose:
+* Copy constructor for Polygon objects.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* void Copy( const AstObject *objin, AstObject *objout, int *status )
+
+* Description:
+* This function implements the copy constructor for Polygon objects.
+
+* Parameters:
+* objin
+* Pointer to the object to be copied.
+* objout
+* Pointer to the object being constructed.
+* status
+* Pointer to the inherited status variable.
+
+* Notes:
+* - This constructor makes a deep copy.
+*/
+
+/* Local Variables: */
+ AstPolygon *out; /* Pointer to output Polygon */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Obtain pointers to the output Polygon. */
+ out = (AstPolygon *) objout;
+
+/* For safety, first clear any references to the input memory from
+ the output Polygon. */
+ out->edges = NULL;
+ out->startsat = NULL;
+
+/* Indicate cached information needs nre-calculating. */
+ astResetCache( (AstPolygon *) out );
+}
+
+
+/* Destructor. */
+/* ----------- */
+static void Delete( AstObject *obj, int *status ) {
+/*
+* Name:
+* Delete
+
+* Purpose:
+* Destructor for Polygon objects.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* void Delete( AstObject *obj, int *status )
+
+* Description:
+* This function implements the destructor for Polygon objects.
+
+* Parameters:
+* obj
+* Pointer to the object to be deleted.
+* status
+* Pointer to the inherited status variable.
+
+* Notes:
+* This function attempts to execute even if the global error status is
+* set.
+*/
+
+/* Local Variables: */
+ AstPointSet *ps; /* Pointer to PointSet inside Region */
+ AstPolygon *this; /* Pointer to Polygon */
+ int i; /* Index of vertex */
+ int istat; /* Original AST error status */
+ int nv; /* Number of vertices */
+ int rep; /* Original error reporting state */
+
+/* Obtain a pointer to the Polygon structure. */
+ this = (AstPolygon *) obj;
+
+/* Annul all resources. */
+ ps = ((AstRegion *) this)->points;
+ if( this->edges && ps ) {
+
+/* Ensure we get a value for "nv" even if an error has occurred. */
+ istat = astStatus;
+ astClearStatus;
+ rep = astReporting( 0 );
+
+ nv = astGetNpoint( ps );
+
+ astSetStatus( istat );
+ astReporting( rep );
+
+/* Free the structures holding the edge information. */
+ for( i = 0; i < nv; i++ ) {
+ this->edges[ i ] = astFree( this->edges[ i ] );
+ }
+ }
+
+ this->edges = astFree( this->edges );
+ this->startsat = astFree( this->startsat );
+}
+
+/* Dump function. */
+/* -------------- */
+static void Dump( AstObject *this_object, AstChannel *channel, int *status ) {
+/*
+* Name:
+* Dump
+
+* Purpose:
+* Dump function for Polygon objects.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* void Dump( AstObject *this, AstChannel *channel, int *status )
+
+* Description:
+* This function implements the Dump function which writes out data
+* for the Polygon class to an output Channel.
+
+* Parameters:
+* this
+* Pointer to the Polygon whose data are being written.
+* channel
+* Pointer to the Channel to which the data are being written.
+* status
+* Pointer to the inherited status variable.
+*/
+
+/* Local Variables: */
+ AstPolygon *this; /* Pointer to the Polygon structure */
+ int ival; /* Integer attribute value */
+ int set; /* Attribute value set? */
+
+/* Check the global error status. */
+ if ( !astOK ) return;
+
+/* Obtain a pointer to the Polygon structure. */
+ this = (AstPolygon *) this_object;
+
+/* Write out values representing the instance variables for the
+ Polygon class. Accompany these with appropriate comment strings,
+ possibly depending on the values being written.*/
+
+/* In the case of attributes, we first use the appropriate (private)
+ Test... member function to see if they are set. If so, we then use
+ the (private) Get... function to obtain the value to be written
+ out.
+
+ For attributes which are not set, we use the astGet... method to
+ obtain the value instead. This will supply a default value
+ (possibly provided by a derived class which over-rides this method)
+ which is more useful to a human reader as it corresponds to the
+ actual default attribute value. Since "set" will be zero, these
+ values are for information only and will not be read back. */
+
+/* SimpVertices. */
+/* ------------ */
+/* Write out the forward-inverse simplification flag. */
+ set = TestSimpVertices( this, status );
+ ival = set ? GetSimpVertices( this, status ) : astGetSimpVertices( this );
+ astWriteInt( channel, "SimpVT", set, 0, ival, "Simplify by transforming vertices?" );
+
+/* A flag indicating the convention used for determining the interior of
+ the polygon. A zero value indicates that the old AST system is in
+ use (inside to the left when moving anti-clockwise round the vertices
+ as viewed from the outside of the celestial sphere). A non-zero value
+ indicates the STC system is in use (inside to the left when moving
+ anti-clockwise round the vertices as viewed from the inside of the
+ celestial sphere). AST currently uses the STC system. */
+ astWriteInt( channel, "Order", 1, 0, 1, "Polygon uses STC vertex order convention" );
+}
+
+/* Standard class functions. */
+/* ========================= */
+/* Implement the astIsAPolygon and astCheckPolygon functions using the macros
+ defined for this purpose in the "object.h" header file. */
+astMAKE_ISA(Polygon,Region)
+astMAKE_CHECK(Polygon)
+
+AstPolygon *astPolygon_( void *frame_void, int npnt, int dim,
+ const double *points, AstRegion *unc,
+ const char *options, int *status, ...) {
+/*
+*++
+* Name:
+c astPolygon
+f AST_POLYGON
+
+* Purpose:
+* Create a Polygon.
+
+* Type:
+* Public function.
+
+* Synopsis:
+c #include "polygon.h"
+c AstPolygon *astPolygon( AstFrame *frame, int npnt, int dim,
+c const double *points, AstRegion *unc,
+c const char *options, ... )
+f RESULT = AST_POLYGON( FRAME, NPNT, DIM, POINTS, UNC, OPTIONS, STATUS )
+
+* Class Membership:
+* Polygon constructor.
+
+* Description:
+* This function creates a new Polygon object and optionally initialises
+* its attributes.
+*
+* The Polygon class implements a polygonal area, defined by a
+* collection of vertices, within a 2-dimensional Frame. The vertices
+* are connected together by geodesic curves within the encapsulated Frame.
+* For instance, if the encapsulated Frame is a simple Frame then the
+* geodesics will be straight lines, but if the Frame is a SkyFrame then
+* the geodesics will be great circles. Note, the vertices must be
+* supplied in an order such that the inside of the polygon is to the
+* left of the boundary as the vertices are traversed. Supplying them
+* in the reverse order will effectively negate the polygon.
+*
+* Within a SkyFrame, neighbouring vertices are always joined using the
+* shortest path. Thus if an edge of 180 degrees or more in length is
+* required, it should be split into section each of which is less
+* than 180 degrees. The closed path joining all the vertices in order
+* will divide the celestial sphere into two disjoint regions. The
+* inside of the polygon is the region which is circled in an
+* anti-clockwise manner (when viewed from the inside of the celestial
+* sphere) when moving through the list of vertices in the order in
+* which they were supplied when the Polygon was created (i.e. the
+* inside is to the left of the boundary when moving through the
+* vertices in the order supplied).
+
+* Parameters:
+c frame
+f FRAME = INTEGER (Given)
+* A pointer to the Frame in which the region is defined. It must
+* have exactly 2 axes. A deep copy is taken of the supplied Frame.
+* This means that any subsequent changes made to the Frame using the
+* supplied pointer will have no effect the Region.
+c npnt
+f NPNT = INTEGER (Given)
+* The number of points in the Region.
+c dim
+f DIM = INTEGER (Given)
+c The number of elements along the second dimension of the "points"
+f The number of elements along the first dimension of the POINTS
+* array (which contains the point coordinates). This value is
+* required so that the coordinate values can be correctly
+* located if they do not entirely fill this array. The value
+c given should not be less than "npnt".
+f given should not be less than NPNT.
+c points
+f POINTS( DIM, 2 ) = DOUBLE PRECISION (Given)
+c The address of the first element of a 2-dimensional array of
+c shape "[2][dim]" giving the physical coordinates of the vertices.
+c These should be stored such that the value of coordinate
+c number "coord" for point number "pnt" is found in element
+c "in[coord][pnt]".
+f A 2-dimensional array giving the physical coordinates of the
+f vertices. These should be stored such that the value of coordinate
+f number COORD for point number PNT is found in element IN(PNT,COORD).
+c unc
+f UNC = INTEGER (Given)
+* An optional pointer to an existing Region which specifies the
+* uncertainties associated with the boundary of the Polygon being created.
+* The uncertainty in any point on the boundary of the Polygon is found by
+* shifting the supplied "uncertainty" Region so that it is centred at
+* the boundary point being considered. The area covered by the
+* shifted uncertainty Region then represents the uncertainty in the
+* boundary position. The uncertainty is assumed to be the same for
+* all points.
+*
+* If supplied, the uncertainty Region must be of a class for which
+* all instances are centro-symetric (e.g. Box, Circle, Ellipse, etc.)
+* or be a Prism containing centro-symetric component Regions. A deep
+* copy of the supplied Region will be taken, so subsequent changes to
+* the uncertainty Region using the supplied pointer will have no
+* effect on the created Polygon. Alternatively,
+f a null Object pointer (AST__NULL)
+c a NULL Object pointer
+* may be supplied, in which case a default uncertainty is used
+* equivalent to a box 1.0E-6 of the size of the Polygon being created.
+*
+* The uncertainty Region has two uses: 1) when the
+c astOverlap
+f AST_OVERLAP
+* function compares two Regions for equality the uncertainty
+* Region is used to determine the tolerance on the comparison, and 2)
+* when a Region is mapped into a different coordinate system and
+* subsequently simplified (using
+c astSimplify),
+f AST_SIMPLIFY),
+* the uncertainties are used to determine if the transformed boundary
+* can be accurately represented by a specific shape of Region.
+c options
+f OPTIONS = CHARACTER * ( * ) (Given)
+c Pointer to a null-terminated string containing an optional
+c comma-separated list of attribute assignments to be used for
+c initialising the new Polygon. The syntax used is identical to
+c that for the astSet function and may include "printf" format
+c specifiers identified by "%" symbols in the normal way.
+f A character string containing an optional comma-separated
+f list of attribute assignments to be used for initialising the
+f new Polygon. The syntax used is identical to that for the
+f AST_SET routine.
+c ...
+c If the "options" string contains "%" format specifiers, then
+c an optional list of additional arguments may follow it in
+c order to supply values to be substituted for these
+c specifiers. The rules for supplying these are identical to
+c those for the astSet function (and for the C "printf"
+c function).
+f STATUS = INTEGER (Given and Returned)
+f The global status.
+
+* Returned Value:
+c astPolygon()
+f AST_POLYGON = INTEGER
+* A pointer to the new Polygon.
+
+* Notes:
+* - A null Object pointer (AST__NULL) will be returned if this
+c function is invoked with the AST error status set, or if it
+f function is invoked with STATUS set to an error value, or if it
+* should fail for any reason.
+
+* Status Handling:
+* The protected interface to this function includes an extra
+* parameter at the end of the parameter list descirbed above. This
+* parameter is a pointer to the integer inherited status
+* variable: "int *status".
+
+*--
+*/
+
+/* Local Variables: */
+ astDECLARE_GLOBALS /* Pointer to thread-specific global data */
+ AstFrame *frame; /* Pointer to Frame structure */
+ AstPolygon *new; /* Pointer to new Polygon */
+ va_list args; /* Variable argument list */
+
+/* Get a pointer to the thread specific global data structure. */
+ astGET_GLOBALS(NULL);
+
+/* Check the global status. */
+ if ( !astOK ) return NULL;
+
+/* Obtain and validate a pointer to the supplied Frame structure. */
+ frame = astCheckFrame( frame_void );
+
+/* Initialise the Polygon, allocating memory and initialising the
+ virtual function table as well if necessary. */
+ new = astInitPolygon( NULL, sizeof( AstPolygon ), !class_init,
+ &class_vtab, "Polygon", frame, npnt,
+ dim, points, unc );
+
+/* If successful, note that the virtual function table has been
+ initialised. */
+ if ( astOK ) {
+ class_init = 1;
+
+/* Obtain the variable argument list and pass it along with the options string
+ to the astVSet method to initialise the new Polygon's attributes. */
+ va_start( args, status );
+ astVSet( new, options, NULL, args );
+ va_end( args );
+
+/* If an error occurred, clean up by deleting the new object. */
+ if ( !astOK ) new = astDelete( new );
+ }
+
+/* Return a pointer to the new Polygon. */
+ return new;
+}
+
+AstPolygon *astPolygonId_( void *frame_void, int npnt, int dim,
+ const double *points, void *unc_void,
+ const char *options, ... ) {
+/*
+* Name:
+* astPolygonId_
+
+* Purpose:
+* Create a Polygon.
+
+* Type:
+* Private function.
+
+* Synopsis:
+* #include "polygon.h"
+* AstPolygon *astPolygonId_( void *frame_void, int npnt,
+* int dim, const double *points, void *unc_void,
+* const char *options, ... )
+
+* Class Membership:
+* Polygon constructor.
+
+* Description:
+* This function implements the external (public) interface to the
+* astPolygon constructor function. It returns an ID value (instead
+* of a true C pointer) to external users, and must be provided
+* because astPolygon_ has a variable argument list which cannot be
+* encapsulated in a macro (where this conversion would otherwise
+* occur).
+*
+* The variable argument list also prevents this function from
+* invoking astPolygon_ directly, so it must be a re-implementation
+* of it in all respects, except for the final conversion of the
+* result to an ID value.
+
+* Parameters:
+* As for astPolygon_.
+
+* Returned Value:
+* The ID value associated with the new Polygon.
+*/
+
+/* Local Variables: */
+ astDECLARE_GLOBALS /* Pointer to thread-specific global data */
+ AstFrame *frame; /* Pointer to Frame structure */
+ AstPolygon *new; /* Pointer to new Polygon */
+ AstRegion *unc; /* Pointer to Region structure */
+ va_list args; /* Variable argument list */
+
+ int *status; /* Get a pointer to the thread specific global data structure. */
+ astGET_GLOBALS(NULL);
+
+/* Get a pointer to the inherited status value. */
+ status = astGetStatusPtr;
+
+/* Check the global status. */
+ if ( !astOK ) return NULL;
+
+/* Obtain a Frame pointer from the supplied ID and validate the
+ pointer to ensure it identifies a valid Frame. */
+ frame = astVerifyFrame( astMakePointer( frame_void ) );
+
+/* Obtain a Region pointer from the supplied "unc" ID and validate the
+ pointer to ensure it identifies a valid Region . */
+ unc = unc_void ? astVerifyRegion( astMakePointer( unc_void ) ) : NULL;
+
+/* Initialise the Polygon, allocating memory and initialising the
+ virtual function table as well if necessary. */
+ new = astInitPolygon( NULL, sizeof( AstPolygon ), !class_init,
+ &class_vtab, "Polygon", frame, npnt, dim,
+ points, unc );
+
+/* If successful, note that the virtual function table has been
+ initialised. */
+ if ( astOK ) {
+ class_init = 1;
+
+/* Obtain the variable argument list and pass it along with the options string
+ to the astVSet method to initialise the new Polygon's attributes. */
+ va_start( args, options );
+ astVSet( new, options, NULL, args );
+ va_end( args );
+
+/* If an error occurred, clean up by deleting the new object. */
+ if ( !astOK ) new = astDelete( new );
+ }
+
+/* Return an ID value for the new Polygon. */
+ return astMakeId( new );
+}
+
+
+AstPolygon *astInitPolygon_( void *mem, size_t size, int init, AstPolygonVtab *vtab,
+ const char *name, AstFrame *frame, int npnt,
+ int dim, const double *points, AstRegion *unc, int *status ) {
+/*
+*+
+* Name:
+* astInitPolygon
+
+* Purpose:
+* Initialise a Polygon.
+
+* Type:
+* Protected function.
+
+* Synopsis:
+* #include "polygon.h"
+* AstPolygon *astInitPolygon( void *mem, size_t size, int init, AstPolygonVtab *vtab,
+* const char *name, AstFrame *frame, int npnt,
+* int dim, const double *points, AstRegion *unc )
+
+* Class Membership:
+* Polygon initialiser.
+
+* Description:
+* This function is provided for use by class implementations to initialise
+* a new Polygon object. It allocates memory (if necessary) to accommodate
+* the Polygon plus any additional data associated with the derived class.
+* It then initialises a Polygon structure at the start of this memory. If
+* the "init" flag is set, it also initialises the contents of a virtual
+* function table for a Polygon at the start of the memory passed via the
+* "vtab" parameter.
+
+* Parameters:
+* mem
+* A pointer to the memory in which the Polygon is to be initialised.
+* This must be of sufficient size to accommodate the Polygon data
+* (sizeof(Polygon)) plus any data used by the derived class. If a value
+* of NULL is given, this function will allocate the memory itself using
+* the "size" parameter to determine its size.
+* size
+* The amount of memory used by the Polygon (plus derived class data).
+* This will be used to allocate memory if a value of NULL is given for
+* the "mem" parameter. This value is also stored in the Polygon
+* structure, so a valid value must be supplied even if not required for
+* allocating memory.
+* init
+* A logical flag indicating if the Polygon's virtual function table is
+* to be initialised. If this value is non-zero, the virtual function
+* table will be initialised by this function.
+* vtab
+* Pointer to the start of the virtual function table to be associated
+* with the new Polygon.
+* name
+* Pointer to a constant null-terminated character string which contains
+* the name of the class to which the new object belongs (it is this
+* pointer value that will subsequently be returned by the astGetClass
+* method).
+* frame
+* A pointer to the Frame in which the region is defined.
+* npnt
+* The number of points in the Region.
+* dim
+* The number of elements along the second dimension of the "points"
+* array (which contains the point coordinates). This value is
+* required so that the coordinate values can be correctly
+* located if they do not entirely fill this array. The value
+* given should not be less than "npnt".
+* points
+* The address of the first element of a 2-dimensional array of
+* shape "[2][dim]" giving the physical coordinates of the
+* points. These should be stored such that the value of coordinate
+* number "coord" for point number "pnt" is found in element
+* "in[coord][pnt]".
+* unc
+* A pointer to a Region which specifies the uncertainty in the
+* supplied positions (all points in the new Polygon being
+* initialised are assumed to have the same uncertainty). A NULL
+* pointer can be supplied, in which case default uncertainties equal
+* to 1.0E-6 of the dimensions of the new Polygon's bounding box are
+* used. If an uncertainty Region is supplied, it must be either a Box,
+* a Circle or an Ellipse, and its encapsulated Frame must be related
+* to the Frame supplied for parameter "frame" (i.e. astConvert
+* should be able to find a Mapping between them). Two positions
+* the "frame" Frame are considered to be co-incident if their
+* uncertainty Regions overlap. The centre of the supplied
+* uncertainty Region is immaterial since it will be re-centred on the
+* point being tested before use. A deep copy is taken of the supplied
+* Region.
+
+* Returned Value:
+* A pointer to the new Polygon.
+
+* Notes:
+* - A null pointer will be returned if this function is invoked with the
+* global error status set, or if it should fail for any reason.
+*-
+*/
+
+/* Local Variables: */
+ AstPolygon *new; /* Pointer to new Polygon */
+ AstPointSet *pset; /* Pointer to PointSet holding points */
+ const double *q; /* Pointer to next supplied axis value */
+ double **ptr; /* Pointer to data in pset */
+ double *p; /* Pointer to next PointSet axis value */
+ int i; /* Axis index */
+ int j; /* Point index */
+ int nin; /* No. of axes */
+
+/* Check the global status. */
+ if ( !astOK ) return NULL;
+
+/* If necessary, initialise the virtual function table. */
+ if ( init ) astInitPolygonVtab( vtab, name );
+
+/* Initialise. */
+ new = NULL;
+
+/* Check the number of axis values per position is correct. */
+ nin = astGetNaxes( frame );
+ if( nin != 2 ) {
+ astError( AST__BADIN, "astInitPolygon(%s): The supplied %s has %d "
+ "axes - polygons must have exactly 2 axes.", status, name,
+ astGetClass( frame ), nin );
+
+/* If so create a PointSet and store the supplied points in it. Check
+ none are bad. */
+ } else {
+ pset = astPointSet( npnt, 2, "", status );
+ ptr = astGetPoints( pset );
+ for( i = 0; i < 2 && astOK; i++ ) {
+ p = ptr[ i ];
+ q = points + i*dim;
+ for( j = 0; j < npnt; j++ ) {
+ if( (*(p++) = *(q++)) == AST__BAD ) {
+ astError( AST__BADIN, "astInitPolygon(%s): One or more "
+ "bad axis values supplied for the vertex "
+ "number %d.", status, name, j + 1 );
+ break;
+ }
+ }
+ }
+
+/* Initialise a Region structure (the parent class) as the first component
+ within the Polygon structure, allocating memory if necessary. */
+ new = (AstPolygon *) astInitRegion( mem, size, 0, (AstRegionVtab *) vtab,
+ name, frame, pset, unc );
+ if ( astOK ) {
+
+/* Initialise the Polygon data. */
+/* ------------------------------ */
+ new->lbnd[ 0 ] = AST__BAD;
+ new->ubnd[ 0 ] = AST__BAD;
+ new->lbnd[ 1 ] = AST__BAD;
+ new->ubnd[ 1 ] = AST__BAD;
+ new->simp_vertices = -INT_MAX;
+ new->edges = NULL;
+ new->startsat = NULL;
+ new->totlen = 0.0;
+ new->acw = 1;
+ new->stale = 1;
+
+/* Ensure the vertices are stored such that the unnegated Polygon
+ represents the inside of the polygon. */
+ EnsureInside( new, status );
+
+/* If an error occurred, clean up by deleting the new Polygon. */
+ if ( !astOK ) new = astDelete( new );
+ }
+
+/* Free resources. */
+ pset = astAnnul( pset );
+
+ }
+
+/* Return a pointer to the new Polygon. */
+ return new;
+}
+
+AstPolygon *astLoadPolygon_( void *mem, size_t size, AstPolygonVtab *vtab,
+ const char *name, AstChannel *channel, int *status ) {
+/*
+*+
+* Name:
+* astLoadPolygon
+
+* Purpose:
+* Load a Polygon.
+
+* Type:
+* Protected function.
+
+* Synopsis:
+* #include "polygon.h"
+* AstPolygon *astLoadPolygon( void *mem, size_t size, AstPolygonVtab *vtab,
+* const char *name, AstChannel *channel )
+
+* Class Membership:
+* Polygon loader.
+
+* Description:
+* This function is provided to load a new Polygon using data read
+* from a Channel. It first loads the data used by the parent class
+* (which allocates memory if necessary) and then initialises a
+* Polygon structure in this memory, using data read from the input
+* Channel.
+*
+* If the "init" flag is set, it also initialises the contents of a
+* virtual function table for a Polygon at the start of the memory
+* passed via the "vtab" parameter.
+
+* Parameters:
+* mem
+* A pointer to the memory into which the Polygon is to be
+* loaded. This must be of sufficient size to accommodate the
+* Polygon data (sizeof(Polygon)) plus any data used by derived
+* classes. If a value of NULL is given, this function will
+* allocate the memory itself using the "size" parameter to
+* determine its size.
+* size
+* The amount of memory used by the Polygon (plus derived class
+* data). This will be used to allocate memory if a value of
+* NULL is given for the "mem" parameter. This value is also
+* stored in the Polygon structure, so a valid value must be
+* supplied even if not required for allocating memory.
+*
+* If the "vtab" parameter is NULL, the "size" value is ignored
+* and sizeof(AstPolygon) is used instead.
+* vtab
+* Pointer to the start of the virtual function table to be
+* associated with the new Polygon. If this is NULL, a pointer
+* to the (static) virtual function table for the Polygon class
+* is used instead.
+* name
+* Pointer to a constant null-terminated character string which
+* contains the name of the class to which the new object
+* belongs (it is this pointer value that will subsequently be
+* returned by the astGetClass method).
+*
+* If the "vtab" parameter is NULL, the "name" value is ignored
+* and a pointer to the string "Polygon" is used instead.
+
+* Returned Value:
+* A pointer to the new Polygon.
+
+* Notes:
+* - A null pointer will be returned if this function is invoked
+* with the global error status set, or if it should fail for any
+* reason.
+*-
+*/
+
+/* Local Variables: */
+ astDECLARE_GLOBALS /* Pointer to thread-specific global data */
+ AstPolygon *new; /* Pointer to the new Polygon */
+ int order; /* Is the new (STC) order convention used? */
+
+/* Initialise. */
+ new = NULL;
+
+/* Check the global error status. */
+ if ( !astOK ) return new;
+
+/* Get a pointer to the thread specific global data structure. */
+ astGET_GLOBALS(channel);
+
+/* If a NULL virtual function table has been supplied, then this is
+ the first loader to be invoked for this Polygon. In this case the
+ Polygon belongs to this class, so supply appropriate values to be
+ passed to the parent class loader (and its parent, etc.). */
+ if ( !vtab ) {
+ size = sizeof( AstPolygon );
+ vtab = &class_vtab;
+ name = "Polygon";
+
+/* If required, initialise the virtual function table for this class. */
+ if ( !class_init ) {
+ astInitPolygonVtab( vtab, name );
+ class_init = 1;
+ }
+ }
+
+/* Invoke the parent class loader to load data for all the ancestral
+ classes of the current one, returning a pointer to the resulting
+ partly-built Polygon. */
+ new = astLoadRegion( mem, size, (AstRegionVtab *) vtab, name,
+ channel );
+
+ if ( astOK ) {
+
+/* Read input data. */
+/* ================ */
+/* Request the input Channel to read all the input data appropriate to
+ this class into the internal "values list". */
+ astReadClassData( channel, "Polygon" );
+
+/* Now read each individual data item from this list and use it to
+ initialise the appropriate instance variable(s) for this class. */
+
+/* In the case of attributes, we first read the "raw" input value,
+ supplying the "unset" value as the default. If a "set" value is
+ obtained, we then use the appropriate (private) Set... member
+ function to validate and set the value properly. */
+
+ new->simp_vertices = astReadInt( channel, "simpvt", -INT_MAX );
+ if ( TestSimpVertices( new, status ) ) SetSimpVertices( new, new->simp_vertices, status );
+
+/* A flag indicating what order the vertices are stored in. See the Dump
+ function. */
+ order = astReadInt( channel, "order", 0 );
+
+/* Initialise other class properties. */
+ new->lbnd[ 0 ] = AST__BAD;
+ new->ubnd[ 0 ] = AST__BAD;
+ new->lbnd[ 1 ] = AST__BAD;
+ new->ubnd[ 1 ] = AST__BAD;
+ new->edges = NULL;
+ new->startsat = NULL;
+ new->totlen = 0.0;
+ new->acw = 1;
+ new->stale = 1;
+
+/* If the order in which the vertices were written used the old AST
+ convention, negate the Polygon so that it is consistent with the
+ current conevtion (based on STC). */
+ if( ! order ) astNegate( new );
+
+/* Ensure the vertices are stored such that the unnegated Polygon
+ represents the inside of the polygon. */
+ EnsureInside( new, status );
+
+/* If an error occurred, clean up by deleting the new Polygon. */
+ if ( !astOK ) new = astDelete( new );
+ }
+
+/* Return the new Polygon pointer. */
+ return new;
+}
+
+/* Virtual function interfaces. */
+/* ============================ */
+/* These provide the external interface to the virtual functions defined by
+ this class. Each simply checks the global error status and then locates and
+ executes the appropriate member function, using the function pointer stored
+ in the object's virtual function table (this pointer is located using the
+ astMEMBER macro defined in "object.h").
+
+ Note that the member function may not be the one defined here, as it may
+ have been over-ridden by a derived class. However, it should still have the
+ same interface. */
+
+
+AstPolygon *astDownsize_( AstPolygon *this, double maxerr, int maxvert,
+ int *status ) {
+ if ( !astOK ) return NULL;
+ return (**astMEMBER(this,Polygon,Downsize))( this, maxerr, maxvert, status );
+}
+
+