diff options
Diffstat (limited to 'ast/cmpregion.c')
-rw-r--r-- | ast/cmpregion.c | 5127 |
1 files changed, 5127 insertions, 0 deletions
diff --git a/ast/cmpregion.c b/ast/cmpregion.c new file mode 100644 index 0000000..94f581d --- /dev/null +++ b/ast/cmpregion.c @@ -0,0 +1,5127 @@ +/* +*class++ +* Name: +* CmpRegion + +* Purpose: +* A combination of two regions within a single Frame + +* Constructor Function: +c astCmpRegion +f AST_CMPREGION + +* Description: +* A CmpRegion is a Region which allows two component +* Regions (of any class) to be combined to form a more complex +* Region. This combination may be performed a boolean AND, OR +* or XOR (exclusive OR) operator. If the AND operator is +* used, then a position is inside the CmpRegion only if it is +* inside both of its two component Regions. If the OR operator is +* used, then a position is inside the CmpRegion if it is inside +* either (or both) of its two component Regions. If the XOR operator +* is used, then a position is inside the CmpRegion if it is inside +* one but not both of its two component Regions. Other operators can +* be formed by negating one or both component Regions before using +* them to construct a new CmpRegion. +* +* The two component Region need not refer to the same coordinate +* Frame, but it must be possible for the +c astConvert +f AST_CONVERT +* function to determine a Mapping between them (an error will be +* reported otherwise when the CmpRegion is created). For instance, +* a CmpRegion may combine a Region defined within an ICRS SkyFrame +* with a Region defined within a Galactic SkyFrame. This is +* acceptable because the SkyFrame class knows how to convert between +* these two systems, and consequently the +c astConvert +f AST_CONVERT +* function will also be able to convert between them. In such cases, +* the second component Region will be mapped into the coordinate Frame +* of the first component Region, and the Frame represented by the +* CmpRegion as a whole will be the Frame of the first component Region. +* +* Since a CmpRegion is itself a Region, it can be used as a +* component in forming further CmpRegions. Regions of arbitrary +* complexity may be built from simple individual Regions in this +* way. + +* Inheritance: +* The CmpRegion class inherits from the Region class. + +* Attributes: +* The CmpRegion class does not define any new attributes beyond those +* which are applicable to all Regions. + +* Functions: +c The CmpRegion class does not define any new functions beyond those +f The CmpRegion class does not define any new routines beyond those +* which are applicable to all Regions. + +* 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: +* 7-OCT-2004 (DSB): +* Original version. +* 28-MAY-2007 (DSB): +* - Corrected RegBaseMesh. +* - In RegBaseBox, if the CmpRegion is bounded find the box by +* finding the extreme position sin a mesh covering the boundary. +* 20-JAN-2009 (DSB): +* Over-ride astRegBasePick. +* 19-MAR-2009 (DSB): +* Over-ride the astDecompose method. +* 8-SEP-2009 (DSB): +* Fix logic in RegTrace. +* 9-SEP-2009 (DSB): +* - Added astCmpRegionList +* - Added support for XOR +* - Override astGetObjSize. +* 27-APR-2012 (DSB): +* - Cache the bounded property. +* - Speed up plotting of CmpRegions by using the cached negation +* of a Region instead of setting the Regions's Negated flag (which +* causes the Region's cache to be cleared). +* 30-APR-2012 (DSB): +* Use geodesic distance to measure distances around the two component +* Regions when tracing the border. Previously, a distance normalised +* from zero to one was used for both component Regions, but this gives +* greater priority to Regions higher in the CmpRegion nesting order, +* resulting in a high chance that lower Regions will not be seen. +* 7-JUN-2012 (DSB): +* Override astRegSplit method. +* 21-NOV-2012 (DSB): +* Map the regions returned by RegSplit into the current Frame of the +* CmpRegion. +*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 CmpRegion + +/* 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" /* Regions (parent class) */ +#include "channel.h" /* I/O channels */ +#include "nullregion.h" /* Boundless Regions */ +#include "cmpregion.h" /* Interface definition for this class */ +#include "unitmap.h" /* Unit Mapings */ + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include <stdarg.h> +#include <stddef.h> +#include <string.h> +#include <stdio.h> +#include <limits.h> + +/* 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 AstRegion *(* parent_getdefunc)( AstRegion *, int * ); +static void (* parent_setregfs)( AstRegion *, AstFrame *, int * ); +static AstMapping *(* parent_simplify)( AstMapping *, int * ); +static int (* parent_equal)( AstObject *, AstObject *, int * ); +static void (* parent_setclosed)( AstRegion *, int, int * ); +static void (* parent_setmeshsize)( AstRegion *, int, int * ); +static void (* parent_clearclosed)( AstRegion *, int * ); +static void (* parent_clearmeshsize)( AstRegion *, int * ); +static double (*parent_getfillfactor)( AstRegion *, int * ); +static void (*parent_regsetattrib)( AstRegion *, const char *, char **, int * ); +static void (*parent_regclearattrib)( AstRegion *, const char *, char **, int * ); +static void (* parent_resetcache)( AstRegion *, int * ); +static int (* parent_getobjsize)( AstObject *, int * ); + +#if defined(THREAD_SAFE) +static int (* parent_managelock)( AstObject *, int, int, AstObject **, int * ); +#endif + + +#ifdef THREAD_SAFE +/* Define how to initialise thread-specific globals. */ +#define GLOBAL_inits \ + globals->Class_Init = 0; + +/* Create the function that initialises global data for this module. */ +astMAKE_INITGLOBALS(CmpRegion) + +/* Define macros for accessing each item of thread specific global data. */ +#define class_init astGLOBAL(CmpRegion,Class_Init) +#define class_vtab astGLOBAL(CmpRegion,Class_Vtab) + + +#include <pthread.h> + + +#else + + +/* Define the class virtual function table and its initialisation flag + as static variables. */ +static AstCmpRegionVtab 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. */ +AstCmpRegion *astCmpRegionId_( void *, void *, int, const char *, ... ); + +/* Prototypes for Private Member Functions. */ +/* ======================================== */ +static AstMapping *Simplify( AstMapping *, int * ); +static AstPointSet *RegBaseMesh( AstRegion *, int * ); +static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); +static AstRegion *GetDefUnc( AstRegion *, int * ); +static AstRegion *MatchRegion( AstRegion *, int, AstRegion *, const char *, int * ); +static AstRegion *RegBasePick( AstRegion *this, int, const int *, int * ); +static AstRegion **RegSplit( AstRegion *, int *, int * ); +static double GetFillFactor( AstRegion *, int * ); +static int CmpRegionList( AstCmpRegion *, int *, AstRegion ***, int * ); +static int Equal( AstObject *, AstObject *, int * ); +static int GetBounded( AstRegion *, int * ); +static int GetObjSize( AstObject *, int * ); +static int RegPins( AstRegion *, AstPointSet *, AstRegion *, int **, int * ); +static int RegTrace( AstRegion *, int, double *, double **, int * ); +static void ClearClosed( AstRegion *, int * ); +static void ClearMeshSize( AstRegion *, int * ); +static void Copy( const AstObject *, AstObject *, int * ); +static void Decompose( AstMapping *, AstMapping **, AstMapping **, int *, int *, int *, int * ); +static void Delete( AstObject *, int * ); +static void Dump( AstObject *, AstChannel *, int * ); +static void GetRegions( AstCmpRegion *, AstRegion **, AstRegion **, int *, int *, int *, int * ); +static void RegBaseBox( AstRegion *, double *, double *, int * ); +static void RegBaseBox2( AstRegion *, double *, double *, int * ); +static void RegClearAttrib( AstRegion *, const char *, char **, int * ); +static void RegSetAttrib( AstRegion *, const char *, char **, int * ); +static void ResetCache( AstRegion *this, int * ); +static void SetBreakInfo( AstCmpRegion *, int, int * ); +static void SetClosed( AstRegion *, int, int * ); +static void SetMeshSize( AstRegion *, int, int * ); +static void SetRegFS( AstRegion *, AstFrame *, int * ); +static void XORCheck( AstCmpRegion *, int * ); + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *, int, int, AstObject **, int * ); +#endif + + +/* Member functions. */ +/* ================= */ +int CmpRegionList( AstCmpRegion *this, int *nreg, AstRegion ***reg_list, + int *status ) { +/* +*+ +* Name: +* astCmpRegionList + +* Purpose: +* Decompose a CmpRegion into a sequence of simpler Regions. + +* Type: +* Protected virtual function. + +* Synopsis: +* #include "cmpregion.h" +* int astCmpRegionList( AstCmpRegion *this, int *nreg, +* AstRegion ***reg_list, int *status ) + +* Class Membership: +* CmpRegion method. + +* Description: +* This function decomposes a CmpRegion into a sequence of simpler +* Regions which may be applied in sequence to achieve the same +* effect. + +* Parameters: +* this +* Pointer to the CmpRegion to be decomposed (the CmpRegion is not +* actually modified by this function). +* nreg +* The address of an int which holds a count of the number of +* individual Regions in the decomposition. On entry, this +* should count the number of Regions already in the +* "*reg_list" array (below). On exit, it is updated to include +* any new Regions appended by this function. +* reg_list +* Address of a pointer to an array of Region pointers. On +* entry, this array pointer should either be NULL (if no +* Regions have yet been obtained) or should point at a +* dynamically allocated array containing Region pointers +* ("*nreg" in number) which have been obtained from a previous +* invocation of this function. +* +* On exit, the dynamic array will be enlarged to contain any +* new Region pointers that result from the decomposition +* requested. These pointers will be appended to any previously +* present, and the array pointer will be updated as necessary +* to refer to the enlarged array (any space released by the +* original array will be freed automatically). +* +* The new Region pointers returned will identify a sequence of +* Region which, when applied in order, will represent an area +* equivalent to that of the original Region. +* +* All the Region pointers returned by this function should be +* annulled by the caller, using astAnnul, when no longer +* required. The dynamic array holding these pointers should +* also be freed, using astFree. + +* Returned Value: +* An integer identifying the boolean operation that should be used to +* combine the Regions returned in "reg_list". This will be AST__AND +* or AST__OR. + +*- +*/ + +/* Local Variables: */ + AstCmpRegion *cmpreg; + int add; + int result; + +/* Check the global error status. */ + if ( !astOK ) return AST__AND; + +/* Check if this CmpRegion has an equivalent XOR representation. Is so, + store details of the XOR representation in the CmpRegion. */ + XORCheck( this, status ); + +/* The CmpRegion class only has full support for AND and OR operators. + However, it can also represent XOR operators, but it does this by + an equivalent set of AND and OR operators. When an XOR CmpRegion is + created, the original supplied argument regions are stored in + "this->xor1" and "this->xor2", and the component Regions placed in the + new CmpRegion are actually CmpRegions that implement the equivalent + of an XOR operation, using AND and OR operators. We want to hide this + to the outside world, so if the supplied CmpRegion represents an XOR + operation, add the XOR regions to the returned list, and return an + XOR operator. */ + if( this->xor1 ) { + *reg_list = astGrow( *reg_list, *nreg + 2, sizeof( AstRegion * ) ); + if( astOK ) { + ( *reg_list )[ (*nreg)++ ] = astClone( this->xor1 ); + ( *reg_list )[ (*nreg)++ ] = astClone( this->xor2 ); + } + result = AST__XOR; + +/* For AND and OR operators, we deal with the component Regions directly. */ + } else { + +/* If the first component of the supplied CmpRegion is itself a CmpRegion + that uses the same boolean operator as "this", call this function + recursively to add its component Regions to the returned list. */ + add = 1; + if( astIsACmpRegion( this->region1 ) ) { + cmpreg = (AstCmpRegion *) this->region1; + if( cmpreg->oper == this->oper ) { + (void) CmpRegionList( cmpreg, nreg, reg_list, status ); + add = 0; + } + } + +/* Otherwise, add the component Region directly into the returned list of + Regions. */ + if( add ) { + *reg_list = astGrow( *reg_list, *nreg + 1, sizeof( AstRegion * ) ); + if( astOK ) { + ( *reg_list )[ *nreg ] = astClone( this->region1 ); + ( *nreg )++; + } + } + +/* Do the same for the second component region */ + add = 1; + if( astIsACmpRegion( this->region2 ) ) { + cmpreg = (AstCmpRegion *) this->region2; + if( cmpreg->oper == this->oper ) { + (void) CmpRegionList( cmpreg, nreg, reg_list, status ); + add = 0; + } + } + + if( add ) { + *reg_list = astGrow( *reg_list, *nreg + 1, sizeof( AstRegion * ) ); + if( astOK ) { + ( *reg_list )[ *nreg ] = astClone( this->region2 ); + ( *nreg )++; + } + } + + result = this->oper; + } + +/* Return the boolean operator used to combine the regions in the + returned array. */ + return result; +} + +static void Decompose( AstMapping *this_mapping, AstMapping **map1, + AstMapping **map2, int *series, int *invert1, + int *invert2, int *status ) { +/* +* +* Name: +* Decompose + +* Purpose: +* Decompose a CmpRegion into two component Regions. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpregion.h" +* void Decompose( AstMapping *this, AstMapping **map1, +* AstMapping **map2, int *series, +* int *invert1, int *invert2, int *status ) + +* Class Membership: +* CmpRegion member function (over-rides the protected astDecompose +* method inherited from the Mapping class). + +* Description: +* This function returns pointers to two Mappings which, when applied +* either in series or parallel, are equivalent to the supplied Mapping. +* +* Since the Frame class inherits from the Mapping class, Frames can +* be considered as special types of Mappings and so this method can +* be used to decompose either CmpMaps, CmpFrames, CmpRegions or Prisms. + +* Parameters: +* this +* Pointer to the Mapping. +* map1 +* Address of a location to receive a pointer to first component +* Mapping. +* map2 +* Address of a location to receive a pointer to second component +* Mapping. +* series +* Address of a location to receive a value indicating if the +* component Mappings are applied in series or parallel. A non-zero +* value means that the supplied Mapping is equivalent to applying map1 +* followed by map2 in series. A zero value means that the supplied +* Mapping is equivalent to applying map1 to the lower numbered axes +* and map2 to the higher numbered axes, in parallel. +* invert1 +* The value of the Invert attribute to be used with map1. +* invert2 +* The value of the Invert attribute to be used with map2. +* status +* Pointer to the inherited status variable. + +* Notes: +* - Any changes made to the component rames using the returned +* pointers will be reflected in the supplied CmpFrame. + +*- +*/ + + +/* Local Variables: */ + AstCmpRegion *this; /* Pointer to CmpRegion structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the CmpMap structure. */ + this = (AstCmpRegion *) this_mapping; + +/* The components Frames of a CmpRegion are considered to be series + Mappings. */ + if( series ) *series = 1; + +/* The Frames are returned in their original order whether or not the + CmpRegion has been inverted. */ + if( map1 ) *map1 = astClone( this->region1 ); + if( map2 ) *map2 = astClone( this->region2 ); + +/* The invert flags dont mean anything for a Region, but we return them + anyway. If the CmpRegion has been inverted, return inverted Invert flags. */ + if( astGetInvert( this ) ) { + if( invert1 ) *invert1 = astGetInvert( this->region1 ) ? 0 : 1; + if( invert2 ) *invert2 = astGetInvert( this->region2 ) ? 0 : 1; + +/* If the CmpRegion has not been inverted, return the current Invert flags. */ + } else { + if( invert1 ) *invert1 = astGetInvert( this->region1 ); + if( invert2 ) *invert2 = astGetInvert( this->region2 ); + } +} + +static int Equal( AstObject *this_object, AstObject *that_object, int *status ) { +/* +* Name: +* Equal + +* Purpose: +* Test if two Objects are equivalent. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpregion.h" +* int Equal( AstObject *this_object, AstObject *that_object, int *status ) + +* Class Membership: +* CmpRegion member function (over-rides the astEqual protected +* method inherited from the Region class). + +* Description: +* This function returns a boolean result (0 or 1) to indicate whether +* two CmpRegions are equivalent. + +* Parameters: +* this +* Pointer to the first CmpRegion. +* that +* Pointer to the second CmpRegion. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* One if the CmpRegions are equivalent, zero otherwise. + +* Notes: +* - The CmpRegions are equivalent if their component Regions are +* equivalent and if they have the same boolean operation, negation +* and closed flags. +* - 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: */ + AstCmpRegion *that; + AstCmpRegion *this; + int result; + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Invoke the Equal method inherited from the parent Region class. This checks + that the Objects are both of the same class, and have the same Negated + and Closed flags (amongst other things). */ + if( (*parent_equal)( this_object, that_object, status ) ) { + +/* Obtain pointers to the two CmpRegion structures. */ + this = (AstCmpRegion *) this_object; + that = (AstCmpRegion *) that_object; + +/* Test their first component Regions for equality. */ + if( astEqual( this->region1, that->region1 ) ) { + +/* Test their second component Regions for equality. */ + if( astEqual( this->region2, that->region2 ) ) { + +/* Test their boolean operator for equality. */ + if( this->oper == that->oper ) result = 1; + } + } + } + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +/* +* Name: +* MAKE_SET + +* Purpose: +* Define a function to set an attribute value for a CmpRegion. + +* Type: +* Private macro. + +* Synopsis: +* #include "cmpregion.h" +* MAKE_SET(attribute,lattribute,type) + +* Class Membership: +* Defined by the CmpRegion class. + +* Description: +* This macro expands to an implementation of a private member function +* of the form: +* +* static void Set<Attribute>( AstRegion *this, <Type> value ) +* +* that sets the value of a specified Region attribute in the parent +* Region structure and also in the component Regions. + +* Parameters: +* attribute +* Name of the attribute, as it appears in the function name. +* lattribute +* Name of the attribute, all in lower case. +* type +* The C type of the attribute. +*/ + +/* Define the macro. */ +#define MAKE_SET(attribute,lattribute,type) \ +static void Set##attribute( AstRegion *this_region, type value, int *status ) { \ +\ +/* Local Variables: */ \ + AstCmpRegion *this; /* Pointer to the CmpRegion structure */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Use the parent method to set the value in the parent Region structure. */ \ + (*parent_set##lattribute)( this_region, value, status ); \ +\ +/* Also set the value in the two component Regions. */ \ + this = (AstCmpRegion *) this_region; \ + astSet##attribute( this->region1, value ); \ + astSet##attribute( this->region2, value ); \ +} + +/* Use the above macro to create accessors for the MeshSize and Closed attributes. */ +MAKE_SET(MeshSize,meshsize,int) +MAKE_SET(Closed,closed,int) + +/* Undefine the macro. */ +#undef MAKE_SET + +/* +* Name: +* MAKE_CLEAR + +* Purpose: +* Define a function to clear an attribute value for a CmpRegion. + +* Type: +* Private macro. + +* Synopsis: +* #include "cmpregion.h" +* MAKE_CLEAR(attribute,lattribute) + +* Class Membership: +* Defined by the CmpRegion class. + +* Description: +* This macro expands to an implementation of a private member function +* of the form: +* +* static void Clear<Attribute>( AstRegion *this ) +* +* that sets the value of a specified Region attribute in the parent +* Region structure and also in the component Regions. + +* Parameters: +* attribute +* Name of the attribute, as it appears in the function name. +* lattribute +* Name of the attribute, all in lower case. +*/ + +/* Define the macro. */ +#define MAKE_CLEAR(attribute,lattribute) \ +static void Clear##attribute( AstRegion *this_region, int *status ) { \ +\ +/* Local Variables: */ \ + AstCmpRegion *this; /* Pointer to the CmpRegion structure */ \ +\ +/* Check the global error status. */ \ + if ( !astOK ) return; \ +\ +/* Use the parent method to clear the value in the parent Region structure. */ \ + (*parent_clear##lattribute)( this_region, status ); \ +\ +/* Also clear the value in the two component Regions. */ \ + this = (AstCmpRegion *) this_region; \ + astClear##attribute( this->region1 ); \ + astClear##attribute( this->region2 ); \ +} + +/* Use the above macro to create accessors for the MeshSize and Closed attributes. */ +MAKE_CLEAR(MeshSize,meshsize) +MAKE_CLEAR(Closed,closed) + +/* Undefine the macro. */ +#undef MAKE_CLEAR + +static int GetBounded( AstRegion *this_region, int *status ) { +/* +* Name: +* GetBounded + +* Purpose: +* Is the Region bounded? + +* Type: +* Private function. + +* Synopsis: +* #include "cmpregion.h" +* int GetBounded( AstRegion *this, int *status ) + +* Class Membership: +* CmpRegion 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, Ellipse, 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: */ + AstCmpRegion *this; /* Pointer to CmpRegion structure */ + AstRegion *reg1; /* Pointer to first component Region */ + AstRegion *reg2; /* Pointer to second component Region */ + int neg1; /* Negated flag to use with first component */ + int neg2; /* Negated flag to use with second component */ + int oper; /* Combination operator */ + int overlap; /* Nature of overlap between components */ + int reg1b; /* Is the first component Region bounded?*/ + int reg2b; /* Is the second component Region bounded?*/ + int result; /* Returned result */ + +/* Initialise */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the CmpRegion structure. */ + this = (AstCmpRegion *) this_region; + +/* Only calculated a new value if there is no cached value in the Region. */ + if( this->bounded == -INT_MAX ) { + +/* Get the component Regions, how they should be combined, and the + Negated values which should be used with them. The returned values + take account of whether the supplied CmpRegion has itself been Negated + or not. The returned Regions represent regions within the base Frame + of the FrameSet encapsulated by the parent Region structure. */ + GetRegions( this, ®1, ®2, &oper, &neg1, &neg2, status ); + +/* If the first component Region does not have the required value for + its "Negated" attribute, use the negation of "reg1" in place of "reg1" + itself. */ + if( neg1 != astGetNegated( reg1 ) ) { + AstRegion *tmp = astGetNegation( reg1 ); + (void) astAnnul( reg1 ); + reg1 = tmp; + } + +/* If the second component Region does not have the required value for + its "Negated" attribute, use the negation of "reg2" in place of "reg2" + itself. */ + if( neg2 != astGetNegated( reg2 ) ) { + AstRegion *tmp = astGetNegation( reg2 ); + (void) astAnnul( reg2 ); + reg2 = tmp; + } + +/* See if either of the component Regions is bounded. */ + reg1b = astGetBounded( reg1 ); + reg2b = astGetBounded( reg2 ); + +/* If the regions are ANDed... */ + if( oper == AST__AND ) { + +/* If either one of the two components are bounded, then the AND region is + bounded. */ + if( reg1b || reg2b ) { + result = 1; + +/* If neither of the two components is bounded, then the AND region is + unbounded if there is partial or no overlap between them and is bounded + otherwise. */ + } else { + overlap = astOverlap( reg1, reg2 ); + if( overlap == 1 || overlap == 4 || overlap == 6 ) { + result = 0; + } else { + result = 1; + } + } + +/* If the regions are ORed... */ + } else { + +/* If either one of the two components is unbounded, then the OR region is + unbounded. */ + if( !reg1b || !reg2b ) { + result = 0; + +/* If both of the two components are bounded, then the OR region is also + bounded. */ + } else { + result = 1; + } + } + +/* Free resources. */ + reg1 = astAnnul( reg1 ); + reg2 = astAnnul( reg2 ); + +/* Cache the value in the CmpRegion. */ + this->bounded = astOK ? result : -INT_MAX; + } + +/* Return zero if an error occurred. Otherwise, return the cached value. */ + if( astOK ) { + result = ( this->bounded == -INT_MAX ) ? 0 : this->bounded; + } else { + result = 0; + } + +/* Return the required pointer. */ + return result; +} + +static double GetFillFactor( AstRegion *this_region, int *status ) { +/* +* Name: +* GetFillFactor + +* Purpose: +* Obtain the value of the FillFactor attribute for a CmpRegion. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpregion.h" +* double GetFillFactor( AstRegion *this, int *status ) + +* Class Membership: +* CmpRegion member function (over-rides the astGetFillFactor method inherited +* from the Region class). + +* Description: +* This function returns the value of the FillFactor attribute for a +* CmpRegion. A suitable default value is returned if no value has +* previously been set. + +* Parameters: +* this +* Pointer to the CmpRegion. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The FillFactor value to use. + +*/ + +/* Local Variables: */ + AstCmpRegion *this; + double result; + +/* Check the global error status. */ + if ( !astOK ) return AST__BAD; + +/* Initialise. */ + result = AST__BAD; + +/* Obtain a pointer to the CmpRegion structure. */ + this = (AstCmpRegion *) this_region; + +/* See if a FillFactor value has been set. If so, use the parent + astGetFillFactor method to obtain it. */ + if ( astTestFillFactor( this ) ) { + result = (*parent_getfillfactor)( this_region, status ); + +/* Otherwise, we will generate a default value equal to the FillFactor values + of the first component Region. */ + } else { + result = astGetFillFactor( this->region1 ); + } + +/* If an error occurred, clear the returned value. */ + if ( !astOK ) result = AST__BAD; + +/* Return the result. */ + return result; +} + +static int GetObjSize( AstObject *this_object, int *status ) { +/* +* Name: +* GetObjSize + +* Purpose: +* Return the in-memory size of an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpregion.h" +* int GetObjSize( AstObject *this, int *status ) + +* Class Membership: +* CmpRegion member function (over-rides the astGetObjSize protected +* method inherited from the parent class). + +* Description: +* This function returns the in-memory size of the supplied CmpRegion, +* in bytes. + +* Parameters: +* this +* Pointer to the CmpRegion. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* The Object size, in bytes. + +* 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: */ + AstCmpRegion *this; /* Pointer to CmpRegion structure */ + int result; /* Result value to return */ + +/* Initialise. */ + result = 0; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Obtain a pointers to the CmpRegion structure. */ + this = (AstCmpRegion *) this_object; + +/* Invoke the GetObjSize method inherited from the parent class, and then + add on any components of the class structure defined by this class + which are stored in dynamically allocated memory. */ + result = (*parent_getobjsize)( this_object, status ); + + result += astGetObjSize( this->region1 ); + result += astGetObjSize( this->region2 ); + if( this->xor1 ) result += astGetObjSize( this->xor1 ); + if( this->xor2 ) result += astGetObjSize( this->xor2 ); + +/* If an error occurred, clear the result value. */ + if ( !astOK ) result = 0; + +/* Return the result, */ + return result; +} + +static void GetRegions( AstCmpRegion *this, AstRegion **reg1, AstRegion **reg2, + int *oper, int *neg1, int *neg2, int *status ) { +/* +* +* Name: +* GetRegions + +* Purpose: +* Get the component Regions of a CmpRegion. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* void GetRegions( AstCmpRegion *this, AstRegion **reg1, AstRegion **reg2, +* int *oper, int *neg1, int *neg2, int *status ) + +* Class Membership: +* CmpRegion member function + +* Description: +* This function returns pointers to two Regions which, when applied +* using the returned boolean operator, are equivalent to the supplied +* Region. If the CmpRegion has been negated, then the returned operator +* and "negated" flags will be set such that they represent the +* negated CmpRegion. +* +* The current Frames in both the returned component Regions will be +* equivalent to the base Frame in the FrameSet encapsulated by the +* parent Region structure. + +* Parameters: +* this +* Pointer to the CmpRegion. +* reg1 +* Address of a location to receive a pointer to first component +* Region. The current Frame in this region will be equivalent to +* the base Frame in the FrameSet +* reg2 +* Address of a location to receive a pointer to second component +* Region. +* oper +* Address of a location to receive a value indicating how the +* component Regions are combined together. This will be one of +* AST__AND or AST__OR +* neg1 +* The value of the Negated attribute to be used with reg1. +* neg2 +* The value of the Negated attribute to be used with reg2. +* status +* Pointer to the inherited status variable. + +* Notes: +* - Any changes made to the component Regions using the returned +* pointers will be reflected in the supplied CmpRegion. + +*- +*/ + +/* Initialise */ + if( reg1 ) *reg1 = NULL; + if( reg2 ) *reg2 = NULL; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Return the component Region pointers. */ + if( reg1 ) *reg1 = astClone( this->region1 ); + if( reg2 ) *reg2 = astClone( this->region2 ); + +/* Initialise the other returned items. Note, the CmpRegion initialiser + stored a deep copy of the supplied component Regions, and so we do not + need to worry about attributes of the components having been changed + after the creation of the CmpRegion. This is different to the CmpMap + class which merely clones its supplied component pointers and so has + to save copies of the original Invert settings within the CmpMap + structure. */ + if( oper ) *oper = this->oper; + if( neg1 ) *neg1 = astGetNegated( this->region1 ); + if( neg2 ) *neg2 = astGetNegated( this->region2 ); + +/* If the CmpRegion has been inverted, we modify the boolean operator and + negation flags so that they reflect the inverted CmpRegion. */ + if( astGetNegated( this ) ) { + +/* If the component Regions are combined using AND, then the negated + CmpRegion combines its negated components using OR. */ + if( this->oper == AST__AND ){ + if( oper ) *oper = AST__OR; + if( neg1 ) *neg1 = *neg1 ? 0 : 1; + if( neg2 ) *neg2 = *neg2 ? 0 : 1; + +/* If the component Regions are combined using OR, then the negated CmpRegion + combines its negated components using AND. */ + } else if( this->oper == AST__OR ){ + if( oper ) *oper = AST__AND; + if( neg1 ) *neg1 = *neg1 ? 0 : 1; + if( neg2 ) *neg2 = *neg2 ? 0 : 1; + + } else if( astOK ) { + astError( AST__INTER, "GetRegions(%s): The %s refers to an unknown " + "boolean operator with identifier %d (internal AST " + "programming error).", status, astGetClass( this ), + astGetClass( this ), this->oper ); + } + } +} + +static AstRegion *GetDefUnc( AstRegion *this_region, int *status ) { +/* +* Name: +* GetDefUnc + +* Purpose: +* Obtain a pointer to the default uncertainty Region for a given Region. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpregion.h" +* AstRegion *GetDefUnc( AstRegion *this ) + +* Class Membership: +* CmpRegion method (over-rides the astGetDefUnc method inherited from +* the Region class). + +* This function returns a pointer to a Region which represents the +* default uncertainty associated with a position on the boundary of the +* given Region. The returned Region refers to the base Frame within the +* FrameSet encapsulated by the supplied Region. + +* Parameters: +* this +* Pointer to the Region. + +* Returned Value: +* A pointer to the Region. This should be annulled (using astAnnul) +* when no longer needed. + +* 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: */ + AstCmpRegion *this; /* Pointer to CmpRegion structure */ + AstRegion *result; /* Returned pointer */ + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the CmpRegion structure. */ + this = (AstCmpRegion *) this_region; + +/* If the first component region has non-default uncertainty, use it as + the default uncertainty for the CmpRegion. Note, the current Frame of + an uncertainty Region is assumed to be the same as the base Frame in the + CmpRegion. */ + if( astTestUnc( this->region1 ) ) { + result = astGetUncFrm( this->region1, AST__CURRENT ); + +/* Otherwise, if the second component region has non-default uncertainty, + use it as the default uncertainty for the CmpRegion. */ + } else if( astTestUnc( this->region2 ) ) { + result = astGetUncFrm( this->region2, AST__CURRENT ); + +/* Otherwise, use the parent method to determine the default uncertainty. */ + } else { + result = (* parent_getdefunc)( this_region, status ); + } + +/* Return NULL if an error occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the required pointer. */ + return result; +} + +void astInitCmpRegionVtab_( AstCmpRegionVtab *vtab, const char *name, int *status ) { +/* +*+ +* Name: +* astInitCmpRegionVtab + +* Purpose: +* Initialise a virtual function table for a CmpRegion. + +* Type: +* Protected function. + +* Synopsis: +* #include "cmpregion.h" +* void astInitCmpRegionVtab( AstCmpRegionVtab *vtab, const char *name ) + +* Class Membership: +* CmpRegion vtab initialiser. + +* Description: +* This function initialises the component of a virtual function +* table which is used by the CmpRegion 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 */ + AstObjectVtab *object; /* Pointer to Object component of Vtab */ + AstRegionVtab *region; /* Pointer to Region 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 astIsACmpRegion) 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->CmpRegionList = CmpRegionList; + +/* 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_getdefunc = region->GetDefUnc; + region->GetDefUnc = GetDefUnc; + + parent_setregfs = region->SetRegFS; + region->SetRegFS = SetRegFS; + + parent_resetcache = region->ResetCache; + region->ResetCache = ResetCache; + + parent_equal = object->Equal; + object->Equal = Equal; + + parent_getobjsize = object->GetObjSize; + object->GetObjSize = GetObjSize; + +#if defined(THREAD_SAFE) + parent_managelock = object->ManageLock; + object->ManageLock = ManageLock; +#endif + + parent_clearclosed = region->ClearClosed; + region->ClearClosed = ClearClosed; + + parent_clearmeshsize = region->ClearMeshSize; + region->ClearMeshSize = ClearMeshSize; + + parent_setclosed = region->SetClosed; + region->SetClosed = SetClosed; + + parent_setmeshsize = region->SetMeshSize; + region->SetMeshSize = SetMeshSize; + + parent_getfillfactor = region->GetFillFactor; + region->GetFillFactor = GetFillFactor; + + parent_regsetattrib = region->RegSetAttrib; + region->RegSetAttrib = RegSetAttrib; + + parent_regclearattrib = region->RegClearAttrib; + region->RegClearAttrib = RegClearAttrib; + +/* Store replacement pointers for methods which will be over-ridden by + new member functions implemented here. */ + mapping->Decompose = Decompose; + region->RegBaseBox = RegBaseBox; + region->RegBaseBox2 = RegBaseBox2; + region->RegBaseMesh = RegBaseMesh; + region->RegSplit = RegSplit; + region->RegPins = RegPins; + region->RegTrace = RegTrace; + region->GetBounded = GetBounded; + region->RegBasePick = RegBasePick; + +/* Declare the copy constructor, destructor and class dump function. */ + astSetCopy( vtab, Copy ); + astSetDelete( vtab, Delete ); + astSetDump( vtab, Dump, "CmpRegion", "Combination of two Regions" ); + +/* 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) ); + } +} + +#if defined(THREAD_SAFE) +static int ManageLock( AstObject *this_object, int mode, int extra, + AstObject **fail, int *status ) { +/* +* Name: +* ManageLock + +* Purpose: +* Manage the thread lock on an Object. + +* Type: +* Private function. + +* Synopsis: +* #include "object.h" +* AstObject *ManageLock( AstObject *this, int mode, int extra, +* AstObject **fail, int *status ) + +* Class Membership: +* CmpRegion member function (over-rides the astManageLock protected +* method inherited from the parent class). + +* Description: +* This function manages the thread lock on the supplied Object. The +* lock can be locked, unlocked or checked by this function as +* deteremined by parameter "mode". See astLock for details of the way +* these locks are used. + +* Parameters: +* this +* Pointer to the Object. +* mode +* An integer flag indicating what the function should do: +* +* AST__LOCK: Lock the Object for exclusive use by the calling +* thread. The "extra" value indicates what should be done if the +* Object is already locked (wait or report an error - see astLock). +* +* AST__UNLOCK: Unlock the Object for use by other threads. +* +* AST__CHECKLOCK: Check that the object is locked for use by the +* calling thread (report an error if not). +* extra +* Extra mode-specific information. +* fail +* If a non-zero function value is returned, a pointer to the +* Object that caused the failure is returned at "*fail". This may +* be "this" or it may be an Object contained within "this". Note, +* the Object's reference count is not incremented, and so the +* returned pointer should not be annulled. A NULL pointer is +* returned if this function returns a value of zero. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A local status value: +* 0 - Success +* 1 - Could not lock or unlock the object because it was already +* locked by another thread. +* 2 - Failed to lock a POSIX mutex +* 3 - Failed to unlock a POSIX mutex +* 4 - Bad "mode" value supplied. + +* Notes: +* - This function attempts to execute even if an error has already +* occurred. +*/ + +/* Local Variables: */ + AstCmpRegion *this; /* Pointer to CmpRegion structure */ + int result; /* Returned status value */ + +/* Initialise */ + result = 0; + +/* Check the supplied pointer is not NULL. */ + if( !this_object ) return result; + +/* Obtain a pointers to the CmpRegion structure. */ + this = (AstCmpRegion *) this_object; + +/* Invoke the ManageLock method inherited from the parent class. */ + if( !result ) result = (*parent_managelock)( this_object, mode, extra, + fail, status ); + +/* Invoke the astManageLock method on any Objects contained within + the supplied Object. */ + if( !result ) result = astManageLock( this->region1, mode, extra, fail ); + if( !result ) result = astManageLock( this->region2, mode, extra, fail ); + + return result; + +} +#endif + +static AstRegion *MatchRegion( AstRegion *this, int ifrm, AstRegion *that, + const char *method, int *status ) { +/* +* Name: +* MatchRegion + +* Purpose: +* Map a Region into the Frame of another Region. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpregion.h" +* AstRegion *MatchRegion( AstRegion *this, int ifrm, AstRegion *that, +* const char *method, int *status ) + +* Class Membership: +* CmpRegion method. + +* Description: +* This function returns a pointer to a new Region which is a copy of +* "that" mapped into either the base or current Frame of "this". + +* Parameters: +* this +* Pointer to a Region defining the Frame of the returned Region. +* ifrm +* The index of a Frame within the FrameSet encapsulated by "this". +* The returned Region will refer to the requested Frame. It should +* be either AST__CURRENT or AST__BASE. +* that +* Pointer to a Region defining the shape and extent of the +* returned Region. +* method +* Pointer to a string holding the calling method.This is only used +* in error messages. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a new Region. This should be annulled (using astAnnul) +* when no longer needed. + +* 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: */ + AstFrame *frm; /* Current Frame from "fs" */ + AstFrameSet *fs; /* FrameSet connecting that to this */ + AstMapping *map; /* Base->Current Mapping from "fs" */ + AstRegion *result; /* Returned pointer */ + +/* Initialise */ + result = NULL; + +/* Check the global error status. Also return NULL if no Regions were + supplied. */ + if ( !astOK || !this || !that ) return result; + +/* Temporarily invert "this" if we are matching its base Frame (since the + astConvert method matches current Frames). */ + if( ifrm == AST__BASE ) astInvert( this ); + +/* Find a FrameSet connecting the current Frames of the two Regions */ + fs = astConvert( that, this, "" ); + +/* Re-instate the original Frame indices in "this" if required. */ + if( ifrm == AST__BASE ) astInvert( this ); + +/* Check a conversion path was found. */ + if( fs ) { + +/* Get the Frame and Mapping form the FrameSet. */ + frm = astGetFrame( fs, AST__CURRENT ); + map = astGetMapping( fs, AST__BASE, AST__CURRENT ); + +/* Re-map the Region. */ + result = astMapRegion( that, map, frm ); + +/* Free resources. */ + frm = astAnnul( frm ); + map = astAnnul( map ); + fs = astAnnul( fs ); + +/* Report an error if there is no conversion between the two Frames. */ + } else { + astError( AST__INTER, "%s(%s): MatchRegion cannot convert between " + "the two supplied coordinate Frames (internal AST " + "programming error).", status, method, astGetClass( this ) ); + } + +/* Annul the returned pointer if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + 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 "cmpregion.h" +* void RegBaseBox( AstRegion *this, double *lbnd, double *ubnd, int *status ) + +* Class Membership: +* CmpRegion 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: */ + AstCmpRegion *this; /* Pointer to CmpRegion structure */ + AstPointSet *ps; /* Mesh pointset */ + AstRegion *reg1; /* Pointer to first component Region */ + AstRegion *reg2; /* Pointer to second component Region */ + double **ptr; /* Pointer to mesh data */ + double *clbnd1; /* Point to 1st comp lower bounds array */ + double *clbnd2; /* Point to 2nd comp lower bounds array */ + double *cubnd1; /* Point to 1st comp upper bounds array */ + double *cubnd2; /* Point to 2nd comp upper bounds array */ + double *p; /* Pointer to next coordinate value */ + double lb; /* Lower limit */ + double ub; /* Upper limit */ + int i; /* Axis index */ + int icoord; /* Coordinate index */ + int inc1; /* First component interval is included? */ + int inc2; /* Second component interval is included? */ + int ipoint; /* Point index */ + int nax; /* Number of axes in Frame */ + int ncoord; /* Number of coords */ + int neg1; /* First component negated? */ + int neg2; /* Second component negated? */ + int npoint; /* Number of points */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the CmpRegion structure */ + this = (AstCmpRegion *) this_region; + +/* If the CmpRegion is bounded, we find the bounding box using a mesh of + points spread evenly over the boundary of the CmpRegion. */ + if( astGetBounded( this ) ) { + ps = astRegBaseMesh( this_region ); + ptr = astGetPoints( ps ); + ncoord = astGetNcoord( ps ); + npoint = astGetNpoint( ps ); + + if( astOK ) { + for( icoord = 0; icoord < ncoord; icoord++ ) { + lbnd[ icoord ] = DBL_MAX; + ubnd[ icoord ] = -DBL_MAX; + p = ptr[ icoord ]; + for( ipoint = 0; ipoint < npoint; ipoint++, p++ ) { + if( *p != AST__BAD ) { + if( *p < lbnd[ icoord ] ) lbnd[ icoord ] = *p; + if( *p > ubnd[ icoord ] ) ubnd[ icoord ] = *p; + } + } + } + } + ps = astAnnul( ps ); + +/* If the CmpRegion is not bounded we look at each axis individually. */ + } else { + +/* Get pointers to the component Regions. */ + reg1 = this->region1; + reg2 = this->region2; + +/* Get their negated flags */ + neg1 = astGetNegated( reg1 ); + neg2 = astGetNegated( reg2 ); + +/* The base Frame of the parent Region structure is the current Frame of + the component Regions. Get the no. of axes in this Frame. */ + nax = astGetNaxes( reg1 ); + +/* Get the bounding boxes of the component Regions in this Frame. */ + clbnd1 = astMalloc( sizeof( double )*(size_t) nax ); + cubnd1 = astMalloc( sizeof( double )*(size_t) nax ); + clbnd2 = astMalloc( sizeof( double )*(size_t) nax ); + cubnd2 = astMalloc( sizeof( double )*(size_t) nax ); + if( astOK ) { + astGetRegionBounds( reg1, clbnd1, cubnd1 ); + astGetRegionBounds( reg2, clbnd2, cubnd2 ); + +/* Loop round every axis. */ + for( i = 0; i < nax; i++ ) { + +/* If the first component Region has been negated, the lower and upper + bounds from the first component are the bounds of an *excluded* axis + interval, not an included interval. If either of the bounds are + infinite, we can swap it to an included interval. If both bounds are + finite, we cannot convert to an included interval. In this case, we + assume that the gap will be filled at some point on another axis, if + there is more than 1 axis, and convert it to an unbouded included + interval. */ + inc1 = 1; + if( neg1 ) { + lb = clbnd1[ i ]; + ub = cubnd1[ i ]; + if( lb == -DBL_MAX ) clbnd1[ i ] = ub; + if( ub == DBL_MAX ) cubnd1[ i ] = lb; + if( lb != -DBL_MAX && ub != DBL_MAX ) { + if( nax == 1 ) { + inc1 = 0; + } else { + clbnd1[ i ] = -DBL_MAX; + cubnd1[ i ] = DBL_MAX; + } + } + } + +/* Likewise attempt to convert an excluded interval into an included + interval for the second component Region. */ + inc2 = 1; + if( neg2 ) { + lb = clbnd2[ i ]; + ub = cubnd2[ i ]; + if( lb == -DBL_MAX ) clbnd2[ i ] = ub; + if( ub == DBL_MAX ) cubnd2[ i ] = lb; + if( lb != -DBL_MAX && ub != DBL_MAX ) { + if( nax == 1 ) { + inc2 = 0; + } else { + clbnd2[ i ] = -DBL_MAX; + cubnd2[ i ] = DBL_MAX; + } + } + } + +/* If the component Regions are combined using AND, find the overlap of + the axis intervals. This depends on whether the intervals are included + or excluded. */ + if( this->oper == AST__AND ) { + + if( inc1 ) { + if( inc2 ) { + lbnd[ i ] = astMAX( clbnd1[ i ], clbnd2[ i ] ); + ubnd[ i ] = astMIN( cubnd1[ i ], cubnd2[ i ] ); + } else { + lbnd[ i ] = clbnd1[ i ] < clbnd2[ i ] ? clbnd1[ i ] : cubnd2[ i ]; + ubnd[ i ] = cubnd1[ i ] > cubnd2[ i ] ? cubnd1[ i ] : clbnd2[ i ]; + } + } else { + if( inc2 ) { + lbnd[ i ] = clbnd2[ i ] < clbnd1[ i ] ? clbnd2[ i ] : cubnd1[ i ]; + ubnd[ i ] = cubnd2[ i ] > cubnd1[ i ] ? cubnd2[ i ] : clbnd1[ i ]; + } else { + lbnd[ i ] = clbnd1[ i ] < clbnd2[ i ] ? clbnd1[ i ] : cubnd2[ i ]; + ubnd[ i ] = cubnd1[ i ] > cubnd2[ i ] ? cubnd1[ i ] : clbnd2[ i ]; + } + } + +/* If the component Regions are not combined using AND, find the union of + the axis intervals. */ + } else { + if( inc1 && inc2 ) { + lbnd[ i ] = astMIN( clbnd1[ i ], clbnd2[ i ] ); + ubnd[ i ] = astMAX( cubnd1[ i ], cubnd2[ i ] ); + } else { + lbnd[ i ] = -DBL_MAX; + ubnd[ i ] = DBL_MAX; + } + } + } + } + +/* Free resources. */ + clbnd1 = astFree( clbnd1 ); + cubnd1 = astFree( cubnd1 ); + clbnd2 = astFree( clbnd2 ); + cubnd2 = astFree( cubnd2 ); + } +} + +static void RegBaseBox2( AstRegion *this_region, double *lbnd, double *ubnd, int *status ){ +/* +* Name: +* RegBaseBox2 + +* Purpose: +* Returns the bounding box of an un-negated Region in the base Frame of +* the encapsulated FrameSet. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpregion.h" +* void RegBaseBox2( AstRegion *this, double *lbnd, double *ubnd, int *status ) + +* Class Membership: +* CmpRegion member function (over-rides the astRegBaseBox2 protected +* method inherited from the Region class). + +* Description: +* This function is similar to astRegBaseBox in that it returns the +* upper and lower axis bounds of a Region in the base Frame of the +* encapsulated FrameSet. But, in addition to assuming that the +* supplied Region has not been negated, it also assumes that any +* component Regions contained within the supplied Region have not been +* negated. + +* 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: */ + AstCmpRegion *this; /* Pointer to CmpRegion structure */ + AstRegion *reg1; /* Pointer to first component Region */ + AstRegion *reg2; /* Pointer to second component Region */ + double *clbnd1; /* Point to 1st comp lower bounds array */ + double *clbnd2; /* Point to 2nd comp lower bounds array */ + double *cubnd1; /* Point to 1st comp upper bounds array */ + double *cubnd2; /* Point to 2nd comp upper bounds array */ + int i; /* Axis index */ + int nax; /* Number of axes in Frame */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the CmpRegion structure */ + this = (AstCmpRegion *) this_region; + +/* Get pointers to the component Regions. */ + reg1 = this->region1; + reg2 = this->region2; + +/* The base Frame of the parent Region structure is the current Frame of + the component Regions. Get the no. of axes in this Frame. */ + nax = astGetNaxes( reg1 ); + +/* Get the bounding boxes of the component Regions in this Frame. */ + clbnd1 = astMalloc( sizeof( double )*(size_t) nax ); + cubnd1 = astMalloc( sizeof( double )*(size_t) nax ); + clbnd2 = astMalloc( sizeof( double )*(size_t) nax ); + cubnd2 = astMalloc( sizeof( double )*(size_t) nax ); + if( astOK ) { + astGetRegionBounds2( reg1, clbnd1, cubnd1 ); + astGetRegionBounds2( reg2, clbnd2, cubnd2 ); + +/* How we combine the two bounding boxes depends on the boolean operator + associated with this CmpRegion. For AND find the overlap of the two + bounding boxes. For other operators find the union. */ + if( this->oper == AST__AND ) { + for( i = 0; i < nax; i++ ) { + lbnd[ i ]= astMAX( clbnd1[ i ], clbnd2[ i ] ); + ubnd[ i ]= astMIN( cubnd1[ i ], cubnd2[ i ] ); + } + + } else { + for( i = 0; i < nax; i++ ) { + lbnd[ i ]= astMIN( clbnd1[ i ], clbnd2[ i ] ); + ubnd[ i ]= astMAX( cubnd1[ i ], cubnd2[ i ] ); + } + } + } + +/* Free resources. */ + clbnd1 = astFree( clbnd1 ); + cubnd1 = astFree( cubnd1 ); + clbnd2 = astFree( clbnd2 ); + cubnd2 = astFree( cubnd2 ); + +} + +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 "cmpregion.h" +* AstPointSet *astRegBaseMesh( AstRegion *this, int *status ) + +* Class Membership: +* CmpRegion 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: */ + AstCmpRegion *this; /* The CmpRegion structure */ + AstPointSet *mesh1; /* PointSet holding mesh for 1st component */ + AstPointSet *mesh1b; /* Mesh for 1st component mapped by 2nd comp. */ + AstPointSet *mesh2; /* PointSet holding mesh for 2nd component */ + AstPointSet *mesh2b; /* Mesh for 2nd component mapped by 1st comp. */ + AstPointSet *result; /* Returned pointer */ + AstRegion *reg1; /* Pointer to first component Region */ + AstRegion *reg2; /* Pointer to second component Region */ + double **ptr1; /* Pointer to array of mesh1b axis value pointers */ + double **ptr2; /* Pointer to array of mesh2b axis value pointers */ + double **ptr; /* Pointer to array of total axis value pointers */ + double *lbnd; /* Pointer to array of bounding box lower bounds */ + double *ubnd; /* Pointer to array of bounding box upper bounds */ + double v; /* Axis value */ + int hasMesh1; /* Does 1st component Region have a mesh? */ + int hasMesh2; /* Does 2nd component Region have a mesh? */ + int ic; /* Axis index */ + int ip; /* Input point index */ + int jp; /* Output point index */ + int nc; /* No. of axis values per point */ + int np1; /* No. of points in mesh1b */ + int np2; /* No. of points in mesh2b */ + int np; /* No. of points in returned PointSet */ + int ok; /* Were all axis values good at this point? */ + +/* Initialise */ + result= NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the CmpRegion structure. */ + this = (AstCmpRegion *) this_region; + +/* 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 pointers to the component regions. */ + reg1 = this->region1; + reg2 = this->region2; + +/* A mesh can only be produced for a Region if it is bounded when either + negated or un-negated. See if meshes can be produced for the component + Regions. */ + hasMesh1 = astGetBounded( reg1 ); + if( !hasMesh1 ){ + astNegate( reg1 ); + hasMesh1 = astGetBounded( reg1 ); + astNegate( reg1 ); + } + + hasMesh2 = astGetBounded( reg2 ); + if( !hasMesh2 ){ + astNegate( reg2 ); + hasMesh2 = astGetBounded( reg2 ); + astNegate( reg2 ); + } + +/* If neither Region has a mesh we cannot produce a mesh. */ + if( !hasMesh1 && !hasMesh2 && astOK ) { + astError( AST__INTER, "astRegBaseMesh(%s): No mesh can be " + "produced for the %s bacause neither of its component " + "Regions has a mesh (internal AST programming error).", status, + astGetClass( this ), astGetClass( this ) ); + +/* If only one Region has a mesh, we can produce a mesh so long as the + boolean operator is not OR. */ + } else if( ( !hasMesh1 || !hasMesh2 ) && this->oper == AST__OR && astOK ) { + astError( AST__INTER, "astRegBaseMesh(%s): No mesh can be produced " + "for the %s bacause one its component Regions has no " + "mesh and the union of the Regions is required (internal " + "AST programming error).", status, astGetClass( this ), astGetClass( this ) ); + } + +/* Allocate memory to hold a bounding box in the base Frame of the CmpRegion. */ + nc = astGetNin( this_region->frameset ); + lbnd = astMalloc( sizeof( double )*(size_t) nc ); + ubnd = astMalloc( sizeof( double )*(size_t) nc ); + +/* Get current Frame meshes covering the two component Regions (the current + Frame of the component Regions is the same as the base Frame of the parent + Region). We now know that at least one Region has a mesh. If the other + one does not have a mesh we may be able to create a mesh by taking the + intersection of the Region with the bounding box of the bounded Region. */ + if( hasMesh1 ) { + mesh1 = astRegMesh( reg1 ); + if( hasMesh2 ) { + mesh2 = astRegMesh( reg2 ); + } else { + astGetRegionBounds( reg1, lbnd, ubnd ); + mesh2 = astBndMesh( reg2, lbnd, ubnd ); + } + + } else { + mesh2 = astRegMesh( reg2 ); + astGetRegionBounds( reg2, lbnd, ubnd ); + mesh1 = astBndMesh( reg1, lbnd, ubnd ); + } + +/* If the CmpRegion represents the intersection of the two component Regions + (AND operator), the total mesh is the sum of the component mesh points + which are inside the other component region. If the CmpRegion represents + the union of the two component Regions (OR operator), the total mesh is + the sum of the component mesh points which are outside the other component + region. So temporarily negate the component Regions if they are + combined using OR. */ + if( this->oper == AST__OR ) { + astNegate( reg1 ); + astNegate( reg2 ); + } + +/* Transform the mesh for the first component using the second component + as a Mapping. Mesh points outside (or inside if "oper" is OR) the bounds + of the second component will be set bad. */ + mesh1b = astTransform( reg2, mesh1, 1, NULL ); + +/* Transform the mesh for the second component using the first component + as a Mapping. Mesh points outside (or inside if "oper" is OR) the bounds + of the first component will be set bad. */ + mesh2b = astTransform( reg1, mesh2, 1, NULL ); + +/* If required, negate them again to bring them back to their original state.*/ + if( this->oper == AST__OR ) { + astNegate( reg1 ); + astNegate( reg2 ); + } + +/* The required mesh contains all the good points form both mesh1b and + mesh2b (i.e. all boundary points which are inside -or inside if "oper" + is OR- the other component Region). Create a PointSet assuming that all + points are good. First allocate an array to hold pointers to the arrays + holding coordinate values for each axis. */ + nc = astGetNcoord( mesh1b ); + np1 = astGetNpoint( mesh1b ); + np2 = astGetNpoint( mesh2b ); + np = np1 + np2; + result = astPointSet( np, nc, "", status ); + ptr = astGetPoints( result ); + +/* Get points to the axis values of the mapped meshes. */ + ptr1 = astGetPoints( mesh1b ); + ptr2 = astGetPoints( mesh2b ); + +/* Check pointers can be used safely. */ + if( astOK ) { + +/* Initialise the index of the next point in the total mesh. */ + jp = 0; + +/* Loop round all the points in the transformed mesh for the first + component. */ + for( ip = 0; ip < np1; ip++ ) { + +/* Assume this point has good axis values */ + ok = 1; + +/* Copy the axis values into the total mesh. Break if a bad axis value is + found. */ + for( ic = 0; ic < nc; ic++ ) { + v = ptr1[ ic ][ ip ]; + if( v != AST__BAD ) { + ptr[ ic ][ jp ] = v; + } else { + ok = 0; + break; + } + } + +/* If no bad axis values were found, increment the index of the next + point in the total mesh. */ + if( ok ) jp++; + } + +/* Now similarly copy the good values from the second transformed mesh onto + the end of the total mesh array. */ + for( ip = 0; ip < np2; ip++ ) { + ok = 1; + for( ic = 0; ic < nc; ic++ ) { + v = ptr2[ ic ][ ip ]; + if( v != AST__BAD ) { + ptr[ ic ][ jp ] = v; + } else { + ok = 0; + break; + } + } + if( ok ) jp++; + } + +/* If the total mesh contains no good points, we will create a PointSet + holding a single bad position. */ + if( jp == 0 ) { + np = 1; + for( ic = 0; ic < nc; ic++ ) ptr[ ic ][ 0 ] = AST__BAD; + } else { + np = jp; + } + +/* Adjust the size of the returned PointSet to exclude the extra space + caused by any axis values being bad in the transformed meshes. */ + astSetNpoint( result, np ); + + } + +/* Free resources. */ + mesh1 = astAnnul( mesh1 ); + mesh2 = astAnnul( mesh2 ); + mesh1b = astAnnul( mesh1b ); + mesh2b = astAnnul( mesh2b ); + lbnd = astFree( lbnd ); + ubnd = astFree( ubnd ); + +/* 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 && result ) this_region->basemesh = astClone( result ); + } + +/* Annul the result if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return a pointer to the output PointSet. */ + return result; +} + +static AstRegion *RegBasePick( AstRegion *this_region, int naxes, + const int *axes, int *status ){ +/* +* Name: +* RegBasePick + +* Purpose: +* Return a Region formed by picking selected base Frame axes from the +* supplied Region. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpregion.h" +* AstRegion *RegBasePick( AstRegion *this, int naxes, const int *axes, +* int *status ) + +* Class Membership: +* CmpRegion member function (over-rides the astRegBasePick protected +* method inherited from the Region class). + +* Description: +* This function attempts to return a Region that is spanned by selected +* axes from the base Frame of the encapsulated FrameSet of the supplied +* Region. This may or may not be possible, depending on the class of +* Region. If it is not possible a NULL pointer is returned. + +* Parameters: +* this +* Pointer to the Region. +* naxes +* The number of base Frame axes to select. +* axes +* An array holding the zero-based indices of the base Frame axes +* that are to be selected. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Pointer to the Region, or NULL if no region can be formed. + +* Notes: +* - A NULL pointer is returned if an error has already occurred, or if +* this function should fail for any reason. +*/ + +/* Local Variables: */ + AstCmpRegion *this; /* Pointer to CmpRegion structure */ + AstFrame *frm1; /* Axes picked from the 1st encapsulated Region */ + AstFrame *frm2; /* Axes picked from the 2nd encapsulated Region */ + AstRegion *result; /* Returned Region */ + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the CmpRegion information. */ + this = (AstCmpRegion *) this_region; + +/* Both encapsulated regions refer to the same Frame (the base Frame of + the parent Region), so attempt to pick the requested axs from them. + If the resulting Frames are not Regions, we cannot pick the requested + axes so return the NULL Frame pointer initialised above. */ + frm1 = astPickAxes( this->region1, naxes, axes, NULL ); + if( astIsARegion( frm1 ) ) { + frm2 = astPickAxes( this->region2, naxes, axes, NULL ); + if( astIsARegion( frm2 ) ) { + +/* Create the new CmpRegion. */ + result = (AstRegion *) astCmpRegion( (AstRegion *) frm1, + (AstRegion *) frm2, + this->oper, "", status ); + } + +/* Free resources */ + frm2 = astAnnul( frm2 ); + } + frm1 = astAnnul( frm1 ); + +/* Return a NULL pointer if an error has occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + 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 CmpRegion. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpregion.h" +* int RegPins( AstRegion *this, AstPointSet *pset, AstRegion *unc, +* int **mask, int *status ) + +* Class Membership: +* CmpRegion 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 CmpRegion. +* +* Some tolerance is allowed, as specified by the uncertainty Region +* stored in the supplied CmpRegion "this", and the supplied uncertainty +* Region "unc" which describes the uncertainty of the supplied points. + +* Parameters: +* this +* Pointer to the CmpRegion. +* 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: */ + AstCmpRegion *this; /* Pointer to the CmpRegion structure. */ + AstPointSet *pset1; /* Points masked by 1st component Region */ + AstPointSet *pset2; /* Points masked by 2nd component Region */ + AstPointSet *psetb1; /* Points in base Frame of 1st component Region */ + AstPointSet *psetb2; /* Points in base Frame of 2nd component Region */ + AstRegion *reg1; /* Pointer to first component Region */ + AstRegion *reg2; /* Pointer to second component Region */ + AstRegion *unc1; /* Base Frame uncertainty in 1st component Region */ + AstRegion *unc2; /* Base Frame uncertainty in 2nd component Region */ + double **ptr1; /* Pointer to axis values in "pset1" */ + double **ptr2; /* Pointer to axis values in "pset2" */ + double *p1; /* Pointer to next axis zero value for pset1 */ + double *p2; /* Pointer to next axis zero value for pset2 */ + int *mask1; /* Mask for first component boundary */ + int *mask2; /* Mask for second component boundary */ + int ip; /* Point index */ + int np; /* Number of points */ + int result; /* Returned flag */ + +/* Initialise */ + result = 0; + if( mask ) *mask = NULL; + +/* Check the inherited status. */ + if( !astOK ) return result; + +/* Get a pointer to the CmpRegion structure. */ + this = (AstCmpRegion *) this_region; + +/* Get pointers to the two component Regions. */ + reg1 = this->region1; + reg2 = this->region2; + +/* Get a mask which indicates if each supplied point is on or off the + boundary of the first component Region. astRegPins expects its "pset" + argument to contain positions in the base Frame of the Region, so + we must first transform the supplied points into the base Frame of + "reg1". We must also map the uncertainty into the base Frame of the + component Region. */ + psetb1 = astRegTransform( reg1, pset, 0, NULL, NULL ); + unc1 = MatchRegion( reg1, AST__BASE, unc, "astRegPins", status ); + astRegPins( reg1, psetb1, unc1, &mask1 ); + +/* Likewise, get a mask which indicates if each supplied point is on or off + the boundary of the second component Region. */ + psetb2 = astRegTransform( reg2, pset, 0, NULL, NULL ); + unc2 = MatchRegion( reg2, AST__BASE, unc, "astRegPins", status ); + astRegPins( reg2, psetb2, unc2, &mask2 ); + +/* The criteria for a point to be on the boundary of the CmpRegion depend + on the boolean operator being used. If component regions A and B are + ANDed together, then a point is on the boundary of the CmpRegion if + either 1) it is on the boundary of A and inside B, or 2) it is on the + boundary of B and inside A. If the component regions are ORed together, + then a point is on the boundary of the CmpRegion if either 1) it is on + the boundary of A and outside B, or 2) it is on the boundary of B and + outside A. Either we need to transform the supplied PointSet using each + component Region as a Mapping. But if using OR we temporarily negate + the Regions. */ + if( this->oper == AST__OR ) { + astNegate( reg1 ); + astNegate( reg2 ); + } + pset1 = astTransform( reg1, pset, 1, NULL ); + pset2 = astTransform( reg2, pset, 1, NULL ); + if( this->oper == AST__OR ) { + astNegate( reg1 ); + astNegate( reg2 ); + } + +/* Get pointers to the axis values in these PointSets */ + ptr1 = astGetPoints( pset1 ); + ptr2 = astGetPoints( pset2 ); + +/* If required, create an output mask array */ + np = astGetNpoint( pset ); + if( mask ) *mask = astMalloc( sizeof(int)*(size_t) np ); + +/* Check pointers can be used safely. */ + if( astOK ) { + +/* We can use the values for the first axis to indicate if a point is + inside or outside a Region. So store pointers to the first axis arrays. */ + p1 = ptr1[ 0 ]; + p2 = ptr2[ 0 ]; + +/* Assume all points are on the boundary of the CmpRegion. */ + result = 1; + +/* If we are creating an output mask, we must check every point. Otherwise + we can stop checking when we find the first point which is not on the + boundary of the CmpRegion. */ + if( mask ) { + + for( ip = 0; ip < np; ip++ ) { + if( ( mask1[ ip ] && p2[ ip ] != AST__BAD ) || + ( mask2[ ip ] && p1[ ip ] != AST__BAD ) ){ + (*mask)[ ip ] = 1; + } else { + (*mask)[ ip ] = 0; + result = 0; + } + } + + } else { + + for( ip = 0; ip < np; ip++ ) { + if( ( !mask1[ ip ] || p2[ ip ] == AST__BAD ) && + ( !mask2[ ip ] || p1[ ip ] == AST__BAD ) ){ + result = 0; + break; + } + } + } + } + +/* Free resources */ + mask1 = astFree( mask1 ); + mask2 = astFree( mask2 ); + pset1 = astAnnul( pset1 ); + pset2 = astAnnul( pset2 ); + psetb1 = astAnnul( psetb1 ); + psetb2 = astAnnul( psetb2 ); + if( unc1 ) unc1 = astAnnul( unc1 ); + if( unc2 ) unc2 = astAnnul( unc2 ); + +/* If an error has occurred, return zero. */ + if( !astOK ) { + result = 0; + if( mask ) *mask = astFree( *mask ); + } + +/* Return the result. */ + return result; +} + +static void RegSetAttrib( AstRegion *this_region, const char *setting, + char **base_setting, int *status ) { +/* +* Name: +* RegSetAttrib + +* Purpose: +* Set an attribute value for a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpregion.h" +* void RegSetAttrib( AstRegion *this, const char *setting, +* char **base_setting, int *status ) + +* Class Membership: +* CmpRegion method (over-rides the astRegSetAttrib method inherited from +* the Region class). + +* Description: +* This function assigns an attribute value to both the base and +* current Frame in the FrameSet encapsulated within a Region, without +* remapping either Frame. +* +* No error is reported if the attribute is not recognised by the base +* Frame. + +* Parameters: +* this +* Pointer to the Region. +* setting +* Pointer to a null terminated attribute setting string. NOTE, IT +* SHOULD BE ENTIRELY LOWER CASE. The supplied string will be +* interpreted using the public interpretation implemented by +* astSetAttrib. This can be different to the interpretation of the +* protected accessor functions. For instance, the public +* interpretation of an unqualified floating point value for the +* Epoch attribute is to interpet the value as a gregorian year, +* but the protected interpretation is to interpret the value as an +* MJD. +* base_setting +* Address of a location at which to return a pointer to the null +* terminated attribute setting string which was applied to the +* base Frame of the encapsulated FrameSet. This may differ from +* the supplied setting if the supplied setting contains an axis +* index and the current->base Mapping in the FrameSet produces an +* axis permutation. The returned pointer should be freed using +* astFree when no longer needed. A NULL pointer may be supplied in +* which case no pointer is returned. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstCmpRegion *this; + char *bset; + int rep; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the CmpRegion structure. */ + this = (AstCmpRegion *) this_region; + +/* Use the RegSetAttrib method inherited from the parent class to apply the + setting to the current and base Frames in the FrameSet encapsulated by the + parent Region structure. */ + (*parent_regsetattrib)( this_region, setting, &bset, status ); + +/* Now apply the base Frame setting to the component Regions (the current + Frame within the component Regions is equivalent to the base Frame in the + parent Region structure). Annul any "attribute unknown" error that results + from attempting to do this. */ + if( astOK ) { + rep = astReporting( 0 ); + astRegSetAttrib( this->region1, bset, NULL ); + astRegSetAttrib( this->region2, bset, NULL ); + if( astStatus == AST__BADAT ) astClearStatus; + astReporting( rep ); + } + +/* If required, return the base Frame setting string, otherwise free it. */ + if( base_setting ) { + *base_setting = bset; + } else { + bset = astFree( bset ); + } +} + +static AstRegion **RegSplit( AstRegion *this_region, int *nlist, int *status ){ +/* +*+ +* Name: +* RegSplit + +* Purpose: +* Split a Region into a list of disjoint component Regions. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* AstRegion **astRegSplit( AstRegion *this, int *nlist ) + +* Class Membership: +* CmpRegion member function (overrides the astRegSplit method +* inherited from the parent Region class). + +* Description: +* This function splits the supplied Region into a set of disjoint +* component Regions. If the Region cannot be split, then the returned +* array contains only one pointer - a clone of the supplied Region +* pointer. + +* Parameters: +* this +* Pointer to the Region. +* nlist +* Pointer to an int in which to return the number of elements in +* the returned array. + +* Returned Value: +* Pointer to dynamically alloctaed memory holding an array of Region +* pointers. The length of this array is given by the value returned +* in "*nlist". The pointers in the returned array should be annulled +* using astAnnul when no longer needed, and the memory used to hold +* the array should be freed using astFree. + +* Notes: +* - A NULL pointer is returned if an error has already occurred, or if +* this function should fail for any reason. +*- +*/ + +/* Local Variables; */ + AstCmpRegion *new; + AstCmpRegion *this; + AstFrame *frm; + AstFrameSet *fs; + AstMapping *map; + AstRegion **cmplist; + AstRegion **result; + AstRegion *cmpreg; + AstRegion *new_reg; + int icomp; + int ifirst; + int ilist; + int iw; + int jcomp; + int ncomp; + int nn; + int unbounded; + +/* Initialise. */ + result = NULL; + *nlist = 0; + +/* Check the local error status. */ + if ( !astOK ) return result; + +/* Get a pointer to the CmpRegion structure. */ + this = (AstCmpRegion *) this_region; + +/* Indicate we have not yet found any unbounded component regions. */ + unbounded = 0; + +/* Can only split non-inverted CmpRegions that combine their components + using the OR operator. */ + if( this->oper == AST__OR && !astGetNegated( this->region1 ) && + !astGetNegated( this->region2 ) ) { + +/* Process each of the two component Regions in turn. */ + for( icomp = 0; icomp < 2 && !unbounded; icomp++ ) { + cmpreg = icomp ? this->region2 : this->region1; + +/* Create a set of disjoint Regions that are equivalent to the current + component Region, and loop round them. */ + cmplist = astRegSplit( cmpreg, &ncomp ); + for( jcomp = 0; jcomp < ncomp; jcomp++ ) { + +/* If any of the components are unbounds, we cannot split the supplied + Region. */ + unbounded = unbounded || !astGetBounded( cmplist[ jcomp ] ); + if( ! unbounded ) { + +/* Initialise the index within the returned list of the first Region that + overlaps the current disjoint component Region. */ + ifirst = -1; + +/* Loop round all the Regions currently in the returned list. */ + for( ilist = 0; ilist < *nlist; ilist++ ) { + if( result[ ilist ] ) { + +/* See if the current disjoint component overlaps the current entry in + the returned list. */ + if( astOverlap( cmplist[ jcomp ], result[ ilist ] ) > 1 ) { + +/* If this is the first overlap found for the current disjoint component, + form a CmpRegion that combines the two overlapping Regions, and use it + to replace the current entry in the returned list. */ + if( ifirst == -1 ) { + new = astCmpRegion( cmplist[ jcomp ], result[ ilist ], + AST__OR, " ", status ); + (void) astAnnul( result[ ilist ] ); + result[ ilist ] = (AstRegion *) new; + +/* Note the index within the returned list of the first Region that overlaps + the current disjoint component Region. */ + ifirst = ilist; + +/* If this is the second or later overlap, add the overlapping returned Region + into the CmpRegion that it is stored at index "ifirsT" in the returned + list. */ + } else { + new = astCmpRegion( result[ ilist ], result[ ifirst ], + AST__OR, " ", status ); + result[ ilist ] = astAnnul( result[ ilist ] ); + (void) astAnnul( result[ ifirst ] ); + result[ ifirst ] = (AstRegion *) new; + } + } + } + } + +/* If the current disjoint component does not overlap any of the Regions + already in the returned list, append the current disjoint component to + the end of the returned list. */ + if( ifirst == -1 ) { + ilist = (*nlist)++; + result = astGrow( result, *nlist, sizeof( *result ) ); + if( astOK ) result[ ilist ] = astClone( cmplist[ jcomp ] ); + } + } + +/* Annul the pointer to the disjoint component Region. */ + cmplist[ jcomp ] = astAnnul( cmplist[ jcomp ] ); + } + +/* Free the mnemory holding the list of disjoint components. */ + cmplist = astFree( cmplist ); + } + } + +/* If any unbounded components were found, ensure the returned list is + empty. */ + if( unbounded && result ) { + for( ilist = 0; ilist < *nlist; ilist++ ) { + if( result[ ilist ] ) result[ ilist ] = astAnnul( result[ ilist ] ); + } + result = astFree( result ); + *nlist = 0; + +/* Otherwise, shuffle later entries down to fill any NULL slots in the returned + list. */ + } else if( result ){ + nn = *nlist; + iw = 0; + for( ilist = 0; ilist < nn; ilist++ ) { + if( result[ ilist ] ) result[ iw++ ] = result[ ilist ]; + } + *nlist = iw; + } + +/* If this CmpRegion cannot be split, the returned list just holds a + clone of the Region pointer. */ + if( !result ) { + result = astMalloc( sizeof( *result ) ); + if( astOK ) { + result[ 0 ] = astClone( this ); + *nlist = 1; + } + } + +/* Remap any returned Regions so that they are defined within the same + coordinate system as the supplied Region. */ + if( result && *nlist > 0 ) { + fs = this_region->frameset; + map = astGetMapping( fs, AST__BASE, AST__CURRENT ); + frm = astGetFrame( fs, AST__CURRENT ); + for( ilist = 0; ilist < *nlist; ilist++ ) { + new_reg = astMapRegion( result[ ilist ], map, frm ); + (void) astAnnul( result[ ilist ] ); + result[ ilist ] = new_reg; + } + map = astAnnul( map ); + frm = astAnnul( frm ); + } + +/* Free all returned pointers if an error has occurred. */ + if( !astOK && result ) { + for( ilist = 0; ilist < *nlist; ilist++ ) { + result[ ilist ] = astAnnul( result[ ilist ] ); + } + result = astFree( result ); + *nlist = 0; + } + +/* 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 "cmpregion.h" +* int astRegTrace( AstRegion *this, int n, double *dist, double **ptr ); + +* Class Membership: +* CmpRegion member function (overrides the astRegTrace 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 astRegTrace method is implemented by the class +* of Region supplied, and zero if not. + +* Notes: +* - The current algorithm results in the boundary of the CmpRegion +* being dis-contiguous - supplied distance values from zero up to some +* mid-value correspond to positions on the first component Region, and +* higher distance (up to 1.0) correspond to points on the second +* component Region. + +*- +*/ + +/* Local Variables; */ + AstCmpRegion *this; + AstFrame *frm; + AstMapping *map; + AstPointSet *bpset; + AstPointSet *cpset; + AstRegion *ureg1; + AstRegion *ureg2; + double **bptr; + int i; + int j; + int ncur; + int result; + double *rval; + double *off; + double *r1d; + double *r2d; + double *r1ptr[ 2 ]; + double *r2ptr[ 2 ]; + double **r1ptrb; + double **r2ptrb; + double dbreak; + double dtot; + double x; + double x0; + int r1n; + int r2n; + AstPointSet *r1pset; + AstPointSet *r2pset; + AstPointSet *r1psetb; + AstPointSet *r2psetb; + +/* Initialise */ + result = 0; + +/* Check inherited status. */ + if( ! astOK ) return result; + +/* Get a pointer to the CmpRegion structure. */ + this = (AstCmpRegion *) this_region; + +/* Get a pointer to the base Frame in the encapsulated FrameSet. */ + frm = astGetFrame( this_region->frameset, AST__BASE ); + +/* Check it is 2-dimensional. */ + result = 1; + if( astGetNaxes( frm ) != 2 ) result = 0; + +/* Check the component Regions can be traced. */ + if( !astRegTrace( this->region1, 0, NULL, NULL ) || + !astRegTrace( this->region1, 0, NULL, NULL ) ) result = 0; + +/* Check we have some points to find. */ + if( result && n > 0 ) { + +/* 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. */ + } else { + bpset = astPointSet( n, 2, " ", status ); + bptr = astGetPoints( bpset ); + ncur = astGetNout( map ); + } + + r1d = astMalloc( sizeof( double )*n ); + r2d = astMalloc( sizeof( double )*n ); + +/* Ensure information about the breaks in the boundary of each component + region is available within the CmpRegion structure. These breaks are + the points at which the two boundaries cross. */ + SetBreakInfo( this, 0, status ); + SetBreakInfo( this, 1, status ); + +/* Get the constants needed to convert the supplied distances (normalised + so that the border of the entire CmpRegion has a length of 1.0), into + geodesic distances around the border of each component Region. */ + dtot = this->d0[ 0 ] + this->d0[ 1 ]; + dbreak = this->d0[ 0 ]/dtot; + +/* Initialise here to avoid compiler warnings. */ + r1n = 0; + r2n = 0; + +/* Check the pointers can be used safely. */ + if( astOK ) { + +/* Loop round all supplied distances, determining if they represent a + position on the first or second component Region. */ + for( i = 0; i < n; i++ ) { + +/* If the current distance represents a point in the second component + Region... */ + if( dist[ i ] > dbreak ) { + +/* Find the correspond distance around the used sections of the second + component region (normalised so that the entire border of the + component region has a length of "this->d0[1]"). */ + x0 = ( dist[ i ] - dbreak )*dtot; + x = x0; + +/* Convert this into the correspond distance around the entire border of + the second component region (normalised so that the entire border of the + component region has unit length). */ + rval = this->rvals[ 1 ]; + off = this->offs[ 1 ]; + + for( j = 0; j < this->nbreak[ 1 ]; j++,rval++,off++ ) { + if( *rval >= x0 ) break; + x += *off; + } + +/* Store this as the next distance to move around the second component + Region, normalising it to the range 0 to 1 as required by astRegTrace. */ + r2d[ r2n++ ] = x/this->dtot[ 1 ]; + +/* Now we do the same if the current distance corresponds to a position + in the first component Region. */ + } else { + + x0 = dist[ i ]*dtot; + x = x0; + + rval = this->rvals[ 0 ]; + off = this->offs[ 0 ]; + + for( j = 0; j < this->nbreak[ 0 ]; j++,rval++,off++ ) { + if( *rval >= x0 ) break; + x += *off; + } + + r1d[ r1n++ ] = x/this->dtot[ 0 ]; + + } + + } + } + +/* Allocate memory to hold the axis values at the corresponding positions + in the first component Region. */ + r1ptr[ 0 ] = astMalloc( sizeof( double )*r1n ); + r1ptr[ 1 ] = astMalloc( sizeof( double )*r1n ); + +/* Allocate memory to hold the axis values at the corresponding positions + in the second component Region. */ + r2ptr[ 0 ] = astMalloc( sizeof( double )*r2n ); + r2ptr[ 1 ] = astMalloc( sizeof( double )*r2n ); + +/* Check the pointers can be used safely. */ + if( astOK ) { + +/* Find the axis values at each of the required positions that fall in + the first component Region. Negate it first if needed to ensure the + Region is bounded (not guaranteed, but likely). */ + if( astGetBounded( this->region1 ) ) { + (void) astRegTrace( this->region1, r1n, r1d, r1ptr ); + } else { + AstRegion *negation = astGetNegation( this->region1 ); + (void) astRegTrace( negation, r1n, r1d, r1ptr ); + negation = astAnnul( negation ); + } + +/* Do the same for the second component Region. */ + if( astGetBounded( this->region2 ) ) { + (void) astRegTrace( this->region2, r2n, r2d, r2ptr ); + } else { + AstRegion *negation = astGetNegation( this->region2 ); + (void) astRegTrace( negation, r2n, r2d, r2ptr ); + negation = astAnnul( negation ); + } + +/* The arrays of positions returned by the above calls to astRegTrace may + include points that should not be there (e.g. points on the boundary + of one component region that should have been blanked due to being inside + the second component region - if the regions are ORed together). This + is a consequence of the relatively low value of the "NP" local constant + in function SetBreakInfo. So we now refine the positions to exclude + any such unwanted positions. + + If the two component Regions are ANDed together, we want to remove the + positions from the boundary of the required component Region that fall + outside the other region. We can do this by simply using the other Region + as a Mapping. If the two component Regions are ORed together, we want to + remove the position that fall within (rather than outside) the other + Region. To do this we need to negate the other region first. */ + if( this->oper == AST__OR ) { + ureg1 = astGetNegation( this->region1 ); + ureg2 = astGetNegation( this->region2 ); + } else { + ureg1 = astClone( this->region1 ); + ureg2 = astClone( this->region2 ); + } + +/* Now transform the points on the boundary of the first Region in order + to set invalid those positions which are not on the boundary of the + supplied CmpRegion. */ + if( r1n > 0 ) { + r1pset = astPointSet( r1n, 2, " ", status ); + astSetPoints( r1pset, r1ptr ); + r1psetb = astTransform( ureg2, r1pset, 1, NULL ); + r1ptrb = astGetPoints( r1psetb ); + } else { + r1pset = NULL; + r1psetb = NULL; + r1ptrb = NULL; + } + +/* Now transform the points on the boundary of the second Region in order + to set invalid those positions which are not on the boundary of the + supplied CmpRegion. */ + if( r2n > 0 ) { + r2pset = astPointSet( r2n, 2, " ", status ); + astSetPoints( r2pset, r2ptr ); + r2psetb = astTransform( ureg1, r2pset, 1, NULL ); + r2ptrb = astGetPoints( r2psetb ); + } else { + r2pset = NULL; + r2psetb = NULL; + r2ptrb = NULL; + } + +/* Free the begation pointers. */ + ureg1 = astAnnul( ureg1 ); + ureg2 = astAnnul( ureg2 ); + +/* Check pointer can be used safely. */ + if( astOK ) { + +/* Copy the boundary positions from each component Region into a single + PointSet. These positions are in the base Frame of the CmpRegion. */ + r1n = 0; + r2n = 0; + for( i = 0; i < n; i++ ) { + if( dist[ i ] > dbreak ) { + bptr[ 0 ][ i ] = r2ptrb[ 0 ][ r2n ]; + bptr[ 1 ][ i ] = r2ptrb[ 1 ][ r2n++ ]; + } else { + bptr[ 0 ][ i ] = r1ptrb[ 0 ][ r1n ]; + bptr[ 1 ][ i ] = r1ptrb[ 1 ][ r1n++ ]; + } + } + + } + +/* Free resources. */ + if( r1pset ) r1pset = astAnnul( r1pset ); + if( r2pset ) r2pset = astAnnul( r2pset ); + if( r1psetb ) r1psetb = astAnnul( r1psetb ); + if( r2psetb ) r2psetb = astAnnul( r2psetb ); + + } + +/* If required, transform the base frame positions into the current + Frame of the CmpRegion, 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 the result. */ + return result; +} + +static void RegClearAttrib( AstRegion *this_region, const char *attrib, + char **base_attrib, int *status ) { +/* +* Name: +* RegClearAttrib + +* Purpose: +* Clear an attribute value for a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpregion.h" +* void RegClearAttrib( AstRegion *this, const char *attrib, +* char **base_attrib, int *status ) + +* Class Membership: +* CmpRegion member function (over-rides the astRegClearAttrib method +* inherited from the Region class). + +* Description: +* This function clears the value of a named attribute in both the base +* and current Frame in the FrameSet encapsulated within a Region, without +* remapping either Frame. +* +* No error is reported if the attribute is not recognised by the base +* Frame. + +* Parameters: +* this +* Pointer to the Region. +* attrib +* Pointer to a null terminated string holding the attribute name. +* NOTE, IT SHOULD BE ENTIRELY LOWER CASE. +* base_attrib +* Address of a location at which to return a pointer to the null +* terminated string holding the attribute name which was cleared in +* the base Frame of the encapsulated FrameSet. This may differ from +* the supplied attribute if the supplied attribute contains an axis +* index and the current->base Mapping in the FrameSet produces an +* axis permutation. The returned pointer should be freed using +* astFree when no longer needed. A NULL pointer may be supplied in +* which case no pointer is returned. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + AstCmpRegion *this; + char *batt; + int rep; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Get a pointer to the CmpRegion structure. */ + this = (AstCmpRegion *) this_region; + +/* Use the RegClearAttrib method inherited from the parent class to clear the + attribute in the current and base Frames in the FrameSet encapsulated by + the parent Region structure. */ + (*parent_regclearattrib)( this_region, attrib, &batt, status ); + +/* Now clear the base Frame attribute to the component Regions (the current + Frame within the component Regions is equivalent to the base Frame in the + parent Region structure). Annul any "attribute unknown" error that results + from attempting to do this. */ + if( astOK ) { + rep = astReporting( 0 ); + astRegClearAttrib( this->region1, batt, NULL ); + astRegClearAttrib( this->region2, batt, NULL ); + if( astStatus == AST__BADAT ) astClearStatus; + astReporting( rep ); + } + +/* If required, return the base Frame attribute name, otherwise free it. */ + if( base_attrib ) { + *base_attrib = batt; + } else { + batt = astFree( batt ); + } +} + +static void ResetCache( AstRegion *this_region, int *status ){ +/* +* Name: +* ResetCache + +* Purpose: +* Clear cached information within the supplied Region. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpregion.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 *: */ + AstCmpRegion *this; + int i; + +/* Check a Region was supplied. */ + if( this_region ) { + +/* Get a pointer to the CmpRegion structure. */ + this = (AstCmpRegion *) this_region; + +/* Clear information cached in the CmpRegion structure. */ + for( i = 0; i < 2; i++ ) { + this->rvals[ i ] = astFree( this->rvals[ i ] ); + this->offs[ i ] = astFree( this->offs[ i ] ); + this->nbreak[ i ] = 0; + this->d0[ i ] = AST__BAD; + this->dtot[ i ] = AST__BAD; + } + + this->bounded = -INT_MAX; + +/* Clear information cached in the component regions. */ + if( this->region1 ) astResetCache( this->region1 ); + if( this->region2 ) astResetCache( this->region2 ); + +/* Clear information cached in the parent Region structure. */ + (*parent_resetcache)( this_region, status ); + } +} + +static void SetBreakInfo( AstCmpRegion *this, int comp, int *status ){ +/* +* Name: +* SetBreakInfo + +* Purpose: +* Ensure that a CmpRegion has information about the breaks in the +* boundaries of one of the two component Regions. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpregion.h" +* void SetBreakInfo( AstCmpRegion *this, int comp, int *status ) + +* Class Membership: +* CmpRegion method. + +* Description: +* This function returns without action if the supplied CmpRegion +* already contains break information for the specified component Region. +* Otherwise, it creates the required information and stores it in the +* CmpRegion. +* +* Each component Region in the CmpRegion has a boundary. But in +* general only part of the boundary of a component Region will also +* be included in the CmpRegion boundary. Thus the component Region +* boundary can be broken up into sections; sections that form part +* of the CmpRegion boundary, and sections that do not. This function +* stores information about the breaks between these sections. +* +* The complete boundary of a component Region is parameterised by a +* geodesic distance that goes from 0.0 to the value found by this +* function and stored in this->dtot (the total geodesic distance +* around the border). This function find the ranges of this parameter +* that correspond to the sections of the boundary that are also on the +* CmpRegion boundary, and thus finds the total length that the component +* boundary contributes to the CmpRegion boundary. This length is stored +* in "this->d0" (a two element array, one for each component Region). +* +* It also find two arrays "this->rvals" and "this->offs" that allow a +* distance value in the range 0.0 to "this->d0" (i.e. a distance +* measured by skipping over the parts of the component boundary that +* are not on the CmpRegion boundary), to be converted into the +* corresponding distance value in the range 0.0 to "this->dtot" (i.e. a +* distance measured round the complete component boundary, including the +* parts not on the CmpRegion boundary). + +* Parameters: +* this +* Pointer to a CmpRegion. +* comp +* Zero or one, indicating which component Region is to be checked. +* status +* Pointer to the inherited status variable. + +*/ + +/* The number of points to be spread evenly over the entire boundary of the + component Region. */ +#define NP 101 + +/* Local Variables: */ + AstFrame *frm; + AstPointSet *pset1; + AstPointSet *pset2; + AstRegion *other; + AstRegion *reg; + AstRegion *uother; + double **ptr2; + double **ptr1; + double *d; + double *offs; + double *p0; + double *p1; + double *p; + double *q; + double *rvals; + double delta; + double dist; + double pnt1[ 2 ]; + double pnt2[ 2 ]; + double rbad; + double rval; + double totdist; + int i; + int j; + int nn; + int prevgood; + +/* Check inherited status */ + if( !astOK ) return; + +/* If the information describing breaks in the component boundary has not + yet been set up, do so now. */ + if( this->d0[ comp ] == AST__BAD ) { + +/* Get a pointer to the component Region for which break information is + required. */ + reg = comp ? this->region2 : this->region1; + +/* Check the component class implements the astRegTrace method. */ + if( astRegTrace( reg, 0, NULL, NULL ) ) { + +/* Create a pointSet to hold axis values at evenly spaced positions along + the entire boundary of the selected component region. */ + pset1 = astPointSet( NP, 2, " ", status ); + ptr1 = astGetPoints( pset1 ); + +/* Allocate memory to hold an array of corresponding scalar distances around + the boundary. */ + d = astMalloc( NP*sizeof( double ) ); + +/* Check pointers can be used safely. */ + if( astOK ) { + +/* Get the distance increment between points (at this point the distances + are normalised so that the entire boundary has unit length, as + required by astRegTrace). */ + delta = 1.0/( NP - 1 ); + +/* Set up the array of evenly spaced distances around the boundary of the + component region. */ + for( i = 0; i < NP; i++ ) d[ i ] = i*delta; + +/* Get the corresponding Frame positions. If the Region is unbounded + (e.g. a negated circle, etc), then negate it first in the hope that + this may produced a bounded Region. */ + if( astGetBounded( reg ) ) { + (void) astRegTrace( reg, NP, d, ptr1 ); + } else { + AstRegion *negation = astGetNegation( reg ); + (void) astRegTrace( negation, NP, d, ptr1 ); + negation = astAnnul( negation ); + } + +/* Get a pointer to the other component Region. */ + other = comp ? this->region1 : this->region2; + +/* If the two component Regions are ANDed together, we want to remove the + positions from the boundary of the required component Region that fall + outside the other region. We can do this by simply using the other Region + as a Mapping. If the two component Regions are ORed together, we want to + remove the position that fall within (rather than outside) the other + Region. To do this we need to negate the other region first. */ + if( this->oper == AST__OR ) { + uother = astGetNegation( other ); + } else { + uother = astClone( other ); + } + +/* Now transform the points on the boundary of the selected Region in + order to set invalid those positions which are not on the boundary of + the supplied CmpRegion. */ + pset2 = astTransform( uother, pset1, 1, NULL ); + +/* Annul the negation pointer */ + uother = astAnnul( uother ); + +/* Modify the distance array by setting invalid each element that is not + on the boundary of the CmpRegion. */ + ptr2 = astGetPoints( pset2 ); + if( astOK ) { + p = ptr2[ 0 ]; + q = ptr2[ 1 ]; + for( i = 0; i < NP; i++,p++,q++ ) { + if( *p == AST__BAD || *q == AST__BAD ) d[ i ] = AST__BAD; + } + +/* At each good/bad junction in this list, extend the good section by one + point. This ensures that the good sections of the curve do in fact + touch each other (they may in fact overlap a little but that does not + matter). */ + prevgood = ( d[ 0 ] != AST__BAD ); + for( i = 1; i < NP; i++,p++,q++ ) { + if( d[ i ] == AST__BAD ) { + if( prevgood ) d[ i ] = i*delta; + prevgood = 0; + + } else { + if( !prevgood ) d[ i - 1 ] = ( i - 1 )*delta; + prevgood = 1; + } + } + +/* Find the total geodesic distance around the border. This is only an + approximation but it is only used to give a relative weight to this + component within the CmpFrame, and so does not need to be very accurate. */ + frm = astGetFrame( reg->frameset, AST__CURRENT ); + p0 = ptr1[ 0 ]; + p1 = ptr1[ 1 ]; + totdist = 0; + pnt1[ 0 ] = *(p0++); + pnt1[ 1 ] = *(p1++); + for( i = 1; i < NP; i++ ) { + pnt2[ 0 ] = *(p0++); + pnt2[ 1 ] = *(p1++); + dist = astDistance( frm, pnt1, pnt2 ); + if( dist != AST__BAD ) totdist += dist; + pnt1[ 0 ] = pnt2[ 0 ]; + pnt1[ 1 ] = pnt2[ 1 ]; + } + +/* Change delta so that it represents a geodesic distance, rather than a + normalised distance in the range zero to one. Working in geodesic distance + (e.g. Radians on a SkyFrame) prevents Regions higher up in a complex nested + CmpRegion being given higher priority than a lower Region. */ + delta *= totdist; + +/* Now create two arrays - "rvals" holds the distance travelled around + the used parts of the border at which breaks occur, "offs" holds the jump + in distance around the complete border at each break. The distance + around the complete border is normalised to the range [0.0,1.0]. + Therefore the total distance around the used parts of the border will in + general be less than 1.0 */ + if( d[ 0 ] == AST__BAD ) { + nn = 1; + j = 0; + rvals = astMalloc( sizeof( double ) ); + offs = astMalloc( sizeof( double ) ); + if( astOK ) rvals[ 0 ] = -0.5*delta; + rbad = 0.5; + prevgood = 0; + rval = -0.5*delta; + + } else { + nn = 0; + rvals = NULL; + offs = NULL; + prevgood = 1; + rbad = 0.0; + rval = 0.0; + } + + for( i = 1; i < NP; i++,p++,q++ ) { + + if( d[ i ] == AST__BAD ) { + if( prevgood ) { + j = nn++; + rvals = astGrow( rvals, nn, sizeof( double ) ); + offs = astGrow( offs, nn, sizeof( double ) ); + if( astOK ) { + rvals[ j ] = rval + 0.5*delta; + rbad = 0.0; + } else { + break; + } + prevgood = 0; + } + + rbad += 1.0; + + } else { + if( !prevgood ) { + offs[ j ] = rbad*delta; + prevgood = 1; + } + rval += delta; + } + } + + if( !prevgood ) { + rval += 0.5*delta; + offs[ j ] = rbad*delta; + } + +/* Record the information in the CmpRegion structure. */ + this->rvals[ comp ] = rvals; + this->offs[ comp ] = offs; + this->nbreak[ comp ] = nn; + this->d0[ comp ] = rval; + this->dtot[ comp ] = totdist; + } + +/* Free resources. */ + pset2 = astAnnul( pset2 ); + } + + pset1 = astAnnul( pset1 ); + d = astFree( d ); + + } + } +} + +#undef NP + +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 "cmpregion.h" +* void SetRegFS( AstRegion *this_region, AstFrame *frm, int *status ) + +* Class Membership: +* CmpRegion 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. + +*/ + +/* Local Variables: */ + AstRegion *creg; /* Pointer to component Region structure */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Invoke the parent method to store the FrameSet in the parent Region + structure. */ + (* parent_setregfs)( this_region, frm, status ); + +/* If either component Region has a dummy FrameSet use this method + recursively to give them the same FrameSet. */ + creg = ((AstCmpRegion *) this_region )->region1; + if( creg && !astGetRegionFS( creg ) ) astSetRegFS( creg, frm ); + + creg = ((AstCmpRegion *) this_region )->region2; + if( creg && !astGetRegionFS( creg ) ) astSetRegFS( creg, frm ); + +} + +static AstMapping *Simplify( AstMapping *this_mapping, int *status ) { +/* +* Name: +* Simplify + +* Purpose: +* Simplify a Region. + +* Type: +* Private function. + +* Synopsis: +* #include "region.h" +* AstMapping *Simplify( AstMapping *this, int *status ) + +* Class Membership: +* CmpRegion method (over-rides the astSimplify method inherited from +* the Region class). + +* Description: +* This function simplifies a CmpRegion to eliminate redundant +* computational steps, or to merge separate steps which can be +* performed more efficiently in a single operation. + +* Parameters: +* this +* Pointer to the original Region. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A new pointer to the (possibly simplified) Region. + +* 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. + +* Deficiencies: +* - Currently, this function does not attempt to map the component +* Regions into the current Frame of the parent Region structure. +* Both components should be mapped into the current Frame, and if the +* resulting base->current Mappings in *both* remapped component Regions are +* UnitMaps, then a new CmpRegion should be created from the re-mapped +* Regions. +*/ + +/* Local Variables: */ + AstCmpRegion *newb; /* New CmpRegion defined in base Frame */ + AstCmpRegion *newc; /* New CmpRegion defined in current Frame */ + AstFrame *frm; /* Current Frame */ + AstMapping *map; /* Base->current Mapping */ + AstMapping *result; /* Result pointer to return */ + AstRegion *csreg1; /* Copy of simplified first component Region */ + AstRegion *csreg2; /* Copy of simplified second component Region */ + AstRegion *nullreg; /* Null or infinfite Region */ + AstRegion *othereg; /* Non-Null and non-infinfite Region */ + AstRegion *reg1; /* First component Region */ + AstRegion *reg2; /* Second component Region */ + AstRegion *sreg1; /* Simplified first component Region */ + AstRegion *sreg2; /* Simplified second component Region */ + int neg1; /* Negated flag to use with first component */ + int neg2; /* Negated flag to use with second component */ + int oper; /* Boolean operator used to combine components */ + int overlap; /* Nature of overlap between components */ + int simpler; /* Has any simplification taken place? */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Invoke the parent Simplify method inherited from the Region class. This + will simplify the encapsulated FrameSet and uncertainty Region. The + returned pointer identifies a region within the current Frame of the + FrameSet encapsulated by the parent Region structure. Note this by + storing the pointer in the "newc" ("c" for "current") variable. */ + newc = (AstCmpRegion *) (*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 = ( (AstMapping *) newc != this_mapping ); + +/* Below we may create a new simplified region which identifies a region + within the base Frame of the FrameSet encapsulated by the parent Region + structure. Such a result will need to be mapped into the current Frame + before being returned. The "newb" variable ("b" for "base") will be + used to store a pointer to such a result. Initialise this variable to + indicate that we do not yet have a base Frame result. */ + newb = NULL; + +/* Get the component Regions, how they should be combined, and the + Negated values which should be used with them. The returned values + take account of whether the supplied CmpRegion has itself been Negated + or not. The returned Regions represent regions within the base Frame + of the FrameSet encapsulated by the parent Region structure. */ + GetRegions( newc, ®1, ®2, &oper, &neg1, &neg2, status ); + +/* If the first component Region does not have the required value for + its "Negated" attribute, use the negation of "reg1" in place of "reg1" + itself. */ + if( neg1 != astGetNegated( reg1 ) ) { + AstRegion *tmp = astGetNegation( reg1 ); + (void) astAnnul( reg1 ); + reg1 = tmp; + } + +/* If the second component Region does not have the required value for + its "Negated" attribute, use the negation of "reg2" in place of "reg2" + itself. */ + if( neg2 != astGetNegated( reg2 ) ) { + AstRegion *tmp = astGetNegation( reg2 ); + (void) astAnnul( reg2 ); + reg2 = tmp; + } + +/* Simplify each of the two components. */ + sreg1 = astSimplify( reg1 ); + sreg2 = astSimplify( reg2 ); + +/* Note if any simplification took place. */ + simpler = simpler || ( sreg1 != reg1 || sreg2 != reg2 ); + +/* If either component is null or infinite we can exclude it from the + returned Region. */ + if( astIsANullRegion( sreg1 ) || astIsANullRegion( sreg2 ) ) { + +/* Get a pointer to the non-null Region. The following is still valid + even if both regions are null or infinite. */ + if( astIsANullRegion( sreg1 ) ){ + nullreg = sreg1; + othereg = sreg2; + } else { + nullreg = sreg2; + othereg = sreg1; + } + +/* If null.. */ + if( !astGetNegated( nullreg ) ){ + if( oper == AST__AND ) { + newb = (AstCmpRegion *) astNullRegion( othereg, + astGetUnc( othereg, 0 ), "", status ); + + } else if( oper == AST__OR ) { + newb = astCopy( othereg ); + + } else { + astError( AST__INTER, "astSimplify(%s): The %s refers to an " + "unknown boolean operator with identifier %d (internal " + "AST programming error).", status, astGetClass( newc ), + astGetClass( newc ), oper ); + } + +/* If infinite.. */ + } else { + if( oper == AST__AND ) { + newb = astCopy( othereg ); + + } else if( oper == AST__OR ) { + newb = (AstCmpRegion *) astNullRegion( othereg, + astGetUnc( othereg, 0 ), "negated=1", status ); + + } else { + astError( AST__INTER, "astSimplify(%s): The %s refers to an " + "unknown boolean operator with identifier %d (internal " + "AST programming error).", status, astGetClass( newc ), + astGetClass( newc ), oper ); + } + } + +/* Flag that we have done some simplication.*/ + simpler = 1; + +/* If neither component is null or infinite, see if it is possible to + remove one or both of the components on the basis of the overlap + between them. */ + } else { + overlap = astOverlap( sreg1, sreg2 ); + +/* If the components have no overlap, and they are combined using AND, then + the CmpRegion is null. */ + if( ( overlap == 1 || overlap == 6 ) && oper == AST__AND ) { + newb = (AstCmpRegion *) astNullRegion( sreg1, astGetUnc( sreg1, 0 ), + "", status ); + simpler = 1; + +/* If one component is the negation of the other component, and they are + combined using OR, then the CmpRegion is infinite. This is represented + by a negated null region.*/ + } else if( overlap == 6 && oper == AST__OR ) { + newb = (AstCmpRegion *) astNullRegion( sreg1, astGetUnc( sreg1, 0 ), + "negated=1", status ); + simpler = 1; + +/* If the two components are identical... */ + } else if( overlap == 5 ) { + simpler = 1; + +/* If combined with AND or OR, the CmpRegion can be replaced by the first + (or second) component Region. */ + if( oper == AST__AND || oper == AST__OR ) { + newb = astCopy( sreg1 ); + } else { + astError( AST__INTER, "astSimplify(%s): The %s refers to an " + "unknown boolean operator with identifier %d (internal " + "AST programming error).", status, astGetClass( newc ), + astGetClass( newc ), oper ); + } + +/* If the first component is entirely contained within the second + component, and they are combined using AND or OR, then the CmpRegion + can be replaced by the first or second component. */ + } else if( overlap == 2 && ( oper == AST__AND || oper == AST__OR ) ){ + newb = astCopy( ( oper == AST__AND ) ? sreg1 : sreg2 ); + simpler = 1; + +/* If the second component is entirely contained within the first + component, and they are combined using AND or OR, then the CmpRegion + can be replaced by the second or first component. */ + } else if( overlap == 3 && ( oper == AST__AND || oper == AST__OR ) ){ + newb = astCopy( ( oper == AST__AND ) ? sreg2 : sreg1 ); + simpler = 1; + +/* Otherwise, no further simplication is possible, so either create a new + CmpRegion or leave the "newb" pointer NULL (which will cause "newc" to + be used), depending on whether the components were simplified. */ + } else if( simpler ){ + csreg1 = astCopy( sreg1 ); + csreg2 = astCopy( sreg2 ); + newb = astCmpRegion( csreg1, csreg2, oper, "", status ); + csreg1 = astAnnul( csreg1 ); + csreg2 = astAnnul( csreg2 ); + + } + } + +/* If any simplification took place, decide whether to use the "newc" or + "newb" pointer for the returned Mapping. If "newb" is non-NULL we use + it, otherwise we use "newc". If "newb" is used we must first map the + result Region from the base Frame of the FrameSet encapsulated + by the parent Region structure, to the current Frame. */ + if( simpler ) { + if( newb ){ + frm = astGetFrame( ((AstRegion *) newc)->frameset, AST__CURRENT ); + map = astGetMapping( ((AstRegion *) newc)->frameset, AST__BASE, AST__CURRENT ); + result = astMapRegion( newb, map, frm ); + frm = astAnnul( frm ); + map = astAnnul( map ); + newb = astAnnul( newb ); + } else { + result = astClone( newc ); + } + +/* If no simplification took place, return a clone of the supplied pointer. */ + } else { + result = astClone( this_mapping ); + } + +/* Free resources. */ + reg1 = astAnnul( reg1 ); + reg2 = astAnnul( reg2 ); + sreg1 = astAnnul( sreg1 ); + sreg2 = astAnnul( sreg2 ); + newc = astAnnul( newc ); + +/* If an error occurred, annul the returned Mapping. */ + if ( !astOK ) result = astAnnul( result ); + +/* Return the result. */ + return result; +} + +static AstPointSet *Transform( AstMapping *this_mapping, AstPointSet *in, + int forward, AstPointSet *out, int *status ) { +/* +* Name: +* Transform + +* Purpose: +* Apply a CmpRegion to transform a set of points. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpregion.h" +* AstPointSet *Transform( AstMapping *this, AstPointSet *in, +* int forward, AstPointSet *out, int *status ) + +* Class Membership: +* CmpRegion member function (over-rides the astTransform method inherited +* from the Region class). + +* Description: +* This function takes a CmpRegion and a set of points encapsulated in a +* PointSet and transforms the points so as to apply the required Region. +* This implies applying each of the CmpRegion's component Regions in turn, +* either in series or in parallel. + +* Parameters: +* this +* Pointer to the CmpRegion. +* in +* Pointer to the PointSet associated with the input coordinate values. +* 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: +* - 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 coordinates for the CmpRegion being applied. +* - 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: */ + AstCmpRegion *this; /* Pointer to the CmpRegion structure */ + AstPointSet *ps1; /* Pointer to PointSet for first component */ + AstPointSet *ps2; /* Pointer to PointSet for second component */ + AstPointSet *pset_tmp; /* Pointer to PointSet holding base Frame positions*/ + AstPointSet *result; /* Pointer to output PointSet */ + AstRegion *reg1; /* Pointer to first component Region */ + AstRegion *reg2; /* Pointer to second component Region */ + double **ptr1; /* Pointer to first component axis values */ + double **ptr2; /* Pointer to second component axis values */ + double **ptr_out; /* Pointer to output coordinate data */ + int coord; /* Zero-based index for coordinates */ + int good; /* Is the point inside the CmpRegion? */ + int ncoord_out; /* No. of coordinates per output point */ + int ncoord_tmp; /* No. of coordinates per base Frame point */ + int neg1; /* Negated value for first component Region */ + int neg2; /* Negated value for second component Region */ + int npoint; /* No. of points */ + int oper; /* Boolean operator to use */ + int point; /* Loop counter for points */ + +/* Initialise. */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Get a Pointer to the CmpRegion structure */ + this = (AstCmpRegion *) this_mapping; + +/* Get the component Regions, how they should be combined, and the + Negated values which should be used with them. The returned values + take account of whether the supplied CmpRegion has itself been Negated + or not. The returned Regions represent regions within the base Frame + of the FrameSet encapsulated by the parent Region structure. */ + GetRegions( this, ®1, ®2, &oper, &neg1, &neg2, status ); + +/* If the first component Region does not have the required value for + its "Negated" attribute, use the negation of "reg1" in place of "reg1" + itself. */ + if( neg1 != astGetNegated( reg1 ) ) { + AstRegion *tmp = astGetNegation( reg1 ); + (void) astAnnul( reg1 ); + reg1 = tmp; + } + +/* If the second component Region does not have the required value for + its "Negated" attribute, use the negation of "reg2" in place of "reg2" + itself. */ + if( neg2 != astGetNegated( reg2 ) ) { + AstRegion *tmp = astGetNegation( reg2 ); + (void) astAnnul( reg2 ); + reg2 = tmp; + } + +/* 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 ); + +/* We will now extend the parent astTransform method by performing the + calculations needed to generate the output coordinate values. */ + +/* First use the encapsulated FrameSet in the parent Region structure to + transform the supplied positions from the current Frame in the + encapsulated FrameSet (the Frame represented by the CmpRegion), to the + base Frame (the Frame in which the component Regions are defined). 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. */ + pset_tmp = astRegTransform( this, in, 0, NULL, NULL ); + +/* Now transform this PointSet using each of the two component Regions in + turn. */ + ps1 = astTransform( reg1, pset_tmp, 0, NULL ); + ps2 = astTransform( reg2, pset_tmp, 0, NULL ); + +/* Determine the numbers of points and coordinates per point for these base + Frame PointSets and obtain pointers for accessing the base Frame and output + coordinate values. */ + npoint = astGetNpoint( pset_tmp ); + ncoord_tmp = astGetNcoord( pset_tmp ); + ptr1 = astGetPoints( ps1 ); + ptr2 = astGetPoints( ps2 ); + ncoord_out = astGetNcoord( result ); + ptr_out = astGetPoints( result ); + +/* Perform coordinate arithmetic. */ +/* ------------------------------ */ + if ( astOK ) { + +/* First deal with ANDed Regions */ + if( oper == AST__AND ) { + for ( point = 0; point < npoint; point++ ) { + good = 0; + + for ( coord = 0; coord < ncoord_tmp; coord++ ) { + if( ptr1[ coord ][ point ] != AST__BAD && + ptr2[ coord ][ point ] != AST__BAD ) { + good = 1; + break; + } + } + + if( !good ) { + for ( coord = 0; coord < ncoord_out; coord++ ) { + ptr_out[ coord ][ point ] = AST__BAD; + } + } + } + +/* Now deal with ORed Regions */ + } else if( oper == AST__OR ) { + for ( point = 0; point < npoint; point++ ) { + good = 0; + + for ( coord = 0; coord < ncoord_tmp; coord++ ) { + if( ptr1[ coord ][ point ] != AST__BAD || + ptr2[ coord ][ point ] != AST__BAD ) { + good = 1; + break; + } + } + + if( !good ) { + for ( coord = 0; coord < ncoord_out; coord++ ) { + ptr_out[ coord ][ point ] = AST__BAD; + } + } + } + +/* Report error for any unknown operator. */ + } else if( astOK ) { + astError( AST__INTER, "astTransform(%s): The %s refers to an unknown " + "boolean operator with identifier %d (internal AST " + "programming error).", status, astGetClass( this ), + astGetClass( this ), oper ); + } + } + +/* Free resources. */ + reg1 = astAnnul( reg1 ); + reg2 = astAnnul( reg2 ); + ps1 = astAnnul( ps1 ); + ps2 = astAnnul( ps2 ); + pset_tmp = astAnnul( pset_tmp ); + +/* If an error occurred, clean up by deleting the output PointSet (if + allocated by this function) and setting a NULL result pointer. */ + if ( !astOK ) { + if ( !out ) result = astDelete( result ); + result = NULL; + } + +/* Return a pointer to the output PointSet. */ + return result; +} + +static void XORCheck( AstCmpRegion *this, int *status ) { +/* +* Name: +* XORCheck + +* Purpose: +* Check if the supplied CmpRegion represents an XOR operation. + +* Type: +* Private function. + +* Synopsis: +* #include "cmpregion.h" +* void XORCheck( AstCmpRegion *this, int *status ) + +* Class Membership: +* CmpRegion method + +* Decription: +* This function analyses the component Regions within the supplied +* CmpRegion to see if the CmpRegion is equivalent to an XOR operation +* on two other Regions. If it is, teh Regions that are XORed are +* stored in the supplied CmpRegion. + +* Parameters: +* this +* Pointer to the CmpRegion. + +*/ + +/* Local Variables: */ + AstCmpRegion *cmpreg1; + AstCmpRegion *cmpreg2; + int xor; + +/* Check the global error status. */ + if ( !astOK ) return; + +/* If the CmpRegion is already known to be an XOR operation, return + without action. */ + if( this->xor1 ) return; + +/* To be equivalent to an XOR operation, the supplied CmpRegion must be an + OR operation and each component Region must be a CmpRegion. */ + if( this->oper == AST__OR && astIsACmpRegion( this->region1 ) + && astIsACmpRegion( this->region2 ) ) { + cmpreg1 = (AstCmpRegion *) this->region1; + cmpreg2 = (AstCmpRegion *) this->region2; + +/* Each component CmpRegion must be an AND operation. */ + if( cmpreg1->oper == AST__AND && cmpreg2->oper == AST__AND ) { + +/* Temporarily negate the first component of the first CmpRegion. */ + astNegate( cmpreg1->region1 ); + +/* Initially, assume the supplied CmpRegion is not equivalent to an XOR + operation. */ + xor = 0; + +/* This negated region must be equal to one of the two component Regions + in the second component CmpRegion. Check the first. */ + if( astEqual( cmpreg1->region1, cmpreg2->region1 ) ) { + +/* We now check that the other two Regions are equal (after negating the + first). If so, set "xor" non-zero. */ + astNegate( cmpreg1->region2 ); + if( astEqual( cmpreg1->region2, cmpreg2->region2 ) ) xor = 1; + astNegate( cmpreg1->region2 ); + +/* Do equiovalent checks the other way round. */ + } else if( astEqual( cmpreg1->region1, cmpreg2->region2 ) ) { + astNegate( cmpreg1->region2 ); + if( astEqual( cmpreg1->region2, cmpreg2->region1 ) ) xor = 1; + astNegate( cmpreg1->region2 ); + } + +/* Re-instate the original state of the Negated attribute in the first + component of the first CmpRegion. */ + astNegate( cmpreg1->region1 ); + +/* If the supplied CmpRegion is equivalent to an XOR operation, store + copies of the components in the supplied CmpRegion. */ + if( xor ) { + this->xor1 = astCopy( cmpreg1->region1 ); + this->xor2 = astCopy( cmpreg1->region2 ); + +/* We need to negate one of these two Region (it doesn't matter which), + and we choose to negate which ever of them is already negated (so that + it becomes un-negated). */ + if( astGetNegated( this->xor1 ) ) { + astNegate( this->xor1 ); + } else { + astNegate( this->xor2 ); + } + } + } + } +} + +/* Copy constructor. */ +/* ----------------- */ +static void Copy( const AstObject *objin, AstObject *objout, int *status ) { +/* +* Name: +* Copy + +* Purpose: +* Copy constructor for CmpRegion objects. + +* Type: +* Private function. + +* Synopsis: +* void Copy( const AstObject *objin, AstObject *objout, int *status ) + +* Description: +* This function implements the copy constructor for CmpRegion objects. + +* Parameters: +* objin +* Pointer to the object to be copied. +* objout +* Pointer to the object being constructed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* - This constructor makes a deep copy, including a copy of the component +* Regions within the CmpRegion. +*/ + +/* Local Variables: */ + AstCmpRegion *in; /* Pointer to input CmpRegion */ + AstCmpRegion *out; /* Pointer to output CmpRegion */ + int i; /* Loop count */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain pointers to the input and output CmpRegions. */ + in = (AstCmpRegion *) objin; + out = (AstCmpRegion *) objout; + +/* For safety, start by clearing any memory references in the output + Region that were copied from the input Region. */ + out->region1 = NULL; + out->region2 = NULL; + out->xor1 = NULL; + out->xor2 = NULL; + + for( i = 0; i < 2; i++ ) { + out->rvals[ i ] = NULL; + out->offs[ i ] = NULL; + } + +/* Make copies of these Regions and store pointers to them in the output + CmpRegion structure. */ + out->region1 = astCopy( in->region1 ); + out->region2 = astCopy( in->region2 ); + if( in->xor1 ) out->xor1 = astCopy( in->xor1 ); + if( in->xor2 ) out->xor2 = astCopy( in->xor2 ); + +/* Copy cached arrays. */ + for( i = 0; i < 2; i++ ) { + out->rvals[ i ] = astStore( NULL, in->rvals[ i ], in->nbreak[ i ]*sizeof( **in->rvals ) ); + out->offs[ i ] = astStore( NULL, in->offs[ i ], in->nbreak[ i ]*sizeof( **in->offs ) ); + } +} + +/* Destructor. */ +/* ----------- */ +static void Delete( AstObject *obj, int *status ) { +/* +* Name: +* Delete + +* Purpose: +* Destructor for CmpRegion objects. + +* Type: +* Private function. + +* Synopsis: +* void Delete( AstObject *obj, int *status ) + +* Description: +* This function implements the destructor for CmpRegion objects. + +* Parameters: +* obj +* Pointer to the object to be deleted. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* void + +* Notes: +* This function attempts to execute even if the global error status is +* set. +*/ + +/* Local Variables: */ + AstCmpRegion *this; /* Pointer to CmpRegion */ + int i; + +/* Obtain a pointer to the CmpRegion structure. */ + this = (AstCmpRegion *) obj; + +/* Free arrays holding cached information. */ + for( i = 0; i < 2; i++ ) { + this->rvals[ i ] = astFree( this->rvals[ i ] ); + this->offs[ i ] = astFree( this->offs[ i ] ); + } + +/* Annul the pointers to the component Regions. */ + this->region1 = astAnnul( this->region1 ); + this->region2 = astAnnul( this->region2 ); + if( this->xor1 ) this->xor1 = astAnnul( this->xor1 ); + if( this->xor2 ) this->xor2 = astAnnul( this->xor2 ); +} + +/* Dump function. */ +/* -------------- */ +static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { +/* +* Name: +* Dump + +* Purpose: +* Dump function for CmpRegion 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 CmpRegion class to an output Channel. + +* Parameters: +* this +* Pointer to the CmpRegion 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: */ + AstRegion *reg1; /* First Region to include in dump */ + AstRegion *reg2; /* Second Region to include in dump */ + AstCmpRegion *this; /* Pointer to the CmpRegion structure */ + const char *comment; /* Pointer to comment string */ + int ival; /* Integer value */ + int oper; /* The operator to include in the dump */ + +/* Check the global error status. */ + if ( !astOK ) return; + +/* Obtain a pointer to the CmpRegion structure. */ + this = (AstCmpRegion *) this_object; + +/* Check if this CmpRegion has an equivalent XOR representation. Is so, + store details of the XOR representation in the CmpRegion. */ + XORCheck( this, status ); + +/* Choose the operator and component regions to include in the dump. If + the CmpRegion originally used an XOR operator, then save the XORed + regions. Otherwise, store the real component Regions. */ + if( this->xor1 ) { + oper = AST__XOR; + reg1 = this->xor1; + reg2 = this->xor2; + } else { + oper = this->oper; + reg1 = this->region1; + reg2 = this->region2; + } + +/* Write out values representing the instance variables for the CmpRegion + 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. */ + +/* Oper */ +/* ------- */ + ival = oper; + if( ival == AST__AND ) { + comment = "Regions combined using Boolean AND"; + } else if( ival == AST__OR ) { + comment = "Regions combined using Boolean OR"; + } else if( ival == AST__XOR ) { + comment = "Regions combined using Boolean XOR"; + } else { + comment = "Regions combined using unknown operator"; + } + astWriteInt( channel, "Operator", 1, 0, ival, comment ); + +/* First Region. */ +/* -------------- */ + astWriteObject( channel, "RegionA", 1, 1, reg1, + "First component Region" ); + +/* Second Region. */ +/* --------------- */ + astWriteObject( channel, "RegionB", 1, 1, reg2, + "Second component Region" ); +} + +/* Standard class functions. */ +/* ========================= */ +/* Implement the astIsACmpRegion and astCheckCmpRegion functions using the + macros defined for this purpose in the "object.h" header file. */ +astMAKE_ISA(CmpRegion,Region) +astMAKE_CHECK(CmpRegion) + +AstCmpRegion *astCmpRegion_( void *region1_void, void *region2_void, int oper, + const char *options, int *status, ...) { +/* +*+ +* Name: +* astCmpRegion + +* Purpose: +* Create a CmpRegion. + +* Type: +* Protected function. + +* Synopsis: +* #include "cmpregion.h" +* AstCmpRegion *astCmpRegion( AstRegion *region1, AstRegion *region2, +* int oper, const char *options, ..., int *status ) + +* Class Membership: +* CmpRegion constructor. + +* Description: +* This function creates a new CmpRegion and optionally initialises its +* attributes. + +* Parameters: +* region1 +* Pointer to the first Region. +* region2 +* Pointer to the second Region. +* oper +* The boolean operator with which to combine the two Regions. Either +* AST__AND or AST__OR. +* options +* Pointer to a null terminated string containing an optional +* comma-separated list of attribute assignments to be used for +* initialising the new CmpRegion. The syntax used is the same as for the +* astSet method and may include "printf" format specifiers identified +* by "%" symbols in the normal way. +* status +* Pointer to the inherited status variable. +* ... +* If the "options" string contains "%" format specifiers, then an +* optional list of arguments may follow it in order to supply values to +* be substituted for these specifiers. The rules for supplying these +* are identical to those for the astSet method (and for the C "printf" +* function). + +* Returned Value: +* A pointer to the new CmpRegion. + +* 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. +*- + +* Implementation Notes: +* - This function implements the basic CmpRegion constructor which is +* available via the protected interface to the CmpRegion class. A +* public interface is provided by the astCmpRegionId_ function. +* - Because this function has a variable argument list, it is +* invoked by a macro that evaluates to a function pointer (not a +* function invocation) and no checking or casting of arguments is +* performed before the function is invoked. Because of this, the +* "region1" and "region2" parameters are of type (void *) and are +* converted and validated within the function itself. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstCmpRegion *new; /* Pointer to new CmpRegion */ + AstRegion *region1; /* Pointer to first Region structure */ + AstRegion *region2; /* Pointer to second Region structure */ + va_list args; /* Variable argument list */ + +/* Initialise. */ + new = NULL; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Check the global status. */ + if ( !astOK ) return new; + +/* Obtain and validate pointers to the Region structures provided. */ + region1 = astCheckRegion( region1_void ); + region2 = astCheckRegion( region2_void ); + if ( astOK ) { + +/* Initialise the CmpRegion, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitCmpRegion( NULL, sizeof( AstCmpRegion ), !class_init, + &class_vtab, "CmpRegion", region1, region2, + oper ); + +/* 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 CmpRegion'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 CmpRegion. */ + return new; +} + +AstCmpRegion *astCmpRegionId_( void *region1_void, void *region2_void, + int oper, const char *options, ... ) { +/* +*++ +* Name: +c astCmpRegion +f AST_CMPREGION + +* Purpose: +* Create a CmpRegion. + +* Type: +* Public function. + +* Synopsis: +c #include "cmpregion.h" +c AstCmpRegion *astCmpRegion( AstRegion *region1, AstRegion *region2, +c int oper, const char *options, ... ) +f RESULT = AST_CMPREGION( REGION1, REGION2, OPER, OPTIONS, STATUS ) + +* Class Membership: +* CmpRegion constructor. + +* Description: +* This function creates a new CmpRegion and optionally initialises +* its attributes. +* +* A CmpRegion is a Region which allows two component +* Regions (of any class) to be combined to form a more complex +* Region. This combination may be performed a boolean AND, OR +* or XOR (exclusive OR) operator. If the AND operator is +* used, then a position is inside the CmpRegion only if it is +* inside both of its two component Regions. If the OR operator is +* used, then a position is inside the CmpRegion if it is inside +* either (or both) of its two component Regions. If the XOR operator +* is used, then a position is inside the CmpRegion if it is inside +* one but not both of its two component Regions. Other operators can +* be formed by negating one or both component Regions before using +* them to construct a new CmpRegion. +* +* The two component Region need not refer to the same coordinate +* Frame, but it must be possible for the +c astConvert +f AST_CONVERT +* function to determine a Mapping between them (an error will be +* reported otherwise when the CmpRegion is created). For instance, +* a CmpRegion may combine a Region defined within an ICRS SkyFrame +* with a Region defined within a Galactic SkyFrame. This is +* acceptable because the SkyFrame class knows how to convert between +* these two systems, and consequently the +c astConvert +f AST_CONVERT +* function will also be able to convert between them. In such cases, +* the second component Region will be mapped into the coordinate Frame +* of the first component Region, and the Frame represented by the +* CmpRegion as a whole will be the Frame of the first component Region. +* +* Since a CmpRegion is itself a Region, it can be used as a +* component in forming further CmpRegions. Regions of arbitrary +* complexity may be built from simple individual Regions in this +* way. + +* Parameters: +c region1 +f REGION1 = INTEGER (Given) +* Pointer to the first component Region. +c region2 +f REGION2 = INTEGER (Given) +* Pointer to the second component Region. This Region will be +* transformed into the coordinate Frame of the first region before +* use. An error will be reported if this is not possible. +c oper +f OPER = INTEGER (Given) +* The boolean operator with which to combine the two Regions. This +* must be one of the symbolic constants AST__AND, AST__OR or AST__XOR. +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 CmpRegion. 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 CmpRegion. 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 astCmpRegion() +f AST_CMPREGION = INTEGER +* A pointer to the new CmpRegion. + +* Notes: +* - If one of the supplied Regions has an associated uncertainty, +* that uncertainty will also be used for the returned CmpRegion. +* If both supplied Regions have associated uncertainties, the +* uncertainty associated with the first Region will be used for the +* returned CmpRegion. +* - Deep copies are taken of the supplied Regions. This means that +* any subsequent changes made to the component Regions using the +* supplied pointers will have no effect on the CmpRegion. +* - 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. +*-- + +* Implementation Notes: +* - This function implements the external (public) interface to +* the astCmpRegion constructor function. It returns an ID value +* (instead of a true C pointer) to external users, and must be +* provided because astCmpRegion_ has a variable argument list which +* cannot be encapsulated in a macro (where this conversion would +* otherwise occur). +* - Because no checking or casting of arguments is performed +* before the function is invoked, the "region1" and "region2" parameters +* are of type (void *) and are converted from an ID value to a +* pointer and validated within the function itself. +* - The variable argument list also prevents this function from +* invoking astCmpRegion_ directly, so it must be a re-implementation +* of it in all respects, except for the conversions between IDs +* and pointers on input/output of Objects. +*/ + +/* Local Variables: */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + AstCmpRegion *new; /* Pointer to new CmpRegion */ + AstRegion *region1; /* Pointer to first Region structure */ + AstRegion *region2; /* Pointer to second Region structure */ + va_list args; /* Variable argument list */ + + int *status; /* Pointer to inherited status value */ + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(NULL); + +/* Initialise. */ + new = NULL; + +/* Get a pointer to the inherited status value. */ + status = astGetStatusPtr; + +/* Check the global status. */ + if ( !astOK ) return new; + +/* Obtain the Region pointers from the ID's supplied and validate the + pointers to ensure they identify valid Regions. */ + region1 = astVerifyRegion( astMakePointer( region1_void ) ); + region2 = astVerifyRegion( astMakePointer( region2_void ) ); + if ( astOK ) { + +/* Initialise the CmpRegion, allocating memory and initialising the + virtual function table as well if necessary. */ + new = astInitCmpRegion( NULL, sizeof( AstCmpRegion ), !class_init, + &class_vtab, "CmpRegion", region1, region2, + oper ); + +/* 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 CmpRegion'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 CmpRegion. */ + return astMakeId( new ); +} + +AstCmpRegion *astInitCmpRegion_( void *mem, size_t size, int init, + AstCmpRegionVtab *vtab, const char *name, + AstRegion *region1, AstRegion *region2, + int oper, int *status ) { +/* +*+ +* Name: +* astInitCmpRegion + +* Purpose: +* Initialise a CmpRegion. + +* Type: +* Protected function. + +* Synopsis: +* #include "cmpregion.h" +* AstCmpRegion *astInitCmpRegion_( void *mem, size_t size, int init, +* AstCmpRegionVtab *vtab, const char *name, +* AstRegion *region1, AstRegion *region2, +* int oper ) + +* Class Membership: +* CmpRegion initialiser. + +* Description: +* This function is provided for use by class implementations to initialise +* a new CmpRegion object. It allocates memory (if necessary) to +* accommodate the CmpRegion plus any additional data associated with the +* derived class. It then initialises a CmpRegion 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 CmpRegion at the start of +* the memory passed via the "vtab" parameter. + +* Parameters: +* mem +* A pointer to the memory in which the CmpRegion is to be initialised. +* This must be of sufficient size to accommodate the CmpRegion data +* (sizeof(CmpRegion)) 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 CmpRegion (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 +* CmpRegion structure, so a valid value must be supplied even if not +* required for allocating memory. +* init +* A logical flag indicating if the CmpRegion'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 CmpRegion. +* 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 Object +* astClass function). +* region1 +* Pointer to the first Region. +* region2 +* Pointer to the second Region. +* oper +* The boolean operator to use. Must be one of AST__AND, AST__OR or +* AST__XOR. + +* Returned Value: +* A pointer to the new CmpRegion. + +* 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: */ + AstCmpRegion *new; /* Pointer to new CmpRegion */ + AstFrame *frm; /* Frame encapsulated by first Region */ + AstFrameSet *fs; /* FrameSet connecting supplied Regions */ + AstMapping *map; /* Mapping between two supplied Regions */ + AstMapping *smap; /* Simplified Mapping between two supplied Regions */ + AstRegion *new_reg1; /* Replacement for first region */ + AstRegion *new_reg2; /* Replacement for second region */ + AstRegion *reg1; /* First Region to store in the CmpRegion */ + AstRegion *reg2; /* Second Region to store in the CmpRegion */ + AstRegion *xor1; /* Copy of first supplied Region or NULL */ + AstRegion *xor2; /* Copy of second supplied Region or NULL */ + int i; /* Loop count */ + int used_oper; /* The boolean operation actually used */ + +/* Check the global status. */ + if ( !astOK ) return NULL; + +/* If necessary, initialise the virtual function table. */ + if ( init ) astInitCmpRegionVtab( vtab, name ); + +/* Initialise. */ + new = NULL; + +/* Check the supplied oper value. */ + if( oper != AST__AND && oper != AST__OR && oper != AST__XOR && astOK ) { + astError( AST__INTRD, "astInitCmpRegion(%s): Illegal " + "boolean operator value (%d) supplied.", status, name, oper ); + } + +/* Take copies of the supplied Regions. */ + reg1 = astCopy( region1 ); + reg2 = astCopy( region2 ); + +/* Get the Mapping from the second to the first Region. */ + fs = astConvert( reg2, reg1, "" ); + +/* Report an error if not possible. */ + if( fs == NULL ) { + frm = NULL; + if( astOK ) astError( AST__INTRD, "astInitCmpRegion(%s): No Mapping can " + "be found between the two supplied Regions.", status, name ); + +/* Otherwise, map the second Region into the Frame of the first (unless + they are already in the same Frame). This results in both component + Frames having the same current Frame. This current Frame is used as the + encapsulated Frame within the parent Region structure. */ + } else { + frm = astGetFrame( fs, AST__CURRENT ); + map = astGetMapping( fs, AST__BASE, AST__CURRENT ); + smap = astSimplify( map ); + if( !astIsAUnitMap( smap ) ) { + new_reg2 = astMapRegion( reg2, smap, frm ); + (void) astAnnul( reg2 ); + reg2 = new_reg2; + } + smap = astAnnul( smap ); + map = astAnnul( map ); + fs = astAnnul( fs ); + } + +/* The CmpRegion class does not implement XOR directly (as it does for + AND and OR). Instead, when requested to create an XOR CmpRegion, it + creates a CmpRegion that uses AND and OR to simulate XOR. The top + level XOR CmpRegion actually uses AST__OR and the two component + regions within it are CmpRegions formed by combing the two supplied + Regions (one being negated first) using AND. Create the required + component Regions. */ + if( oper == AST__XOR ) { + astNegate( reg1 ); + new_reg1 = (AstRegion *) astCmpRegion( reg1, reg2, AST__AND, " ", + status ); + astNegate( reg1 ); + + astNegate( reg2 ); + new_reg2 = (AstRegion *) astCmpRegion( reg1, reg2, AST__AND, " ", + status ); + astNegate( reg2 ); + + xor1 = reg1; + xor2 = reg2; + + reg1 = new_reg1; + reg2 = new_reg2; + + used_oper = AST__OR; + +/* For AND and OR, use the supplied operator. */ + } else { + xor1 = NULL; + xor2 = NULL; + used_oper = oper; + } + +/* Initialise a Region structure (the parent class) as the first component + within the CmpRegion structure, allocating memory if necessary. A NULL + PointSet is suppled as the two component Regions will perform the function + of defining the Region shape. The base Frame of the FrameSet in the + parent Region structure will be the same as the current Frames of the + FrameSets in the two component Regions. */ + if ( astOK ) { + new = (AstCmpRegion *) astInitRegion( mem, size, 0, + (AstRegionVtab *) vtab, name, + frm, NULL, NULL ); + +/* Initialise the CmpRegion data. */ +/* --------------------------- */ +/* Store pointers to the component Regions. */ + new->region1 = astClone( reg1 ); + new->region2 = astClone( reg2 ); + +/* Note the operator used to combine the somponent Regions. */ + new->oper = used_oper; + +/* If we are creating an XOR CmpRegion, save copies of the supplied + Regions (i.e. the supplied Regions which are XORed). These will not + be the same as "reg1" and "reg2" since each of those two regions will + be CmpRegions that combine the supplied Regions using AST__AND. */ + if( oper == AST__XOR ) { + new->xor1 = xor1; + new->xor2 = xor2; + } else { + new->xor1 = NULL; + new->xor2 = NULL; + } + +/* Initialised cached values to show they have not yet been found. */ + for( i = 0; i < 2; i++ ) { + new->rvals[ i ] = NULL; + new->offs[ i ] = NULL; + new->nbreak[ i ] = 0; + new->d0[ i ] = AST__BAD; + new->dtot[ i ] = AST__BAD; + } + new->bounded = -INT_MAX; + +/* If the base->current Mapping in the FrameSet within each component Region + is a UnitMap, then the FrameSet does not need to be included in the + Dump of the new CmpRegion. Set the RegionFS attribute of the component + Region to zero to flag this. */ + map = astGetMapping( reg1->frameset, AST__BASE, AST__CURRENT ); + if( astIsAUnitMap( map ) ) astSetRegionFS( reg1, 0 ); + map = astAnnul( map ); + + map = astGetMapping( reg2->frameset, AST__BASE, AST__CURRENT ); + if( astIsAUnitMap( map ) ) astSetRegionFS( reg2, 0 ); + map = astAnnul( map ); + +/* Copy attribute values from the first component Region to the parent + Region. */ + if( astTestMeshSize( new->region1 ) ) { + astSetMeshSize( new, astGetMeshSize( new->region1 ) ); + } + if( astTestClosed( new->region1 ) ) { + astSetClosed( new, astGetClosed( new->region1 ) ); + } + +/* If an error occurred, clean up by annulling the Region pointers and + deleting the new object. */ + if ( !astOK ) { + new->region1 = astAnnul( new->region1 ); + new->region2 = astAnnul( new->region2 ); + new = astDelete( new ); + } + } + +/* Free resources */ + reg1 = astAnnul( reg1 ); + reg2 = astAnnul( reg2 ); + if( frm ) frm = astAnnul( frm ); + +/* Return a pointer to the new object. */ + return new; +} + +AstCmpRegion *astLoadCmpRegion_( void *mem, size_t size, + AstCmpRegionVtab *vtab, const char *name, + AstChannel *channel, int *status ) { +/* +*+ +* Name: +* astLoadCmpRegion + +* Purpose: +* Load a CmpRegion. + +* Type: +* Protected function. + +* Synopsis: +* #include "cmpregion.h" +* AstCmpRegion *astLoadCmpRegion( void *mem, size_t size, +* AstCmpRegionVtab *vtab, const char *name, +* AstChannel *channel ) + +* Class Membership: +* CmpRegion loader. + +* Description: +* This function is provided to load a new CmpRegion 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 +* CmpRegion 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 CmpRegion at the start of the memory +* passed via the "vtab" parameter. + + +* Parameters: +* mem +* A pointer to the memory into which the CmpRegion is to be +* loaded. This must be of sufficient size to accommodate the +* CmpRegion data (sizeof(CmpRegion)) 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 CmpRegion (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 CmpRegion 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(AstCmpRegion) is used instead. +* vtab +* Pointer to the start of the virtual function table to be +* associated with the new CmpRegion. If this is NULL, a pointer to +* the (static) virtual function table for the CmpRegion 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 "CmpRegion" is used instead. + +* Returned Value: +* A pointer to the new CmpRegion. + +* 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: */ + AstCmpRegion *new; /* Pointer to the new CmpRegion */ + AstRegion *reg1; /* First Region read from dump */ + AstRegion *reg2; /* Second Region read from dump */ + AstFrame *f1; /* Base Frame in parent Region */ + AstRegion *creg; /* Pointer to component Region */ + astDECLARE_GLOBALS /* Pointer to thread-specific global data */ + int i; /* Loop count */ + int oper; /* The operator to include in the dump */ + +/* Initialise. */ + new = NULL; + +/* Get a pointer to the thread specific global data structure. */ + astGET_GLOBALS(channel); + +/* Check the global error status. */ + if ( !astOK ) return new; + +/* If a NULL virtual function table has been supplied, then this is + the first loader to be invoked for this CmpRegion. In this case the + CmpRegion belongs to this class, so supply appropriate values to be + passed to the parent class loader (and its parent, etc.). */ + if ( !vtab ) { + size = sizeof( AstCmpRegion ); + vtab = &class_vtab; + name = "CmpRegion"; + +/* If required, initialise the virtual function table for this class. */ + if ( !class_init ) { + astInitCmpRegionVtab( 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 CmpRegion. */ + 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, "CmpRegion" ); + +/* 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. */ + +/* Operator */ +/* -------- */ + oper = astReadInt( channel, "operator", AST__AND ); + +/* First Region. */ +/* -------------- */ + reg1 = astReadObject( channel, "regiona", NULL ); + +/* Second Region. */ +/* --------------- */ + reg2 = astReadObject( channel, "regionb", NULL ); + +/* Initialised cached values to show they have not yet been found. */ + for( i = 0; i < 2; i++ ) { + new->rvals[ i ] = NULL; + new->offs[ i ] = NULL; + new->nbreak[ i ] = 0; + new->d0[ i ] = AST__BAD; + new->dtot[ i ] = AST__BAD; + } + new->bounded = -INT_MAX; + +/* The CmpRegion class does not implement XOR directly (as it does for + AND and OR). Instead, when requested to create an XOR CmpRegion, it + creates a CmpRegion that uses AND and OR to simulate XOR. The top + level XOR CmpRegion actually uses AST__OR and the two component + regions within it are CmpRegions formed by combing the two supplied + Regions (one being negated first) using AND. Create the required + component Regions. */ + if( oper == AST__XOR ) { + astNegate( reg1 ); + new->region1 = (AstRegion *) astCmpRegion( reg1, reg2, AST__AND, + " ", status ); + astNegate( reg1 ); + + astNegate( reg2 ); + new->region2 = (AstRegion *) astCmpRegion( reg1, reg2, AST__AND, + " ", status ); + astNegate( reg2 ); + + new->xor1 = reg1; + new->xor2 = reg2; + + new->oper = AST__OR; + +/* For AND and OR, use the supplied Regions and operator. */ + } else { + new->region1 = reg1; + new->region2 = reg2; + new->xor1 = NULL; + new->xor2 = NULL; + new->oper = oper; + } + +/* If either component Region has a dummy FrameSet rather than the correct + FrameSet, the correct FrameSet will have copies of the base Frame of the + new CmpRegion as both its current and base Frames, connected by a UnitMap + (this is equivalent to a FrameSet containing a single Frame). However if + the new CmpRegion being loaded has itself got a dummy FrameSet, then we do + not do this since we do not yet know what the correct FrameSet is. In this + case we wait until the parent Region invokes the astSetRegFS method on the + new CmpRegion. */ + if( !astRegDummyFS( new ) ) { + f1 = astGetFrame( ((AstRegion *) new)->frameset, AST__BASE ); + creg = new->region1; + if( astRegDummyFS( creg ) ) astSetRegFS( creg, f1 ); + creg = new->region2; + if( astRegDummyFS( creg ) ) astSetRegFS( creg, f1 ); + f1 = astAnnul( f1 ); + } + +/* If an error occurred, clean up by deleting the new CmpRegion. */ + if ( !astOK ) new = astDelete( new ); + } + +/* Return the new CmpRegion 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. */ + +int astCmpRegionList_( AstCmpRegion *this, int *nreg, AstRegion ***reg_list, + int *status ) { + if ( !astOK ) return AST__AND; + return (**astMEMBER(this,CmpRegion,CmpRegionList))( this, nreg, reg_list, + status ); +} + + + + + + |