/* *class++ * Name: * Mapping * Purpose: * Inter-relate two coordinate systems. * Constructor Function: * None. * Description: * This class provides the basic facilities for transforming a set * of coordinates (representing "input" points) to give a new set * of coordinates (representing "output" points). It is used to * describe the relationship which exists between two different * coordinate systems and to implement operations which make use of * this (such as transforming coordinates and resampling grids of * data). However, the Mapping class does not have a constructor * function of its own, as it is simply a container class for a * family of specialised Mappings which implement particular types * of coordinate transformation. * Inheritance: * The Mapping class inherits from the Object class. * Attributes: * In addition to those attributes common to all Objects, every * Mapping also has the following attributes: * * - Invert: Mapping inversion flag * - IsLinear: Is the Mapping linear? * - IsSimple: Has the Mapping been simplified? * - Nin: Number of input coordinates for a Mapping * - Nout: Number of output coordinates for a Mapping * - Report: Report transformed coordinates? * - TranForward: Forward transformation defined? * - TranInverse: Inverse transformation defined? * Functions: c In addition to those functions applicable to all Objects, the c following functions may also be applied to all Mappings: f In addition to those routines applicable to all Objects, the f following routines may also be applied to all Mappings: * c - astDecompose: Decompose a Mapping into two component Mappings c - astTranGrid: Transform a grid of positions c - astInvert: Invert a Mapping c - astLinearApprox: Calculate a linear approximation to a Mapping c - astMapBox: Find a bounding box for a Mapping c - astMapSplit: Split a Mapping up into parallel component Mappings c - astQuadApprox: Calculate a quadratic approximation to a 2D Mapping c - astRate: Calculate the rate of change of a Mapping output c - astRebin: Rebin a region of a data grid c - astRebinSeq: Rebin a region of a sequence of data grids c - astResample: Resample a region of a data grid c - astRemoveRegions: Remove any Regions from a Mapping c - astSimplify: Simplify a Mapping c - astTran1: Transform 1-dimensional coordinates c - astTran2: Transform 2-dimensional coordinates c - astTranN: Transform N-dimensional coordinates c - astTranP: Transform N-dimensional coordinates held in separate arrays f - AST_DECOMPOSE: Decompose a Mapping into two component Mappings f - AST_TRANGRID: Transform a grid of positions f - AST_INVERT: Invert a Mapping f - AST_LINEARAPPROX: Calculate a linear approximation to a Mapping f - AST_QUADAPPROX: Calculate a quadratic approximation to a 2D Mapping f - AST_MAPBOX: Find a bounding box for a Mapping f - AST_MAPSPLIT: Split a Mapping up into parallel component Mappings f - AST_RATE: Calculate the rate of change of a Mapping output f - AST_REBIN: Rebin a region of a data grid f - AST_REBINSEQ: Rebin a region of a sequence of data grids f - AST_REMOVEREGIONS: Remove any Regions from a Mapping f - AST_RESAMPLE: Resample a region of a data grid f - AST_SIMPLIFY: Simplify a Mapping f - AST_TRAN1: Transform 1-dimensional coordinates f - AST_TRAN2: Transform 2-dimensional coordinates f - AST_TRANN: Transform N-dimensional coordinates * Copyright: * Copyright (C) 1997-2006 Council for the Central Laboratory of the * Research Councils * 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 * . * Authors: * RFWS: R.F. Warren-Smith (Starlink) * MBT: Mark Taylor (Starlink) * DSB: David S. Berry (Starlink) * History: * 1-FEB-1996 (RFWS): * Original version. * 29-FEB-1996 (RFWS): * Minor improvements to error messages. * 15-JUL-1996 (RFWS): * Support external interface. * 13-DEC-1996 (RFWS): * Added the astMapMerge method. * 13-DEC-1996 (RFWS): * Added the astSimplify method. * 27-MAY-1997 (RFWS): * Improved the astSimplify method to use astMapMerge to * simplify a single Mapping where possible. * 29-MAY-1998 (RFWS): * Added the MapBox method. * 13-NOV-1998 (RFWS): * Made default MapBox convergence accuracy larger (i.e. less * accurate). * 10-DEC-1998 (RFWS): * First useful implementation of astResample. * 16-AUG-1999 (RFWS): * Fixed bug in SpecialBounds - wrong number of coordinates being used * when checking for bad output coordinate values. * 17-AUG-1999 (RFWS): * Improved the convergence security of MapBox (return to older but * less efficient setting). * 24-NOV-2000 (MBT): * Fixed bug (function being invoked as wrong type) in AST__UINTERP * scheme, and added new AST__BLOCKAVE scheme, in astResample. * 9-JAN-2001 (DSB): * Changed in and out arguments for TranN from type "double (*)[]" * to "double *". * 8-JAN-2003 (DSB): * Changed private InitVtab method to protected astInitMappingVtab * method. * 10-JUL-2003 (DSB): * Added method astRate. * 2-SEP-2004 (DSB): * Free resources before leaving astRate. * 31-AUG-2004 (DSB): * Make the LinearApprox function protected rather than private, * rename it to astLinearApprox, and make the bounds parameters of * type double rather than int. Also, correct the size of the fit * coefficient array (was "(nin+1)*nout", now is "(nout+1)*nin"). * Also correct the index of the first gradient coefficient from * "fit+nout" to "fit+nin". These errors have probably never been * noticed because they make no difference if nin=nout, which is * usually the case. * 6-SEP-2004 (DSB): * Make astRate more robust by adding checks for unusal conditions. * 20-SEP-2004 (DSB): * Make the LinearApprox function public and change its interface * to be more appropriate for public use. This involved swapping the * direction of the fit (the original astLinearApprox fitted the * inverse transformation, but the public version now fits the forwrd * transformation). * 4-OCT-2004 (DSB): * Modify astMapList to return flag indicating presence of inverted * CmpMaps in supplied Mapping. * 9-NOV-2004 (DSB): * Override astEqual method. * 6-DEC-2004 (DSB): * Remove the second derivative estimate from the astRate function * since CmpMap has trouble calculating it. * 17-DEC-2004 (DSB): * Added astMapSplit * 22-APR-2005 (DSB): * Modified SpecialBounds to handle cases where some irrelevant * output always produces bad values (e.g. a PermMap may do this). * 30-JUN-2005 (DSB): * Added astRebin. * 7-JUL-2005 (DSB): * Make MapSplit public rather than protected. * 11-AUG-2005 (DSB): * Added the AST__CONSERVEFLUX flag (used by astResampleX). * 17-AUG-2005 (DSB): * Added the AST__SOMBCOS kernel. * 31-AUG-2005 (DSB): * Added astRebinSeq. * 9-SEP-2005 (DSB): * Corrected axis indices returned by public interface for astMapSplit. * 31-JAN-2006 (DSB): * Added IsSimple attribute. * 2-FEB-2006 (DSB): * Corrections to prologue of astLinearApprox. * 16-FEB-2006 (DSB): * Some speed optimisations to rebinning code. * 2-MAR-2006 (DSB): * Use HAVE_LONG_DOUBLE in place of AST_LONG_DOUBLE * 7-MAR-2006 (DSB): * Added astTranGrid. * 14-MAR-2006 (DSB): * - The constructor no longer reports an error if the resulting * Mapping cannot transform points in either direction. This is * because it may be possible to simplify such a Mapping and the * simplified Mapping may have defined transformations. E.g. if a * Mapping which has only a forward transformation is combined in * series with its own inverse, the combination CmpMap will simplify * to a UnitMap (usually). * - Reset the "issimple" flag when the Invert flag is changed. * 9-MAY-2006 (DSB): * Correct upper bounds for idim in RebinWithblocking. Also, remove * the single precision "F" instantiation of the MAKE_REBINSEQ macro. * Also correct the "nout = astGetNin" line in the MAKE_REBINSEQ * macro to "nout = astGetNout". * 12-MAY-2006 (DSB): * Modify SpecialBounds to include points slightly inside the * corners. This is because some Mappings may have singularies at * the the edges. * 17-MAY-2006 (DSB): * Correct the "nout = astGetNin" line in the MAKE_RESAMPLE * and MAKE_REBIN macros to "nout = astGetNout". * 7-JUL-2006 (DSB): * Change -CHAR_MAX value (used as a "not set" value for boolean * attributes) to +CHAR_MAX, since some compilers do not allow * chars to have negative values. * 23-AUG-2006 (DSB): * Change the Equal function so that it reports an error when * called, rather than using astSimplify to determine if two Mappings * are equal. All concrete Mapping classes should now provide * their own implementation of astEqual, avoiding the use of * astSimplify. This is so that astSimplify can use astEqual safely * (i.e. without danger of entering an infinite loop). * 24-NOV-2006 (DSB): * Allow astRebinSeq to be called with a NULL pointer for the input * data array. * 14-MAR-2007 (DSB): * Modify astRebinSeq to allow input variances to be used as weights. * 19-MAR-2007 (DSB): * Fix bug in LINEAR_2D macro that caused bad input pixel values to be * treated as good. * 16-APR-2007 (DSB): * Account for reduction in number of degrees of freedom when * calculating output variances on the basis of spread of input values in * astReinSeq. * 28-APR-2007 (DSB): * Correct code within Rebin... and Resample... functions that provides * optimal handling for 1- and 2- dimensional mappings. Previously, the * check for whether or not to use these optimisations was based only on * the dimensionality of either input (Rebin) or output (Resample). This * could cause the optimised code to be used at inappropriate times, * leading to an incorrect effective Mapping between input and output. The * checks now check both input and output dimensionality in all cases. * 3-MAY-2007 (DSB): * An extra parameter ("nused") has been added to astRebinSeq, and * all the rebinning stuff has been modified to keep "nused" up to date. * This is needed to correct a fault in the generation of GENVAR * variances. * 12-DEC-2007 (DSB): * Some rebinning kernels (e.g. SINCSINC) have negative values and * can result in overall negative output weights. Therefore do not * set output pixels with negative weights bad. * 6-MAR-2008 (DSB): * Add an option for astResample to leave unchanged any output pixels * for which an interpolated value cannot be obtained. This is * controlled by the new AST__NOBAD flag. * 7-MAY-2008 (DSB): * Clarified meaning of AST__GENVAR, AST__USEVAR and AST__VARWGT flags * in astRebinSeq. * 9-MAY-2008 (DSB): * Prevent memory over-run in RebinSeq. * 5-MAY-2009 (DSB): * Added astRemoveRegions. * 11-NOV-2009 (DSB): * In astRebinSeq initialise "*nused" to zero (as documented) if the * AST__REBININIT flag is supplied. * 17-NOV-2009 (DSB): * Added AST_DISVAR flag for use with astRebinSeq. * 15-DEC-2009 (DSB): * Ensure that all axes span at least one pixel when calling * astLinearApprox. * 18-DEC-2009 (DSB): * When using a 1D spreading kernel (in astRebin(Seq)), if the kernel * is not contained completely within the output array, reflect the * section of the kernel that falls outside the output array back into * the output array so that no flux is lost. Also discovered that the * n-D code (i.e. the KERNEL_ND macro) incorrectly uses the first * user-supplied parameter as the full kernel width rather than the * half-width. This has been fixed. * 26-FEB-2010 (DSB): * Add astQuadApprox. * 27-FEB-2010 (DSB): * - Make astQuadApprox faster, and fix a bug in the calculation of * the matrix. * 7-JUN-2010 (DSB): * In the KERNEL_D rebinning macros, correct the test for the * central point being outside the bounds of the output image. * 13-AUG-2010 (DSB): * In astRebinSeq, scale WLIM to take account of weighting by * input variances. * 13-DEC-2010 (DSB): * Ensure that astMapSplit returns a Mapping that is independent of * the supplied Mapping (i.e. return a deep copy). This means that * subsequent changes to the supplied Mapping cannot affect the returned * Mapping. * 10-FEB-2011 (DSB): * When rebinning (in macros NEAR_1/2/ND, KERNEL_1/2/ND, LINEAR_1/2/ND), * do not treat a zero variance as bad unless the reciprocals of the * variances are being used as weights. * 16-JUN-2011 (DSB): * Allow a check for NaNs to be performed as a debugging tool after * every invocation of astTransform. This is controlled by the * AST_REPLACE_NAN environment variable: if unset, no check is * performed, if set to "1" NaNs are changed to AST__BAD but no * error is reported, if set to anything else NaNs are changed to * AST__BAD and an error is reported. * 6-JUL-2012 (DSB): * The astRebinSeq family was normalising the returned data and * variances values incorrectly, when the AST__REBINEND flag was * supplied. The exact size of the error depended on the nature of * the Mapping and the spreading method, and so is hard to predict. * 20-JUL-2012 (DSB): * Major re-structuring of astRebinSeq to add further * corrections to the normalisation. The model is now that each * input array is first rebinned and then scaled to preserve the * total data sum, and then each final output pixel is the weighed * mean of all the aligned rebinned pixels. * 13-AUG-2012 (DSB): * Added AST__NONORM flag for asstRebuinSeq. * 30-AUG_2012 (DSB): * Added AST__CONSERVEFLUX flag for astRebinSeq. * 10-SEP-2012 (DSB): * Cater for Mappings that have different numbers of inputs and * outputs when finding the flux conservation factor within * astRebinSeq and astResample. * 1-OCT-2012 (DSB): * Ensure astRebinSeq does not create any negative output * variances. * 2-OCT-2012 (DSB): * - Check for Infs as well as NaNs. * - In Rate, break out of the loop if the RMS is very small, not * just if it is exactly zero. * 5-OCT-2012 (DSB): * Complete re-write of Rate. It's now much simpler, faster and * more reliable. * 16-OCT-2012 (DSB): * In MatrixDet, ignore rows/columns filled with AST_BAD as well as * rows/columns filled with zeros. * 26-APR-2013 (DSB): * Change the "nused" parameter of astRebinSeq from "int *" to * "size_t *" to allow greater amounts of data to be pasted into * the output array. * 29-APR-2013 (DSB): * Do not simplify Mappings that have a set value for their Ident * attribute. If an Ident value has been set then it means that we * should be trying to preserve the identify of the Mapping. This * is implemented via a new protected method (astDoNotSimplify) which * is overridden by the Frame class so that this restriction applies * only to genuine Mappings, not Frames. * 9-MAY-2013 (DSB): * Change the "nused" parameter of astRebinSeq from "size_t *" to * "int64_t *" to cater for systems where "size_t" is only 32 bits long. * 20-MAY-2013 (DSB): * Always perform a linear fit in RebinAdaptively if flux * conservation is requested. * 18-JUL-2013 (DSB): * Correct logic for determining whether to divide or not in * RebinAdaptively. The old logic could lead to infinite recursion. * 1-SEP-2014 (DSB): * Modify astLinearApprox to avoid using regularly placed * test points, as such regular placement may result in * non-representative behaviour. * 25-SEP-2014 (DSB): * Add support for B and UB data types to astRebin and astRebinSeq. * 23-OCT-2014 (DSB): * Report an error if arrays have too many pixels to count in a 32 * bit int (astTranGrid, astResample, astRebin and astRebinSeq). * 23-APR-2015 (DSB): * Use one bit of this->flags to store the "IsSimple" attribute * rather using a whole char (this->issimple). * 16-JUN-2017 (DSB): * If a simplification fails because the simplification process makes * an inappropriate assumption about the supplied Mapping (e.g. that * it has a defined inverse transformation) - thus causing an error to * be reported, then clear the error status and return a clone of the * unmodified supplied Mapping. Putting this check in the astSimplify_ * wrapper function in the base Mapping class is much simpler and less * error prone than performing tests on the appropriateness of the * mapping in teh astMapMerge method of each and every mapping class. * 9-JAN-2018 (DSB): * Modify astLinearApprox so that a linear mapping in parallel with a * mapping that generates bad values is considered linear. The returned * coeffs for the bad outputs are set bad. * 9-MAR-2018 (DSB): * Added the AST__PARWGT flag in astRebinSeq. * *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 Mapping /* Define numerical constants for use in thie module. */ #define GETATTRIB_BUFF_LEN 50 #define RATEFUN_MAX_CACHE 5 #define RATE_ORDER 8 /* Include files. */ /* ============== */ /* Configuration results */ /* ---------------------- */ #if HAVE_CONFIG_H #include #endif /* 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 "channel.h" /* I/O channels */ #include "mapping.h" /* Interface definition for this class */ #include "cmpmap.h" /* Compund Mappings */ #include "unitmap.h" /* Unit Mappings */ #include "permmap.h" /* Axis permutations */ #include "winmap.h" /* Window scalings */ #include "pal.h" /* SLALIB interface */ #include "globals.h" /* Thread-safe global data access */ /* Error code definitions. */ /* ----------------------- */ #include "ast_err.h" /* AST error codes */ /* C header files. */ /* --------------- */ #include #include #include #include #include #include #include /* Module type definitions. */ /* ======================== */ /* Enum to represent the data type when resampling a grid of data. */ typedef enum DataType { #if HAVE_LONG_DOUBLE /* Not normally implemented */ TYPE_LD, #endif TYPE_D, TYPE_F, TYPE_L, TYPE_UL, TYPE_K, TYPE_UK, TYPE_I, TYPE_UI, TYPE_S, TYPE_US, TYPE_B, TYPE_UB } DataType; /* Data structure to hold information about a Mapping for use by optimisation algorithms. */ typedef struct MapData { AstMapping *mapping; /* Pointer to the Mapping */ AstPointSet *pset_in; /* Pointer to input PointSet */ AstPointSet *pset_out; /* Pointer to output PointSet */ double *lbnd; /* Pointer to lower constraints on input */ double *ubnd; /* Pointer to upper constraints on input */ double **ptr_in; /* Pointer to input PointSet coordinates */ double **ptr_out; /* Pointer to output PointSet coordinates */ int coord; /* Index of output coordinate to optimise */ int forward; /* Use forward transformation? */ int negate; /* Negate the output value? */ int nin; /* Number of input coordinates per point */ int nout; /* Number of output coordinates per point */ } MapData; /* Convert from floating point to floating point or integer */ #define CONV(IntType,val) ( ( IntType ) ? (int) ( (val) + (((val)>0)?0.5:-0.5) ) : (val) ) /* 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 const char *(* parent_getattrib)( AstObject *, const char *, int * ); static int (* parent_testattrib)( AstObject *, const char *, int * ); static void (* parent_clearattrib)( AstObject *, const char *, int * ); static void (* parent_setattrib)( AstObject *, const char *, int * ); static int (* parent_equal)( AstObject *, AstObject *, int * ); /* Define macros for accessing each item of thread specific global data. */ #ifdef THREAD_SAFE /* Define how to initialise thread-specific globals. */ #define GLOBAL_inits \ globals->Class_Init = 0; \ globals->GetAttrib_Buff[ 0 ] = 0; \ globals->Unsimplified_Mapping = NULL; \ globals->Rate_Disabled = 0; /* Create the function that initialises global data for this module. */ astMAKE_INITGLOBALS(Mapping) /* Define macros for accessing each item of thread specific global data. */ #define class_init astGLOBAL(Mapping,Class_Init) #define class_vtab astGLOBAL(Mapping,Class_Vtab) #define getattrib_buff astGLOBAL(Mapping,GetAttrib_Buff) #define unsimplified_mapping astGLOBAL(Mapping,Unsimplified_Mapping) #define rate_disabled astGLOBAL(Mapping,Rate_Disabled) #define ratefun_pset1_cache astGLOBAL(Mapping,RateFun_Pset1_Cache) #define ratefun_pset2_cache astGLOBAL(Mapping,RateFun_Pset2_Cache) #define ratefun_next_slot astGLOBAL(Mapping,RateFun_Next_Slot) #define ratefun_pset_size astGLOBAL(Mapping,RateFun_Pset_Size) /* If thread safety is not needed, declare and initialise globals at static variables. */ #else /* Buffer returned by GetAttrib. */ static char getattrib_buff[ GETATTRIB_BUFF_LEN + 1 ]; /* Pointer to origin (unsimplified) Mapping, only used for reporting error messages. */ static AstMapping *unsimplified_mapping = NULL; /* A flag which indicates if the astRate method should be disabled in order to improve algorithm speed in cases where the rate value is not significant. If astRate is disabled then it always returns a constant value of 1.0. */ static int rate_disabled = 0; /* static values used in function "RateFun". */ static AstPointSet *ratefun_pset1_cache[ RATEFUN_MAX_CACHE ]; static AstPointSet *ratefun_pset2_cache[ RATEFUN_MAX_CACHE ]; static int ratefun_next_slot; static int ratefun_pset_size[ RATEFUN_MAX_CACHE ]; /* Define the class virtual function table and its initialisation flag as static variables. */ static AstMappingVtab class_vtab; /* Virtual function table */ static int class_init = 0; /* Virtual function table initialised? */ #endif /* Prototypes for private member functions. */ /* ======================================== */ #define DECLARE_GENERIC(X,Xtype) \ static int InterpolateKernel1##X( AstMapping *, int, const int *, const int *, \ const Xtype *, const Xtype *, int, \ const int *, const double *const *, \ void (*)( double, const double *, int, \ double *, int * ), \ void (*)( double, const double *, int, \ double * ), \ int, const double *, int, Xtype, \ Xtype *, Xtype *, int * );\ \ static int InterpolateLinear##X( int, const int *, const int *, const Xtype *, \ const Xtype *, int, const int *, \ const double *const *, int, Xtype, Xtype *, \ Xtype *, int * ); \ \ static int InterpolateNearest##X( int, const int *, const int *, const Xtype *, \ const Xtype *, int, const int *, \ const double *const *, int, Xtype, Xtype *, \ Xtype *, int * ); \ \ static int Resample##X( AstMapping *, int, const int [], const int [], \ const Xtype [], const Xtype [], int, \ void (*)( void ), const double [], int, double, int, \ Xtype, int, const int [], const int [], \ const int [], const int [], Xtype [], Xtype [], int * ); \ \ static void ConserveFlux##X( double, int, const int *, Xtype, Xtype *, Xtype *, \ int * ); \ \ static void InterpolateBlockAverage##X( int, const int[], const int[], \ const Xtype [], const Xtype [], int, const int[], \ const double *const[], const double[], int, \ Xtype, Xtype *, Xtype *, int * ); DECLARE_GENERIC(B,signed char) DECLARE_GENERIC(D,double) DECLARE_GENERIC(F,float) DECLARE_GENERIC(I,int) DECLARE_GENERIC(K,INT_BIG) DECLARE_GENERIC(L,long int) DECLARE_GENERIC(S,short int) DECLARE_GENERIC(UB,unsigned char) DECLARE_GENERIC(UI,unsigned int) DECLARE_GENERIC(UK,UINT_BIG) DECLARE_GENERIC(UL,unsigned long int) DECLARE_GENERIC(US,unsigned short int) #if HAVE_LONG_DOUBLE /* Not normally implemented */ DECLARE_GENERIC(LD,long double) #endif #undef DECLARE_GENERIC #define DECLARE_GENERIC(X,Xtype) \ static void Rebin##X( AstMapping *, double, int, const int [], const int [], \ const Xtype [], const Xtype [], int, const double [], int, \ double, int, Xtype, int, const int [], const int [], \ const int [], const int [], Xtype [], Xtype [], int * ); \ \ static void RebinSeq##X( AstMapping *, double, int, const int [], const int [], \ const Xtype [], const Xtype [], int, const double [], \ int, double, int, Xtype, int, const int [], \ const int [], const int [], const int [], Xtype [], \ Xtype [], double [], int64_t *, int * ); \ \ static void SpreadKernel1##X( AstMapping *, int, const int *, const int *, \ const Xtype *, const Xtype *, double, int, const int *, \ const double *const *, \ void (*)( double, const double *, int, double *, int * ), \ int, const double *, double, int, Xtype, int, Xtype *, \ Xtype *, double *, int64_t *, int * ); \ \ static void SpreadLinear##X( int, const int *, const int *, const Xtype *, \ const Xtype *, double, int, const int *, const double *const *, \ double, int, Xtype, int, Xtype *, Xtype *, double *, int64_t *, \ int * ); \ \ static void SpreadNearest##X( int, const int *, const int *, const Xtype *, \ const Xtype *, double, int, const int *, const double *const *, \ double, int, Xtype, int, Xtype *, Xtype *, double *, \ int64_t *, int * ); DECLARE_GENERIC(D,double) DECLARE_GENERIC(F,float) DECLARE_GENERIC(I,int) DECLARE_GENERIC(UB,unsigned char) DECLARE_GENERIC(B,signed char) #if HAVE_LONG_DOUBLE /* Not normally implemented */ DECLARE_GENERIC(LD,long double) #endif #undef DECLARE_GENERIC static AstMapping *RemoveRegions( AstMapping *, int * ); static AstMapping *Simplify( AstMapping *, int * ); static AstPointSet *Transform( AstMapping *, AstPointSet *, int, AstPointSet *, int * ); static const char *GetAttrib( AstObject *, const char *, int * ); static double FindGradient( AstMapping *, double *, int, int, double, double, double *, int * ); static double J1Bessel( double, int * ); static double LocalMaximum( const MapData *, double, double, double [], int * ); static double MapFunction( const MapData *, const double [], int *, int * ); static double MatrixDet( int, int, const double *, int * ); static double MaxD( double, double, int * ); static double NewVertex( const MapData *, int, double, double [], double [], int *, double [], int * ); static double Random( long int *, int * ); static double Rate( AstMapping *, double *, int, int, int * ); static double UphillSimplex( const MapData *, double, int, const double [], double [], double *, int *, int * ); static int *MapSplit( AstMapping *, int, const int *, AstMapping **, int * ); static int Equal( AstObject *, AstObject *, int * ); static int GetInvert( AstMapping *, int * ); static int GetIsLinear( AstMapping *, int * ); static int GetIsSimple( AstMapping *, int * ); static int GetNin( AstMapping *, int * ); static int GetNout( AstMapping *, int * ); static int GetReport( AstMapping *, int * ); static int GetTranForward( AstMapping *, int * ); static int GetTranInverse( AstMapping *, int * ); static int LinearApprox( AstMapping *, const double *, const double *, double, double *, int * ); static int MapList( AstMapping *, int, int, int *, AstMapping ***, int **, int * ); static int MapMerge( AstMapping *, int, int, int *, AstMapping ***, int **, int * ); static int MaxI( int, int, int * ); static int MinI( int, int, int * ); static int DoNotSimplify( AstMapping *, int * ); static int QuadApprox( AstMapping *, const double[2], const double[2], int, int, double *, double *, int * ); static int RebinAdaptively( AstMapping *, int, const int *, const int *, const void *, const void *, DataType, int, const double *, int, double, int, const void *, int, const int *, const int *, const int *, const int *, int, void *, void *, double *, int64_t *, int * ); static int RebinWithBlocking( AstMapping *, const double *, int, const int *, const int *, const void *, const void *, DataType, int, const double *, int, const void *, int, const int *, const int *, const int *, const int *, int, void *, void *, double *, int64_t *, int * ); static int ResampleAdaptively( AstMapping *, int, const int *, const int *, const void *, const void *, DataType, int, void (*)( void ), const double *, int, double, int, const void *, int, const int *, const int *, const int *, const int *, void *, void *, int * ); static int ResampleSection( AstMapping *, const double *, int, const int *, const int *, const void *, const void *, DataType, int, void (*)( void ), const double *, double, int, const void *, int, const int *, const int *, const int *, const int *, void *, void *, int * ); static int ResampleWithBlocking( AstMapping *, const double *, int, const int *, const int *, const void *, const void *, DataType, int, void (*)( void ), const double *, int, const void *, int, const int *, const int *, const int *, const int *, void *, void *, int * ); static int SpecialBounds( const MapData *, double *, double *, double [], double [], int * ); static int TestAttrib( AstObject *, const char *, int * ); static int TestInvert( AstMapping *, int * ); static int TestReport( AstMapping *, int * ); static void ClearAttrib( AstObject *, const char *, int * ); static void ClearInvert( AstMapping *, int * ); static void ClearReport( AstMapping *, 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 Gauss( double, const double [], int, double *, int * ); static void GlobalBounds( MapData *, double *, double *, double [], double [], int * ); static void Invert( AstMapping *, int * ); static void MapBox( AstMapping *, const double [], const double [], int, int, double *, double *, double [], double [], int * ); static void RateFun( AstMapping *, double *, int, int, int, double *, double *, int * ); static void RebinSection( AstMapping *, const double *, int, const int *, const int *, const void *, const void *, double, DataType, int, const double *, int, const void *, int, const int *, const int *, const int *, const int *, int, void *, void *, double *, int64_t *, int * ); static void ReportPoints( AstMapping *, int, AstPointSet *, AstPointSet *, int * ); static void SetAttrib( AstObject *, const char *, int * ); static void SetInvert( AstMapping *, int, int * ); static void SetReport( AstMapping *, int, int * ); static void Sinc( double, const double [], int, double *, int * ); static void SincCos( double, const double [], int, double *, int * ); static void SincGauss( double, const double [], int, double *, int * ); static void SincSinc( double, const double [], int, double *, int * ); static void Somb( double, const double [], int, double *, int * ); static void SombCos( double, const double [], int, double *, int * ); static void Tran1( AstMapping *, int, const double [], int, double [], int * ); static void Tran2( AstMapping *, int, const double [], const double [], int, double [], double [], int * ); static void TranGrid( AstMapping *, int, const int[], const int[], double, int, int, int, int, double *, int * ); static void TranGridAdaptively( AstMapping *, int, const int[], const int[], const int[], const int[], double, int, int, double *[], int * ); static void TranGridSection( AstMapping *, const double *, int, const int *, const int *, const int *, const int *, int, double *[], int * ); static void TranGridWithBlocking( AstMapping *, const double *, int, const int *, const int *, const int *, const int *, int, double *[], int * ); static void TranN( AstMapping *, int, int, int, const double *, int, int, int, double *, int * ); static void TranP( AstMapping *, int, int, const double *[], int, int, double *[], int * ); static void ValidateMapping( AstMapping *, int, int, int, int, const char *, int * ); /* Member functions. */ /* ================= */ static void ClearAttrib( AstObject *this_object, const char *attrib, int *status ) { /* * Name: * ClearAttrib * Purpose: * Clear an attribute value for a Mapping. * Type: * Private function. * Synopsis: * #include "mapping.h" * void ClearAttrib( AstObject *this, const char *attrib, int *status ) * Class Membership: * Mapping member function (over-rides the astClearAttrib protected * method inherited from the Object class). * Description: * This function clears the value of a specified attribute for a * Mapping, so that the default value will subsequently be used. * Parameters: * this * Pointer to the Mapping. * attrib * Pointer to a null terminated string specifying the attribute * name. This should be in lower case with no surrounding white * space. * status * Pointer to the inherited status variable. */ /* Local Variables: */ AstMapping *this; /* Pointer to the Mapping structure */ /* Check the global error status. */ if ( !astOK ) return; /* Obtain a pointer to the Mapping structure. */ this = (AstMapping *) this_object; /* Check the attribute name and clear the appropriate attribute. */ /* Invert. */ /* ------- */ if ( !strcmp( attrib, "invert" ) ) { astClearInvert( this ); /* Report. */ /* ------- */ } else if ( !strcmp( attrib, "report" ) ) { astClearReport( this ); /* If the name was not recognised, test if it matches any of the read-only attributes of this class. If it does, then report an error. */ } else if ( !strcmp( attrib, "nin" ) || !strcmp( attrib, "nout" ) || !strcmp( attrib, "issimple" ) || !strcmp( attrib, "islinear" ) || !strcmp( attrib, "tranforward" ) || !strcmp( attrib, "traninverse" ) ) { astError( AST__NOWRT, "astClear: Invalid attempt to clear the \"%s\" " "value for a %s.", status, attrib, astGetClass( this ) ); astError( AST__NOWRT, "This is a read-only attribute." , status); /* If the attribute is still not recognised, pass it on to the parent method for further interpretation. */ } else { (*parent_clearattrib)( this_object, attrib, status ); } } /* * Name: * ConserveFlux * Purpose: * Scale the output data and variance values produced by ResampleSection * by the given flux conservation factor. * Type: * Private function. * Synopsis: * #include "mapping.h" * void ConserveFlux( double factor, int npoint, const int *offset, * badval, *out, * *out_var ) * Class Membership: * Mapping member function. * Description: * This is a set of functions which scale the supplied resampled data * values by the given flux conservation factor. It also scales any * variances by the square of the factor. * Parameters: * factor * The flux conservation factor. This should be the ratio of the * output pixel size to the input pixel size, in the locality of * the supplied data values. * npoint * The number of points at which the input grid was resampled. * offset * Pointer to an array of integers with "npoint" elements. For * each output point, this array should contain the zero-based * offset in the output array(s) (i.e. the "out" and, * optionally, the "out_var" arrays) at which the resampled * output value(s) is stored. * badval * This parameter specifies the value which is used to identify * bad data and/or variance values in the output array(s). * out * Pointer to an array in which the resampled data is supplied. Note * that details of how the output grid maps on to this array * (e.g. the storage order, number of dimensions, etc.) is * arbitrary and is specified entirely by means of the "offset" * array. The "out" array should therefore contain sufficient * elements to accommodate the "offset" values supplied. There * is no requirement that all elements of the "out" array should * be assigned values, and any which are not addressed by the * contents of the "offset" array will be left unchanged. * out_var * An optional pointer to an array with the same data type and * size as the "out" array, in which variance estimates for * the resampled values are supplied. If no output variance estimates * are available, a NULL pointer should be given. * Notes: * - There is a separate function for each numerical type of * gridded data, distinguished by replacing the in the function * name by the appropriate 1- or 2-character suffix. */ /* Define a macro to implement the function for a specific data type. */ #define MAKE_CONSERVEFLUX(X,Xtype) \ static void ConserveFlux##X( double factor, int npoint, const int *offset, \ Xtype badval, Xtype *out, Xtype *out_var, int *status ) { \ \ /* Local Variables: */ \ int off_out; /* Pixel offset into output array */ \ int point; /* Loop counter for output points */ \ \ \ /* Check the global error status. */ \ if ( !astOK ) return; \ \ for ( point = 0; point < npoint; point++ ) { \ off_out = offset[ point ]; \ if( out[ off_out ] != badval ) out[ off_out ] *= factor; \ } \ \ if( out_var ) { \ factor *= factor; \ for ( point = 0; point < npoint; point++ ) { \ off_out = offset[ point ]; \ if( out_var[ off_out ] != badval ) out_var[ off_out ] *= factor; \ } \ } \ } /* Expand the macro above to generate a function for each required data type. */ #if HAVE_LONG_DOUBLE /* Not normally implemented */ MAKE_CONSERVEFLUX(LD,long double) #endif MAKE_CONSERVEFLUX(D,double) MAKE_CONSERVEFLUX(F,float) MAKE_CONSERVEFLUX(K,INT_BIG) MAKE_CONSERVEFLUX(L,long int) MAKE_CONSERVEFLUX(I,int) MAKE_CONSERVEFLUX(S,short int) MAKE_CONSERVEFLUX(B,signed char) MAKE_CONSERVEFLUX(UL,unsigned long int) MAKE_CONSERVEFLUX(UI,unsigned int) MAKE_CONSERVEFLUX(UK,UINT_BIG) MAKE_CONSERVEFLUX(US,unsigned short int) MAKE_CONSERVEFLUX(UB,unsigned char) /* Undefine the macros used above. */ #undef MAKE_CONSERVEFLUX static void Decompose( AstMapping *this, AstMapping **map1, AstMapping **map2, int *series, int *invert1, int *invert2, int *status ) { /* *+ * Name: * astDecompose * Purpose: * Decompose a Mapping into two component Mappings. * Type: * Protected virtual function. * Synopsis: * #include "mapping.h" * void astDecompose( AstMapping *this, AstMapping **map1, * AstMapping **map2, int *series, int *invert1, * int *invert2 ) * Class Membership: * Mapping method. * 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 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. * Applicability: * CmpMap * If the supplied Mapping is a CmpMap, then map1 and map2 will be * returned holding pointers to the component Mappings used to * create the CmpMap, either in series or parallel. * Mapping * For any class of Mapping other than a CmpMap, map1 will be * returned holding a clone of the supplied Mapping pointer, and map2 * will be returned holding a NULL pointer. * CmpFrame * If the supplied Mapping is a CmpFrame, then map1 and map2 will be * returned holding pointers to the component Frames used to * create the CmpFrame. The component Frames are considered to be in * applied in parallel. * Frame * For any class of Frame other than a CmpFrame, map1 will be * returned holding a clone of the supplied Frame pointer, and map2 * will be returned holding a NULL pointer. * Notes: * - Any changes made to the component Mappings using the returned * pointers will be reflected in the supplied Mapping. * - The returned Invert values should be used in preference to the * current values of the Invert attribute in map1 and map2. This is * because the attributes may have changed value since the Mappings * were combined. * Implementation Notes: * - This function implements the basic astDecompose method * available via the protected interface to the Frame class. The * public interface to this method is provided by the * astDecomposeId_ function. *- */ /* Check the global error status. */ if ( !astOK ) return; /* The basic Mapping class returns a clone of the supplied Mapping as map1 and a NULL pointer as map2. */ if( map1 ) *map1 = astClone( this ); if( map2 ) *map2 = NULL; if( series ) *series = 1; if( invert1 ) *invert1 = astGetInvert( this ); if( invert2 ) *invert2 = 0; } static int DoNotSimplify( AstMapping *this, int *status ) { /* *+ * Name: * astMapMerge * Purpose: * Check if a Mapping is appropriate for simplification. * Type: * Protected virtual function. * Synopsis: * #include "mapping.h" * int astDoNotSImplify( AstMapping *this ); * Class Membership: * Mapping method. * Description: * This function returns a flag indivating if the supplied Mapping is * appropriate for simplification. * Parameters: * this * Pointer to the Mapping. * Returned Value: * Non-zero if the supplied Mapping is not appropriate for * simplification, and zero otherwise. * Notes: * - A value of 0 will be returned if this function is invoked * with the global error status set, or if it should fail for any * reason. *- */ /* Check inherited status. */ if( !astOK ) return 0; /* Mappings that have a set value for the Ident attribute should not be simplified since we want to preserve their individual identify (otherwise why would the user have given them an Ident value?). */ return astTestIdent( this ); } int astRateState_( int disabled, int *status ) { /* *+ * Name: * astRateState * Purpose: * Control whether the astRate method is disabled or not. * Type: * Protected function. * Synopsis: * #include "mapping.h" * int astRateState( int disabled ) * Class Membership: * Mapping member function * Description: * Some algorithms which use use the astRate method do not actually need * to know what the Rate value is. For instance, when the Plot class draws * a border it evaluates the GRAPHICS->Current Mapping hundreds of time. * If the Mapping includes a RateMap then this can be very very slow * (depending on how the astRate method is implemented). In fact the * border drawing algorithm onlyneeds to know if the result is bad or * not - the actual value produced by the Mappign does not matter. * * Such algorithms can be speeded up by forcing the astRate method to * return a constant value rather than actually doing the numerical * differentiation. This can be accomplised by calling this method prior * to implementing the algorithm. It should be called at the end in * order to re-instate the original disabled flag. * Parameters: * disabled * The new value for the astRate disabled flag. * Returned Value: * The original value of the astRate disabled flag. *- */ astDECLARE_GLOBALS int result; astGET_GLOBALS(NULL); result = rate_disabled; rate_disabled = disabled; return result; } static int Equal( AstObject *this_object, AstObject *that_object, int *status ) { /* * Name: * Equal * Purpose: * Test if two Mappings are equivalent. * Type: * Private function. * Synopsis: * #include "mapping.h" * int Equal( AstObject *this, AstObject *that, int *status ) * Class Membership: * Mapping member function (over-rides the astEqual protected * method inherited from the Object class). * Description: * This function returns a boolean result (0 or 1) to indicate whether * two Mappings are equivalent. * * The implementation provided by this class (the base Mapping class) * simply reports an error when called, since all concrete Mapping * subclasses should provide their own implementation. * * Note, sub-class implementations should not use astSimplify (e.g. * combining the two Mapping and then simplifying it), since the * astSimplify method for certain classes (e.g. CmpMap) may use * astEqual. Consequently, if astEqual called astSimplify, there would * be possibilities for infinite loops. * Parameters: * this * Pointer to the first Object (a Mapping). * that * Pointer to the second Object. * status * Pointer to the inherited status variable. * Returned Value: * One if the Frames are equivalent, zero otherwise. * Notes: * - The two Mappings are considered equivalent if the combination of * the first in series with the inverse of the second simplifies to a * UnitMap. * - 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: */ int result; /* Result value to return */ /* Initialise. */ result = 0; /* Check the global error status. */ if ( !astOK ) return result; /* Invoke the Equal method inherited from the parent Object class. This checks that the Objects are both of the same class (amongst other things). */ if( (*parent_equal)( this_object, that_object, status ) ) { /* Report an error since the concrete sub-class should have over-riden this method. */ astError( AST__INTER, "astEqual(Mapping): The %s class does " "not override the abstract astEqual method inherited " "from the base Mapping class (internal AST programming " "error).", status, astGetClass( this_object ) ); } /* If an error occurred, clear the result value. */ if ( !astOK ) result = 0; /* Return the result, */ return result; } static double FindGradient( AstMapping *map, double *at, int ax1, int ax2, double x0, double h, double *range, int *status ){ /* * Name: * FindGradient * Purpose: * Find the mean gradient in an interval, and the range of gradients * within the interval. * Type: * Private function. * Synopsis: * #include "mapping.h" * double FindGradient( AstMapping *map, double *at, int ax1, int ax2, * double x0, double h, double *range, int *status ) * Class Membership: * Mapping method. * Description: * This function finds the mean gradient in an interval, and the range * of gradients within the interval. * Parameters: * map * Pointer to a Mapping which yields the value of the function at x. * The Mapping may have any number of inputs and outputs; the specific * output representing the function value, f, is specified by ax1 and * the specific input representing the argument, x, is specified by ax2. * at * A pointer to an array holding axis values at the position at which * the function is to be evaluated. The number of values supplied * must equal the number of inputs to the Mapping. The value supplied * for axis "ax2" is ignored (the value of "x" is used for axis "ax2"). * ax1 * The zero-based index of the Mapping output which is to be * differentiated. Set this to -1 to allocate, or -2 to release, * the static resources used by this function. * ax2 * The zero-based index of the Mapping input which is to be varied. * x0 * The central axis value at which the function is to be evaluated. * h * The interval over which the fitting is to be performed. * range * A pointer to a location at which to return the range of * gradients found within the interval. * status * Pointer to the inherited status variable. * Returns: * The mean gradient, or AST__BAD if the mean gradient cannot be * calculated. */ /* Local Variables: */ double dh; double g; double gmax; double gmin; double ret; double x1; double x2; double x[ RATE_ORDER + 2 ]; double y1; double y2; double y[ RATE_ORDER + 2 ]; int i0; int i; int ngood; /* Initialise */ ret = AST__BAD; /* Check the global error status. */ if ( !astOK ) return ret; /* Store the x values at (RATE_ORDER+1) evenly spaced points over the interval "h" centred on "x0". */ i0 = RATE_ORDER/2; dh = h/RATE_ORDER; for( i = 0; i <= RATE_ORDER; i++ ) { x[ i ] = x0 + ( i - i0 )*dh; } /* Get the function values at these positions. */ RateFun( map, at, ax1, ax2, RATE_ORDER + 1, x, y, status ); /* Find the maximum and minimum mean gradient within any sub-interval, and note the (x,y) values at the first and last good point within the interval. */ y1 = AST__BAD; y2 = AST__BAD; gmax = AST__BAD; gmin = AST__BAD; ngood = 0; for( i = 0; i < RATE_ORDER; i++ ) { if( y[ i + 1 ] !=AST__BAD && y[ i ] != AST__BAD && x[ i + 1 ] != x[ i ] ) { ngood++; g = ( y[ i + 1 ] - y[ i ] )/( x[ i + 1 ] - x[ i ] ); if( ngood == 1 ) { gmax = gmin = g; } else if( g < gmin ) { gmin = g; } else if( g > gmax) { gmax = g; } if( y1 == AST__BAD ) { y1 = y[ i ]; x1 = x[ i ]; } y2 = y[ i + 1 ]; x2 = x[ i + 1 ]; } } /* If two or more sub-intervals were usable, return the range of gradients found, and the mean gradient. */ if( ngood > 1 ) { ret = ( y2 - y1 )/( x2 - x1 ); if( range ) *range = ( gmax - gmin ); } return ret; } static void Gauss( double offset, const double params[], int flags, double *value, int *status ) { /* * Name: * Gauss * Purpose: * 1-dimensional Gaussian spreading kernel. * Type: * Private function. * Synopsis: * #include "mapping.h" * void Gauss( double offset, const double params[], int flags, * double *value, int *status ) * Class Membership: * Mapping member function. * Description: * This function calculates the value of a 1-dimensional sub-pixel * spreading kernel. The function used is exp(-k*x*x). * Parameters: * offset * The offset of a pixel from the central output point, measured * in pixels. * params * The first element of this array should give a value for "k" * in the exp(-k*x*x) term. * flags * Not used. * value * Pointer to a double to receive the calculated kernel value. * status * Pointer to the inherited status variable. * Notes: * - This function does not perform error checking and does not * generate errors. */ /* Calculate the result. */ *value = exp( -params[ 0 ] * offset * offset ); } static const char *GetAttrib( AstObject *this_object, const char *attrib, int *status ) { /* * Name: * GetAttrib * Purpose: * Get the value of a specified attribute for a Mapping. * Type: * Private function. * Synopsis: * #include "mapping.h" * const char *GetAttrib( AstObject *this, const char *attrib, int *status ) * Class Membership: * Mapping member function (over-rides the protected astGetAttrib * method inherited from the Object class). * Description: * This function returns a pointer to the value of a specified * attribute for a Mapping, formatted as a character string. * Parameters: * this * Pointer to the Mapping. * attrib * Pointer to a null terminated string containing the name of * the attribute whose value is required. This name should be in * lower case, with all white space removed. * status * Pointer to the inherited status variable. * Returned Value: * Pointer to a null terminated string containing the attribute * value. * Notes: * - The returned string pointer may point at memory allocated * within the Mapping, or at static memory. The contents of the * string may be over-written or the pointer may become invalid * following a further invocation of the same function or any * modification of the Mapping. A copy of the string should * therefore be made if necessary. * - A NULL pointer will be returned if this function is invoked * with the global error status set, or if it should fail for any * reason. */ /* Local Variables: */ astDECLARE_GLOBALS /* Pointer to thread-specific global data */ AstMapping *this; /* Pointer to the Mapping structure */ const char *result; /* Pointer value to return */ int invert; /* Invert attribute value */ int islinear; /* IsLinear attribute value */ int issimple; /* IsSimple attribute value */ int nin; /* Nin attribute value */ int nout; /* Nout attribute value */ int report; /* Report attribute value */ int tran_forward; /* TranForward attribute value */ int tran_inverse; /* TranInverse attribute value */ /* Initialise. */ result = NULL; /* Check the global error status. */ if ( !astOK ) return result; /* Get a pointer to the thread specific global data structure. */ astGET_GLOBALS(this_object); /* Obtain a pointer to the Mapping structure. */ this = (AstMapping *) this_object; /* Compare "attrib" with each recognised attribute name in turn, obtaining the value of the required attribute. If necessary, write the value into "getattrib_buff" as a null terminated string in an appropriate format. Set "result" to point at the result string. */ /* Invert. */ /* ------- */ if ( !strcmp( attrib, "invert" ) ) { invert = astGetInvert( this ); if ( astOK ) { (void) sprintf( getattrib_buff, "%d", invert ); result = getattrib_buff; } /* IsLinear. */ /* --------- */ } else if ( !strcmp( attrib, "islinear" ) ) { islinear = astGetIsLinear( this ); if ( astOK ) { (void) sprintf( getattrib_buff, "%d", islinear ); result = getattrib_buff; } /* IsSimple. */ /* --------- */ } else if ( !strcmp( attrib, "issimple" ) ) { issimple = astGetIsSimple( this ); if ( astOK ) { (void) sprintf( getattrib_buff, "%d", issimple ); result = getattrib_buff; } /* Nin. */ /* ---- */ } else if ( !strcmp( attrib, "nin" ) ) { nin = astGetNin( this ); if ( astOK ) { (void) sprintf( getattrib_buff, "%d", nin ); result = getattrib_buff; } /* Nout. */ /* ----- */ } else if ( !strcmp( attrib, "nout" ) ) { nout = astGetNout( this ); if ( astOK ) { (void) sprintf( getattrib_buff, "%d", nout ); result = getattrib_buff; } /* Report. */ /* ------- */ } else if ( !strcmp( attrib, "report" ) ) { report = astGetReport( this ); if ( astOK ) { (void) sprintf( getattrib_buff, "%d", report ); result = getattrib_buff; } /* TranForward. */ /* ------------ */ } else if ( !strcmp( attrib, "tranforward" ) ) { tran_forward = astGetTranForward( this ); if ( astOK ) { (void) sprintf( getattrib_buff, "%d", tran_forward ); result = getattrib_buff; } /* TranInverse. */ /* ------------ */ } else if ( !strcmp( attrib, "traninverse" ) ) { tran_inverse = astGetTranInverse( this ); if ( astOK ) { (void) sprintf( getattrib_buff, "%d", tran_inverse ); result = getattrib_buff; } /* If the attribute name was not recognised, pass it on to the parent method for further interpretation. */ } else { result = (*parent_getattrib)( this_object, attrib, status ); } /* Return the result. */ return result; } static int GetIsLinear( AstMapping *this, int *status ) { /* *+ * Name: * astGetIsLinear * Purpose: * Determine if a Mapping is an instance of a linear Mapping class. * Type: * Protected virtual function. * Synopsis: * #include "mapping.h" * int astGetIsLinear( AstMapping *this ) * Class Membership: * Mapping method. * Description: * This function returns a value indicating whether a Mapping is * a member of a class of linear Mappings. The base Mapping class * returns a value of zero. Linear Mapping classes should over-ride * this function to return a non-zero value. * Parameters: * this * Pointer to the Mapping. * Returned Value: * One if the Mapping is a member of a linear Mapping class. Zero * otherwise. * Notes: * - A value of zero will be returned if this function is invoked * with the global error status set, or if it should fail for any * reason. *- */ return 0; } static int GetNin( AstMapping *this, int *status ) { /* *+ * Name: * astGetNin * Purpose: * Get the number of input coordinates for a Mapping. * Type: * Protected virtual function. * Synopsis: * #include "mapping.h" * int astGetNin( AstMapping *this ) * Class Membership: * Mapping method. * Description: * This function returns the number of input coordinate values * required per point by a Mapping (i.e. the number of dimensions * of the space in which input points reside). * Parameters: * this * Pointer to the Mapping. * Returned Value: * Number of coordinate values required. * Notes: * - A value of zero will be returned if this function is invoked * with the global error status set, or if it should fail for any * reason. *- */ /* Local Variables: */ int invert; /* Invert attribute value */ int result; /* Result value to return */ /* Initialise. */ result = 0; /* Check the global error status. */ if ( !astOK ) return result; /* Determine if the Mapping has been inverted. */ invert = astGetInvert( this ); /* Obtain the Nin value. */ if ( astOK ) result = invert ? this->nout : this->nin; /* Return the result. */ return result; } static int GetNout( AstMapping *this, int *status ) { /* *+ * Name: * astGetNout * Purpose: * Get the number of output coordinates for a Mapping. * Type: * Protected virtual function. * Synopsis: * #include "mapping.h" * int astGetNout( AstMapping *this ) * Class Membership: * Mapping method. * Description: * This function returns the number of output coordinate values * generated per point by a Mapping (i.e. the number of dimensions * of the space in which output points reside). * Parameters: * this * Pointer to the Mapping. * Returned Value: * Number of coordinate values generated. * Notes: * - A value of zero will be returned if this function is invoked * with the global error status set, or if it should fail for any * reason. *- */ /* Local Variables: */ int invert; /* Invert attribute value */ int result; /* Result value to return */ /* Initialise. */ result = 0; /* Check the global error status. */ if ( !astOK ) return result; /* Determine if the Mapping has been inverted. */ invert = astGetInvert( this ); /* Obtain the Nout value. */ if ( astOK ) result = invert ? this->nin : this->nout; /* Return the result. */ return result; } static int GetTranForward( AstMapping *this, int *status ) { /* *+ * Name: * astGetTranForward * Purpose: * Determine if a Mapping defines a forward coordinate transformation. * Type: * Protected virtual function. * Synopsis: * #include "mapping.h" * int astGetTranForward( AstMapping *this ) * Class Membership: * Mapping method. * Description: * This function returns a value indicating whether a Mapping is * able to perform a coordinate transformation in the "forward" * direction. * Parameters: * this * Pointer to the Mapping. * Returned Value: * Zero if the forward coordinate transformation is not defined, or * 1 if it is. * Notes: * - A value of zero will be returned if this function is invoked * with the global error status set, or if it should fail for any * reason. *- */ /* Local Variables: */ int invert; /* Mapping inverted? */ int result; /* Result value to return */ /* Initialise. */ result = 0; /* Check the global error status. */ if ( !astOK ) return result; /* Determine if the Mapping has been inverted. */ invert = astGetInvert( this ); /* If OK, obtain the result. */ if ( astOK ) result = invert ? this->tran_inverse : this->tran_forward; /* Return the result. */ return result; } static int GetTranInverse( AstMapping *this, int *status ) { /* *+ * Name: * astGetTranInverse * Purpose: * Determine if a Mapping defines an inverse coordinate transformation. * Type: * Protected virtual function. * Synopsis: * #include "mapping.h" * int astGetTranInverse( AstMapping *this ) * Class Membership: * Mapping method. * Description: * This function returns a value indicating whether a Mapping is * able to perform a coordinate transformation in the "inverse" * direction. * Parameters: * this * Pointer to the Mapping. * Returned Value: * Zero if the inverse coordinate transformation is not defined, or * 1 if it is. * Notes: * - A value of zero will be returned if this function is invoked * with the global error status set, or if it should fail for any * reason. *- */ /* Local Variables: */ int invert; /* Mapping inverted? */ int result; /* Result value to return */ /* Initialise. */ result = 0; /* Check the global error status. */ if ( !astOK ) return result; /* Determine if the Mapping has been inverted. */ invert = astGetInvert( this ); /* If OK, obtain the result. */ if ( astOK ) result = invert ? this->tran_forward : this->tran_inverse; /* Return the result. */ return result; } static void GlobalBounds( MapData *mapdata, double *lbnd, double *ubnd, double xl[], double xu[], int *status ) { /* * Name: * GlobalBounds * Purpose: * Estimate global coordinate bounds for a Mapping. * Type: * Private function. * Synopsis: * #include "mapping.h" * void GlobalBounds( MapData *mapdata, double *lbnd, double *ubnd, * double xl[], double xu[], int *status ); * Class Membership: * Mapping member function. * Description: * This function estimates the global lower and upper bounds of a * Mapping function within a constrained region of its input * coordinate space. It uses a robust global optimisation algorithm * based on the selection of pseudo-random starting positions, * followed by the location of local minima and maxima using the * downhill (or uphill) simplex method. The algorithm will cope * with the case where there are several competing minima (or * maxima) with nearly equal values. It attempts to locate the * global bounds to full machine precision when possible. * Parameters: * mapdata * Pointer to a MapData structure describing the Mapping * function, its coordinate constraints, etc. * lbnd * Pointer to a double. On entry, this should contain a * previously-obtained upper limit on the global lower bound, or * AST__BAD if no such limit is available. On exit, it will be * updated with a new estimate of the global lower bound, if a * better one has been found. * ubnd * Pointer to a double. On entry, this should contain a * previously-obtained lower limit on the global upper bound, or * AST__BAD if no such limit is available. On exit, it will be * updated with a new estimate of the global upper bound, if a * better one has been found. * xl * Pointer to an array of double, with one element for each * input coordinate. On entry, if *lbnd is not equal to AST__OK, * this should contain the input coordinates of a point at which * the Mapping function takes the value *lbnd. On exit, this * function returns the position of a (not necessarily unique) * input point at which the Mapping function takes the value of * the new global lower bound. This array is not altered if an * improved estimate of the global lower bound cannot be found. * xu * Pointer to an array of double, with one element for each * input coordinate. On entry, if *ubnd is not equal to AST__OK, * this should contain the input coordinates of a point at which * the Mapping function takes the value *ubnd. On exit, this * function returns the position of a (not necessarily unique) * input point at which the Mapping function takes the value of * the new global upper bound. This array is not altered if an * improved estimate of the global upper bound cannot be found. * status * Pointer to the inherited status variable. * Notes: * - The efficiency of this function will usually be improved if * previously-obtained estimates of the extrema and their locations * are provided. * - The values returned via "lbnd", "ubnd", "xl" and "xu" will be * set to the value AST__BAD if this function should fail for any * reason. Their initial values on entry will not be altered if the * function is invoked with the global error status set. */ /* Local Constants: */ const double default_acc = 3.0e-5; /* Default convergence accuracy */ const int maxiter = 10000; /* Maximum number of iterations */ const int minsame = 5; /* Minimum no. consistent extrema required */ const int nbatch = 32; /* No. function samples obtained per batch */ /* Local Variables: */ AstPointSet *pset_in; /* Input PointSet for batch transformation */ AstPointSet *pset_out; /* Output PointSet for batch transformation */ double **ptr_in; /* Pointer to batch input coordinates */ double **ptr_out; /* Pointer to batch output coordinates */ double *active_hi; /* Estimated upper limits of active region */ double *active_lo; /* Estimated lower limits of active region */ double *sample_hi; /* Upper limits of sampled region */ double *sample_lo; /* Lower limits of sampled region */ double *sample_width; /* Nominal widths of sampled region */ double *x; /* Pointer to array of coordinates */ double acc; /* Convergence accuracy for finding maximum */ double active_width; /* Estimated width of active region */ double new_max; /* Value of new local maximum */ double new_min; /* Value of new local minimum */ double oversize; /* Over-size factor for sampled region */ double random; /* Pseudo-random number */ int bad; /* Transformed position is bad? */ int batch; /* Next element to use in position batch */ int coord; /* Loop counter for coordinates */ int done_max; /* Satisfactory global maximum found? */ int done_min; /* Satisfactory global minimum found? */ int iter; /* Loop counter for iterations */ int ncoord; /* Number of coordinates in search space */ int nmax; /* Number of local maxima found */ int nmin; /* Number of local minima found */ int nsame_max; /* Number of equivalent local maxima found */ int nsame_min; /* Number of equivalent local minima found */ long int seed = 1776655449; /* Arbitrary pseudo-random number seed */ /* Check the global error status */ if ( !astOK ) return; /* Initialise. */ done_max = 0; done_min = 0; nmax = 0; nmin = 0; nsame_max = 0; nsame_min = 0; pset_in = NULL; pset_out = NULL; ptr_in = NULL; ptr_out = NULL; oversize = 0; bad = 0; /* Extract the number of input coordinates for the Mapping function and allocate workspace. */ ncoord = mapdata->nin; active_hi = astMalloc( sizeof( double ) * (size_t) ncoord ); active_lo = astMalloc( sizeof( double ) * (size_t) ncoord ); sample_hi = astMalloc( sizeof( double ) * (size_t) ncoord ); sample_lo = astMalloc( sizeof( double ) * (size_t) ncoord ); sample_width = astMalloc( sizeof( double ) * (size_t) ncoord ); x = astMalloc( sizeof( double ) * (size_t) ncoord ); if ( astOK ) { /* Calculate the factor by which the size of the region we sample will exceed the size of the Mapping function's active region (the region where the transformed coordinates are non-bad) in each dimension. This is chosen so that the volume ratio will be 2. */ oversize = pow( 2.0, 1.0 / (double) ncoord ); /* Initialise the limits of the active region to unknown. */ for ( coord = 0; coord < ncoord; coord++ ) { active_lo[ coord ] = DBL_MAX;; active_hi[ coord ] = -DBL_MAX; /* Initialise the nominal widths of the sampled region to be the actual widths of the search region times the over-size factor. */ sample_width[ coord ] = ( mapdata->ubnd[ coord ] - mapdata->lbnd[ coord ] ) * oversize; /* Initialise the sampled region to match the search region. */ sample_lo[ coord ] = mapdata->lbnd[ coord ]; sample_hi[ coord ] = mapdata->ubnd[ coord ]; } /* Set up position buffer. */ /* ======================= */ /* Create two PointSets to act as buffers to hold a complete batch of input and output coordinates. Obtain pointers to their coordinate arrays. */ pset_in = astPointSet( nbatch, ncoord, "", status ); pset_out = astPointSet( nbatch, mapdata->nout, "", status ); ptr_in = astGetPoints( pset_in ); ptr_out = astGetPoints( pset_out ); /* Initialise the next element to be used in the position buffer to indicate that the buffer is initially empty. */ batch = nbatch; } /* Define a macro to fill the position buffer with a set of pseudo-random positions and to transform them. */ #define FILL_POSITION_BUFFER {\ \ /* We first generate a suitable volume over which to distribute the\ batch of pseudo-random positions. Initially, this will be the\ entire search volume, but if we find that the only non-bad\ transformed coordinates we obtain are restricted to a small\ sub-region of this input volume, then we reduce the sampled volume\ so as to concentrate more on the active region. */\ \ /* Loop through each input coordinate, checking that at least one\ non-bad transformed point has been obtained. If not, we do not\ adjust the sampled volume, as we do not yet know where the active\ region lies. */\ for ( coord = 0; coord < ncoord; coord++ ) {\ if ( active_hi[ coord ] >= active_lo[ coord ] ) {\ \ /* Estimate the width of the active region from the range of input\ coordinates that have so far produced non-bad transformed\ coordinates. */\ active_width = active_hi[ coord ] - active_lo[ coord ];\ \ /* If the current width of the sampled volume exceeds this estimate by\ more than the required factor, then reduce the width of the sampled\ volume. The rate of reduction is set so that the volume of the\ sampled region can halve with every fourth batch of positions. */\ if ( ( active_width * oversize ) < sample_width[ coord ] ) {\ sample_width[ coord ] /= pow( oversize, 0.25 );\ \ /* If the width of the sampled volume does not exceed that of the\ known active region by the required factor, then adjust it so that\ it does. Note that we must continue to sample some points outside\ the known active region in case we have missed any (in which case\ the sampled region will expand again to include them). */\ } else if ( ( active_width * oversize ) > sample_width[ coord ] ) {\ sample_width[ coord ] = active_width * oversize;\ }\ \ /* Calculate the lower and upper bounds on the sampled volume, using\ the new width calculated above and centring it on the active\ region, as currently known. */\ sample_lo[ coord ] = ( active_lo[ coord ] + active_hi[ coord ] -\ sample_width[ coord ] ) * 0.5;\ sample_hi[ coord ] = ( active_lo[ coord ] + active_hi[ coord ] +\ sample_width[ coord ] ) * 0.5;\ \ /* Ensure that the sampled region does not extend beyond the original\ search region. */\ if ( sample_lo[ coord ] < mapdata->lbnd[ coord ] ) {\ sample_lo[ coord ] = mapdata->lbnd[ coord ];\ }\ if ( sample_hi[ coord ] > mapdata->ubnd[ coord ] ) {\ sample_hi[ coord ] = mapdata->ubnd[ coord ];\ }\ }\ }\ \ /* Having determined the size of the sampled volume, create a batch of\ pseudo-random positions uniformly distributed within it. */\ for ( batch = 0; batch < nbatch; batch++ ) {\ for ( coord = 0; coord < ncoord; coord++ ) {\ random = Random( &seed, status );\ ptr_in[ coord ][ batch ] = sample_lo[ coord ] * random +\ sample_hi[ coord ] * ( 1.0 - random );\ }\ }\ \ /* Transform these positions. We process them in a single batch in\ order to minimise the overheads in doing this. */\ (void) astTransform( mapdata->mapping, pset_in, mapdata->forward,\ pset_out );\ \ /* Indicate that the position buffer is now full. */\ batch = 0;\ } /* Fill the position buffer using the above macro. (Note that because we do not yet have an estimate of the size of the active region, this does not change the sampled region size from our earlier initialised values. */ FILL_POSITION_BUFFER; /* Iterate. */ /* ======== */ /* Loop to perform up to "maxiter" iterations to estimate the global minimum and maximum. */ for ( iter = 0; astOK && ( iter < maxiter ); iter++ ) { /* Determine the search accuracy. */ /* ============================== */ /* Decide the accuracy to which local extrema should be found. The intention here is to optimise performance, especially where one extremum lies near zero and so could potentially be found to unnecessarily high precision. If we make a mis-assumption (the code below is not fool-proof), we will slow things down for this iteration, but the error will be corrected in future iterations once better estimates are available. */ /* If we have no current estimate of either global extremum, we assume the values we eventually obtain will be of order unity and required to the default accuracy. */ acc = default_acc; /* If we already have an estimate of both global extrema, we set the accuracy level so that the difference between them will be known to the default accuracy. */ if ( ( *lbnd != AST__BAD ) && ( *ubnd != AST__BAD ) ) { acc = fabs( *ubnd - *lbnd ) * default_acc; /* If we have an estimate of only one global extremum, we assume that the difference between the two global extrema will eventually be of the same order as the estimate we currently have, so long as this is not less than unity. */ } else if ( *lbnd != AST__BAD ) { if ( fabs( *lbnd ) > 1.0 ) acc = fabs( *lbnd) * default_acc; } else if ( *ubnd != AST__BAD ) { if ( fabs( *ubnd ) > 1.0 ) acc = fabs( *ubnd) * default_acc; } /* Search for a new local minimum. */ /* =============================== */ /* If we are still searching for the global minimum, then obtain a set of starting coordinates from which to find a new local minimum. */ if ( !done_min ) { /* On the first iteration, start searching at the position where the best estimate of the global minimum (if any) has previously been found. We know that this produces non-bad transformed coordinates. */ bad = 0; if ( !iter && ( *lbnd != AST__BAD ) ) { for ( coord = 0; coord < ncoord; coord++ ) { x[ coord ] = xl[ coord ]; } /* Otherwise, if no estimate of the global minimum is available, then start searching at the position where the best estimate of the global maximum (if any) has been found. This may be a long way from a local minimum, but at least it will yield a non-bad value for the Mapping function, so some sort of estimate of the global minimum will be obtained. This is important in cases where finding the active region of the function is the main problem. Note that this condition can only occur once, since the global minimum will have an estimate on the next iteration. */ } else if ( ( *lbnd == AST__BAD ) && ( *ubnd != AST__BAD ) ) { for ( coord = 0; coord < ncoord; coord++ ) { x[ coord ] = xu[ coord ]; } /* Having exhausted the above possibilities, we use pseudo-random starting positions which are uniformly distributed throughout the search volume. First check to see if the buffer containing such positions is empty and refill it if necessary. */ } else { if ( batch >= nbatch ) FILL_POSITION_BUFFER; /* Test the next available set of output (transformed) coordinates in the position buffer to see if they are bad. */ if ( astOK ) { for ( coord = 0; coord < mapdata->nout; coord++ ) { bad = ( ptr_out[ coord ][ batch ] == AST__BAD ); if ( bad ) break; } /* If not, we have a good starting position for finding a local minimum, so extract the corresponding input coordinates. */ if ( !bad ) { for ( coord = 0; coord < ncoord; coord++ ) { x[ coord ] = ptr_in[ coord ][ batch ]; } } /* Increment the position buffer location. */ batch++; } } /* If we do not have a good starting position, we can't do anything more on this iteration. A new position will be obtained and tested on the next iteration and this (we hope) will eventually identify a suitable starting point. */ if ( astOK && !bad ) { /* Form estimates of the lower and upper limits of the active region from the starting positions used. */ for ( coord = 0; coord < ncoord; coord++ ) { if ( x[ coord ] < active_lo[ coord ] ) { active_lo[ coord ] = x[ coord ]; } if ( x[ coord ] > active_hi[ coord ] ) { active_hi[ coord ] = x[ coord ]; } } /* Indicate that the Mapping function should be negated (because we want a local minimum) and then search for a local maximum in this negated function. If the result is non-bad (as it should always be, barring an error), then negate it to obtain the value of the local minimum found. */ mapdata->negate = 1; new_min = LocalMaximum( mapdata, acc, 0.01, x, status ); if ( new_min != AST__BAD ) { new_min = -new_min; /* Update the estimates of the lower and upper bounds of the active region to take account of where the minimum was found. */ for ( coord = 0; coord < ncoord; coord++ ) { if ( x[ coord ] < active_lo[ coord ] ) { active_lo[ coord ] = x[ coord ]; } if ( x[ coord ] > active_hi[ coord ] ) { active_hi[ coord ] = x[ coord ]; } } /* Count the number of times we successfully locate a local minimum (ignoring the fact they might all be the same one). */ nmin++; /* Update the global minimum. */ /* ========================== */ /* If this is the first estimate of the global minimum, then set to one the count of the number of consecutive iterations where this estimate remains unchanged. Store the minimum value and its position. */ if ( *lbnd == AST__BAD ) { nsame_min = 1; *lbnd = new_min; for ( coord = 0; coord < ncoord; coord++ ) { xl[ coord ] = x[ coord ]; } /* Otherwise, test if this local minimum is lower than the previous estimate of the global minimum. If so, then reset the count of unchanged estimates of the global mimimum to one if the difference exceeds the accuracy with which the minimum was found (i.e. if we have found a significantly different minimum). Otherwise, just increment this count (because we have found the same minimum but by chance with slightly improved accuracy). Store the new minimum and its position. */ } else if ( new_min < *lbnd ) { nsame_min = ( ( *lbnd - new_min ) > acc ) ? 1 : nsame_min + 1; *lbnd = new_min; for ( coord = 0; coord < ncoord; coord++ ) { xl[ coord ] = x[ coord ]; } /* If the latest local minimum is no improvement on previous estimates of the global minimum, then increment the count of unchanged estimates of the global mimimum, but do not save the new one. */ } else { nsame_min++; } /* Determine if a satisfactory estimate of the global minimum has been obtained. It has if the number of consecutive local minima which have not significantly improved the estimate is at least equal to "minsame", and at least 30% of the total number of local minima found. */ if ( ( nsame_min >= minsame ) && ( nsame_min >= (int) ( 0.3f * (float) nmin + 0.5f ) ) ) { done_min = 1; } } } } /* Search for a new local maximum. */ /* =============================== */ /* Now repeat all of the above to find a new local maximum which estimates the global maximum. */ if ( !done_max ) { /* Choose a suitable starting position, based on one already available if appropriate. */ if ( !iter && ( *ubnd != AST__BAD ) ) { for ( coord = 0; coord < ncoord; coord++ ) { x[ coord ] = xu[ coord ]; } } else if ( ( *ubnd == AST__BAD ) && ( *lbnd != AST__BAD ) ) { for ( coord = 0; coord < ncoord; coord++ ) { x[ coord ] = xl[ coord ]; } /* Otherwise use a pseudo-random position, refilling the position buffer if necessary. Check if the transformed coordinates are bad. */ } else { if ( batch >= nbatch ) FILL_POSITION_BUFFER; if ( astOK ) { for ( coord = 0; coord < mapdata->nout; coord++ ) { bad = ( ptr_out[ coord ][ batch ] == AST__BAD ); if ( bad ) break; } if ( !bad ) { for ( coord = 0; coord < ncoord; coord++ ) { x[ coord ] = ptr_in[ coord ][ batch ]; } } batch++; } } /* If the coordinates are OK, update the active region limits. */ if ( astOK && !bad ) { for ( coord = 0; coord < ncoord; coord++ ) { if ( x[ coord ] < active_lo[ coord ] ) { active_lo[ coord ] = x[ coord ]; } if ( x[ coord ] > active_hi[ coord ] ) { active_hi[ coord ] = x[ coord ]; } } /* Find a local maximum in the Mapping function. */ mapdata->negate = 0; new_max = LocalMaximum( mapdata, acc, 0.01, x, status ); if ( new_max != AST__BAD ) { /* Use the result to further update the active region limits. */ for ( coord = 0; coord < ncoord; coord++ ) { if ( x[ coord ] < active_lo[ coord ] ) { active_lo[ coord ] = x[ coord ]; } if ( x[ coord ] > active_hi[ coord ] ) { active_hi[ coord ] = x[ coord ]; } } /* Count the number of local maxima found. */ nmax++; /* Update the estimate of the global maximum. */ if ( *ubnd == AST__BAD ) { nsame_max = 1; *ubnd = new_max; for ( coord = 0; coord < ncoord; coord++ ) { xu[ coord ] = x[ coord ]; } } else if ( new_max > *ubnd ) { nsame_max = ( ( new_max - *ubnd ) > acc ) ? 1 : nsame_max + 1; *ubnd = new_max; for ( coord = 0; coord < ncoord; coord++ ) { xu[ coord ] = x[ coord ]; } } else { nsame_max++; } /* Test for a satisfactory global maximum estimate. */ if ( ( nsame_max >= minsame ) && ( nsame_max >= (int) ( 0.3f * (float) nmax + 0.5 ) ) ) { done_max = 1; } } } } /* Quit iterating once both the global minimum and the global maximum have been found. */ if ( done_min && done_max ) break; } /* Free workspace. */ active_hi = astFree( active_hi ); active_lo = astFree( active_lo ); sample_hi = astFree( sample_hi ); sample_lo = astFree( sample_lo ); sample_width = astFree( sample_width ); x = astFree( x ); /* Annul temporary PointSets. */ pset_in = astAnnul( pset_in ); pset_out = astAnnul( pset_out ); /* If the global minimum has been found, attempt to polish the result to machine precision by requesting that it be found with an accuracy tolerance of zero (subject to the maximum number of iterations that LocalMaximum will perform,). */ if ( astOK ) { if ( *lbnd != AST__BAD ) { mapdata->negate = 1; *lbnd = LocalMaximum( mapdata, 0.0, sqrt( DBL_EPSILON ), xl, status ); if ( *lbnd != AST__BAD ) *lbnd = - *lbnd; } /* Similarly polish the estimate of the global maximum. */ if ( *ubnd != AST__BAD ) { mapdata->negate = 0; *ubnd = LocalMaximum( mapdata, 0.0, sqrt( DBL_EPSILON ), xu, status ); } /* If either extremum could not be found, then report an error. */ if ( ( *lbnd == AST__BAD ) || ( *ubnd == AST__BAD ) ) { astError( AST__MBBNF, "astMapBox(%s): No valid output coordinates " "(after %d test points).", status, astGetClass( mapdata->mapping ), 2 * maxiter ); } /* If an error occurred, then return bad extremum values and coordinates. */ if ( !astOK ) { *lbnd = AST__BAD; *ubnd = AST__BAD; for ( coord = 0; coord < ncoord; coord++ ) { xl[ coord ] = AST__BAD; xu[ coord ] = AST__BAD; } } } /* Undefine macros local to this function. */ #undef FILL_POSITION_BUFFER } void astInitMappingVtab_( AstMappingVtab *vtab, const char *name, int *status ) { /* *+ * Name: * astInitMappingVtab * Purpose: * Initialise a virtual function table for a Mapping. * Type: * Protected function. * Synopsis: * #include "mapping.h" * void astInitMappingVtab( AstMappingVtab *vtab, const char *name ) * Class Membership: * Mapping vtab initialiser. * Description: * This function initialises the component of a virtual function * table which is used by the Mapping 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 */ AstObjectVtab *object; /* Pointer to Object component of Vtab */ /* Check the local error status. */ if ( !astOK ) return; /* Get a pointer to the thread specific global data structure. */ astGET_GLOBALS(NULL); /* Initialize the component of the virtual function table used by the parent class. */ astInitObjectVtab( (AstObjectVtab *) vtab, name ); /* Store a unique "magic" value in the virtual function table. This will be used (by astIsAMapping) 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 = &(((AstObjectVtab *) vtab)->id); /* Initialise member function pointers. */ /* ------------------------------------ */ /* Store pointers to the member functions (implemented here) that provide virtual methods for this class. */ #define VTAB_GENERIC(X) \ vtab->Resample##X = Resample##X; VTAB_GENERIC(B) VTAB_GENERIC(D) VTAB_GENERIC(F) VTAB_GENERIC(I) VTAB_GENERIC(K) VTAB_GENERIC(L) VTAB_GENERIC(S) VTAB_GENERIC(UB) VTAB_GENERIC(UI) VTAB_GENERIC(UK) VTAB_GENERIC(UL) VTAB_GENERIC(US) #if HAVE_LONG_DOUBLE /* Not normally implemented */ VTAB_GENERIC(LD) #endif #undef VTAB_GENERIC #define VTAB_GENERIC(X) \ vtab->Rebin##X = Rebin##X; \ vtab->RebinSeq##X = RebinSeq##X; VTAB_GENERIC(D) VTAB_GENERIC(F) VTAB_GENERIC(I) VTAB_GENERIC(B) VTAB_GENERIC(UB) #if HAVE_LONG_DOUBLE /* Not normally implemented */ VTAB_GENERIC(LD) #endif #undef VTAB_GENERIC vtab->ClearInvert = ClearInvert; vtab->ClearReport = ClearReport; vtab->Decompose = Decompose; vtab->DoNotSimplify = DoNotSimplify; vtab->GetInvert = GetInvert; vtab->GetIsLinear = GetIsLinear; vtab->GetIsSimple = GetIsSimple; vtab->GetNin = GetNin; vtab->GetNout = GetNout; vtab->GetReport = GetReport; vtab->GetTranForward = GetTranForward; vtab->GetTranInverse = GetTranInverse; vtab->Invert = Invert; vtab->LinearApprox = LinearApprox; vtab->MapBox = MapBox; vtab->MapList = MapList; vtab->MapMerge = MapMerge; vtab->MapSplit = MapSplit; vtab->QuadApprox = QuadApprox; vtab->Rate = Rate; vtab->ReportPoints = ReportPoints; vtab->RemoveRegions = RemoveRegions; vtab->SetInvert = SetInvert; vtab->SetReport = SetReport; vtab->Simplify = Simplify; vtab->TestInvert = TestInvert; vtab->TestReport = TestReport; vtab->Tran1 = Tran1; vtab->Tran2 = Tran2; vtab->TranGrid = TranGrid; vtab->TranN = TranN; vtab->TranP = TranP; vtab->Transform = Transform; /* Save the inherited pointers to methods that will be extended, and replace them with pointers to the new member functions. */ object = (AstObjectVtab *) vtab; parent_clearattrib = object->ClearAttrib; object->ClearAttrib = ClearAttrib; parent_getattrib = object->GetAttrib; object->GetAttrib = GetAttrib; parent_setattrib = object->SetAttrib; object->SetAttrib = SetAttrib; parent_testattrib = object->TestAttrib; object->TestAttrib = TestAttrib; parent_equal = object->Equal; object->Equal = Equal; /* Declare the destructor, copy constructor and dump function. */ astSetDelete( vtab, Delete ); astSetCopy( vtab, Copy ); astSetDump( vtab, Dump, "Mapping", "Mapping between coordinate systems" ); /* 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) ); } } /* * Name: * InterpolateKernel1 * Purpose: * Resample a data grid, using a 1-d interpolation kernel. * Type: * Private function. * Synopsis: * #include "mapping.h" * int InterpolateKernel1( AstMapping *this, int ndim_in, * const int *lbnd_in, const int *ubnd_in, * const *in, const *in_var, * int npoint, const int *offset, * const double *const *coords, * void (* kernel)( double, const double [], int, * double *, int * ), * void (* fkernel)( double, const double [], int, * double * ), * int neighb, const double *params, int flags, * badval, * *out, *out_var ) * Class Membership: * Mapping member function. * Description: * This is a set of functions which resample a rectangular input * grid of data (and, optionally, associated statistical variance * values) so as to place them into a new output grid. Each output * grid point may be mapped on to a position in the input grid in * an arbitrary way. The input and output grids may have any number * of dimensions, not necessarily equal. * * Where the positions given do not correspond with a pixel centre * in the input grid, interpolation is performed using a weighted * sum of the surrounding pixel values. The weights are determined * by a separable kernel which is the product of a 1-dimensional * kernel function evaluated along each input dimension. A pointer * should be supplied to the 1-dimensional kernel function to be * used. * Parameters: * this * Pointer to the Mapping being used in the resampling operation * (this is only used for constructing error messages). * ndim_in * The number of dimensions in the input grid. This should be at * least one. * lbnd_in * Pointer to an array of integers, with "ndim_in" elements. * This should give the coordinates of the centre of the first * pixel in the input grid along each dimension. * ubnd_in * Pointer to an array of integers, with "ndim_in" elements. * This should give the coordinates of the centre of the last * pixel in the input grid along each dimension. * * Note that "lbnd_in" and "ubnd_in" together define the shape * and size of the input grid, its extent along a particular * (i'th) dimension being ubnd_in[i]-lbnd_in[i]+1 (assuming "i" * is zero-based). They also define the input grid's coordinate * system, with each pixel being of unit extent along each * dimension with integral coordinate values at its centre. * in * Pointer to the array of data to be resampled (with an element * for each pixel in the input grid). The numerical type of * these data should match the function used, as given by the * suffix on the function name. The storage order should be such * that the index of the first grid dimension varies most * rapidly and that of the final dimension least rapidly * (i.e. Fortran array storage order). * in_var * An optional pointer to a second array of positive numerical * values (with the same size and type as the "in" array), which * represent estimates of the statistical variance associated * with each element of the "in" array. If this second array is * given (along with the corresponding "out_var" array), then * estimates of the variance of the resampled data will also be * returned. * * If no variance estimates are required, a NULL pointer should * be given. * npoint * The number of points at which the input grid is to be * resampled. * offset * Pointer to an array of integers with "npoint" elements. For * each output point, this array should contain the zero-based * offset in the output array(s) (i.e. the "out" and, * optionally, the "out_var" arrays) at which the resampled * output value(s) should be stored. * coords * An array of pointers to double, with "ndim_in" * elements. Element "coords[coord]" should point at the first * element of an array of double (with "npoint" elements) which * contains the values of coordinate number "coord" for each * interpolation point. The value of coordinate number "coord" * for interpolation point number "point" is therefore given by * "coords[coord][point]" (assuming both indices to be * zero-based). If any point has a coordinate value of AST__BAD * associated with it, then the corresponding output data (and * variance) will be set to the value given by "badval" (unles the * AST__NOBAD flag is specified). * kernel * Pointer to the 1-dimensional kernel function to be used. * fkernel * Pointer to the 1-dimensional kernel function to be used with no * trailing status argument. This is only used if "kernel" is NULL. * neighb * The number of neighbouring pixels in each dimension (on each * side of the interpolation position) which are to contribute * to the interpolated value. This value should be at least 1. * params * Pointer to an optional array of parameter values to be passed * to the interpolation kernel function. If no parameters are * required by this function, then a NULL pointer may be * supplied. * flags * The bitwise OR of a set of flag values which provide * additional control over the resampling operation. * badval * If the AST__USEBAD flag is set in the "flags" value (above), * this parameter specifies the value which is used to identify * bad data and/or variance values in the input array(s). Its * numerical type must match that of the "in" (and "in_var") * arrays. Unles the AST__NOBAD flag is specified in "flags", the * same value will also be used to flag any output array elements * for which resampled values could not be obtained. The output * arrays(s) may be flagged with this value whether or not the * AST__USEBAD flag is set (the function return value indicates * whether any such values have been produced). * out * Pointer to an array with the same data type as the "in" * array, into which the resampled data will be returned. Note * that details of how the output grid maps on to this array * (e.g. the storage order, number of dimensions, etc.) is * arbitrary and is specified entirely by means of the "offset" * array. The "out" array should therefore contain sufficient * elements to accommodate the "offset" values supplied. There * is no requirement that all elements of the "out" array should * be assigned values, and any which are not addressed by the * contents of the "offset" array will be left unchanged. * out_var * An optional pointer to an array with the same data type and * size as the "out" array, into which variance estimates for * the resampled values may be returned. This array will only be * used if the "in_var" array has been given. It is addressed in * exactly the same way (via the "offset" array) as the "out" * array. The values returned are estimates of the statistical * variance of the corresponding values in the "out" array, on * the assumption that all errors in input grid values (in the * "in" array) are statistically independent and that their * variance estimates (in the "in_var" array) may simply be * summed (with appropriate weighting factors). * * If no output variance estimates are required, a NULL pointer * should be given. * Returned Value: * The number of output grid points for which no valid output value * could be obtained. * Notes: * - There is a separate function for each numerical type of * gridded data, distinguished by replacing the in the function * name by the appropriate 1- or 2-character suffix. * - A value of zero will be returned if any of these functions is * invoked with the global error status set, or if it should fail * for any reason. */ /* Define macros to implement the function for a specific data type. */ #define MAKE_INTERPOLATE_KERNEL1(X,Xtype,Xfloating,Xfloattype,Xsigned) \ static int InterpolateKernel1##X( AstMapping *this, int ndim_in, \ const int *lbnd_in, const int *ubnd_in, \ const Xtype *in, const Xtype *in_var, \ int npoint, const int *offset, \ const double *const *coords, \ void (* kernel)( double, const double [], \ int, double *, int * ), \ void (* fkernel)( double, const double [], \ int, double * ), \ int neighb, const double *params, \ int flags, Xtype badval, \ Xtype *out, Xtype *out_var, int *status ) { \ \ /* Local Variables: */ \ astDECLARE_GLOBALS /* Thread-specific data */ \ Xfloattype hi_lim; /* Upper limit on output values */ \ Xfloattype lo_lim; /* Lower limit on output values */ \ Xfloattype sum; /* Weighted sum of pixel data values */ \ Xfloattype sum_var; /* Weighted sum of pixel variance values */ \ Xfloattype val; /* Data value to be assigned to output */ \ Xfloattype val_var; /* Variance to be assigned to output */ \ Xfloattype wtsum; /* Sum of weight values */ \ Xfloattype wtsum_sq; /* Square of sum of weights */ \ Xtype var; /* Variance value */ \ double **wtptr; /* Pointer to array of weight pointers */ \ double **wtptr_last; /* Array of highest weight pointer values */ \ double *kval; /* Pointer to array of kernel values */ \ double *wtprod; /* Accumulated weight value array pointer */ \ double *xn_max; /* Pointer to upper limits array (n-d) */ \ double *xn_min; /* Pointer to lower limits array (n-d) */ \ double pixwt; /* Weight to apply to individual pixel */ \ double wt_y; /* Value of y-dependent pixel weight */ \ double x; /* x coordinate value */ \ double xmax; /* x upper limit */ \ double xmin; /* x lower limit */ \ double xn; /* Coordinate value (n-d) */ \ double y; /* y coordinate value */ \ double ymax; /* y upper limit */ \ double ymin; /* y lower limit */ \ int *hi; /* Pointer to array of upper indices */ \ int *lo; /* Pointer to array of lower indices */ \ int *stride; /* Pointer to array of dimension strides */ \ int bad; /* Output pixel bad? */ \ int bad_var; /* Output variance bad? */ \ int done; /* All pixel indices done? */ \ int hi_x; /* Upper pixel index (x dimension) */ \ int hi_y; /* Upper pixel index (y dimension) */ \ int idim; /* Loop counter for dimensions */ \ int ii; /* Loop counter for dimensions */ \ int ix; /* Pixel index in input grid x dimension */ \ int ixn; /* Pixel index in input grid (n-d) */ \ int iy; /* Pixel index in input grid y dimension */ \ int kerror; /* Error signalled by kernel function? */ \ int lo_x; /* Lower pixel index (x dimension) */ \ int lo_y; /* Lower pixel index (y dimension) */ \ int nobad; /* Was the AST__NOBAD flag set? */ \ int off1; /* Input pixel offset due to y index */ \ int off_in; /* Offset to input pixel */ \ int off_out; /* Offset to output pixel */ \ int pixel; /* Offset to input pixel containing point */ \ int point; /* Loop counter for output points */ \ int result; /* Result value to return */ \ int s; /* Temporary variable for strides */ \ int usebad; /* Use "bad" input pixel values? */ \ int usevar; /* Process variance array? */ \ int ystride; /* Stride along input grid y dimension */ \ \ /* Initialise. */ \ result = 0; \ \ /* Check the global error status. */ \ if ( !astOK ) return result; \ \ /* Get a pointer to a structure holding thread-specific global data values */ \ astGET_GLOBALS(this); \ \ /* Further initialisation. */ \ kerror = 0; \ sum_var = 0; \ val = 0; \ val_var = 0; \ wtsum = 0; \ bad = 0; \ bad_var = 0; \ sum = 0.0; \ \ /* Determine if we are processing bad pixels or variances. */ \ nobad = flags & AST__NOBAD; \ usebad = flags & AST__USEBAD; \ usevar = in_var && out_var; \ \ /* Set up limits for checking output values to ensure that they do not \ overflow the range of the data type being used. */ \ lo_lim = LO_##X; \ hi_lim = HI_##X; \ \ /* Handle the 1-dimensional case optimally. */ \ /* ---------------------------------------- */ \ if ( ndim_in == 1 ) { \ \ /* Calculate the coordinate limits of the input grid. */ \ xmin = (double) lbnd_in[ 0 ] - 0.5; \ xmax = (double) ubnd_in[ 0 ] + 0.5; \ \ /* Identify four cases, according to whether bad pixels and/or \ variances are being processed. In each case, loop through all the \ output points to (a) assemble the input data needed to form the \ interpolated value, and (b) calculate the result and assign it to \ the output arrays(s). In each case we assign constant values (0 or \ 1) to the "Usebad" and "Usevar" flags so that code for handling bad \ pixels and variances can be eliminated when not required. */ \ if ( nobad ) { \ if ( usebad ) { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,1,1) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,0,1) \ } \ } \ } else { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,1,1) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,0,1) \ } \ } \ } \ \ /* Four more cases as above, but this time with the AST__NOBAD flag \ un-set. */ \ } else { \ if ( usebad ) { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,1,0) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,0,0) \ } \ } \ } else { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,1,0) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,0,0) \ } \ } \ } \ } \ \ /* Exit point on error in kernel function */ \ Kernel_Error_1d: ; \ \ /* Handle the 2-dimensional case optimally. */ \ /* ---------------------------------------- */ \ } else if ( ndim_in == 2 ) { \ \ /* Allocate workspace. */ \ kval = astMalloc( sizeof( double ) * (size_t) ( 2 * neighb ) ); \ if ( astOK ) { \ \ /* Calculate the stride along the y dimension of the input grid. */ \ ystride = ubnd_in[ 0 ] - lbnd_in[ 0 ] + 1; \ \ /* Calculate the coordinate limits of the input grid in each \ dimension. */ \ xmin = (double) lbnd_in[ 0 ] - 0.5; \ xmax = (double) ubnd_in[ 0 ] + 0.5; \ ymin = (double) lbnd_in[ 1 ] - 0.5; \ ymax = (double) ubnd_in[ 1 ] + 0.5; \ \ /* Identify four cases, according to whether bad pixels and/or \ variances are being processed. In each case, loop through all the \ output points to (a) assemble the input data needed to form the \ interpolated value, and (b) calculate the result and assign it to \ the output arrays(s). In each case we assign constant values (0 or \ 1) to the "Usebad" and "Usevar" flags so that code for handling bad \ pixels and variances can be eliminated when not required. */ \ if ( nobad ) { \ if ( usebad ) { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,1,1) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,0,1) \ } \ } \ } else { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,1,1) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,0,1) \ } \ } \ } \ \ /* Another four cases, as above, but this time without the AST__NOBAD \ flag. */ \ } else { \ if ( usebad ) { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,1,0) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,0,0) \ } \ } \ } else { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,1,0) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,0,0) \ } \ } \ } \ } \ \ /* Exit point on error in kernel function */ \ Kernel_Error_2d: ; \ } \ \ /* Free the workspace. */ \ kval = astFree( kval ); \ \ /* Handle other numbers of dimensions. */ \ /* ----------------------------------- */ \ } else { \ \ /* Allocate workspace. */ \ hi = astMalloc( sizeof( int ) * (size_t) ndim_in ); \ lo = astMalloc( sizeof( int ) * (size_t) ndim_in ); \ stride = astMalloc( sizeof( int ) * (size_t) ndim_in ); \ xn_max = astMalloc( sizeof( double ) * (size_t) ndim_in ); \ xn_min = astMalloc( sizeof( double ) * (size_t) ndim_in ); \ kval = astMalloc( sizeof( double ) * (size_t) \ ( 2 * neighb * ndim_in ) ); \ wtprod = astMalloc( sizeof( double ) * (size_t) ndim_in ); \ wtptr = astMalloc( sizeof( double * ) * (size_t) ndim_in ); \ wtptr_last = astMalloc( sizeof( double * ) * (size_t) ndim_in ); \ if ( astOK ) { \ \ /* Calculate the stride along each dimension of the input grid. */ \ for ( s = 1, idim = 0; idim < ndim_in; idim++ ) { \ stride[ idim ] = s; \ s *= ubnd_in[ idim ] - lbnd_in[ idim ] + 1; \ \ /* Calculate the coordinate limits of the input grid in each \ dimension. */ \ xn_min[ idim ] = (double) lbnd_in[ idim ] - 0.5; \ xn_max[ idim ] = (double) ubnd_in[ idim ] + 0.5; \ } \ \ /* Identify four cases, according to whether bad pixels and/or \ variances are being processed. In each case, loop through all the \ output points to (a) assemble the input data needed to form the \ interpolated value, and (b) calculate the result and assign it to \ the output arrays(s). In each case we assign constant values (0 or \ 1) to the "Usebad" and "Usevar" flags so that code for handling bad \ pixels and variances can be eliminated when not required. */ \ if( nobad ) { \ if ( usebad ) { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,1,1) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,0,1) \ } \ } \ } else { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,1,1) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,0,1) \ } \ } \ } \ \ /* Another 4 cases as above, but this time with the AST__NOBAD flag \ un-set. */ \ } else { \ if ( usebad ) { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,1,0) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,0,0) \ } \ } \ } else { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,1,0) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,0,0) \ } \ } \ } \ } \ \ /* Exit point on error in kernel function */ \ Kernel_Error_Nd: ;\ } \ \ /* Free the workspace. */ \ hi = astFree( hi ); \ lo = astFree( lo ); \ stride = astFree( stride ); \ xn_max = astFree( xn_max ); \ xn_min = astFree( xn_min ); \ kval = astFree( kval ); \ wtprod = astFree( wtprod ); \ wtptr = astFree( wtptr ); \ wtptr_last = astFree( wtptr_last ); \ } \ \ /* If an error occurred in the kernel function, then report a \ contextual error message. */ \ if ( kerror ) { \ astError( astStatus, "astResample"#X"(%s): Error signalled by " \ "user-supplied 1-d interpolation kernel.", status, \ astGetClass( unsimplified_mapping ) ); \ } \ \ /* If an error has occurred, clear the returned result. */ \ if ( !astOK ) result = 0; \ \ /* Return the result. */ \ return result; \ } /* This subsidiary macro assembles the input data needed in preparation for forming the interpolated value in the 1-dimensional case. */ #define ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,Usebad,Usevar) \ \ /* Obtain the x coordinate of the current point and test if it lies \ outside the input grid, or is bad. */ \ x = coords[ 0 ][ point ]; \ bad = ( x < xmin ) || ( x >= xmax ) || ( x == AST__BAD ); \ if ( !bad ) { \ \ /* If input bad pixels must be detected, then obtain the offset along \ the input grid x dimension of the input pixel which contains the \ current coordinate, and calculate this pixel's offset from the \ start of the input array. */ \ if ( Usebad ) { \ pixel = (int) floor( x + 0.5 ) - lbnd_in[ 0 ]; \ \ /* Test if the pixel is bad. */ \ bad = ( in[ pixel ] == badval ); \ } \ \ /* If OK, calculate the lowest and highest indices (in the x \ dimension) of the region of neighbouring pixels that will \ contribute to the interpolated result. Constrain these values to \ lie within the input grid. */ \ if ( !bad ) { \ ix = (int) floor( x ); \ lo_x = MaxI( ix - neighb + 1, lbnd_in[ 0 ], status ); \ hi_x = MinI( ix + neighb, ubnd_in[ 0 ], status ); \ \ /* Initialise sums for forming the interpolated result. */ \ sum = (Xfloattype) 0.0; \ wtsum = (Xfloattype) 0.0; \ if ( Usevar ) { \ sum_var = (Xfloattype) 0.0; \ bad_var = 0; \ } \ \ /* Loop to inspect all the contributing pixels, calculating the offset \ of each pixel from the start of the input array. */ \ off_in = lo_x - lbnd_in[ 0 ]; \ for ( ix = lo_x; ix <= hi_x; ix++, off_in++ ) { \ \ /* If necessary, test if the input pixel is bad. If not, calculate its \ weight by evaluating the kernel function. */ \ if ( !( Usebad ) || ( in[ off_in ] != badval ) ) { \ if( kernel ) { \ ( *kernel )( (double) ix - x, params, flags, &pixwt, status ); \ } else { \ ( *fkernel )( (double) ix - x, params, flags, &pixwt ); \ } \ \ /* Check for errors arising in the kernel function. */ \ if ( !astOK ) { \ kerror = 1; \ goto Kernel_Error_1d; \ } \ \ /* Form the weighted sums required for finding the interpolated \ value. */ \ sum += ( (Xfloattype) pixwt ) * ( (Xfloattype) in[ off_in ] ); \ wtsum += (Xfloattype) pixwt; \ \ /* If a variance estimate is required and it still seems possible to \ obtain one, then obtain the variance value associated with the \ current input pixel. */ \ if ( Usevar ) { \ if ( !( ( Xsigned ) || ( Usebad ) ) || !bad_var ) { \ var = in_var[ off_in ]; \ \ /* If necessary, test if this value is bad (if the data type is \ signed, also check that it is not negative). */ \ if ( Usebad ) bad_var = ( var == badval ); \ CHECK_FOR_NEGATIVE_VARIANCE(Xtype) \ \ /* If any bad input variance value is obtained, we cannot generate a \ valid output variance estimate. Otherwise, form the sum needed to \ calculate this estimate. */ \ if ( !( ( Xsigned ) || ( Usebad ) ) || !bad_var ) { \ sum_var += ( (Xfloattype) ( pixwt * pixwt ) ) * \ ( (Xfloattype) var ); \ } \ } \ } \ } \ } \ } \ } /* This subsidiary macro assembles the input data needed in preparation for forming the interpolated value in the 2-dimensional case. */ #define ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,Usebad,Usevar) \ \ /* Obtain the x coordinate of the current point and test if it lies \ outside the input grid, or is bad. */ \ x = coords[ 0 ][ point ]; \ bad = ( x < xmin ) || ( x >= xmax ) || ( x == AST__BAD ); \ if ( !bad ) { \ \ /* If not, then similarly obtain and test the y coordinate. */ \ y = coords[ 1 ][ point ]; \ bad = ( y < ymin ) || ( y >= ymax ) || ( y == AST__BAD ); \ if ( !bad ) { \ \ /* If input bad pixels must be detected, then obtain the offsets along \ each input grid dimension of the input pixel which contains the \ current coordinates, and calculate this pixel's offset from the \ start of the input array. */ \ if ( Usebad ) { \ ix = (int) floor( x + 0.5 ); \ iy = (int) floor( y + 0.5 ); \ pixel = ix - lbnd_in[ 0 ] + ystride * ( iy - lbnd_in[ 1 ] ); \ \ /* Test if the pixel is bad. */ \ bad = ( in[ pixel ] == badval ); \ } \ \ /* If OK, calculate the lowest and highest indices (in each dimension) \ of the region of neighbouring pixels that will contribute to the \ interpolated result. Constrain these values to lie within the input \ grid. */ \ if ( !bad ) { \ ix = (int) floor( x ); \ lo_x = MaxI( ix - neighb + 1, lbnd_in[ 0 ], status ); \ hi_x = MinI( ix + neighb, ubnd_in[ 0 ], status ); \ iy = (int) floor( y ); \ lo_y = MaxI( iy - neighb + 1, lbnd_in[ 1 ], status ); \ hi_y = MinI( iy + neighb, ubnd_in[ 1 ], status ); \ \ /* Loop to evaluate the kernel function along the x dimension, storing \ the resulting values. The function's argument is the offset of the \ contributing pixel (along this dimension) from the input \ position. */ \ for ( ix = lo_x; ix <= hi_x; ix++ ) { \ if( kernel ) { \ ( *kernel )( (double) ix - x, params, flags, \ kval + ix - lo_x, status ); \ } else { \ ( *fkernel )( (double) ix - x, params, flags, \ kval + ix - lo_x ); \ } \ \ /* Check for errors arising in the kernel function. */ \ if ( !astOK ) { \ kerror = 1; \ goto Kernel_Error_2d; \ } \ } \ \ /* Initialise sums for forming the interpolated result. */ \ sum = (Xfloattype) 0.0; \ wtsum = (Xfloattype) 0.0; \ if ( Usevar ) { \ sum_var = (Xfloattype) 0.0; \ bad_var = 0; \ } \ \ /* Loop over the y index to inspect all the contributing pixels, while \ keeping track of their offset within the input array. Evaluate the \ kernel function for each y index value. */ \ off1 = lo_x - lbnd_in[ 0 ] + ystride * ( lo_y - lbnd_in[ 1 ] ); \ for ( iy = lo_y; iy <= hi_y; iy++, off1 += ystride ) { \ if( kernel ) { \ ( *kernel )( (double) iy - y, params, flags, &wt_y, status ); \ } else { \ ( *fkernel )( (double) iy - y, params, flags, &wt_y ); \ } \ \ /* Check for errors arising in the kernel function. */ \ if ( !astOK ) { \ kerror = 1; \ goto Kernel_Error_2d; \ } \ \ /* Loop over the x index, calculating the pixel offset in the input \ array. */ \ off_in = off1; \ for ( ix = lo_x; ix <= hi_x; ix++, off_in++ ) { \ \ /* If necessary, test if the input pixel is bad. If not, calculate its \ weight as the product of the kernel function's value for the x and \ y dimensions. */ \ if ( !( Usebad ) || ( in[ off_in ] != badval ) ) { \ pixwt = kval[ ix - lo_x ] * wt_y; \ \ /* Form the weighted sums required for finding the interpolated \ value. */ \ sum += ( (Xfloattype) pixwt ) * \ ( (Xfloattype) in[ off_in ] ); \ wtsum += (Xfloattype) pixwt; \ \ /* If a variance estimate is required and it still seems possible to \ obtain one, then obtain the variance value associated with the \ current input pixel. */ \ if ( Usevar ) { \ if ( !( ( Xsigned ) || ( Usebad ) ) || !bad_var ) { \ var = in_var[ off_in ]; \ \ /* If necessary, test if this value is bad (if the data type is \ signed, also check that it is not negative). */ \ if ( Usebad ) bad_var = ( var == badval ); \ CHECK_FOR_NEGATIVE_VARIANCE(Xtype) \ \ /* If any bad input variance value is obtained, we cannot generate a \ valid output variance estimate. Otherwise, form the sum needed to \ calculate this estimate. */ \ if ( !( ( Xsigned ) || ( Usebad ) ) || \ !bad_var ) { \ sum_var += ( (Xfloattype) ( pixwt * pixwt ) ) * \ ( (Xfloattype) var ); \ } \ } \ } \ } \ } \ } \ } \ } \ } /* This subsidiary macro assembles the input data needed in preparation for forming the interpolated value in the n-dimensional case. */ #define ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,Usebad,Usevar) \ \ /* Initialise offsets into the input array. Then loop to obtain each \ coordinate associated with the current output point. */ \ pixel = 0; \ off_in = 0; \ for ( idim = 0; idim < ndim_in; idim++ ) { \ xn = coords[ idim ][ point ]; \ \ /* Test if the coordinate lies outside the input grid, or is bad. If \ either is true, the corresponding output pixel value will be bad, \ so give up on this point. */ \ bad = ( xn < xn_min[ idim ] ) || ( xn >= xn_max[ idim ] ) || \ ( xn == AST__BAD ); \ if ( bad ) break; \ \ /* If input bad pixels must be detected, then obtain the index along \ the current input grid dimension of the pixel which contains this \ coordinate and accumulate the pixel's offset from the start of the \ input array. */ \ if ( Usebad ) { \ pixel += stride[ idim ] * \ ( (int) floor( xn + 0.5 ) - lbnd_in[ idim ] ); \ } \ \ /* Calculate the lowest and highest indices (in the current dimension) \ of the region of neighbouring pixels that will contribute to the \ interpolated result. Constrain these values to lie within the input \ grid. */ \ ixn = (int) floor( xn ); \ lo[ idim ] = MaxI( ixn - neighb + 1, lbnd_in[ idim ], status ); \ hi[ idim ] = MinI( ixn + neighb, ubnd_in[ idim ], status ); \ \ /* Accumulate the offset (from the start of the input array) of the \ contributing pixel which has the lowest index in each dimension. */ \ off_in += stride[ idim ] * ( lo[ idim ] - lbnd_in[ idim ] ); \ } \ \ /* Once the input pixel which contains the required coordinates has \ been identified, test if it is bad, if necessary. */ \ if ( Usebad ) bad = bad || ( in[ pixel ] == badval ); \ \ /* If OK, loop to evaluate the kernel function which will be used to \ weight the contributions from surrounding pixels. */ \ if ( !bad ) { \ for ( idim = 0; idim < ndim_in; idim++ ) { \ \ /* Set up an array of pointers to locate kernel values (stored in the \ "kval" array) for each dimension. Initially, each of these pointers \ locates the first weight value which applies to the contributing \ pixel with the lowest index in each dimension. */ \ wtptr[ idim ] = kval + ( 2 * neighb * idim ); \ \ /* Also set up pointers to the last weight value in each dimension. */ \ wtptr_last[ idim ] = wtptr[ idim ] + ( hi[ idim ] - lo[ idim ] ); \ \ /* Loop to evaluate the kernel function along each dimension, storing \ the resulting values. The function's argument is the offset of the \ contributing pixel (along the relevant dimension) from the input \ point. */ \ xn = coords[ idim ][ point ]; \ for ( ixn = lo[ idim ]; ixn <= hi[ idim ]; ixn++ ) { \ if( kernel ) { \ ( *kernel )( (double) ixn - xn, params, flags, \ wtptr[ idim ] + ixn - lo[ idim ], status ); \ } else { \ ( *fkernel )( (double) ixn - xn, params, flags, \ wtptr[ idim ] + ixn - lo[ idim ] ); \ } \ \ /* Check for errors arising in the kernel function. */ \ if ( !astOK ) { \ kerror = 1; \ goto Kernel_Error_Nd; \ } \ } \ } \ \ /* Initialise, and loop over the neighbouring input pixels to obtain \ an interpolated value. */ \ sum = (Xfloattype) 0.0; \ wtsum = (Xfloattype) 0.0; \ if ( Usevar ) { \ sum_var = (Xfloattype) 0.0; \ bad_var = 0; \ } \ idim = ndim_in - 1; \ wtprod[ idim ] = 1.0; \ done = 0; \ do { \ \ /* Each contributing pixel is weighted by the product of the kernel \ weight factors evaluated along each input dimension. However, since \ we typically only change the index of one dimension at a time, we \ can avoid forming this product repeatedly by retaining an array of \ accumulated products for all higher dimensions. We need then only \ update the lower elements in this array, corresponding to those \ dimensions whose index has changed. We do this here, "idim" being \ the index of the most significant dimension to have changed. Note \ that on the first pass, all dimensions are considered changed, \ causing this array to be initialised. */ \ for ( ii = idim; ii >= 1; ii-- ) { \ wtprod[ ii - 1 ] = wtprod[ ii ] * *( wtptr[ ii ] ); \ } \ \ /* If necessary, test each pixel which may contribute to the result to \ see if it is bad. If not, obtain its weight from the accumulated \ product of weights. Also multiply by the weight for dimension zero, \ which is not included in the "wtprod" array). */ \ if ( !( Usebad ) || ( in[ off_in ] != badval ) ) { \ pixwt = wtprod[ 0 ] * *( wtptr[ 0 ] ); \ \ /* Form the weighted sums required for finding the interpolated \ value. */ \ sum += ( (Xfloattype) pixwt ) * ( (Xfloattype) in[ off_in ] ); \ wtsum += (Xfloattype) pixwt; \ \ /* If a variance estimate is required and it still seems possible to \ obtain one, then obtain the variance value associated with the \ current input pixel. */ \ if ( Usevar ) { \ if ( !( ( Xsigned ) || ( Usebad ) ) || !bad_var ) { \ var = in_var[ off_in ]; \ \ /* If necessary, test if this value is bad (if the data type is \ signed, also check that it is not negative). */ \ if ( Usebad ) bad_var = ( var == badval ); \ CHECK_FOR_NEGATIVE_VARIANCE(Xtype) \ \ /* If any bad input variance value is obtained, we cannot generate a \ valid output variance estimate. Otherwise, form the sum needed to \ calculate this estimate. */ \ if ( !( ( Xsigned ) || ( Usebad ) ) || !bad_var ) { \ sum_var += ( (Xfloattype) ( pixwt * pixwt ) ) * \ ( (Xfloattype) var ); \ } \ } \ } \ } \ \ /* Now update the weight value pointers and pixel offset to refer to \ the next input pixel to be considered. */ \ idim = 0; \ do { \ \ /* The first input dimension whose weight value pointer has not yet \ reached its final value has this pointer incremented, and the pixel \ offset into the input array is updated accordingly. */ \ if ( wtptr[ idim ] != wtptr_last[ idim ] ) { \ wtptr[ idim ]++; \ off_in += stride[ idim ]; \ break; \ \ /* Any earlier dimensions (which have reached the final pointer value) \ have this pointer returned to its lowest value. Again, the pixel \ offset into the input image is updated accordingly. */ \ } else { \ wtptr[ idim ] -= ( hi[ idim ] - lo[ idim ] ); \ off_in -= stride[ idim ] * \ ( hi[ idim ] - lo[ idim ] ); \ done = ( ++idim == ndim_in ); \ } \ } while ( !done ); \ } while ( !done ); \ } /* This subsidiary macro calculates the interpolated output value (and variance) from the sums over contributing pixels, checks the results for validity, and assigns them to the output array(s). */ #define CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Usebad,Usevar,Nobad) \ \ /* If the output data value has not yet been flagged as bad, then \ check that an interpolated value can actually be produced. First \ check that the sum of weights is not zero. */ \ if ( !bad ) { \ bad = ( wtsum == (Xfloattype) 0.0 ); \ \ /* If OK, calculate the interpolated value. Then, if the output data \ type is not floating point, check that this value will not overflow \ the available output range. */ \ if ( !bad ) { \ val = sum / wtsum; \ if ( !( Xfloating ) ) { \ bad = ( val <= lo_lim ) || ( val >= hi_lim ); \ } \ } \ \ /* If no interpolated data value can be produced, then no associated \ variance will be required either. */ \ if ( ( Usevar ) && bad ) bad_var = 1; \ } \ \ /* Now perform similar checks on the output variance value (if \ required). This time we check that the square of the sum of \ weights is not zero (since this might underflow before the sum of \ weights). Again we also check to prevent the result overflowing the \ output data type. */ \ if ( ( Usevar ) && !bad_var ) { \ wtsum_sq = wtsum * wtsum; \ bad_var = ( wtsum_sq == (Xfloattype) 0.0 ); \ if ( !bad_var ) { \ val_var = sum_var / wtsum_sq; \ if ( !( Xfloating ) ) { \ bad_var = ( val_var <= lo_lim ) || ( val_var >= hi_lim ); \ } \ } \ } \ \ /* Obtain the pixel offset into the output array. */ \ off_out = offset[ point ]; \ \ /* Assign a bad output value (and variance) if required and count it. */ \ if ( bad ) { \ if( !Nobad ) { \ out[ off_out ] = badval; \ if ( Usevar ) out_var[ off_out ] = badval; \ } \ result++; \ \ /* Otherwise, assign the interpolated value. If the output data type \ is floating point, the result can be stored directly, otherwise we \ must round to the nearest integer. */ \ } else { \ if ( Xfloating ) { \ out[ off_out ] = (Xtype) val; \ } else { \ out[ off_out ] = (Xtype) ( val + ( ( val >= (Xfloattype) 0.0 ) ? \ ( (Xfloattype) 0.5 ) : \ ( (Xfloattype) -0.5 ) ) ); \ } \ \ /* If a variance estimate is required but none can be obtained, then \ store a bad output variance value and count it. */ \ if ( Usevar ) { \ if ( bad_var ) { \ if( !Nobad ) { \ out_var[ off_out ] = badval; \ } \ result++; \ \ /* Otherwise, store the variance estimate, rounding to the nearest \ integer if necessary. */ \ } else { \ if ( Xfloating ) { \ out_var[ off_out ] = (Xtype) val_var; \ } else { \ out_var[ off_out ] = (Xtype) ( val_var + \ ( ( val_var >= (Xfloattype) 0.0 ) ? \ ( (Xfloattype) 0.5 ) : \ ( (Xfloattype) -0.5 ) ) ); \ } \ } \ } \ } /* These subsidiary macros define limits for range checking of results before conversion to the final data type. For each data type code , HI_ gives the least positive floating point value which just overflows that data type towards plus infinity, while LO_ gives the least negative floating point value which just overflows that data type towards minus infinity. Thus, a floating point value must satisfy LO is a floating point type, the limits are not actually used, but must be present to permit error-free compilation. */ #if HAVE_LONG_DOUBLE /* Not normally implemented */ #define HI_LD ( 0.0L ) #define LO_LD ( 0.0L ) #endif #define HI_D ( 0.0 ) #define LO_D ( 0.0 ) #define HI_F ( 0.0f ) #define LO_F ( 0.0f ) #if HAVE_LONG_DOUBLE /* Not normally implemented */ #define HI_K ( 0.5L + (long double) LONG_MAX ) #define LO_K ( -0.5L + (long double) LONG_MIN ) #define HI_UK ( 0.5L + (long double) ULONG_MAX ) #define LO_UK ( -0.5L ) #define HI_L ( 0.5L + (long double) LONG_MAX ) #define LO_L ( -0.5L + (long double) LONG_MIN ) #define HI_UL ( 0.5L + (long double) ULONG_MAX ) #define LO_UL ( -0.5L ) #else #define HI_K ( 0.5 + (double) LONG_MAX ) #define LO_K ( -0.5 + (double) LONG_MIN ) #define HI_UK ( 0.5 + (double) ULONG_MAX ) #define LO_UK ( -0.5 ) #define HI_L ( 0.5 + (double) LONG_MAX ) #define LO_L ( -0.5 + (double) LONG_MIN ) #define HI_UL ( 0.5 + (double) ULONG_MAX ) #define LO_UL ( -0.5 ) #endif #define HI_I ( 0.5 + (double) INT_MAX ) #define LO_I ( -0.5 + (double) INT_MIN ) #define HI_UI ( 0.5 + (double) UINT_MAX ) #define LO_UI ( -0.5 ) #define HI_S ( 0.5f + (float) SHRT_MAX ) #define LO_S ( -0.5f + (float) SHRT_MIN ) #define HI_US ( 0.5f + (float) USHRT_MAX ) #define LO_US ( -0.5f ) #define HI_B ( 0.5f + (float) SCHAR_MAX ) #define LO_B ( -0.5f + (float) SCHAR_MIN ) #define HI_UB ( 0.5f + (float) UCHAR_MAX ) #define LO_UB ( -0.5f ) /* This subsidiary macro tests for negative variance values. This check is required only for signed data types. */ #define CHECK_FOR_NEGATIVE_VARIANCE(Xtype) \ bad_var = bad_var || ( var < ( (Xtype) 0 ) ); /* Expand the main macro above to generate a function for each required signed data type. */ #if HAVE_LONG_DOUBLE /* Not normally implemented */ MAKE_INTERPOLATE_KERNEL1(LD,long double,1,long double,1) MAKE_INTERPOLATE_KERNEL1(L,long int,0,long double,1) MAKE_INTERPOLATE_KERNEL1(K,INT_BIG,0,long double,1) #else MAKE_INTERPOLATE_KERNEL1(L,long int,0,double,1) MAKE_INTERPOLATE_KERNEL1(K,INT_BIG,0,double,1) #endif MAKE_INTERPOLATE_KERNEL1(D,double,1,double,1) MAKE_INTERPOLATE_KERNEL1(F,float,1,float,1) MAKE_INTERPOLATE_KERNEL1(I,int,0,double,1) MAKE_INTERPOLATE_KERNEL1(S,short int,0,float,1) MAKE_INTERPOLATE_KERNEL1(B,signed char,0,float,1) /* Re-define the macro for testing for negative variances to do nothing. */ #undef CHECK_FOR_NEGATIVE_VARIANCE #define CHECK_FOR_NEGATIVE_VARIANCE(Xtype) /* Expand the main macro above to generate a function for each required unsigned data type. */ #if HAVE_LONG_DOUBLE /* Not normally implemented */ MAKE_INTERPOLATE_KERNEL1(UL,unsigned long int,0,long double,0) MAKE_INTERPOLATE_KERNEL1(UK,UINT_BIG,0,long double,0) #else MAKE_INTERPOLATE_KERNEL1(UL,unsigned long int,0,double,0) MAKE_INTERPOLATE_KERNEL1(UK,UINT_BIG,0,double,0) #endif MAKE_INTERPOLATE_KERNEL1(UI,unsigned int,0,double,0) MAKE_INTERPOLATE_KERNEL1(US,unsigned short int,0,float,0) MAKE_INTERPOLATE_KERNEL1(UB,unsigned char,0,float,0) /* Undefine the macros used above. */ #undef CHECK_FOR_NEGATIVE_VARIANCE #if HAVE_LONG_DOUBLE /* Not normally implemented */ #undef HI_LD #undef LO_LD #endif #undef HI_D #undef LO_D #undef HI_F #undef LO_F #undef HI_L #undef LO_L #undef HI_UL #undef LO_UL #undef HI_K #undef LO_K #undef HI_UK #undef LO_UK #undef HI_I #undef LO_I #undef HI_UI #undef LO_UI #undef HI_S #undef LO_S #undef HI_US #undef LO_US #undef HI_B #undef LO_B #undef HI_UB #undef LO_UB #undef CALC_AND_ASSIGN_OUTPUT #undef ASSEMBLE_INPUT_ND #undef ASSEMBLE_INPUT_2D #undef ASSEMBLE_INPUT_1D #undef MAKE_INTERPOLATE_KERNEL1 /* * Name: * InterpolateLinear * Purpose: * Resample a data grid, using the linear interpolation scheme. * Type: * Private function. * Synopsis: * #include "mapping.h" * int InterpolateLinear( int ndim_in, * const int *lbnd_in, const int *ubnd_in, * const *in, const *in_var, * int npoint, const int *offset, * const double *const *coords, * int flags, badval, * *out, *out_var ) * Class Membership: * Mapping member function. * Description: * This is a set of functions which resample a rectangular input * grid of data (and, optionally, associated statistical variance * values) so as to place them into a new output grid. Each output * grid point may be mapped on to a position in the input grid in * an arbitrary way. Where the positions given do not correspond * with a pixel centre in the input grid, the interpolation scheme * used is linear interpolation between the centres of the nearest * neighbouring pixels in each dimension (there are 2 nearest * neighbours in 1 dimension, 4 in 2 dimensions, 8 in 3 dimensions, * etc.). * Parameters: * ndim_in * The number of dimensions in the input grid. This should be at * least one. * lbnd_in * Pointer to an array of integers, with "ndim_in" elements. * This should give the coordinates of the centre of the first * pixel in the input grid along each dimension. * ubnd_in * Pointer to an array of integers, with "ndim_in" elements. * This should give the coordinates of the centre of the last * pixel in the input grid along each dimension. * * Note that "lbnd_in" and "ubnd_in" together define the shape * and size of the input grid, its extent along a particular * (i'th) dimension being ubnd_in[i]-lbnd_in[i]+1 (assuming "i" * is zero-based). They also define the input grid's coordinate * system, with each pixel being of unit extent along each * dimension with integral coordinate values at its centre. * in * Pointer to the array of data to be resampled (with an element * for each pixel in the input grid). The numerical type of * these data should match the function used, as given by the * suffix on the function name. The storage order should be such * that the index of the first grid dimension varies most * rapidly and that of the final dimension least rapidly * (i.e. Fortran array storage order). * in_var * An optional pointer to a second array of positive numerical * values (with the same size and type as the "in" array), which * represent estimates of the statistical variance associated * with each element of the "in" array. If this second array is * given (along with the corresponding "out_var" array), then * estimates of the variance of the resampled data will also be * returned. * * If no variance estimates are required, a NULL pointer should * be given. * npoint * The number of points at which the input grid is to be * resampled. * offset * Pointer to an array of integers with "npoint" elements. For * each output point, this array should contain the zero-based * offset in the output array(s) (i.e. the "out" and, * optionally, the "out_var" arrays) at which the resampled * output value(s) should be stored. * coords * An array of pointers to double, with "ndim_in" * elements. Element "coords[coord]" should point at the first * element of an array of double (with "npoint" elements) which * contains the values of coordinate number "coord" for each * interpolation point. The value of coordinate number "coord" * for interpolation point number "point" is therefore given by * "coords[coord][point]" (assuming both indices to be * zero-based). If any point has a coordinate value of AST__BAD * associated with it, then the corresponding output data (and * variance) will be set to the value given by "badval" (unles the * AST__NOBAD flag is specified). * flags * The bitwise OR of a set of flag values which control the * operation of the function. Currently, only the flag * AST__USEBAD is significant and indicates whether there are * "bad" (i.e. missing) data in the input array(s) which must be * recognised and propagated to the output array(s). If this * flag is not set, all input values are treated literally. * badval * If the AST__USEBAD flag is set in the "flags" value (above), * this parameter specifies the value which is used to identify * bad data and/or variance values in the input array(s). Its * numerical type must match that of the "in" (and "in_var") * arrays. Unles the AST__NOBAD flag is specified in "flags", the * same value will also be used to flag any output array elements * for which resampled values could not be obtained. The output * arrays(s) may be flagged with this value whether or not the * AST__USEBAD flag is set (the function return value indicates * whether any such values have been produced). * out * Pointer to an array with the same data type as the "in" * array, into which the resampled data will be returned. Note * that details of how the output grid maps on to this array * (e.g. the storage order, number of dimensions, etc.) is * arbitrary and is specified entirely by means of the "offset" * array. The "out" array should therefore contain sufficient * elements to accommodate the "offset" values supplied. There * is no requirement that all elements of the "out" array should * be assigned values, and any which are not addressed by the * contents of the "offset" array will be left unchanged. * out_var * An optional pointer to an array with the same data type and * size as the "out" array, into which variance estimates for * the resampled values may be returned. This array will only be * used if the "in_var" array has been given. It is addressed in * exactly the same way (via the "offset" array) as the "out" * array. The values returned are estimates of the statistical * variance of the corresponding values in the "out" array, on * the assumption that all errors in input grid values (in the * "in" array) are statistically independent and that their * variance estimates (in the "in_var" array) may simply be * summed (with appropriate weighting factors). * * If no output variance estimates are required, a NULL pointer * should be given. * Returned Value: * The number of output grid points to which a data value (or a * variance value if relevant) equal to "badval" has been assigned * because no valid output value could be obtained. * Notes: * - There is a separate function for each numerical type of * gridded data, distinguished by replacing the in the function * name by the appropriate 1- or 2-character suffix. * - A value of zero will be returned if any of these functions is * invoked with the global error status set, or if it should fail * for any reason. */ /* Define macros to implement the function for a specific data type. */ #define MAKE_INTERPOLATE_LINEAR(X,Xtype,Xfloating,Xfloattype,Xsigned) \ static int InterpolateLinear##X( int ndim_in, \ const int *lbnd_in, const int *ubnd_in, \ const Xtype *in, const Xtype *in_var, \ int npoint, const int *offset, \ const double *const *coords, \ int flags, Xtype badval, \ Xtype *out, Xtype *out_var, int *status ) { \ \ /* Local Variables: */ \ Xfloattype sum; /* Weighted sum of pixel data values */ \ Xfloattype sum_var; /* Weighted sum of pixel variance values */ \ Xfloattype val; /* Value to be asigned to output pixel */ \ Xfloattype wtsum; /* Sum of weight values */ \ Xtype var; /* Variance value */ \ double *frac_hi; /* Pointer to array of weights */ \ double *frac_lo; /* Pointer to array of weights */ \ double *wt; /* Pointer to array of weights */ \ double *wtprod; /* Array of accumulated weights pointer */ \ double *xn_max; /* Pointer to upper limits array (n-d) */ \ double *xn_min; /* Pointer to lower limits array (n-d) */ \ double frac_hi_x; /* Pixel weight (x dimension) */ \ double frac_hi_y; /* Pixel weight (y dimension) */ \ double frac_lo_x; /* Pixel weight (x dimension) */ \ double frac_lo_y; /* Pixel weight (y dimension) */ \ double pixwt; /* Weight to apply to individual pixel */ \ double x; /* x coordinate value */ \ double xmax; /* x upper limit */ \ double xmin; /* x lower limit */ \ double xn; /* Coordinate value (n-d) */ \ double y; /* y coordinate value */ \ double ymax; /* y upper limit */ \ double ymin; /* y lower limit */ \ int *dim; /* Pointer to array of pixel indices */ \ int *hi; /* Pointer to array of upper indices */ \ int *lo; /* Pointer to array of lower indices */ \ int *stride; /* Pointer to array of dimension strides */ \ int bad; /* Output pixel bad? */ \ int bad_var; /* Output variance bad? */ \ int done; /* All pixel indices done? */ \ int hi_x; /* Upper pixel index (x dimension) */ \ int hi_y; /* Upper pixel index (y dimension) */ \ int idim; /* Loop counter for dimensions */ \ int ii; /* Loop counter for weights */ \ int ix; /* Pixel index in input grid x dimension */ \ int ixn; /* Pixel index (n-d) */ \ int iy; /* Pixel index in input grid y dimension */ \ int lo_x; /* Lower pixel index (x dimension) */ \ int lo_y; /* Lower pixel index (y dimension) */ \ int nobad; /* Was the AST__NOBAD flag set? */ \ int off_in; /* Offset to input pixel */ \ int off_lo; /* Offset to "first" input pixel */ \ int off_out; /* Offset to output pixel */ \ int pixel; /* Offset to input pixel containing point */ \ int point; /* Loop counter for output points */ \ int result; /* Result value to return */ \ int s; /* Temporary variable for strides */ \ int usebad; /* Use "bad" input pixel values? */ \ int usevar; /* Process variance array? */ \ int ystride; /* Stride along input grid y dimension */ \ \ /* Initialise. */ \ result = 0; \ \ /* Check the global error status. */ \ if ( !astOK ) return result; \ \ /* Initialise variables to avoid "used of uninitialised variable" \ messages from dumb compilers. */ \ sum = 0; \ sum_var = 0; \ wtsum = 0; \ bad = 0; \ bad_var = 0; \ \ /* Determine if we are processing bad pixels or variances. */ \ nobad = flags & AST__NOBAD; \ usebad = flags & AST__USEBAD; \ usevar = in_var && out_var; \ \ /* Handle the 1-dimensional case optimally. */ \ /* ---------------------------------------- */ \ if ( ndim_in == 1 ) { \ \ /* Calculate the coordinate limits of the input grid. */ \ xmin = (double) lbnd_in[ 0 ] - 0.5; \ xmax = (double) ubnd_in[ 0 ] + 0.5; \ \ /* Identify four cases, according to whether bad pixels and/or \ variances are being processed. In each case, loop through all the \ output points to (a) assemble the input data needed to form the \ interpolated value, and (b) calculate the result and assign it to \ the output arrays(s). In each case we assign constant values (0 or \ 1) to the "Usebad" and "Usevar" flags so that code for handling bad \ pixels and variances can be eliminated when not required. */ \ if ( nobad ) { \ if ( usebad ) { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \ 1,1,1) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \ 1,0,1) \ } \ } \ } else { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \ 0,1,1) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \ 0,0,1) \ } \ } \ } \ \ /* Four more cases as above, but this time with the AST__NOBAD flag \ un-set. */ \ } else { \ if ( usebad ) { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \ 1,1,0) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \ 1,0,0) \ } \ } \ } else { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \ 0,1,0) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \ 0,0,0) \ } \ } \ } \ } \ \ /* Handle the 2-dimensional case optimally. */ \ /* ---------------------------------------- */ \ } else if ( ndim_in == 2 ) { \ \ /* Calculate the stride along the y dimension of the input grid. */ \ ystride = ubnd_in[ 0 ] - lbnd_in[ 0 ] + 1; \ \ /* Calculate the coordinate limits of the input grid in each \ dimension. */ \ xmin = (double) lbnd_in[ 0 ] - 0.5; \ xmax = (double) ubnd_in[ 0 ] + 0.5; \ ymin = (double) lbnd_in[ 1 ] - 0.5; \ ymax = (double) ubnd_in[ 1 ] + 0.5; \ \ /* Identify four cases, according to whether bad pixels and/or \ variances are being processed. In each case, loop through all the \ output points to (a) assemble the input data needed to form the \ interpolated value, and (b) calculate the result and assign it to \ the output arrays(s). In each case we assign constant values (0 or \ 1) to the "Usebad" and "Usevar" flags so that code for handling bad \ pixels and variances can be eliminated when not required. */ \ if ( nobad ) { \ if ( usebad ) { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \ 1,1,1) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \ 1,0,1) \ } \ } \ } else { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \ 0,1,1) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \ 0,0,1) \ } \ } \ } \ \ /* Four more case as above, but without the AST__NOBAD flag. */ \ } else { \ if ( usebad ) { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \ 1,1,0) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \ 1,0,0) \ } \ } \ } else { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \ 0,1,0) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \ 0,0,0) \ } \ } \ } \ } \ \ /* Handle other numbers of dimensions. */ \ /* ----------------------------------- */ \ } else { \ \ /* Allocate workspace. */ \ dim = astMalloc( sizeof( int ) * (size_t) ndim_in ); \ frac_hi = astMalloc( sizeof( double ) * (size_t) ndim_in ); \ frac_lo = astMalloc( sizeof( double ) * (size_t) ndim_in ); \ hi = astMalloc( sizeof( int ) * (size_t) ndim_in ); \ lo = astMalloc( sizeof( int ) * (size_t) ndim_in ); \ stride = astMalloc( sizeof( int ) * (size_t) ndim_in ); \ wt = astMalloc( sizeof( double ) * (size_t) ndim_in ); \ wtprod = astMalloc( sizeof( double ) * (size_t) ndim_in ); \ xn_max = astMalloc( sizeof( double ) * (size_t) ndim_in ); \ xn_min = astMalloc( sizeof( double ) * (size_t) ndim_in ); \ if ( astOK ) { \ \ /* Calculate the stride along each dimension of the input grid. */ \ for ( s = 1, idim = 0; idim < ndim_in; idim++ ) { \ stride[ idim ] = s; \ s *= ubnd_in[ idim ] - lbnd_in[ idim ] + 1; \ \ /* Calculate the coordinate limits of the input grid in each \ dimension. */ \ xn_min[ idim ] = (double) lbnd_in[ idim ] - 0.5; \ xn_max[ idim ] = (double) ubnd_in[ idim ] + 0.5; \ } \ \ /* Identify four cases, according to whether bad pixels and/or \ variances are being processed. In each case, loop through all the \ output points to (a) assemble the input data needed to form the \ interpolated value, and (b) calculate the result and assign it to \ the output arrays(s). In each case we assign constant values (0 or \ 1) to the "Usebad" and "Usevar" flags so that code for handling bad \ pixels and variances can be eliminated when not required. */ \ if ( nobad ) { \ if ( usebad ) { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype, \ Xsigned,1,1,1) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype, \ Xsigned,1,0,1) \ } \ } \ } else { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype, \ Xsigned,0,1,1) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype, \ Xsigned,0,0,1) \ } \ } \ } \ \ /* Four more case as above, but without the AST__NOBAD flag. */ \ } else { \ if ( usebad ) { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype, \ Xsigned,1,1,0) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype, \ Xsigned,1,0,0) \ } \ } \ } else { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype, \ Xsigned,0,1,0) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype, \ Xsigned,0,0,0) \ } \ } \ } \ } \ } \ \ /* Free the workspace. */ \ dim = astFree( dim ); \ frac_hi = astFree( frac_hi ); \ frac_lo = astFree( frac_lo ); \ hi = astFree( hi ); \ lo = astFree( lo ); \ stride = astFree( stride ); \ wt = astFree( wt ); \ wtprod = astFree( wtprod ); \ xn_max = astFree( xn_max ); \ xn_min = astFree( xn_min ); \ } \ \ /* If an error has occurred, clear the returned result. */ \ if ( !astOK ) result = 0; \ \ /* Return the result. */ \ return result; \ } /* This subsidiary macro assembles the input data needed in preparation for forming the interpolated value in the 1-dimensional case. */ #define ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,Usebad,Usevar) \ \ /* Obtain the x coordinate of the current point and test if it lies \ outside the input grid. Also test if it is bad. */ \ x = coords[ 0 ][ point ]; \ bad = ( x < xmin ) || ( x >= xmax ) || ( x == AST__BAD ); \ if ( !bad ) { \ \ /* If input bad pixels must be detected, then obtain the offset along \ the input grid x dimension of the input pixel which contains the \ current coordinate and calculate this pixel's offset from the start \ of the input array. */ \ if ( Usebad ) { \ pixel = (int) floor( x + 0.5 ) - lbnd_in[ 0 ]; \ \ /* Test if the pixel is bad. */ \ bad = ( in[ pixel ] == badval ); \ } \ \ /* If OK, obtain the indices along the input grid x dimension of the \ two adjacent pixels which will contribute to the interpolated \ result. Also obtain the fractional weight to be applied to each of \ these pixels. */ \ if ( !bad ) { \ lo_x = (int) floor( x ); \ hi_x = lo_x + 1; \ frac_lo_x = (double) hi_x - x; \ frac_hi_x = 1.0 - frac_lo_x; \ \ /* Obtain the offset within the input array of the first pixel to be \ used for interpolation (the one with the smaller index). */ \ off_lo = lo_x - lbnd_in[ 0 ]; \ \ /* Initialise sums for forming the interpolated result. */ \ sum = (Xfloattype) 0.0; \ wtsum = (Xfloattype) 0.0; \ if ( Usevar ) { \ sum_var = (Xfloattype) 0.0; \ if ( ( Xsigned ) || ( Usebad ) ) bad_var = 0; \ } \ \ /* For each of the two pixels which may contribute to the result, \ test if the pixel index lies within the input grid. Where it does, \ accumulate the sums required for forming the interpolated \ result. In each case, we supply the pixel's offset within the input \ array and the weight to be applied to it. */ \ if ( lo_x >= lbnd_in[ 0 ] ) { \ FORM_LINEAR_INTERPOLATION_SUM(off_lo,frac_lo_x,Xtype, \ Xfloattype,Xsigned,Usebad,Usevar) \ } \ if ( hi_x <= ubnd_in[ 0 ] ) { \ FORM_LINEAR_INTERPOLATION_SUM(off_lo + 1,frac_hi_x,Xtype, \ Xfloattype,Xsigned,Usebad,Usevar) \ } \ } \ } /* This subsidiary macro assembles the input data needed in preparation for forming the interpolated value in the 2-dimensional case. */ #define ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,Usebad,Usevar) \ \ /* Obtain the x coordinate of the current point and test if it lies \ outside the input grid. Also test if it is bad. */ \ x = coords[ 0 ][ point ]; \ bad = ( x < xmin ) || ( x >= xmax ) || ( x == AST__BAD ); \ if ( !bad ) { \ \ /* If OK, then similarly obtain and test the y coordinate. */ \ y = coords[ 1 ][ point ]; \ bad = ( y < ymin ) || ( y >= ymax ) || ( y == AST__BAD ); \ if ( !bad ) { \ \ /* If input bad pixels must be detected, then obtain the offsets along \ each input grid dimension of the input pixel which contains the \ current coordinates. */ \ if ( Usebad ) { \ ix = (int) floor( x + 0.5 ); \ iy = (int) floor( y + 0.5 ); \ \ /* Calculate this pixel's offset from the start of the input array. */ \ pixel = ix - lbnd_in[ 0 ] + ystride * ( iy - lbnd_in[ 1 ] ); \ \ /* Test if the pixel is bad. */ \ bad = ( in[ pixel ] == badval ); \ } \ \ /* If OK, obtain the indices along the input grid x dimension of the \ two adjacent pixels which will contribute to the interpolated \ result. Also obtain the fractional weight to be applied to each of \ these pixels. */ \ if ( !bad ) { \ lo_x = (int) floor( x ); \ hi_x = lo_x + 1; \ frac_lo_x = (double) hi_x - x; \ frac_hi_x = 1.0 - frac_lo_x; \ \ /* Repeat this process for the y dimension. */ \ lo_y = (int) floor( y ); \ hi_y = lo_y + 1; \ frac_lo_y = (double) hi_y - y; \ frac_hi_y = 1.0 - frac_lo_y; \ \ /* Obtain the offset within the input array of the first pixel to be \ used for interpolation (the one with the smaller index along both \ dimensions). */ \ off_lo = lo_x - lbnd_in[ 0 ] + ystride * ( lo_y - lbnd_in[ 1 ] ); \ \ /* Initialise sums for forming the interpolated result. */ \ sum = (Xfloattype) 0.0; \ wtsum = (Xfloattype) 0.0; \ if ( Usevar ) { \ sum_var = (Xfloattype) 0.0; \ if ( ( Xsigned ) || ( Usebad ) ) bad_var = 0; \ } \ \ /* For each of the four pixels which may contribute to the result, \ test if the pixel indices lie within the input grid. Where they do, \ accumulate the sums required for forming the interpolated \ result. In each case, we supply the pixel's offset within the input \ array and the weight to be applied to it. */ \ if ( lo_y >= lbnd_in[ 1 ] ) { \ if ( lo_x >= lbnd_in[ 0 ] ) { \ FORM_LINEAR_INTERPOLATION_SUM(off_lo, \ frac_lo_x * frac_lo_y,Xtype, \ Xfloattype, Xsigned, \ Usebad,Usevar) \ } \ if ( hi_x <= ubnd_in[ 0 ] ) { \ FORM_LINEAR_INTERPOLATION_SUM(off_lo + 1, \ frac_hi_x * frac_lo_y,Xtype, \ Xfloattype,Xsigned, \ Usebad,Usevar) \ } \ } \ if ( hi_y <= ubnd_in[ 1 ] ) { \ if ( lo_x >= lbnd_in[ 0 ] ) { \ FORM_LINEAR_INTERPOLATION_SUM(off_lo + ystride, \ frac_lo_x * frac_hi_y,Xtype, \ Xfloattype,Xsigned, \ Usebad,Usevar) \ } \ if ( hi_x <= ubnd_in[ 0 ] ) { \ FORM_LINEAR_INTERPOLATION_SUM(off_lo + ystride + 1, \ frac_hi_x * frac_hi_y,Xtype, \ Xfloattype,Xsigned, \ Usebad,Usevar) \ } \ } \ } \ } \ } /* This subsidiary macro assembles the input data needed in preparation for forming the interpolated value in the n-dimensional case. */ #define ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,Usebad,Usevar) \ \ /* Initialise offsets into the input array. Then loop to obtain each coordinate associated with the current output point. */ \ off_in = 0; \ if ( Usebad ) pixel = 0; \ for ( idim = 0; idim < ndim_in; idim++ ) { \ xn = coords[ idim ][ point ]; \ \ /* Test if the coordinate lies outside the input grid. Also test if \ it is bad. If either is true, the corresponding output pixel value \ will be bad, so give up on this point. */ \ bad = ( xn < xn_min[ idim ] ) || ( xn >= xn_max[ idim ] ) || \ ( xn == AST__BAD ); \ if ( bad ) break; \ \ /* If input bad pixels must be detected, obtain the index along the \ current input grid dimension of the pixel which contains this \ coordinate and accumulate the pixel's offset from the start of the \ input array. */ \ if ( Usebad ) { \ pixel += stride[ idim ] * \ ( (int) floor( xn + 0.5 ) - lbnd_in[ idim ] ); \ } \ \ /* Obtain the indices along the current dimension of the input grid of \ the two (usually adjacent) pixels which will contribute to the \ output value. If necessary, however, restrict each index to ensure \ it does not lie outside the input grid. Also calculate the \ fractional weight to be given to each pixel in order to interpolate \ linearly between them. */ \ ixn = (int) floor( xn ); \ lo[ idim ] = MaxI( ixn, lbnd_in[ idim ], status ); \ hi[ idim ] = MinI( ixn + 1, ubnd_in[ idim ], status ); \ frac_lo[ idim ] = 1.0 - fabs( xn - (double) lo[ idim ] ); \ frac_hi[ idim ] = 1.0 - fabs( xn - (double) hi[ idim ] ); \ \ /* Store the lower index involved in interpolation along each \ dimension and accumulate the offset from the start of the input \ array of the pixel which has these indices. */ \ dim[ idim ] = lo[ idim ]; \ off_in += stride[ idim ] * ( lo[ idim ] - lbnd_in[ idim ] ); \ \ /* Also store the fractional weight associated with the lower pixel \ along each dimension. */ \ wt[ idim ] = frac_lo[ idim ]; \ } \ \ /* If the input pixel which contains the required coordinates has \ been identified, test if it is bad. */ \ if ( Usebad ) bad = bad || ( in[ pixel ] == badval ); \ \ /* If OK, initialise and loop over adjacent input pixels to obtain an \ interpolated value. */ \ if ( !bad ) { \ sum = (Xfloattype) 0.0; \ wtsum = (Xfloattype) 0.0; \ if ( Usevar ) { \ sum_var = (Xfloattype) 0.0; \ if ( ( Xsigned ) || ( Usebad ) ) bad_var = 0; \ } \ idim = ndim_in - 1; \ wtprod[ idim ] = 1.0; \ done = 0; \ do { \ \ /* Each contributing pixel is weighted by the product of the weights \ which account for the displacement of its centre from the required \ position along each dimension. However, since we typically only \ change the index of one dimension at a time, we can avoid forming \ this product repeatedly by retaining an array of accumulated weight \ products for all higher dimensions. We need then only update the \ lower elements in this array, corresponding to those dimensions \ whose index has changed. We do this here, "idim" being the index of \ the most significant dimension to have changed. Note that on the \ first pass, all dimensions are considered changed, causing this \ array to be initialised. */ \ for ( ii = idim; ii >= 1; ii-- ) { \ wtprod[ ii - 1 ] = wtprod[ ii ] * wt[ ii ]; \ } \ \ /* Accumulate the sums required for forming the interpolated \ result. We supply the pixel's offset within the input array and the \ weight to be applied to it. The pixel weight is formed by including \ the weight factor for dimension zero, since this is not included in \ the "wtprod" array. */ \ FORM_LINEAR_INTERPOLATION_SUM(off_in,wtprod[ 0 ] * wt[ 0 ], \ Xtype,Xfloattype,Xsigned, \ Usebad,Usevar) \ \ /* Now update the indices, offset and weight factors to refer to the \ next input pixel to be considered. */ \ idim = 0; \ do { \ \ /* The first input dimension which still refers to the pixel with the \ lower of the two possible indices is switched to refer to the other \ pixel (with the higher index). The offset into the input array and \ the fractional weight factor for this dimension are also updated \ accordingly. */ \ if ( dim[ idim ] != hi[ idim ] ) { \ dim[ idim ] = hi[ idim ]; \ off_in += stride[ idim ]; \ wt[ idim ] = frac_hi[ idim ]; \ break; \ \ /* Any earlier dimensions (referring to the higher index) are switched \ back to the lower index, if not already there, before going on to \ consider the next dimension. (This process is the same as \ incrementing a binary number and propagating overflows up through \ successive digits, except that dimensions where the "lo" and "hi" \ values are the same can only take one value.) The process stops at \ the first attempt to return the final dimension to the lower \ index. */ \ } else { \ if ( dim[ idim ] != lo[ idim ] ) { \ dim[ idim ] = lo[ idim ]; \ off_in -= stride[ idim ]; \ wt[ idim ] = frac_lo[ idim ]; \ } \ done = ( ++idim == ndim_in ); \ } \ } while ( !done ); \ } while ( !done ); \ } /* This subsidiary macro adds the contribution from a specified input pixel to the accumulated sums for forming the linearly interpolated value. */ #define FORM_LINEAR_INTERPOLATION_SUM(off,wt,Xtype,Xfloattype,Xsigned, \ Usebad,Usevar) \ \ /* Obtain the offset of the input pixel to use. */ \ off_in = ( off ); \ \ /* If necessary, test if this pixel is bad. If not, then obtain the \ weight to apply to it. */ \ if ( !( Usebad ) || ( in[ off_in ] != badval ) ) { \ pixwt = ( wt ); \ \ /* Increment the weighted sum of pixel values and the sum of weights. */ \ sum += ( (Xfloattype) in[ off_in ] ) * ( (Xfloattype) pixwt ); \ wtsum += (Xfloattype) pixwt; \ \ /* If an output variance estimate is to be generated, and it still \ seems possible to produce one, then obtain the input variance \ value. */ \ if ( Usevar ) { \ if ( !( ( Xsigned ) || ( Usebad ) ) || !bad_var ) { \ var = in_var[ off_in ]; \ \ /* Test if the variance value is bad (if the data type is signed, also \ check that it is not negative). */ \ if ( Usebad ) bad_var = ( var == badval ); \ CHECK_FOR_NEGATIVE_VARIANCE(Xtype) \ \ /* If OK, increment the weighted sum of variance values. */ \ if ( !( ( Xsigned ) || ( Usebad ) ) || !bad_var ) { \ sum_var += ( (Xfloattype) ( pixwt * pixwt ) ) * \ ( (Xfloattype) var ); \ } \ } \ } \ } /* This subsidiary macro calculates the interpolated output value (and variance) from the sums over contributing pixels and assigns them to the output array(s). */ #define CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Xsigned, \ Usebad,Usevar,Nobad) \ \ /* Obtain the pixel offset into the output array. */ \ off_out = offset[ point ]; \ \ /* Assign a bad output value (and variance) if required and count it. */ \ if ( bad ) { \ if( !Nobad ) { \ out[ off_out ] = badval; \ if ( Usevar ) out_var[ off_out ] = badval; \ } \ result++; \ \ /* Otherwise, calculate the interpolated value. If the output data \ type is floating point, this result can be stored directly, \ otherwise we must round to the nearest integer. */ \ } else { \ val = sum / wtsum; \ if ( Xfloating ) { \ out[ off_out ] = (Xtype) val; \ } else { \ out[ off_out ] = (Xtype) ( val + ( ( val >= (Xfloattype) 0.0 ) ? \ ( (Xfloattype) 0.5 ) : \ ( (Xfloattype) -0.5 ) ) ); \ } \ \ /* If a variance estimate is required but none can be obtained, then \ store a bad output variance value and count it. */ \ if ( Usevar ) { \ if ( ( ( Xsigned ) || ( Usebad ) ) && bad_var ) { \ if( !Nobad ) out_var[ off_out ] = badval; \ result++; \ \ /* Otherwise, calculate the variance estimate and store it, rounding \ to the nearest integer if necessary. */ \ } else { \ val = sum_var / ( wtsum * wtsum ); \ if ( Xfloating ) { \ out_var[ off_out ] = (Xtype) val; \ } else { \ out_var[ off_out ] = (Xtype) ( val + \ ( ( val >= (Xfloattype) 0.0 ) ? \ ( (Xfloattype) 0.5 ) : \ ( (Xfloattype) -0.5 ) ) ); \ } \ } \ } \ } /* This subsidiary macro tests for negative variance values in the macros above. This check is required only for signed data types. */ #define CHECK_FOR_NEGATIVE_VARIANCE(Xtype) \ bad_var = bad_var || ( var < ( (Xtype) 0 ) ); /* Expand the main macro above to generate a function for each required signed data type. */ #if HAVE_LONG_DOUBLE /* Not normally implemented */ MAKE_INTERPOLATE_LINEAR(LD,long double,1,long double,1) MAKE_INTERPOLATE_LINEAR(L,long int,0,long double,1) MAKE_INTERPOLATE_LINEAR(K,INT_BIG,0,long double,1) #else MAKE_INTERPOLATE_LINEAR(L,long int,0,double,1) MAKE_INTERPOLATE_LINEAR(K,INT_BIG,0,double,1) #endif MAKE_INTERPOLATE_LINEAR(D,double,1,double,1) MAKE_INTERPOLATE_LINEAR(F,float,1,float,1) MAKE_INTERPOLATE_LINEAR(I,int,0,double,1) MAKE_INTERPOLATE_LINEAR(S,short int,0,float,1) MAKE_INTERPOLATE_LINEAR(B,signed char,0,float,1) /* Re-define the macro for testing for negative variances to do nothing. */ #undef CHECK_FOR_NEGATIVE_VARIANCE #define CHECK_FOR_NEGATIVE_VARIANCE(Xtype) /* Expand the main macro above to generate a function for each required unsigned data type. */ #if HAVE_LONG_DOUBLE /* Not normally implemented */ MAKE_INTERPOLATE_LINEAR(UL,unsigned long int,0,long double,0) MAKE_INTERPOLATE_LINEAR(UK,UINT_BIG,0,long double,0) #else MAKE_INTERPOLATE_LINEAR(UL,unsigned long int,0,double,0) MAKE_INTERPOLATE_LINEAR(UK,UINT_BIG,0,double,0) #endif MAKE_INTERPOLATE_LINEAR(UI,unsigned int,0,double,0) MAKE_INTERPOLATE_LINEAR(US,unsigned short int,0,float,0) MAKE_INTERPOLATE_LINEAR(UB,unsigned char,0,float,0) /* Undefine the macros uxsed above. */ #undef CHECK_FOR_NEGATIVE_VARIANCE #undef CALC_AND_ASSIGN_OUTPUT #undef FORM_LINEAR_INTERPOLATION_SUM #undef ASSEMBLE_INPUT_ND #undef ASSEMBLE_INPUT_2D #undef ASSEMBLE_INPUT_1D #undef MAKE_INTERPOLATE_LINEAR /* * Name: * InterpolateNearest * Purpose: * Resample a data grid, using the nearest-pixel interpolation scheme. * Type: * Private function. * Synopsis: * #include "mapping.h" * int InterpolateNearest( int ndim_in, * const int *lbnd_in, const int *ubnd_in, * const *in, const *in_var, * int npoint, const int *offset, * const double *const *coords, * int flags, badval, * *out, *out_var ) * Class Membership: * Mapping member function. * Description: * This is a set of functions which resample a rectangular input * grid of data (and, optionally, associated statistical variance * values) so as to place them into a new output grid. Each output * grid point may be mapped on to a position in the input grid in * an arbitrary way. Where the positions given do not correspond * with a pixel centre in the input grid, the interpolation scheme * used is simply to select the nearest pixel (i.e. the one whose * bounds contain the supplied position). * Parameters: * ndim_in * The number of dimensions in the input grid. This should be at * least one. * lbnd_in * Pointer to an array of integers, with "ndim_in" elements. * This should give the coordinates of the centre of the first * pixel in the input grid along each dimension. * ubnd_in * Pointer to an array of integers, with "ndim_in" elements. * This should give the coordinates of the centre of the last * pixel in the input grid along each dimension. * * Note that "lbnd_in" and "ubnd_in" together define the shape * and size of the input grid, its extent along a particular * (i'th) dimension being ubnd_in[i]-lbnd_in[i]+1 (assuming "i" * is zero-based). They also define the input grid's coordinate * system, with each pixel being of unit extent along each * dimension with integral coordinate values at its centre. * in * Pointer to the array of data to be resampled (with an element * for each pixel in the input grid). The numerical type of * these data should match the function used, as given by the * suffix on the function name. The storage order should be such * that the index of the first grid dimension varies most * rapidly and that of the final dimension least rapidly * (i.e. Fortran array storage order). * in_var * An optional pointer to a second array of positive numerical * values (with the same size and type as the "in" array), which * represent estimates of the statistical variance associated * with each element of the "in" array. If this second array is * given (along with the corresponding "out_var" array), then * estimates of the variance of the resampled data will also be * returned. * * If no variance estimates are required, a NULL pointer should * be given. * npoint * The number of points at which the input grid is to be * resampled. * offset * Pointer to an array of integers with "npoint" elements. For * each output point, this array should contain the zero-based * offset in the output array(s) (i.e. the "out" and, * optionally, the "out_var" arrays) at which the resampled * output value(s) should be stored. * coords * An array of pointers to double, with "ndim_in" * elements. Element "coords[coord]" should point at the first * element of an array of double (with "npoint" elements) which * contains the values of coordinate number "coord" for each * interpolation point. The value of coordinate number "coord" * for interpolation point number "point" is therefore given by * "coords[coord][point]" (assuming both indices to be * zero-based). If any point has a coordinate value of AST__BAD * associated with it, then the corresponding output data (and * variance) will be set to the value given by "badval" (unles the * AST__NOBAD flag is specified). * flags * The bitwise OR of a set of flag values which control the * operation of the function. Currently, only the flag * AST__USEBAD is significant and indicates whether there are * "bad" (i.e. missing) data in the input array(s) which must be * recognised and propagated to the output array(s). If this * flag is not set, all input values are treated literally. * badval * If the AST__USEBAD flag is set in the "flags" value (above), * this parameter specifies the value which is used to identify * bad data and/or variance values in the input array(s). Its * numerical type must match that of the "in" (and "in_var") * arrays. Unles the AST__NOBAD flag is specified in "flags", the * same value will also be used to flag any output array elements * for which resampled values could not be obtained. The output * arrays(s) may be flagged with this value whether or not the * AST__USEBAD flag is set (the function return value indicates * whether any such values have been produced). * out * Pointer to an array with the same data type as the "in" * array, into which the resampled data will be returned. Note * that details of how the output grid maps on to this array * (e.g. the storage order, number of dimensions, etc.) is * arbitrary and is specified entirely by means of the "offset" * array. The "out" array should therefore contain sufficient * elements to accommodate the "offset" values supplied. There * is no requirement that all elements of the "out" array should * be assigned values, and any which are not addressed by the * contents of the "offset" array will be left unchanged. * out_var * An optional pointer to an array with the same data type and * size as the "out" array, into which variance estimates for * the resampled values may be returned. This array will only be * used if the "in_var" array has been given. It is addressed in * exactly the same way (via the "offset" array) as the "out" * array. The values returned are estimates of the statistical * variance of the corresponding values in the "out" array, on * the assumption that all errors in input grid values (in the * "in" array) are statistically independent and that their * variance estimates (in the "in_var" array) may simply be * summed (with appropriate weighting factors). * * If no output variance estimates are required, a NULL pointer * should be given. * Returned Value: * The number of output grid points to which a data value (or a * variance value if relevant) equal to "badval" has been assigned * because no valid output value could be obtained. * Notes: * - There is a separate function for each numerical type of * gridded data, distinguished by replacing the in the function * name by the appropriate 1- or 2-character suffix. * - A value of zero will be returned if any of these functions is * invoked with the global error status set, or if it should fail * for any reason. */ /* Define a macro to implement the function for a specific data type. */ #define MAKE_INTERPOLATE_NEAREST(X,Xtype,Xsigned) \ static int InterpolateNearest##X( int ndim_in, \ const int *lbnd_in, const int *ubnd_in, \ const Xtype *in, const Xtype *in_var, \ int npoint, const int *offset, \ const double *const *coords, \ int flags, Xtype badval, \ Xtype *out, Xtype *out_var, int *status ) { \ \ /* Local Variables: */ \ Xtype var; /* Variance value */ \ double *xn_max; /* Pointer to upper limits array (n-d) */ \ double *xn_min; /* Pointer to lower limits array (n-d) */ \ double x; /* x coordinate value */ \ double xmax; /* x upper limit */ \ double xmin; /* x lower limit */ \ double xn; /* Coordinate value (n-d) */ \ double y; /* y coordinate value */ \ double ymax; /* y upper limit */ \ double ymin; /* y lower limit */ \ int *stride; /* Pointer to array of dimension strides */ \ int bad; /* Output pixel bad? */ \ int idim; /* Loop counter for dimensions */ \ int ix; /* Number of pixels offset in x direction */ \ int ixn; /* Number of pixels offset (n-d) */ \ int iy; /* Number of pixels offset in y direction */ \ int nobad; /* Was the AST__NOBAD flag set? */ \ int off_in; /* Pixel offset into input array */ \ int off_out; /* Pixel offset into output array */ \ int point; /* Loop counter for output points */ \ int result; /* Returned result value */ \ int s; /* Temporary variable for strides */ \ int usebad; /* Use "bad" input pixel values? */ \ int usevar; /* Process variance array? */ \ int ystride; /* Stride along input grid y direction */ \ \ /* Initialise. */ \ result = 0; \ \ /* Check the global error status. */ \ if ( !astOK ) return result; \ \ /* Initialise variables to avoid "used of uninitialised variable" \ messages from dumb compilers. */ \ bad = 0; \ off_in = 0; \ \ /* Determine if we are processing bad pixels or variances. */ \ nobad = flags & AST__NOBAD; \ usebad = flags & AST__USEBAD; \ usevar = in_var && out_var; \ \ /* Handle the 1-dimensional case optimally. */ \ /* ---------------------------------------- */ \ if ( ndim_in == 1 ) { \ \ /* Calculate the coordinate limits of the input array. */ \ xmin = (double) lbnd_in[ 0 ] - 0.5; \ xmax = (double) ubnd_in[ 0 ] + 0.5; \ \ /* Identify four cases, according to whether bad pixels and/or \ variances are being processed. In each case, loop through all the \ output points to (a) assemble the input data needed to form the \ interpolated value, and (b) calculate the result and assign it to \ the output arrays(s). In each case we assign constant values (0 or \ 1) to the "Usebad" and "Usevar" flags so that code for handling bad \ pixels and variances can be eliminated when not required. */ \ if ( nobad ) { \ if ( usebad ) { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_1D(X,Xtype,1,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,1,1,1) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_1D(X,Xtype,1,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,1,0,1) \ } \ } \ } else { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_1D(X,Xtype,0,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,0,1,1) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_1D(X,Xtype,0,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,0,0,1) \ } \ } \ } \ \ /* Four more cases as above, but without the AST__NOBAD flag. */ \ } else { \ if ( usebad ) { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_1D(X,Xtype,1,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,1,1,0) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_1D(X,Xtype,1,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,1,0,0) \ } \ } \ } else { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_1D(X,Xtype,0,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,0,1,0) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_1D(X,Xtype,0,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,0,0,0) \ } \ } \ } \ } \ \ /* Handle the 2-dimensional case optimally. */ \ /* ---------------------------------------- */ \ } else if ( ndim_in == 2 ) { \ \ /* Calculate the stride along the y dimension of the input grid. */ \ ystride = ubnd_in[ 0 ] - lbnd_in[ 0 ] + 1; \ \ /* Calculate the coordinate limits of the input array in each \ dimension. */ \ xmin = (double) lbnd_in[ 0 ] - 0.5; \ xmax = (double) ubnd_in[ 0 ] + 0.5; \ ymin = (double) lbnd_in[ 1 ] - 0.5; \ ymax = (double) ubnd_in[ 1 ] + 0.5; \ \ /* Identify four cases, according to whether bad pixels and/or \ variances are being processed. In each case, loop through all the \ output points to (a) assemble the input data needed to form the \ interpolated value, and (b) calculate the result and assign it to \ the output arrays(s). In each case we assign constant values (0 or \ 1) to the "Usebad" and "Usevar" flags so that code for handling bad \ pixels and variances can be eliminated when not required. */ \ if ( nobad ) { \ if ( usebad ) { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_2D(X,Xtype,1,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,1,1,1) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_2D(X,Xtype,1,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,1,0,1) \ } \ } \ } else { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_2D(X,Xtype,0,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,0,1,1) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_2D(X,Xtype,0,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,0,0,1) \ } \ } \ } \ \ /* Four more cases as above, but without the AST__NOBAD flag. */ \ } else { \ if ( usebad ) { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_2D(X,Xtype,1,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,1,1,0) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_2D(X,Xtype,1,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,1,0,0) \ } \ } \ } else { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_2D(X,Xtype,0,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,0,1,0) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_2D(X,Xtype,0,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,0,0,0) \ } \ } \ } \ } \ \ /* Handle other numbers of dimensions. */ \ /* ----------------------------------- */ \ } else { \ \ /* Allocate workspace. */ \ stride = astMalloc( sizeof( int ) * (size_t) ndim_in ); \ xn_max = astMalloc( sizeof( double ) * (size_t) ndim_in ); \ xn_min = astMalloc( sizeof( double ) * (size_t) ndim_in ); \ if ( astOK ) { \ \ /* Calculate the stride along each dimension of the input grid. */ \ for ( s = 1, idim = 0; idim < ndim_in; idim++ ) { \ stride[ idim ] = s; \ s *= ubnd_in[ idim ] - lbnd_in[ idim ] + 1; \ \ /* Calculate the coordinate limits of the input grid in each \ dimension. */ \ xn_min[ idim ] = (double) lbnd_in[ idim ] - 0.5; \ xn_max[ idim ] = (double) ubnd_in[ idim ] + 0.5; \ } \ \ /* Identify four cases, according to whether bad pixels and/or \ variances are being processed. In each case, loop through all the \ output points to (a) assemble the input data needed to form the \ interpolated value, and (b) calculate the result and assign it to \ the output arrays(s). In each case we assign constant values (0 or \ 1) to the "Usebad" and "Usevar" flags so that code for handling bad \ pixels and variances can be eliminated when not required. */ \ if ( nobad ) { \ if ( usebad ) { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_ND(X,Xtype,1,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,1,1,1) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_ND(X,Xtype,1,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,1,0,1) \ } \ } \ } else { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_ND(X,Xtype,0,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,0,1,1) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_ND(X,Xtype,0,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,0,0,1) \ } \ } \ } \ \ /* Another 4 cases as above, but without the AST__NOBAD flag. */ \ } else { \ if ( usebad ) { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_ND(X,Xtype,1,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,1,1,0) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_ND(X,Xtype,1,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,1,0,0) \ } \ } \ } else { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_ND(X,Xtype,0,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,0,1,0) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_ND(X,Xtype,0,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,0,0,0) \ } \ } \ } \ } \ } \ \ /* Free the workspace. */ \ stride = astFree( stride ); \ xn_max = astFree( xn_max ); \ xn_min = astFree( xn_min ); \ } \ \ /* If an error has occurred, clear the returned result. */ \ if ( !astOK ) result = 0; \ \ /* Return the result. */ \ return result; \ } /* This subsidiary macro assembles the input data needed in preparation for forming the interpolated value in the 1-dimensional case. */ #define ASSEMBLE_INPUT_1D(X,Xtype,Usebad,Usevar) \ \ /* Obtain the x coordinate of the current point and test if it lies \ outside the input grid, or is bad. */ \ x = coords[ 0 ][ point ]; \ bad = ( x < xmin ) || ( x >= xmax ) || ( x == AST__BAD ); \ if ( !bad ) { \ \ /* If not, then obtain the offset within the input grid of the pixel \ which contains the current point. */ \ off_in = (int) floor( x + 0.5 ) - lbnd_in[ 0 ]; \ \ /* If necessary, test if the input pixel is bad. */ \ if ( Usebad ) bad = ( in[ off_in ] == badval ); \ } /* This subsidiary macro assembles the input data needed in preparation for forming the interpolated value in the 2-dimensional case. */ #define ASSEMBLE_INPUT_2D(X,Xtype,Usebad,Usevar) \ \ /* Obtain the x coordinate of the current point and test if it lies \ outside the input grid, or is bad. */ \ x = coords[ 0 ][ point ]; \ bad = ( x < xmin ) || ( x >= xmax ) || ( x == AST__BAD ); \ if ( !bad ) { \ \ /* If not, then similarly obtain and test the y coordinate. */ \ y = coords[ 1 ][ point ]; \ bad = ( y < ymin ) || ( y >= ymax ) || ( y == AST__BAD ); \ if ( !bad ) { \ \ /* Obtain the offsets along each input grid dimension of the input \ pixel which contains the current point. */ \ ix = (int) floor( x + 0.5 ) - lbnd_in[ 0 ]; \ iy = (int) floor( y + 0.5 ) - lbnd_in[ 1 ]; \ \ /* Calculate this pixel's offset from the start of the input array. */ \ off_in = ix + ystride * iy; \ \ /* If necessary, test if the input pixel is bad. */ \ if ( Usebad ) bad = ( in[ off_in ] == badval ); \ } \ } /* This subsidiary macro assembles the input data needed in preparation for forming the interpolated value in the n-dimensional case. */ #define ASSEMBLE_INPUT_ND(X,Xtype,Usebad,Usevar) \ \ /* Initialise the offset into the input array. Then loop to obtain \ each coordinate associated with the current output point. */ \ off_in = 0; \ for ( idim = 0; idim < ndim_in; idim++ ) { \ xn = coords[ idim ][ point ]; \ \ /* Test if the coordinate lies outside the input grid, or is bad. If \ either is true, the corresponding output pixel value will be bad, \ so give up on this point. */ \ bad = ( xn < xn_min[ idim ] ) || ( xn >= xn_max[ idim ] ) || \ ( xn == AST__BAD ); \ if ( bad ) break; \ \ /* Obtain the offset along the current input grid dimension of the \ input pixel which contains the current point. */ \ ixn = (int) floor( xn + 0.5 ) - lbnd_in[ idim ]; \ \ /* Accumulate this pixel's offset from the start of the input \ array. */ \ off_in += ixn * stride[ idim ]; \ } \ \ /* Once the required input pixel has been located, test if it is \ bad, if necessary. */ \ if ( Usebad ) bad = bad || ( in[ off_in ] == badval ); /* This subsidiary macro assigns the output value (and variance) to the output array(s). */ #define CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xsigned,Usebad,Usevar,Nobad) \ \ /* Obtain the pixel offset into the output array. */ \ off_out = offset[ point ]; \ \ /* If the input data value is bad, assign a bad output value (and \ variance, if required) and count it. */ \ if ( bad ) { \ if( !Nobad ) { \ out[ off_out ] = badval; \ if ( Usevar ) out_var[ off_out ] = badval; \ } \ result++; \ \ /* Otherwise, assign the value obtained from the input grid. */ \ } else { \ out[ off_out ] = in[ off_in ]; \ \ /* If required, obtain the associated variance value. If necessary, \ test if it is bad (if the data type is signed, also check that it \ is not negative). */ \ if ( Usevar ) { \ var = in_var[ off_in ]; \ if ( Usebad ) bad = ( var == badval ); \ CHECK_FOR_NEGATIVE_VARIANCE(Xtype) \ \ /* If the variance value can be bad, and is, then store a bad value in \ the output array and count it. Otherwise, store the variance \ value. */ \ if ( ( ( Xsigned ) || ( Usebad ) ) && bad ) { \ if( !Nobad ) out_var[ off_out ] = badval; \ result++; \ } else { \ out_var[ off_out ] = var; \ } \ } \ } /* This subsidiary macro tests for negative variance values in the macros above. This check is required only for signed data types. */ #define CHECK_FOR_NEGATIVE_VARIANCE(Xtype) \ bad = bad || ( var < ( (Xtype) 0 ) ); /* Expand the main macro above to generate a function for each required signed data type. */ #if HAVE_LONG_DOUBLE /* Not normally implemented */ MAKE_INTERPOLATE_NEAREST(LD,long double,1) #endif MAKE_INTERPOLATE_NEAREST(D,double,1) MAKE_INTERPOLATE_NEAREST(F,float,1) MAKE_INTERPOLATE_NEAREST(L,long int,1) MAKE_INTERPOLATE_NEAREST(K,INT_BIG,1) MAKE_INTERPOLATE_NEAREST(I,int,1) MAKE_INTERPOLATE_NEAREST(S,short int,1) MAKE_INTERPOLATE_NEAREST(B,signed char,1) /* Re-define the macro for testing for negative variances to do nothing. */ #undef CHECK_FOR_NEGATIVE_VARIANCE #define CHECK_FOR_NEGATIVE_VARIANCE(Xtype) /* Expand the main macro above to generate a function for each required unsigned data type. */ MAKE_INTERPOLATE_NEAREST(UK,UINT_BIG,0) MAKE_INTERPOLATE_NEAREST(UL,unsigned long int,0) MAKE_INTERPOLATE_NEAREST(UI,unsigned int,0) MAKE_INTERPOLATE_NEAREST(US,unsigned short int,0) MAKE_INTERPOLATE_NEAREST(UB,unsigned char,0) /* Undefine the macros used above. */ #undef CHECK_FOR_NEGATIVE_VARIANCE #undef CALC_AND_ASSIGN_OUTPUT #undef ASSEMBLE_INPUT_ND #undef ASSEMBLE_INPUT_2D #undef ASSEMBLE_INPUT_1D #undef MAKE_INTERPOLATE_NEAREST /* * Name: * InterpolateBlockAverage * Purpose: * Resample a data grid, using multidimensional block averaging. * Type: * Private function. * Synopsis: * #include "mapping.h" * void InterpolateBlockAverage( int ndim_in, * const int lbnd_in[], * const int ubnd_in[], * const in[], * const in_var[], * int npoint, const int offset[], * const double *const coords[], * const double params[], int flags, * badval, *out, * *out_var, int *nbad ) * Class Membership: * Mapping member function. * Description: * This is a set of functions which resample a rectangular input * grid of data (and, optionally, associated statistical variance * values) so as to place them into a new output grid. To generate * an output grid pixel, a block average is taken over an ndim- * dimensional hypercube of pixels in the input grid. If variances * are being used then the input pixels will be weighted according * to the reciprocals of the corresponding variance values, and * input pixels without a valid variance will be ignored; * otherwise an unweighted average will be taken over * all non-bad pixels in the cube. The size of the cube over which * the average is taken is determined by the first element of the * params array. * * This "interpolation" scheme is appropriate where an input grid * is to be resampled onto a much coarser output grid. * Parameters: * ndim_in * The number of dimensions in the input grid. This should be at * least one. * lbnd_in * Pointer to an array of integers, with "ndim_in" elements. * This should give the coordinates of the centre of the first * pixel in the input grid along each dimension. * ubnd_in * Pointer to an array of integers, with "ndim_in" elements. * This should give the coordinates of the centre of the last * pixel in the input grid along each dimension. * * Note that "lbnd_in" and "ubnd_in" together define the shape * and size of the input grid, its extent along a particular * (i'th) dimension being ubnd_in[i]-lbnd_in[i]+1 (assuming "i" * is zero-based). They also define the input grid's coordinate * system, with each pixel being of unit extent along each * dimension with integral coordinate values at its centre. * in * Pointer to the array of data to be resampled (with an element * for each pixel in the input grid). The numerical type of * these data should match the function used, as given by the * suffix on the function name. The storage order should be such * that the index of the first grid dimension varies most * rapidly and that of the final dimension least rapidly * (i.e. Fortran array storage order). * in_var * An optional pointer to a second array of positive numerical * values (with the same size and type as the "in" array), which * represent estimates of the statistical variance associated * with each element of the "in" array. If this second array is * given (along with the corresponding "out_var" array), then * estimates of the variance of the resampled data will also be * returned. * * If no variance estimates are required, a NULL pointer should * be given. * npoint * The number of points at which the input grid is to be * resampled. * offset * Pointer to an array of integers with "npoint" elements. For * each output point, this array should contain the zero-based * offset in the output array(s) (i.e. the "out" and, * optionally, the "out_var" arrays) at which the resampled * output value(s) should be stored. * coords * An array of pointers to double, with "ndim_in" * elements. Element "coords[coord]" should point at the first * element of an array of double (with "npoint" elements) which * contains the values of coordinate number "coord" for each * interpolation point. The value of coordinate number "coord" * for interpolation point number "point" is therefore given by * "coords[coord][point]" (assuming both indices to be * zero-based). If any point has a coordinate value of AST__BAD * associated with it, then the corresponding output data (and * variance) will be set to the value given by "badval" (unles the * AST__NOBAD flag is specified). * params * A pointer to an array of doubles giving further information * about how the resampling is to proceed. Only the first * element is significant; the nearest integer to this gives * the number of pixels on either side of the central input * grid pixel to use in each dimension. Therefore * (1 + 2*params[0])**ndim_in pixels will be averaged over to * generate each output pixel. * flags * The bitwise OR of a set of flag values which control the * operation of the function. Currently, only the flag * AST__USEBAD is significant and indicates whether there are * "bad" (i.e. missing) data in the input array(s) which must be * recognised and propagated to the output array(s). If this * flag is not set, all input values are treated literally. * badval * If the AST__USEBAD flag is set in the "flags" value (above), * this parameter specifies the value which is used to identify * bad data and/or variance values in the input array(s). Its * numerical type must match that of the "in" (and "in_var") * arrays. Unles the AST__NOBAD flag is specified in "flags", the * same value will also be used to flag any output array elements * for which resampled values could not be obtained. The output * arrays(s) may be flagged with this value whether or not the * AST__USEBAD flag is set (the function return value indicates * whether any such values have been produced). * out * Pointer to an array with the same data type as the "in" * array, into which the resampled data will be returned. Note * that details of how the output grid maps on to this array * (e.g. the storage order, number of dimensions, etc.) is * arbitrary and is specified entirely by means of the "offset" * array. The "out" array should therefore contain sufficient * elements to accommodate the "offset" values supplied. There * is no requirement that all elements of the "out" array should * be assigned values, and any which are not addressed by the * contents of the "offset" array will be left unchanged. * out_var * An optional pointer to an array with the same data type and * size as the "out" array, into which variance estimates for * the resampled values may be returned. This array will only be * used if the "in_var" array has been given. It is addressed in * exactly the same way (via the "offset" array) as the "out" * array. The values returned are estimates of the statistical * variance of the corresponding values in the "out" array, on * the assumption that all errors in input grid values (in the * "in" array) are statistically independent and that their * variance estimates (in the "in_var" array) may simply be * summed (with appropriate weighting factors). * * If no output variance estimates are required, a NULL pointer * should be given. * nbad * Pointer to an int in which to return the number of * interpolation points at which an output data value (and/or a * variance value if relevant) equal to "badval" has been * assigned because no valid interpolated value could be * obtained. The maximum value that will be returned is * "npoint" and the minimum is zero (indicating that all output * values were successfully obtained). * Notes: * - There is a separate function for each numerical type of * gridded data, distinguished by replacing the in the function * name by the appropriate 1- or 2-character suffix. */ /* Define a macro to implement the function for a specific data type. */ #define MAKE_INTERPOLATE_BLOCKAVE(X,Xtype,Xfloating,Xfloattype,Xsigned) \ static void InterpolateBlockAverage##X( int ndim_in, \ const int lbnd_in[], \ const int ubnd_in[], \ const Xtype in[], \ const Xtype in_var[], \ int npoint, const int offset[], \ const double *const coords[], \ const double params[], int flags, \ Xtype badval, Xtype *out, \ Xtype *out_var, int *nbad ) { \ \ /* Local Variables: */ \ Xfloattype hi_lim; /* Upper limit on output values */ \ Xfloattype lo_lim; /* Lower limit on output values */ \ Xfloattype pixwt; /* Weight to apply to individual pixel */ \ Xfloattype sum; /* Weighted sum of pixel data values */ \ Xfloattype sum_var; /* Weighted sum of pixel variance values */ \ Xfloattype val; /* Data value to be assigned to output */ \ Xfloattype val_var; /* Variance to be assigned to output */ \ Xfloattype wtsum; /* Sum of weight values */ \ Xfloattype wtsum_sq; /* Square of sum of weights */ \ Xtype var; /* Variance value */ \ double *xn_max; /* Pointer to upper limits array (n-d) */ \ double *xn_min; /* Pointer to lower limits array (n-d) */ \ double x; /* x coordinate value */ \ double xn; /* Coordinate value (n-d) */ \ double y; /* y coordinate value */ \ int *hi; /* Pointer to array of upper indices */ \ int *ixm; /* Pointer to array of current indices */ \ int *lo; /* Pointer to array of lower indices */ \ int *status; /* Pointer to inherited status value */ \ int *stride; /* Pointer to array of dimension strides */ \ int bad; /* Output pixel bad? */ \ int bad_var; /* Output variance bad? */ \ int done; /* All pixel indices done? */ \ int hi_x; /* Upper pixel index (x dimension) */ \ int hi_y; /* Upper pixel index (y dimension) */ \ int idim; /* Loop counter for dimensions */ \ int ix; /* Pixel index in input grid x dimension */ \ int ixn; /* Pixel index in input grid (n-d) */ \ int iy; /* Pixel index in input grid y dimension */ \ int lo_x; /* Lower pixel index (x dimension) */ \ int lo_y; /* Lower pixel index (y dimension) */ \ int neighb; /* Number of adjacent pixels on each side */ \ int nobad; /* Was the AST__NOBAD flag set? */ \ int off1; /* Input pixel offset due to y index */ \ int off_in; /* Offset to input pixel */ \ int off_out; /* Offset to output pixel */ \ int point; /* Loop counter for output points */ \ int s; /* Temporary variable for strides */ \ int usebad; /* Use "bad" input pixel values? */ \ int usevar; /* Process variance array? */ \ int ystride; /* Stride along input grid y dimension */ \ \ /* Initialise. */ \ *nbad = 0; \ \ /* Get a pointer to the inherited status argument. */ \ status = astGetStatusPtr; \ \ /* Check the global error status. */ \ if ( !astOK ) return; \ \ /* Initialise variables to avoid "used of uninitialised variable" \ messages from dumb compilers. */ \ val = 0; \ val_var = 0; \ sum_var = 0; \ wtsum = 0; \ bad = 0; \ bad_var = 0; \ \ /* Determine if we are processing bad pixels or variances. */ \ nobad = flags & AST__NOBAD; \ usebad = flags & AST__USEBAD; \ usevar = in_var && out_var; \ \ /* Set the number of pixels each side of central pixel to use. */ \ neighb = (int) floor( params[ 0 ] + 0.5 ); \ \ /* Set up limits for checking output values to ensure that they do not \ overflow the range of the data type being used. */ \ lo_lim = LO_##X; \ hi_lim = HI_##X; \ \ /* Handle the 1-dimensional case optimally. */ \ /* ---------------------------------------- */ \ if ( ndim_in == 1 ) { \ \ /* Identify four cases, according to whether bad pixels and/or \ variances are being processed. In each case, loop through all the \ output points to (a) assemble the input data needed to form the \ interpolated value, and (b) calculate the result and assign it to \ the output arrays(s). In each case we assign constant values (0 or \ 1) to the "Usebad" and "Usevar" flags so that code for handling bad \ pixels and variances can be eliminated when not required. */ \ if ( nobad ) { \ if ( usebad ) { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,1,1) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,0,1) \ } \ } \ } else { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,1,1) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,0,1) \ } \ } \ } \ \ /* Another 4 cases as above, but without the AST__NOBAD flag. */ \ } else { \ if ( usebad ) { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,1,0) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,0,0) \ } \ } \ } else { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,1,0) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,0,0) \ } \ } \ } \ } \ \ /* Handle the 2-dimensional case optimally. */ \ /* ---------------------------------------- */ \ } else if ( ndim_in == 2 ) { \ \ /* Calculate the stride along the y dimension of the input grid. */ \ ystride = ubnd_in[ 0 ] - lbnd_in[ 0 ] + 1; \ \ /* Identify four cases, according to whether bad pixels and/or \ variances are being processed. In each case, loop through all the \ output points to (a) assemble the input data needed to form the \ interpolated value, and (b) calculate the result and assign it to \ the output arrays(s). In each case we assign constant values (0 or \ 1) to the "Usebad" and "Usevar" flags so that code for handling bad \ pixels and variances can be eliminated when not required. */ \ if ( nobad ) { \ if ( usebad ) { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,1,1) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,0,1) \ } \ } \ } else { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,1,1) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,0,1) \ } \ } \ } \ \ /* Another 4 cases as above, but without the AST__NOBAD flag. */ \ } else { \ if ( usebad ) { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,1,0) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,0,0) \ } \ } \ } else { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,1,0) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,0,0) \ } \ } \ } \ } \ \ /* Handle other numbers of dimensions. */ \ /* ----------------------------------- */ \ } else { \ \ /* Allocate workspace. */ \ hi = astMalloc( sizeof( int ) * (size_t) ndim_in ); \ lo = astMalloc( sizeof( int ) * (size_t) ndim_in ); \ stride = astMalloc( sizeof( int ) * (size_t) ndim_in ); \ ixm = astMalloc( sizeof( int ) * (size_t) ndim_in ); \ xn_max = astMalloc( sizeof( double ) * (size_t) ndim_in ); \ xn_min = astMalloc( sizeof( double ) * (size_t) ndim_in ); \ if ( astOK ) { \ \ /* Calculate the stride along each dimension of the input grid. */ \ for ( s = 1, idim = 0; idim < ndim_in; idim++ ) { \ stride[ idim ] = s; \ s *= ubnd_in[ idim ] - lbnd_in[ idim ] + 1; \ \ /* Calculate the coordinate limits of the input grid in each \ dimension. */ \ xn_min[ idim ] = (double) lbnd_in[ idim ] - 0.5; \ xn_max[ idim ] = (double) ubnd_in[ idim ] + 0.5; \ } \ \ /* Identify four cases, according to whether bad pixels and/or \ variances are being processed. In each case, loop through all the \ output points to (a) assemble the input data needed to form the \ interpolated value, and (b) calculate the result and assign it to \ the output arrays(s). In each case we assign constant values (0 or \ 1) to the "Usebad" and "Usevar" flags so that code for handling bad \ pixels and variances can be eliminated when not required. */ \ if ( nobad ) { \ if ( usebad ) { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,1,1) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,0,1) \ } \ } \ } else { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,1,1) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,0,1) \ } \ } \ } \ \ /* Another 4 cases as above, but this time without the AST__NOBAD flag. */ \ } else { \ if ( usebad ) { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,1,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,1,0) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,1,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,1,0,0) \ } \ } \ } else { \ if ( usevar ) { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,0,1) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,1,0) \ } \ } else { \ for ( point = 0; point < npoint; point++ ) { \ ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,0,0) \ CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,0,0,0) \ } \ } \ } \ } \ } \ \ /* Free the workspace. */ \ hi = astFree( hi ); \ lo = astFree( lo ); \ stride = astFree( stride ); \ ixm = astFree( ixm ); \ xn_max = astFree( xn_max ); \ xn_min = astFree( xn_min ); \ } \ \ /* If an error has occurred, clear the returned result. */ \ if ( !astOK ) *nbad = 0; \ \ /* Return. */ \ } /* This subsidiary macro assembles the input data needed in preparation for forming the interpolated value in the 1-dimensional case. */ #define ASSEMBLE_INPUT_1D(X,Xtype,Xfloating,Xfloattype,Xsigned,Usebad,Usevar) \ \ /* Obtain the x coordinate of the current point and test if it is bad. */ \ x = coords[ 0 ][ point ]; \ bad = ( x == AST__BAD ); \ \ /* Note we do not need to check here whether the pixel in this position is \ bad; if any pixels in the cube are good we can form an average. */ \ \ /* If OK, calculate the lowest and highest indices (in the x \ dimension) of the region of neighbouring pixels that will \ contribute to the interpolated result. Constrain these values to \ lie within the input grid. */ \ if ( !bad ) { \ ix = (int) floor( x ); \ lo_x = MaxI( ix - neighb + 1, lbnd_in[ 0 ], status ); \ hi_x = MinI( ix + neighb, ubnd_in[ 0 ], status ); \ \ /* Initialise sums for forming the interpolated result. */ \ sum = (Xfloattype) 0.0; \ wtsum = (Xfloattype) 0.0; \ if ( Usevar ) { \ sum_var = (Xfloattype) 0.0; \ bad_var = 0; \ } \ \ /* Loop to inspect all the contributing pixels, calculating the offset \ of each pixel from the start of the input array. */ \ off_in = lo_x - lbnd_in[ 0 ]; \ for ( ix = lo_x; ix <= hi_x; ix++, off_in++ ) { \ \ /* If necessary, test if the input pixel is bad. */ \ if ( !( Usebad ) || ( in[ off_in ] != badval ) ) { \ \ /* If we are using variances, then check that the variance is valid; \ if it is invalid then ignore this pixel altogether. */ \ if ( Usevar ) { \ var = in_var[ off_in ]; \ if ( Usebad ) bad_var = ( var == badval ); \ CHECK_FOR_NEGATIVE_VARIANCE(Xtype) \ \ /* If variance is valid then accumulate suitably weighted values into \ the totals. */ \ if ( !( ( Xsigned ) || ( Usebad ) ) || !bad_var ) { \ pixwt = (Xfloattype) 1.0 / var; \ sum += pixwt * ( (Xfloattype) in[ off_in ] ); \ wtsum += pixwt; \ sum_var += pixwt; \ } \ \ /* If we are not using variances, then accumulate values into the \ totals with a weighting of unity. */ \ } else { \ sum += (Xfloattype) in[ off_in ]; \ wtsum++; \ } \ } \ } \ } /* This subsidiary macro assembles the input data needed in preparation for forming the interpolated value in the 2-dimensional case. */ #define ASSEMBLE_INPUT_2D(X,Xtype,Xfloating,Xfloattype,Xsigned,Usebad,Usevar) \ \ /* Obtain the x coordinate of the current point and test if it is bad. */ \ x = coords[ 0 ][ point ]; \ bad = ( x == AST__BAD ); \ if ( !bad ) { \ \ /* If not, then similarly obtain and test the y coordinate. */ \ y = coords[ 1 ][ point ]; \ bad = ( y == AST__BAD ); \ \ /* Note we do not need to check here whether the pixel in this position is \ bad; if any pixels in the cube are good we can form an average. */ \ \ /* If OK, calculate the lowest and highest indices (in each dimension) \ of the region of neighbouring pixels that will contribute to the \ interpolated result. Constrain these values to lie within the input \ grid. */ \ if ( !bad ) { \ ix = (int) floor( x ); \ lo_x = MaxI( ix - neighb + 1, lbnd_in[ 0 ], status ); \ hi_x = MinI( ix + neighb, ubnd_in[ 0 ], status ); \ iy = (int) floor( y ); \ lo_y = MaxI( iy - neighb + 1, lbnd_in[ 1 ], status ); \ hi_y = MinI( iy + neighb, ubnd_in[ 1 ], status ); \ \ /* Initialise sums for forming the interpolated result. */ \ sum = (Xfloattype) 0.0; \ wtsum = (Xfloattype) 0.0; \ if ( Usevar ) { \ sum_var = (Xfloattype) 0.0; \ bad_var = 0; \ } \ \ /* Loop to inspect all the contributing pixels, calculating the offset \ of each pixel from the start of the input array. */ \ off1 = lo_x - lbnd_in[ 0 ] + ystride * ( lo_y - lbnd_in[ 1 ] ); \ for ( iy = lo_y; iy <= hi_y; iy++, off1 += ystride ) { \ off_in = off1; \ for ( ix = lo_x; ix <= hi_x; ix++, off_in++ ) { \ \ /* If necessary, test if the input pixel is bad. */ \ if ( !( Usebad ) || ( in[ off_in ] != badval ) ) { \ \ /* If we are using variances, then check that the variance is valid; \ if it is invalid then ignore this pixel altogether. */ \ if ( Usevar ) { \ var = in_var[ off_in ]; \ if ( Usebad ) bad_var = ( var == badval ); \ CHECK_FOR_NEGATIVE_VARIANCE(Xtype) \ \ /* If variance is valid then accumulate suitably weighted values into \ the totals. */ \ if ( !( ( Xsigned ) || ( Usebad ) ) || !bad_var ) { \ pixwt = (Xfloattype) 1.0 / var; \ sum += pixwt * ( (Xfloattype) in[ off_in ] ); \ wtsum += pixwt; \ sum_var += pixwt; \ } \ \ /* If we are not using variances, then accumulate values into the \ totals with a weighting of unity. */ \ } else { \ sum += (Xfloattype) in[ off_in ]; \ wtsum++; \ } \ } \ } \ } \ } \ } /* This subsidiary macro assembles the input data needed in preparation for forming the interpolated value in the n-dimensional case. */ #define ASSEMBLE_INPUT_ND(X,Xtype,Xfloating,Xfloattype,Xsigned,Usebad,Usevar) \ \ /* Initialise offsets into the input array. then loop to obtain each \ coordinate associated with the current output poitn. */ \ off_in = 0; \ for ( idim = 0; idim < ndim_in; idim++ ) { \ xn = coords[ idim ][ point ]; \ \ /* Test if the coordinate is bad. If so give up on this point. */ \ bad = ( xn == AST__BAD ); \ if ( bad ) break; \ \ /* Calculate the lowest and highest indices (in the current dimension) \ of the region of neighbouring pixels that will contribute to the \ interpolated result. Constrain these values to lie within the input \ grid. */ \ ixn = (int) floor( xn ); \ lo[ idim ] = MaxI( ixn - neighb + 1, lbnd_in[ idim ], status ); \ hi[ idim ] = MinI( ixn + neighb, ubnd_in[ idim ], status ); \ \ /* If the cube has a zero dimension then no data can come from it. */ \ bad = ( lo[ idim ] > hi[ idim ] ); \ if ( bad ) break; \ \ /* Accumulate the offset (from the start of the input array) of the \ contributing pixel which has the lowest index in each dimension. */ \ off_in += stride[ idim ] * ( lo[ idim ] - lbnd_in[ idim ] ); \ \ /* Initialise an array to keep track of the current position in the \ input cube. */ \ ixm[ idim ] = lo[ idim ]; \ } \ \ /* Note we do not need to check here whether the pixel in this position is \ bad; if any pixels in the cube are good we can form an average. */ \ \ /* If OK, initialise sums for forming the interpolated result. */ \ if ( !bad ) { \ sum = (Xfloattype) 0.0; \ wtsum= (Xfloattype) 0.0; \ if ( Usevar ) { \ sum_var = (Xfloattype) 0.0; \ bad_var = 0; \ } \ \ /* Loop to inspect all the contributing pixels, calculating the offset \ of each pixel from the start of the input array. */ \ do { \ \ /* If necessary, test if the input pixel is bad. */ \ if ( !( Usebad ) || ( in[ off_in ] != badval ) ) { \ \ /* If we are using variances, then check that the variance is valid; \ if it is invalid then ignore this pixel altogether. */ \ if ( Usevar ) { \ var = in_var[ off_in ]; \ if ( Usebad ) bad_var = ( var == badval ); \ CHECK_FOR_NEGATIVE_VARIANCE(Xtype) \ \ /* If variance is valid then accumulate suitably weighted values into \ the totals. */ \ if ( !( ( Xsigned ) || ( Usebad ) ) || !bad_var ) { \ pixwt = (Xfloattype) 1.0 / var; \ sum += pixwt * ( (Xfloattype) in[ off_in ] ); \ wtsum += pixwt; \ sum_var += pixwt; \ } \ \ /* If we are not using variances, then accumulate values into the \ totals with a weighting of unity. */ \ } else { \ sum += (Xfloattype) in[ off_in ]; \ wtsum++; \ } \ } \ \ /* Locate the next pixel in the input cube; try incrementing the lowest \ dimension index first, if that rolls over increment the next \ dimension index, and so on. */ \ for ( idim = 0; idim < ndim_in; idim++ ) { \ if ( ixm[ idim ] < hi[ idim ] ) { \ off_in += stride[ idim ]; \ ixm[ idim ]++; \ break; \ } else { \ off_in -= stride[ idim ] * ( hi[ idim ] - lo[ idim ] ); \ ixm[ idim ] = lo[ idim ]; \ } \ } \ \ /* If the highest dimension index has rolled over, we have done all \ the pixels in the cube. */ \ done = ( idim == ndim_in ); \ } while ( !done ); \ } /* This subsidiary macro calculates the interpolated output value (and variance) from the sums over contributing pixels, checks the results for validity, and assigns them to the output array(s). */ #define CALC_AND_ASSIGN_OUTPUT(X,Xtype,Xfloating,Xfloattype,Usebad,Usevar,Nobad) \ \ /* If the output data value has not yet been flagged as bad, then \ check that an interpolated value can actually be produced. First \ check that the sum of weights is not zero. */ \ if ( !bad ) { \ bad = ( wtsum == (Xfloattype) 0.0 ); \ \ /* If OK, calculate the interpolated value. Then, if the output data \ type is not floating point, check that this value will not overflow \ the available output range. */ \ if ( !bad ) { \ val = sum / wtsum; \ if ( !( Xfloating ) ) { \ bad = ( val <= lo_lim ) || ( val >= hi_lim ); \ } \ } \ \ /* If no interpolated data value can be produced, then no associated \ variance will be required either. */ \ if ( ( Usevar ) && bad ) bad_var = 1; \ } \ \ /* Now perform similar checks on the output variance value (if \ required). This time we check that the square of the sum of \ weights is not zero (since this might underflow before the sum of \ weights). Again we also check to prevent the result overflowing the \ output data type. */ \ if ( ( Usevar ) && !bad_var ) { \ wtsum_sq = wtsum * wtsum; \ bad_var = ( wtsum_sq == (Xfloattype) 0.0 ); \ if ( !bad_var ) { \ val_var = sum_var / wtsum_sq; \ if ( !( Xfloating ) ) { \ bad_var = ( val_var <= lo_lim ) || ( val_var >= hi_lim ); \ } \ } \ } \ \ /* Obtain the pixel offset into the output array. */ \ off_out = offset[ point ]; \ \ /* Assign a bad output value (and variance) if required and count it. */ \ if ( bad ) { \ if( !Nobad ) { \ out[ off_out ] = badval; \ if ( Usevar ) out_var[ off_out ] = badval; \ } \ (*nbad)++; \ \ /* Otherwise, assign the interpolated value. If the output data type \ is floating point, the result can be stored directly, otherwise we \ must round to the nearest integer. */ \ } else { \ if ( Xfloating ) { \ out[ off_out ] = (Xtype) val; \ } else { \ out[ off_out ] = (Xtype) ( val + ( ( val >= (Xfloattype) 0.0 ) ? \ ( (Xfloattype) 0.5 ) : \ ( (Xfloattype) -0.5 ) ) ); \ } \ \ /* If a variance estimate is required but none can be obtained, then \ store a bad output variance value and count it. */ \ if ( Usevar ) { \ if ( bad_var ) { \ if( !Nobad ) out_var[ off_out ] = badval; \ (*nbad)++; \ \ /* Otherwise, store the variance estimate, rounding to the nearest \ integer if necessary. */ \ } else { \ if ( Xfloating ) { \ out_var[ off_out ] = (Xtype) val_var; \ } else { \ out_var[ off_out ] = (Xtype) ( val_var + \ ( ( val_var >= (Xfloattype) 0.0 ) ? \ ( (Xfloattype) 0.5 ) : \ ( (Xfloattype) -0.5 ) ) ); \ } \ } \ } \ } /* These subsidiary macros define limits for range checking of results before conversion to the final data type. For each data type code , HI_ gives the least positive floating point value which just overflows that data type towards plus infinity, while LO_ gives the least negative floating point value which just overflows that data type towards minus infinity. Thus, a floating point value must satisfy LO is a floating point type, the limits are not actually used, but must be present to permit error-free compilation. */ #if HAVE_LONG_DOUBLE /* Not normally implemented */ #define HI_LD ( 0.0L ) #define LO_LD ( 0.0L ) #endif #define HI_D ( 0.0 ) #define LO_D ( 0.0 ) #define HI_F ( 0.0f ) #define LO_F ( 0.0f ) #if HAVE_LONG_DOUBLE /* Not normally implemented */ #define HI_L ( 0.5L + (long double) LONG_MAX ) #define LO_L ( -0.5L + (long double) LONG_MIN ) #define HI_UL ( 0.5L + (long double) ULONG_MAX ) #define LO_UL ( -0.5L ) #define HI_K ( 0.5L + (long double) LONG_MAX ) #define LO_K ( -0.5L + (long double) LONG_MIN ) #define HI_UK ( 0.5L + (long double) ULONG_MAX ) #define LO_UK ( -0.5L ) #else #define HI_L ( 0.5 + (double) LONG_MAX ) #define LO_L ( -0.5 + (double) LONG_MIN ) #define HI_UL ( 0.5 + (double) ULONG_MAX ) #define LO_UL ( -0.5 ) #define HI_K ( 0.5 + (double) LONG_MAX ) #define LO_K ( -0.5 + (double) LONG_MIN ) #define HI_UK ( 0.5 + (double) ULONG_MAX ) #define LO_UK ( -0.5 ) #endif #define HI_I ( 0.5 + (double) INT_MAX ) #define LO_I ( -0.5 + (double) INT_MIN ) #define HI_UI ( 0.5 + (double) UINT_MAX ) #define LO_UI ( -0.5 ) #define HI_S ( 0.5f + (float) SHRT_MAX ) #define LO_S ( -0.5f + (float) SHRT_MIN ) #define HI_US ( 0.5f + (float) USHRT_MAX ) #define LO_US ( -0.5f ) #define HI_B ( 0.5f + (float) SCHAR_MAX ) #define LO_B ( -0.5f + (float) SCHAR_MIN ) #define HI_UB ( 0.5f + (float) UCHAR_MAX ) #define LO_UB ( -0.5f ) /* This subsidiary macro tests for negative variance values. This check is required only for signed data types. */ #define CHECK_FOR_NEGATIVE_VARIANCE(Xtype) \ bad_var = bad_var || ( var < ( (Xtype) 0 ) ); /* Expand the main macro above to generate a function for each required signed data type. */ #if HAVE_LONG_DOUBLE /* Not normally implemented */ MAKE_INTERPOLATE_BLOCKAVE(LD,long double,1,long double,1) MAKE_INTERPOLATE_BLOCKAVE(L,long int,0,long double,1) MAKE_INTERPOLATE_BLOCKAVE(K,INT_BIG,0,long double,1) #else MAKE_INTERPOLATE_BLOCKAVE(L,long int,0,double,1) MAKE_INTERPOLATE_BLOCKAVE(K,INT_BIG,0,double,1) #endif MAKE_INTERPOLATE_BLOCKAVE(D,double,1,double,1) MAKE_INTERPOLATE_BLOCKAVE(F,float,1,float,1) MAKE_INTERPOLATE_BLOCKAVE(I,int,0,double,1) MAKE_INTERPOLATE_BLOCKAVE(S,short int,0,float,1) MAKE_INTERPOLATE_BLOCKAVE(B,signed char,0,float,1) /* Re-define the macro for testing for negative variances to do nothing. */ #undef CHECK_FOR_NEGATIVE_VARIANCE #define CHECK_FOR_NEGATIVE_VARIANCE(Xtype) /* Expand the main macro above to generate a function for each required unsigned data type. */ #if HAVE_LONG_DOUBLE /* Not normally implemented */ MAKE_INTERPOLATE_BLOCKAVE(UL,unsigned long int,0,long double,0) MAKE_INTERPOLATE_BLOCKAVE(UK,UINT_BIG,0,long double,0) #else MAKE_INTERPOLATE_BLOCKAVE(UL,unsigned long int,0,double,0) MAKE_INTERPOLATE_BLOCKAVE(UK,UINT_BIG,0,double,0) #endif MAKE_INTERPOLATE_BLOCKAVE(UI,unsigned int,0,double,0) MAKE_INTERPOLATE_BLOCKAVE(US,unsigned short int,0,float,0) MAKE_INTERPOLATE_BLOCKAVE(UB,unsigned char,0,float,0) /* Undefine the macros used above. */ #undef CHECK_FOR_NEGATIVE_VARIANCE #if HAVE_LONG_DOUBLE /* Not normally implemented */ #undef HI_LD #undef LO_LD #endif #undef HI_D #undef LO_D #undef HI_F #undef LO_F #undef HI_L #undef LO_L #undef HI_UL #undef LO_UL #undef HI_K #undef LO_K #undef HI_UK #undef LO_UK #undef HI_I #undef LO_I #undef HI_UI #undef LO_UI #undef HI_S #undef LO_S #undef HI_US #undef LO_US #undef HI_B #undef LO_B #undef HI_UB #undef LO_UB #undef CALC_AND_ASSIGN_OUTPUT #undef ASSEMBLE_INPUT_ND #undef ASSEMBLE_INPUT_2D #undef ASSEMBLE_INPUT_1D #undef MAKE_INTERPOLATE_BLOCKAVE static void Invert( AstMapping *this, int *status ) { /* *++ * Name: c astInvert f AST_INVERT * Purpose: * Invert a Mapping. * Type: * Public virtual function. * Synopsis: c #include "mapping.h" c void astInvert( AstMapping *this ) f CALL AST_INVERT( THIS, STATUS ) * Class Membership: * Mapping method. * Description: c This function inverts a Mapping by reversing the boolean sense f This routine inverts a Mapping by reversing the boolean sense * of its Invert attribute. If this attribute is zero (the * default), the Mapping will transform coordinates in the way * specified when it was created. If it is non-zero, the input and * output coordinates will be inter-changed so that the direction * of the Mapping is reversed. This will cause it to display the * inverse of its original behaviour. * Parameters: c this f THIS = INTEGER (Given) * Pointer to the Mapping. f STATUS = INTEGER (Given and Returned) f The global status. *-- */ /* Local Variables: */ int invert; /* New Invert attribute value */ /* Check the global error status. */ if ( !astOK ) return; /* Determine the new Invert attribute value. */ invert = !astGetInvert( this ); /* Clear the old value. */ astClearInvert( this ); /* If the resulting default value is not the one required, then set a new value explicitly. */ if ( astGetInvert( this ) != invert ) astSetInvert( this, invert ); } static double J1Bessel( double x, int *status ) { /* * Name: * J1Bessel * Purpose: * Calculates the first-order Bessel function of the first kind. * Type: * Private function. * Synopsis: * #include "mapping.h" * double J1Bessel( double x, int *status ) * Class Membership: * Mapping member function. * Description: * This function calculates the value of the first-order Bessel function * of the first kind. * Parameters: * x * The argument for J1. * status * Pointer to the inherited status variable. * Returned Value: * The calculated J1(x) value. * Notes: * - The algorithm is taken from the SCUBA routine SCULIB_BESSJ1, by * J.Lightfoot. * - This function does not perform error checking and does not * generate errors. */ /* Local Variables: */ static double p1 = 1.0; static double p2 = 0.183105E-2; static double p3 = -0.3516396496E-4; static double p4 = 0.2457520174E-5; static double p5 = -0.240337019E-6; static double q1 = 0.04687499995; static double q2 = -0.2002690873E-3; static double q3 = 0.8449199096E-5; static double q4 = -0.88228987E-6; static double q5 = 0.105787412E-6; static double r1 = 72362614232.0; static double r2 = -7895059235.0; static double r3 = 242396853.1; static double r4 = -2972611.439; static double r5 = 15704.48260; static double r6 = -30.16036606; static double s1 = 144725228442.0; static double s2 = 2300535178.0; static double s3 = 18583304.74; static double s4 = 99447.43394; static double s5 = 376.9991397; static double s6 = 1.0; double ax; double xx; double z; double y; double value; int s; /* Calculate the value */ ax = fabs( x ); if( ax < 8.0 ) { y = x*x; value = x*( r1 + y*( r2 + y*( r3 + y*( r4 + y*( r5 + y*r6 ) ) ) ) ) / ( s1 + y*( s2 + y*( s3 + y*( s4 + y*( s5 + y*s6 ) ) ) ) ); } else { s = ( x >= 0.0 ) ? 1 : -1; z = 8.0 / ax; y = z*z; xx = ax - 2.356194491; value = sqrt ( 0.636619772/ax )*( cos( xx )*( p1 + y*( p2 + y* ( p3 + y*( p4 + y*p5 ) ) ) )-z*sin( xx )*( q1 + y*( q2 + y*( q3 + y* ( q4 + y*q5 ) ) ) ) )*s; } return value; } static int LinearApprox( AstMapping *this, const double *lbnd, const double *ubnd, double tol, double *fit, int *status ) { /* *++ * Name: c astLinearApprox f AST_LINEARAPPROX * Purpose: * Obtain a linear approximation to a Mapping, if appropriate. * Type: * Public virtual function. * Synopsis: c #include "mapping.h" c int astLinearApprox( AstMapping *this, const double *lbnd, c const double *ubnd, double tol, double *fit ) f RESULT = AST_LINEARAPPROX( THIS, LBND, UBND, TOL, FIT, STATUS ) * Class Membership: * Mapping function. * Description: * This function tests the forward coordinate transformation * implemented by a Mapping over a given range of input coordinates. If * the transformation is found to be linear to a specified level of * accuracy, then an array of fit coefficients is returned. These * may be used to implement a linear approximation to the Mapping's * forward transformation within the specified range of output coordinates. * If the transformation is not sufficiently linear, no coefficients * are returned. * Parameters: c this f THIS = INTEGER (Given) * Pointer to the Mapping. c lbnd f LBND( * ) = DOUBLE PRECISION (Given) c Pointer to an array of doubles f An array * containing the lower bounds of a box defined within the input * coordinate system of the Mapping. The number of elements in this * array should equal the value of the Mapping's Nin attribute. This * box should specify the region over which linearity is required. c ubnd f UBND( * ) = DOUBLE PRECISION (Given) c Pointer to an array of doubles f An array * containing the upper bounds of the box specifying the region over * which linearity is required. c tol f TOL = DOUBLE PRECISION (Given) * The maximum permitted deviation from linearity, expressed as * a positive Cartesian displacement in the output coordinate * space of the Mapping. If a linear fit to the forward * transformation of the Mapping deviates from the true transformation * by more than this amount at any point which is tested, then no fit * coefficients will be returned. c fit f FIT( * ) = DOUBLE PRECISION (Returned) c Pointer to an array of doubles f An array * in which to return the co-efficients of the linear * approximation to the specified transformation. This array should * have at least "( Nin + 1 ) * Nout", elements. The first Nout elements * hold the constant offsets for the transformation outputs. The * remaining elements hold the gradients. So if the Mapping has 2 inputs * and 3 outputs the linear approximation to the forward transformation * is: * c X_out = fit[0] + fit[3]*X_in + fit[4]*Y_in f X_out = fit(1) + fit(4)*X_in + fit(5)*Y_in * c Y_out = fit[1] + fit[5]*X_in + fit[6]*Y_in f Y_out = fit(2) + fit(6)*X_in + fit(7)*Y_in * c Z_out = fit[2] + fit[7]*X_in + fit[8]*Y_in f Z_out = fit(3) + fit(8)*X_in + fit(9)*Y_in * f STATUS = INTEGER (Given and Returned) f The global status. * Returned Value: c astLinearApprox() f AST_LINEARAPPROX = LOGICAL * If the forward transformation is sufficiently linear, c a non-zero value is returned. Otherwise zero is returned f .TRUE is returned. Otherwise .FALSE. is returned * and the fit co-efficients are set to AST__BAD. * Notes: * - This function fits the Mapping's forward transformation. To fit * the inverse transformation, the Mapping should be inverted using c astInvert f AST_INVERT * before invoking this function. * - If a Mapping output is found to have a bad value (AST__BAD) at * one or more of the test points used in the linearity test, then all * the values in the returned fit that correspond to that output are * set to AST__BAD. However, this does not affect the linearity tests * on the other Mapping outputs - if they are all found to be linear * then usable coefficients will be returned for them in the fit, and * the function will return a c non-zero value. f .TRUE. value. * Consequently, it may be necessary to check that the values in the * returned fit are not AST__BAD before using them. If all Mapping * outputs generate bad values, then c zero is returned as the function value. f .FALSE. is returned as the function value. c - A value of zero f - A value of .FALSE. * will be returned if this function is invoked * with the global error status set, or if it should fail for any * reason. * - If all tested positions within the supplied box generate bad * output positions, then the returned function value will be c zero. f .FALSE. * However, the returned coefficients will represent a unit * transformation, except that the constant term for each output * will be set to AST__BAD. *-- * Implementation Deficiencies: * Sub-classes which implement linear mappings should probably * over-ride this function to get better accuracy and faster execution, * but currently they do not. */ /* Local Variables: */ AstPointSet *pset_in_f; /* PointSet for input fitting points */ AstPointSet *pset_in_t; /* PointSet for input test points */ AstPointSet *pset_out_f; /* PointSet for output fitting points */ AstPointSet *pset_out_t; /* PointSet for output test points */ double **ptr_in_f; /* Input coordinate array pointers */ double **ptr_in_t; /* Input coordinate array pointers */ double **ptr_out_f; /* Output coordinate array pointers */ double **ptr_out_t; /* Output coordinate array pointers */ double *grad; /* Pointer to matrix of gradients */ double *zero; /* Pointer to array of zero point values */ double diff; /* Difference in coordinate values */ double err; /* Sum of squared error */ double frac; /* Fraction of input coordinate range */ double in1; /* Input coordinate value */ double in2; /* Input coordinate value */ double indiff; /* Difference in input coordinate values */ double out1; /* Output coordinate value */ double out2; /* Output coordinate value */ double x0; /* Coordinate of grid centre */ double y; /* Output coordinate (transformed) */ double yfit; /* Coordinate resulting from fit */ double z; /* Sum for calculating zero points */ int *vertex; /* Pointer to flag array for vertices */ int bad_output; /* Does the Mapping output generate bad values? */ int coord_in; /* Loop counter for input coordinates */ int coord_out; /* Loop counter for output coordinates. */ int done; /* All vertices visited? */ int face1; /* Index of first face coordinates */ int face2; /* Index of second face coordinates */ int face; /* Loop counter for faces */ int ii; /* Index into gradient matrix */ int linear; /* Mapping is linear? */ int nc; /* Number of coeffs in fit */ int ndim_in; /* Number of Mapping inputs */ int ndim_out; /* Number of Mapping outputs */ int npoint; /* Number of test points required */ int point; /* Counter for points */ int result; /* Returned flag */ /* Initialise. */ result = 0; /* Check the global error status. */ if ( !astOK ) return result; /* Further initialisation. */ linear = 1; grad = NULL; zero = NULL; /* Get the number of Mapping output and inputs. */ ndim_in = astGetNin( this ); ndim_out = astGetNout( this ); /* Store the number of coefficients in the fit.*/ nc = ( ndim_in + 1 ) * ndim_out; /* Initialise the supplied array to hold bad values. */ for( ii = 0; ii < nc; ii++ ) fit[ ii ] = AST__BAD; /* Create a PointSet to hold input coordinates and obtain a pointer to its coordinate arrays. */ pset_in_f = astPointSet( 2 * ndim_in, ndim_in, "", status ); ptr_in_f = astGetPoints( pset_in_f ); if ( astOK ) { /* Set up and transform an initial set of points. */ /* ---------------------------------------------- */ /* Loop to set up input coordinates at the centre of each face of the input grid, storing them in the PointSet created above. */ point = 0; for ( face = 0; face < ( 2 * ndim_in ); face++ ) { for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { ptr_in_f[ coord_in ][ point ] = 0.5 * ( lbnd[ coord_in ] + ubnd[ coord_in ] ); } ptr_in_f[ face / 2 ][ point ] = ( face % 2 ) ? ubnd[ face / 2 ] : lbnd[ face / 2 ]; point++; } } /* Transform these coordinates into the output grid's coordinate system and obtain an array of pointers to the resulting coordinate data. */ pset_out_f = astTransform( this, pset_in_f, 1, NULL ); ptr_out_f = astGetPoints( pset_out_f ); if ( astOK ) { /* Fit a linear approximation to the points. */ /* ----------------------------------------- */ /* Obtain pointers to the locations in the fit coefficients array where the gradients and zero points should be stored. */ grad = fit + ndim_out; zero = fit; /* On the assumption that the transformation applied above is approximately linear, loop to determine the matrix of gradients and the zero points which describe it. */ ii = 0; for ( coord_out = 0; coord_out < ndim_out; coord_out++ ) { bad_output = 0; z = 0.0; for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { /* Find the indices of opposite faces in each input dimension. */ face1 = 2 * coord_in; face2 = face1 + 1; /* Obtain the input and output coordinates at these face centres. */ in1 = ptr_in_f[ coord_in ][ face1 ]; in2 = ptr_in_f[ coord_in ][ face2 ]; out1 = ptr_out_f[ coord_out ][ face1 ]; out2 = ptr_out_f[ coord_out ][ face2 ]; /* Check whether any transformed coordinates are bad. Mapping outputs that are bad at one or more test points have bad values stored for the corresponding coefficients in the returned fit, but do not invalidate the linearity of the fit to other outputs. */ if ( ( out1 == AST__BAD ) || ( out2 == AST__BAD ) ) { bad_output = 1; break; } /* If possible, determine the gradient along this dimension, storing it in the appropriate element of the gradient matrix. */ indiff = in2 - in1; if ( indiff != 0.0 ) { grad[ ii++ ] = ( out2 - out1 ) / indiff; } else { grad[ ii++ ] = 0.0; } /* Accumulate the sum used to determine the zero point. */ z += ( out1 + out2 ); } /* Determine the average zero point from all dimensions. */ if( !bad_output ) zero[ coord_out ] = z / (double) ( 2 * ndim_in ); } /* The zero points of the above fit will be appropriate to an input coordinate system with an origin at the centre of the input grid (we assume this to simplify the calculations above). To correct for this, we transform the actual input coordinates of the grid's centre through the matrix of gradients and subtract the resulting coordinates from the zero point values. The zero points are then correct for the actual output and input coordinate systems we are using. Also, if all Mapping outputs generate bad values, flag that we do not have a linear fit. */ linear = 0; ii = 0; for ( coord_out = 0; coord_out < ndim_out; coord_out++ ) { if( zero[ coord_out ] != AST__BAD ) { linear = 1; for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { x0 = 0.5 * ( lbnd[ coord_in ] + ubnd[ coord_in ] ); zero[ coord_out ] -= grad[ ii++ ] * x0; } } else { for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { grad[ ii++ ] = AST__BAD; } } } } /* Annul the pointers to the PointSets used above. */ pset_out_f = astAnnul( pset_out_f ); pset_in_f = astAnnul( pset_in_f ); /* Calculate the number of test points required. */ /* --------------------------------------------- */ /* The linear fit obtained above, will (by construction) be exact at the centre of each face of the input grid. However, it may not fit anywhere else. We therefore set up some test points to determine if it is an adequate approximation elsewhere. */ if( astOK && linear ) { /* Calculate the number of test points required to place one at each vertex of the grid. */ npoint = 1; for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { npoint *= 2; } /* Now calculate the total number of test points required, also allowing one at the centre, one at half the distance to each face, and one at half the distance to each vertex. */ npoint = 1 + 2 * ( ndim_in + npoint ); /* Set up test points in the input coordinate system. */ /* --------------------------------------------------- */ /* Create a PointSet to hold the test coordinates and obtain an array of pointers to its coordinate data. */ pset_in_t = astPointSet( npoint, ndim_in, "", status ); ptr_in_t = astGetPoints( pset_in_t ); if ( astOK ) { /* If the input array is 1-dimensional, the face and vertex positions calculated below will co-incide. Therefore, we simply distribute the required number of test points uniformly throughout the input coordinate range (avoiding the end-points, where the fit has been obtained). The coordinates are stored in the PointSet created above. */ if ( ndim_in == 1 ) { for ( point = 0; point < npoint; point++ ) { frac = ( (double) ( point + 1 ) ) / (double) ( npoint + 1 ); ptr_in_t[ 0 ][ point ] = ( 1.0 - frac ) * lbnd[ 0 ] + frac * ubnd[ 0 ]; } /* Otherwise, generate one point at the grid centre (offset slightly since the exact centre may not be very representative). */ } else { point = 0; for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { ptr_in_t[ coord_in ][ point ] = 0.49 * lbnd[ coord_in ] + 0.51 * ubnd[ coord_in ]; } point++; /* Similarly generate a point half way between the grid centre and the centre of each face. Again introduce some small random offsets to break any regularity in the grid. */ for ( face = 0; face < ( 2 * ndim_in ); face++ ) { for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { ptr_in_t[ coord_in ][ point ] = 0.48 * lbnd[ coord_in ] + 0.52 * ubnd[ coord_in ]; } ptr_in_t[ face / 2 ][ point ] = ( 0.51 * ( ( ( face % 2 ) ? ubnd[ face / 2 ] : lbnd[ face / 2 ] ) ) + 0.49 * ptr_in_t[ face / 2 ][ 0 ] ); point++; } /* Allocate workspace and initialise flags for identifying the vertices. */ vertex = astMalloc( sizeof( int ) * (size_t) ndim_in ); if ( astOK ) { for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { vertex[ coord_in ] = 0; } /* Now loop to visit each input grid vertex. */ done = 0; do { /* Generate a test point at each vertex. */ for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { ptr_in_t[ coord_in ][ point ] = vertex[ coord_in ] ? ubnd[ coord_in ] : lbnd[ coord_in ]; /* Also place one half way between the grid centre and each vertex. */ ptr_in_t[ coord_in ][ point + 1 ] = ( 0.52 * ptr_in_t[ coord_in ][ point ] + 0.48 * ptr_in_t[ coord_in ][ 0 ] ); } point += 2; /* Now update the array of vertex flags to identify the next vertex. */ coord_in = 0; do { /* The least significant dimension which does not have its upper bound as one of the vertex coordinates is changed to use its upper bound in the next vertex. */ if ( !vertex[ coord_in ] ) { vertex[ coord_in ] = 1; break; /* Any less significant dimensions whose upper bounds are already being used are changed to use their lower bounds in the next vertex. */ } else { vertex[ coord_in ] = 0; /* All vertices have been visited when the most significant dimension is changed back to using its lower bound. */ done = ( ++coord_in == ndim_in ); } } while ( !done ); } while ( !done ); } /* Free the workspace used for vertex flags. */ vertex = astFree( vertex ); } /* Transform the test points. */ /* -------------------------- */ /* Use the Mapping to transform the test points into the output grid's coordinate system, obtaining a pointer to the resulting arrays of output coordinates. */ pset_out_t = astTransform( this, pset_in_t, 1, NULL ); ptr_out_t = astGetPoints( pset_out_t ); /* Test the linear fit for accuracy. */ /* --------------------------------- */ /* If OK so far, then loop to use this fit to transform each test point and compare the result with the result of applying the Mapping. */ if ( astOK ) { for ( point = 0; point < npoint; point++ ) { /* Initialise the fitting error for the current point. */ err = 0.0; /* Obtain each output coordinate (produced by using the Mapping) in turn and check that it is not bad. If it is, then store bad values for the output's coefficients and pass on to the next output. */ bad_output = 0; ii = 0; for ( coord_out = 0; coord_out < ndim_out; coord_out++ ) { y = ptr_out_t[ coord_out ][ point ]; if ( y == AST__BAD || zero[ coord_out ] == AST__BAD ) { zero[ coord_out ] = AST__BAD; for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { grad[ ii++ ] = AST__BAD; } bad_output++; break; } /* Apply the fitted transformation to the input coordinates to obtain the approximate output coordinate value. */ yfit = zero[ coord_out ]; for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { yfit += grad[ ii++ ] * ptr_in_t[ coord_in ][ point ]; } /* Form the sum of squared differences between the Mapping's transformation and the fit. */ diff = ( y - yfit ); err += diff * diff; } /* Test if the Cartesian distance between the true output coordinate and the approximate one exceeds the accuracy tolerance. If this happens for any test point, we declare the Mapping non-linear and give up. Reduce the allowed tolerance in proproprtion to the number of bad Mapping outputs that were found. */ if ( sqrt( err ) > (tol*(ndim_out - bad_output))/ndim_out ) { linear = 0; break; } } } /* Annul the pointers to the PointSets used above. */ pset_out_t = astAnnul( pset_out_t ); } pset_in_t = astAnnul( pset_in_t ); /* If all Mapping outputs generate bad values, return the coefficients of a unit transformation, but put a bad value into the constant term for each output. */ } else if( astOK ){ grad = fit + ndim_out; zero = fit; ii = 0; for ( coord_out = 0; coord_out < ndim_out; coord_out++ ) { for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { if ( coord_out == coord_in ) { grad[ ii++ ] = 1.0; } else { grad[ ii++ ] = 0.0; } } zero[ coord_out ] = AST__BAD; } } /* If an error occurred, or the Mapping was found to be non-linear, then set the coefficients to AST_BAD. Otherwise, set the returned flag to indicate that the fit was succesful. */ if ( !astOK || !linear ) { for( ii = 0; ii < nc; ii++ ) fit[ ii ] = AST__BAD; } else { result = 1; } /* Return the result. */ return result; } static double LocalMaximum( const MapData *mapdata, double acc, double fract, double x[], int *status ) { /* * Name: * LocalMaximum * Purpose: * Find a local maximum in a Mapping function. * Type: * Private function. * Synopsis: * #include "mapping.h" * double LocalMaximum( const MapData *mapdata, double acc, double fract, * double x[], int *status ); * Class Membership: * Mapping member function. * Description: * This function finds a local maximum in the Mapping function * supplied. It employs the modified simplex method (as * implemented by UphillSimplex), but repeatedly re-starts the * simplex algorithm and tests for convergence of successive * maxima, so as to further improve robustness on difficult * problems. * Parameters: * mapdata * Pointer to a MapData structure describing the Mapping * function, its coordinate constraints, etc. * acc * The required accuracy with which the maximum is to be found. * fract * A value between 0.0 and 1.0 which determines the initial step * length along each coordinate axis. It should be given as a * fraction of the difference between the upper and lower * constraint values for each axis (as specified in the * "mapdata" structure). * x * Pointer to an array of double containing the coordinates of * an initial estimate of the position of the maximum. On exit, * this will be updated to contain the best estimate of the * maximum's position, as found by this function. * status * Pointer to the inherited status variable. * Returned Value: * The best estimate of the Mapping function's maximum value. * Notes: * - A value of AST__BAD will be returned, and no useful * information about a solution will be produced, if this function * is invoked with the global error status set or if it should fail * for any reason. */ /* Local Constants: */ const int maxcall = 1500; /* Maximum number of function evaluations */ const int maxiter = 5; /* Maximum number of iterations */ /* Local Variables: */ double *dx; /* Pointer to array of step lengths */ double err; /* Simplex error estimate */ double maximum; /* Simplex maximum value */ double middle; /* Middle coordinate between bounds */ double result; /* Result value to return */ int coord; /* Loop counter for coordinates */ int done; /* Iterations complete? */ int iter; /* Loop counter for iterations */ int ncall; /* Number of function calls (junk) */ /* Initialise. */ result = AST__BAD; /* Check the global error status. */ if ( !astOK ) return result; /* Further initialise to avoid compiler warnings. */ err = 0.0; /* Allocate workspace. */ dx = astMalloc( sizeof( double ) * (size_t) mapdata->nin ); /* Perform iterations to repeatedly identify a local maximum. */ for ( iter = 0; astOK && ( iter < maxiter ); iter++ ) { /* Set up initial step lengths along each coordinate axis, adjusting their signs to avoid placing points outside the coordinate constraints (i.e. step away from the closer boundary on each axis). */ for ( coord = 0; coord < mapdata->nin; coord++ ) { middle = 0.5 * ( mapdata->lbnd[ coord ] + mapdata->ubnd[ coord ] ); dx[ coord ] = fract * ( mapdata->ubnd[ coord ] - mapdata->lbnd[ coord ] ); if ( x[ coord ] > middle ) dx[ coord ] = -dx[ coord ]; } /* Find an approximation to a local maximum using the simplex method and check for errors. */ maximum = UphillSimplex( mapdata, acc, maxcall, dx, x, &err, &ncall, status ); if ( astOK ) { /* Use this maximum value if no previous maximum has been found. */ if ( result == AST__BAD ) { result = maximum; /* Otherwise use it only if it improves on the previous maximum. */ } else if ( maximum >= result ) { /* We iterate, re-starting the simplex algorithm from its previous best position so as to guard against premature false convergence. Iterations continue until the improvement in the maximum is no greater than the required accuracy (and the simplex algorithm itself has converged to the required accuracy). Note when iterations should cease. */ done = ( ( ( maximum - result ) <= acc ) && ( err <= acc ) ); /* Store the best maximum and quit iterating if appropriate. */ result = maximum; if ( done ) break; } /* Otherwise, decrement the initial step size for the next iteration. */ fract /= 1000.0; } } /* Free the workspace. */ dx = astFree( dx ); /* If an error occurred, clear the result value. */ if ( !astOK ) result = AST__BAD; /* return the result. */ return result; } static void MapBox( AstMapping *this, const double lbnd_in[], const double ubnd_in[], int forward, int coord_out, double *lbnd_out, double *ubnd_out, double xl[], double xu[], int *status ) { /* *+ * Name: * astMapBox * Purpose: * Find a bounding box for a Mapping. * Type: * Protected virtual function. * Synopsis: * #include "mapping.h" * void astMapBox( AstMapping *this, * const double lbnd_in[], const double ubnd_in[], * int forward, int coord_out, * double *lbnd_out, double *ubnd_out, * double xl[], double xu[] ); * Class Membership: * Mapping method. * Description: * This function allows you to find the "bounding box" which just * encloses another box after it has been transformed by a Mapping * (using either its forward or inverse transformation). A typical * use might be to calculate the size which an image would have * after being transformed by the Mapping. * * The function works on one dimension at a time. When supplied * with the lower and upper bounds of a rectangular region (box) of * input coordinate space, it finds the lowest and highest values * taken by a nominated output coordinate within that * region. Optionally, it also returns the input coordinates where * these bounding values are attained. It should be used repeatedly * if the extent of the bounding box is required in more than one * dimension. * Parameters: * this * Pointer to the Mapping. * lbnd_in * Pointer to an array of double, with one element for each * Mapping input coordinate. This should contain the lower bound * of the input box in each dimension. * ubnd_in * Pointer to an array of double, with one element for each * Mapping input coordinate. This should contain the upper bound * of the input box in each dimension. * * Note that it is permissible for the lower bound to exceed the * corresponding upper bound, as the values will simply be * swapped before use. * forward * If this value is non-zero, then the Mapping's forward * transformation will be used to transform the input * box. Otherwise, its inverse transformation will be used. * * (If the inverse transformation is selected, then references * to "input" and "output" coordinates in this description * should be transposed. For example, the size of the "lbnd_in" * and "ubnd_in" arrays should match the number of output * coordinates, as given by the Mapping's Nout attribute.) * coord_out * The (zero-based) index of the output coordinate for which the * lower and upper bounds are required. * lbnd_out * Pointer to a double in which to return the lowest value taken * by the nominated output coordinate within the specified * region of input coordinate space. * ubnd_out * Pointer to a double in which to return the highest value * taken by the nominated output coordinate within the specified * region of input coordinate space. * xl * An optional pointer to an array of double, with one element * for each Mapping input coordinate. If given, this array will * be filled with the coordinates of an input point (although * not necessarily a unique one) for which the nominated output * coordinate takes the lower bound value returned in * "*lbnd_out". * * If these coordinates are not required, a NULL pointer may be * supplied. * xu * An optional pointer to an array of double, with one element * for each Mapping input coordinate. If given, this array will * be filled with the coordinates of an input point (although * not necessarily a unique one) for which the nominated output * coordinate takes the upper bound value returned in * "*ubnd_out". * * If these coordinates are not required, a NULL pointer may be * supplied. * Notes: * - Any input points which are transformed by the Mapping to give * output coordinates containing the value AST__BAD are regarded as * invalid and are ignored, They will make no contribution to * determining the output bounds, even although the nominated * output coordinate might still have a valid value at such points. * - An error will occur if the required output bounds cannot be * found. Typically, this might occur if all the input points which * the function considers turn out to be invalid (see above). The * number of points considered before generating such an error is * quite large, however, so this is unlikely to occur by accident * unless valid points are restricted to a very small subset of the * input coordinate space. * - The values returned via "lbnd_out", "ubnd_out", "xl" and "xu" * will be set to the value AST__BAD if this function should fail * for any reason. Their initial values on entry will not be * altered if the function is invoked with the global error status * set. *- * Implementation Notes: * - This function implements the basic astMapBox method available * via the protected interface to the Mapping class. The public * interface to this method is provided by the astMapBoxId_ * function. */ /* Local Variables: */ MapData mapdata; /* Structure to describe Mapping function */ double *x_l; /* Pointer to coordinate workspace */ double *x_u; /* Pointer to coordinate workspace */ double lbnd; /* Required lower bound */ double ubnd; /* Required upper bound */ int coord; /* Loop counter for coordinates. */ int nin; /* Effective number of input coordinates */ int nout; /* Effective number of output coordinates */ int refine; /* Can bounds be refined? */ /* Check the global error status. */ if ( !astOK ) return; /* Initialisation to avoid compiler warnings. */ lbnd = AST__BAD; ubnd = AST__BAD; /* Obtain the effective numbers of input and output coordinates for the Mapping, taking account of which transformation is to be used. */ nin = forward ? astGetNin( this ) : astGetNout( this ); nout = forward ? astGetNout( this ) : astGetNin( this ); /* Check that the output coordinate index supplied is valid and report an error if it is not. Use public (one-based) coordinate numbering in the error message. */ if ( astOK ) { if ( ( coord_out < 0 ) || ( coord_out >= nout ) ) { astError( AST__BADCI, "astMapBox(%s): Output coordinate index (%d) " "invalid - it should be in the range 1 to %d.", status, astGetClass( this ), coord_out + 1, nout ); } } /* Initialise a MapData structure to describe the Mapping function whose limits are to be found. Since it may be evaluated many times, we attempt to simplify the Mapping supplied. */ if ( astOK ) { mapdata.mapping = astSimplify( this ); /* Store the number of input/output coordinates and the index of the output coordinate in which we are interested. */ mapdata.nin = nin; mapdata.nout = nout; mapdata.coord = coord_out; /* Note which Mapping transformation is being used. */ mapdata.forward = forward; /* Store pointers to arrays which will contain the input coordinate bounds. */ mapdata.lbnd = astMalloc( sizeof( double ) * (size_t) nin ); mapdata.ubnd = astMalloc( sizeof( double ) * (size_t) nin ); /* Create PointSets for passing coordinate data to and from the Mapping. */ mapdata.pset_in = astPointSet( 1, nin, "", status ); mapdata.pset_out = astPointSet( 1, nout, "", status ); /* Obtain pointers to these PointSets' coordinate arrays. */ mapdata.ptr_in = astGetPoints( mapdata.pset_in ); mapdata.ptr_out = astGetPoints( mapdata.pset_out ); /* Allocate workspace for the returned input coordinates. */ x_l = astMalloc( sizeof( double ) * (size_t) nin ); x_u = astMalloc( sizeof( double ) * (size_t) nin ); if ( astOK ) { /* Initialise the output bounds and corresponding input coordinates to "unknown". */ for ( coord = 0; coord < nin; coord++ ) { x_l[ coord ] = AST__BAD; x_u[ coord ] = AST__BAD; /* Initialise the input bounds, ensuring they are the correct way around (if not already supplied this way). */ mapdata.lbnd[ coord ] = ( lbnd_in[ coord ] < ubnd_in[ coord ] ) ? lbnd_in[ coord ] : ubnd_in[ coord ]; mapdata.ubnd[ coord ] = ( ubnd_in[ coord ] > lbnd_in[ coord ] ) ? ubnd_in[ coord ] : lbnd_in[ coord ]; } /* First examine a set of special input points to obtain an initial estimate of the required output bounds. Do this only so long as the number of points involved is not excessive. */ if ( nin <= 12 ) { refine = SpecialBounds( &mapdata, &lbnd, &ubnd, x_l, x_u, status ); } else { refine = 1; } /* Then attempt to refine this estimate using a global search algorithm. */ if( refine ) GlobalBounds( &mapdata, &lbnd, &ubnd, x_l, x_u, status ); /* If an error occurred, generate a contextual error message. */ if ( !astOK ) { astError( astStatus, "Unable to find a bounding box for a %s.", status, astGetClass( this ) ); } } /* Return the output bounds and, if required, the input coordinate values which correspond with them. */ if ( astOK ) { *lbnd_out = lbnd; *ubnd_out = ubnd; for ( coord = 0; coord < nin; coord++ ) { if ( xl ) xl[ coord ] = x_l[ coord ]; if ( xu ) xu[ coord ] = x_u[ coord ]; } } /* Annul the simplified Mapping pointer and the temporary PointSets. Also free the workspace. */ mapdata.mapping = astAnnul( mapdata.mapping ); mapdata.lbnd = astFree( mapdata.lbnd ); mapdata.ubnd = astFree( mapdata.ubnd ); mapdata.pset_in = astAnnul( mapdata.pset_in ); mapdata.pset_out = astAnnul( mapdata.pset_out ); x_l = astFree( x_l ); x_u = astFree( x_u ); } /* If an error occurred, then return bad bounds values and coordinates. */ if ( !astOK ) { *lbnd_out = AST__BAD; *ubnd_out = AST__BAD; for ( coord = 0; coord < nin; coord++ ) { if ( xl ) xl[ coord ] = AST__BAD; if ( xu ) xu[ coord ] = AST__BAD; } } } static double MapFunction( const MapData *mapdata, const double in[], int *ncall, int *status ) { /* * Name: * MapFunction * Purpose: * Return the value of a selected transformed coordinate. * Type: * Private function. * Synopsis: * #include "mapping.h" * double MapFunction( const MapData *mapdata, const double in[], * int *ncall, int *status ); * Class Membership: * Mapping member function. * Description: * This function takes a set of input coordinates and applies a * Mapping's coordinate transformation to them. It then returns the * value of one of the transformed coordinates. * * It is provided for use by optimisation functions (e.g. those * used for finding bounding boxes). The Mapping to be used and * associated parameters (such as constraints on the range of input * coordinates and the index of the output coordinate to be * returned) are supplied in a MapData structure. The value * returned will be negated if the "negate" component of this * structure is non-zero. * * The value AST__BAD will be returned by this function if the * input coordinates lie outside the constrained range given in * the MapData structure, or if any of the transformed output * coordinates is bad. * Parameters: * mapdata * Pointer to a MapData structure which describes the Mapping to * be used. * in * A double array containing the input coordinates of a single point. * ncall * Pointer to an int containing a count of the number of times * the Mapping's coordinate transformation has been used. This * value will be updated to reflect any use made by this * function. Normally, this means incrementing the value by 1, * but this will be omitted if the input coordinates supplied * are outside the constrained range so that no transformation * is performed. * status * Pointer to the inherited status variable. * Returned Value: * The selected output coordinate value, or AST__BAD, as appropriate. * Notes: * - A value of AST__BAD will be returned if this function is * invoked with the global error status set, or if it should fail * for any reason. */ /* Local Variables: */ double result; /* Result to be returned */ int bad; /* Output coordinates invalid? */ int coord_in; /* Loop counter for input coordinates */ int coord_out; /* Loop counter for output coordinates */ int outside; /* Input point outside bounds? */ /* Initialise. */ result = AST__BAD; /* Check the global error status. */ if ( !astOK ) return result; /* See if the input point lies outside the required bounds. */ outside = 0; for ( coord_in = 0; coord_in < mapdata->nin; coord_in++ ) { if ( ( in[ coord_in ] < mapdata->lbnd[ coord_in ] ) || ( in[ coord_in ] > mapdata->ubnd[ coord_in ] ) ) { outside = 1; break; } /* Also store the input coordinates in the memory associated with the Mapping's input PointSet. */ mapdata->ptr_in[ coord_in ][ 0 ] = in[ coord_in ]; } /* If the input coordinates are within bounds, transform them, using the PointSets identified in the "mapdata" structure. */ if ( !outside ) { (void) astTransform( mapdata->mapping, mapdata->pset_in, mapdata->forward, mapdata->pset_out ); /* Increment the number of calls to astTransform and check the error status. */ ( *ncall )++; if ( astOK ) { /* If OK, test if any of the output coordinates is bad. */ bad = 0; for ( coord_out = 0; coord_out < mapdata->nout; coord_out++ ) { if ( mapdata->ptr_out[ coord_out ][ 0 ] == AST__BAD ) { bad = 1; break; } } /* If not, then extract the required output coordinate, negating it if necessary. */ if ( !bad ) { result = mapdata->ptr_out[ mapdata->coord ][ 0 ]; if ( mapdata->negate ) result = -result; } } } /* Return the result. */ return result; } static int MapList( AstMapping *this, int series, int invert, int *nmap, AstMapping ***map_list, int **invert_list, int *status ) { /* *+ * Name: * astMapList * Purpose: * Decompose a Mapping into a sequence of simpler Mappings. * Type: * Protected virtual function. * Synopsis: * #include "mapping.h" * int astMapList( AstMapping *this, int series, int invert, int *nmap, * AstMapping ***map_list, int **invert_list ) * Class Membership: * Mapping method. * Description: * This function decomposes a Mapping (which, in derived classes, * may be a compound Mapping) into a sequence of simpler Mappings * which may be applied in sequence to achieve the same effect. The * Mapping is decomposed as far as possible, but it is not * guaranteed that this will necessarily yield any more than one * Mapping, which may actually be the original one supplied. * * This function is provided to support both the simplification of * compound Mappings, and the analysis of Mapping structure so that * particular forms can be recognised. * Parameters: * this * Pointer to the Mapping to be decomposed (the Mapping is not * actually modified by this function). * series * If this value is non-zero, an attempt will be made to * decompose the Mapping into a sequence of equivalent Mappings * which can be applied in series (i.e. one after the other). If * it is zero, the decomposition will instead yield Mappings * which can be applied in parallel (i.e. on successive sub-sets * of the input/output coordinates). * invert * The value to which the Mapping's Invert attribute is to be * (notionally) set before performing the * decomposition. Normally, the value supplied here will be the * actual Invert value obtained from the Mapping (e.g. using * astGetInvert). Sometimes, however, when a Mapping is * encapsulated within another structure, that structure may * retain an Invert value (in order to prevent external * interference) which should be used instead. * * Note that the actual Invert value of the Mapping supplied is * not used (or modified) by this function. * nmap * The address of an int which holds a count of the number of * individual Mappings in the decomposition. On entry, this * should count the number of Mappings already in the * "*map_list" array (below). On exit, it is updated to include * any new Mappings appended by this function. * map_list * Address of a pointer to an array of Mapping pointers. On * entry, this array pointer should either be NULL (if no * Mappings have yet been obtained) or should point at a * dynamically allocated array containing Mapping pointers * ("*nmap" 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 Mapping 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 Mapping pointers returned will identify a sequence of * Mappings which, when applied in order, will perform a forward * transformation equivalent to that of the original Mapping * (after its Invert flag has first been set to the value * requested above). The Mappings should be applied in series or * in parallel according to the type of decomposition requested. * * All the Mapping 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. * invert_list * Address of a pointer to an array of int. On entry, this array * pointer should either be NULL (if no Mappings have yet been * obtained) or should point at a dynamically allocated array * containing Invert attribute values ("*nmap" 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 Invert attribute values that result from the * decomposition requested. These values 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 Invert values returned identify the values which must * be assigned to the Invert attributes of the corresponding * Mappings (whose pointers are in the "*map_list" array) before * they are applied. Note that these values may differ from the * actual Invert attribute values of these Mappings, which are * not relevant. * * The dynamic array holding these values should be freed by the * caller, using astFree, when no longer required. * Returned Value: * A non-zero value is returned if the supplied Mapping contained any * inverted CmpMaps. * Notes: * - It is unspecified to what extent the original Mapping and the * individual (decomposed) Mappings are * inter-dependent. Consequently, the individual Mappings cannot be * modified without risking modification of the original. * - If this function is invoked with the global error status set, * or if it should fail for any reason, then the *nmap value, the * list of Mapping pointers and the list of Invert values will all * be returned unchanged. *- */ /* Check the global error status. */ if ( !astOK ) return 0; /* Since we are dealing with a basic Mapping, only one new Mapping pointer will be returned. Extend the dynamic arrays to accommodate this Mapping. */ *map_list = astGrow( *map_list, *nmap + 1, sizeof( AstMapping * ) ); *invert_list = astGrow( *invert_list, *nmap + 1, sizeof( int ) ); if ( astOK ) { /* Return the invert flag value for the Mapping and a clone of the Mapping pointer. */ ( *invert_list )[ *nmap ] = ( invert != 0 ); ( *map_list )[ *nmap ] = astClone( this ); /* If OK, return the new Mapping count. */ if ( astOK ) ( *nmap )++; } return 0; } static int MapMerge( AstMapping *this, int where, int series, int *nmap, AstMapping ***map_list, int **invert_list, int *status ) { /* *+ * Name: * astMapMerge * Purpose: * Simplify a sequence of Mappings. * Type: * Protected virtual function. * Synopsis: * #include "mapping.h" * int astMapMerge( AstMapping *this, int where, int series, int *nmap, * AstMapping ***map_list, int **invert_list ) * Class Membership: * Mapping method. * Description: * This function attempts to simplify a sequence of Mappings by * merging a nominated Mapping in the sequence with its neighbours, * so as to shorten the sequence if possible. * * In many cases, simplification will not be possible and the * function will return -1 to indicate this, without further * action. * * In most cases of interest, however, this function will either * attempt to replace the nominated Mapping with one which it * considers simpler, or to merge it with the Mappings which * immediately precede it or follow it in the sequence (both will * normally be considered). This is sufficient to ensure the * eventual simplification of most Mapping sequences by repeated * application of this function. * * In some cases, the function may attempt more elaborate * simplification, involving any number of other Mappings in the * sequence. It is not restricted in the type or scope of * simplification it may perform, but will normally only attempt * elaborate simplification in cases where a more straightforward * approach is not adequate. * Parameters: * this * Pointer to the nominated Mapping which is to be merged with * its neighbours. This should be a cloned copy of the Mapping * pointer contained in the array element "(*map_list)[where]" * (see below). This pointer will not be annulled, and the * Mapping it identifies will not be modified by this function. * where * Index in the "*map_list" array (below) at which the pointer * to the nominated Mapping resides. * series * A non-zero value indicates that the sequence of Mappings to * be simplified will be applied in series (i.e. one after the * other), whereas a zero value indicates that they will be * applied in parallel (i.e. on successive sub-sets of the * input/output coordinates). * nmap * Address of an int which counts the number of Mappings in the * sequence. On entry this should be set to the initial number * of Mappings. On exit it will be updated to record the number * of Mappings remaining after simplification. * map_list * Address of a pointer to a dynamically allocated array of * Mapping pointers (produced, for example, by the astMapList * method) which identifies the sequence of Mappings. On entry, * the initial sequence of Mappings to be simplified should be * supplied. * * On exit, the contents of this array will be modified to * reflect any simplification carried out. Any form of * simplification may be performed. This may involve any of: (a) * removing Mappings by annulling any of the pointers supplied, * (b) replacing them with pointers to new Mappings, (c) * inserting additional Mappings and (d) changing their order. * * The intention is to reduce the number of Mappings in the * sequence, if possible, and any reduction will be reflected in * the value of "*nmap" returned. However, simplifications which * do not reduce the length of the sequence (but improve its * execution time, for example) may also be performed, and the * sequence might conceivably increase in length (but normally * only in order to split up a Mapping into pieces that can be * more easily merged with their neighbours on subsequent * invocations of this function). * * If Mappings are removed from the sequence, any gaps that * remain will be closed up, by moving subsequent Mapping * pointers along in the array, so that vacated elements occur * at the end. If the sequence increases in length, the array * will be extended (and its pointer updated) if necessary to * accommodate any new elements. * * Note that any (or all) of the Mapping pointers supplied in * this array may be annulled by this function, but the Mappings * to which they refer are not modified in any way (although * they may, of course, be deleted if the annulled pointer is * the final one). * invert_list * Address of a pointer to a dynamically allocated array which, * on entry, should contain values to be assigned to the Invert * attributes of the Mappings identified in the "*map_list" * array before they are applied (this array might have been * produced, for example, by the astMapList method). These * values will be used by this function instead of the actual * Invert attributes of the Mappings supplied, which are * ignored. * * On exit, the contents of this array will be updated to * correspond with the possibly modified contents of the * "*map_list" array. If the Mapping sequence increases in * length, the "*invert_list" array will be extended (and its * pointer updated) if necessary to accommodate any new * elements. * Returned Value: * If simplification was possible, the function returns the index * in the "map_list" array of the first element which was * modified. Otherwise, it returns -1 (and makes no changes to the * arrays supplied). * Notes: * - A value of -1 will be returned if this function is invoked * with the global error status set, or if it should fail for any * reason. *- */ /* This is the default method which is inherited by all Mappings which do not explicitly provide their own simplification method. Return -1 to indicate that no simplification is provided. */ return -1; } static int *MapSplit( AstMapping *this, int nin, const int *in, AstMapping **map, int *status ){ /* *+ * Name: * astMapSplit * Purpose: * Create a Mapping representing a subset of the inputs of an existing * Mapping. * Type: * Protected virtual function. * Synopsis: * #include "mapping.h" * int *astMapSplit( AstMapping *this, int nin, const int *in, * AstMapping **map ) * Class Membership: * Mapping method. * Description: * This function creates a new Mapping by picking specified inputs from * an existing Mapping. This is only possible if the specified inputs * correspond to some subset of the Mapping outputs. That is, there * must exist a subset of the Mapping outputs for which each output * depends only on the selected Mapping inputs, and not on any of the * inputs which have not been selected. Also, any output which is not in * this subset must not depend on any of the selected inputs. If these * conditions are not met by the supplied Mapping, then a NULL Mapping * is returned. * Parameters: * this * Pointer to the Mapping to be split (the Mapping is not * actually modified by this function). * nin * The number of inputs to pick from "this". * in * Pointer to an array of indices (zero based) for the inputs which * are to be picked. This array should have "nin" elements. If "Nin" * is the number of inputs of the supplied Mapping, then each element * should have a value in the range zero to Nin-1. * map * Address of a location at which to return a pointer to the new * Mapping. This Mapping will have "nin" inputs (the number of * outputs may be differetn to "nin"). A NULL pointer will be * returned if the supplied Mapping has no subset of outputs which * depend only on the selected inputs. The returned Mapping is a * deep copy of the required parts of the supplied Mapping. * Returned Value: * A pointer to a dynamically allocated array of ints. The number of * elements in this array will equal the number of outputs for the * returned Mapping. Each element will hold the index of the * corresponding output in the supplied Mapping. The array should be * freed using astFree when no longer needed. A NULL pointer will * be returned if no output Mapping can be created. * Notes: * - If this function is invoked with the global error status set, * or if it should fail for any reason, then NULL values will be * returned as the function value and for the "map" pointer. *- * Implementation Notes: * - This function implements the basic astMapSplit method available * via the protected interface to the Mapping class. The public * interface to this method is provided by the astMapSplitId_ * function. */ /* Local Variables: */ AstCmpMap *rmap; /* Unsimplified result mapping */ AstPermMap *pm; /* PermMap which rearranges the inputs */ int *outperm; /* PermMap output axis permutation array */ int *result; /* Pointer to returned array */ int iin; /* Input index */ int iout; /* Output index */ int mapnin; /* Number of Mapping inputs */ int nout; /* No of outputs */ int ok; /* Can the supplied "in" array be used? */ int perm; /* Are the inputs permuted? */ /* Initialise */ result = NULL; *map = NULL; /* Check the global error status. */ if ( !astOK ) return result; /* Verify the input axis indices.*/ mapnin = astGetNin( this ); for( iin = 0; iin < nin; iin++ ){ if( in[ iin ] < 0 || in[ iin ] >= mapnin ) { astError( AST__AXIIN, "astMapSplit(%s): One of the supplied Mapping " "input indices has value %d which is invalid; it should " "be in the range 1 to %d.", status, astGetClass( this ), in[ iin ] + 1, mapnin ); break; } } /* Since we are dealing with a basic Mapping, we can only create the required output Mapping if all inputs are being selected. */ if( nin == mapnin ) { /* The inputs may have been selected in a different order to that in which they occur in the supplied Mapping. We therefore create a PermMap which rearranges the inputs into the order they have in the supplied Mapping. The supplied "in" array can act as the PermMap's "inperm" array. Allocate memory for the "outperm" array. */ outperm = astMalloc( sizeof(int)*(size_t) nin ); if( astOK ) { /* Store the input index for each output in the outperm array and check that each input has been selected once and only once. Also set a flag indicating if a PermMap is needed. */ perm = 0; ok = 1; for( iout = 0; iout < nin; iout++ ) outperm[ iout ] = -1; for( iin = 0; iin < nin; iin++ ) { iout = in[ iin ]; if( outperm[ iout ] != -1 ) { ok = 0; break; } else { outperm[ iout ] = iin; } } for( iout = 0; iout < nin; iout++ ) { if( outperm[ iout ] == -1 ) { ok = 0; break; } else if( outperm[ iout ] != iout ) { perm = 1; } } if( ok ) { /* Allocate the array to hold the returned output indices. */ nout = astGetNout( this ); result = astMalloc( sizeof(int)*(size_t) nout ); if( astOK ) { /* The outputs are copied from the supplied Mapping. */ for( iout = 0; iout < nout; iout++ ) result[ iout ] = iout; /* If the inputs are to be permuted, create the PermMap. */ if( perm ) { pm = astPermMap( nin, in, nin, outperm, NULL, "", status ); /* The returned Mapping is a series CmpMap containing this PermMap followed by the supplied Mapping. */ rmap = astCmpMap( pm, this, 1, "", status ); *map = astSimplify( rmap ); rmap = astAnnul( rmap ); /* Annul the PermMap pointer. */ pm = astAnnul( pm ); /* If no input permutation is needed, the resturned Mapping is just the supplied Mapping. */ } else { *map = astClone( this ); } } } /* Free resources. */ outperm = astFree( outperm ); } } /* Free resources if an error has occurred. */ if( !astOK ) { result = astFree( result ); *map = astAnnul( *map ); } /* Return the list of output indices. */ return result; } static double MatrixDet( int nrow, int ncol, const double *matrix, int *status ){ /* * Name: * MatrixDet * Purpose: * Return the determinant of a matrix. * Type: * Private function. * Synopsis: * #include "mapping.h" * double MatrixDet( int nrow, int ncol, const double *matrix, int *status ) * Class Membership: * Mapping member function. * Description: * This function returns the determinant of the supplied matrix. Any * rows or columns that hold only zeros or AST_BAD values are first * removed from the matrix. If the resulting matrix is not square, a * value of AST__BAD is returned for the determinant. * Parameters: * nrow * The number of rows in the matrix. * ncol * The number of columns in the matrix. * matrix * The matrix element values. The first row of "ncol" elements * should be supplied first, followed by the second row, etc. * status * Pointer to the inherited status variable. * Returned Value: * The determinant, or AST__BAD if the determinant could not be * caclculated. */ /* Local Variables: */ const double *sqmat; const double *m; double *a; double *y; double result; int *iw; int *usecol; int *userow; int i; int icol; int irow; int jf; int ncoluse; int ndim; int nrowuse; /* Initialise */ result = AST__BAD; /* Check the global error status. */ if ( !astOK ) return result; /* Initialise... */ sqmat = NULL; nrowuse = 0; ncoluse = 0; /* Flag any rows and columns that should be ignored because they contain only bad values or zeros. */ userow = astCalloc( nrow, sizeof( *userow ) ); usecol = astCalloc( ncol, sizeof( *userow ) ); if( astOK ) { m = matrix; for( irow = 0; irow < nrow; irow++ ) { for( icol = 0; icol < ncol; icol++,m++ ) { if( *m != AST__BAD && *m != 0.0 ) { usecol[ icol ] = 1; userow[ irow ] = 1; } } } /* Find the number of usable rows and columns. */ for( irow = 0; irow < nrow; irow++ ) { if( userow[ irow ] ) nrowuse++; } for( icol = 0; icol < ncol; icol++ ) { if( usecol[ icol ] ) ncoluse++; } } /* Return AST__BAD if the resulting matrix is not square. */ if( ncoluse == nrowuse ) { ndim = ncoluse; /* If any rows or columns contained just bad or zero values, create a new matrix that excludes them. */ if( ncol > ndim || nrow > ndim ) { sqmat = astMalloc( ndim*ndim*sizeof(*sqmat) ); if( astOK ) { m = matrix; a = (double *) sqmat; for( irow = 0; irow < nrow; irow++ ) { if( userow[ irow ] ) { for( icol = 0; icol < ncol; icol++,m++ ) { if( usecol[ icol ] ) *(a++) = *m; } } else { m += ncol; } } } /* If no rows or columns contained just bad values, use the supplied matrix. */ } else { sqmat = matrix; } /* Calculate the determinant of the modified matrix */ if( ndim == 1 ) { result = sqmat[ 0 ]; } else if( ndim == 2 ) { result = sqmat[ 0 ]*sqmat[ 3 ] - sqmat[ 1 ]*sqmat[ 2 ]; } else { a = astStore( NULL, sqmat, sizeof( double )*(size_t) (ndim*ndim) ); iw = astMalloc( sizeof( int )*(size_t) ndim ); y = astMalloc( sizeof( double )*(size_t) ndim ); if( y ) { for( i = 0; i < ndim; i++ ) y[ i ] = 1.0; palDmat( ndim, a, y, &result, &jf, iw ); } y = astFree( y ); iw = astFree( iw ); a = astFree( a ); } } /* Free the square matrix if it was allocated here. */ if( sqmat != matrix ) sqmat = astFree( (void *) sqmat ); /* Free the usable row/column flags. */ userow = astFree( userow ); usecol = astFree( usecol ); return result; } static double MaxD( double a, double b, int *status ) { /* * Name: * MaxD * Purpose: * Return the maximum of two double values. * Type: * Private function. * Synopsis: * #include "mapping.h" * double MaxD( double a, double b, int *status ) * Class Membership: * Mapping member function. * Description: * This function returns the maximum of two double values. * Parameters: * a * The first value. * b * The second value. * status * Pointer to the inherited status variable. * Returned Value: * The maximum. */ /* Return the larger value. */ return ( a > b ) ? a : b; } static int MaxI( int a, int b, int *status ) { /* * Name: * MaxI * Purpose: * Return the maximum of two integer values. * Type: * Private function. * Synopsis: * #include "mapping.h" * int MaxI( int a, int b, int *status ) * Class Membership: * Mapping member function. * Description: * This function returns the maximum of two integer values. * Parameters: * a * The first value. * b * The second value. * status * Pointer to the inherited status variable. * Returned Value: * The maximum. */ /* Return the larger value. */ return ( a > b ) ? a : b; } static int MinI( int a, int b, int *status ) { /* * Name: * MinI * Purpose: * Return the minimum of two integer values. * Type: * Private function. * Synopsis: * #include "mapping.h" * int MinI( int a, int b, int *status ) * Class Membership: * Mapping member function. * Description: * This function returns the minimum of two integer values. * Parameters: * a * The first value. * b * The second value. * status * Pointer to the inherited status variable. * Returned Value: * The minimum. */ /* Return the smaller value. */ return ( a < b ) ? a : b; } static double NewVertex( const MapData *mapdata, int lo, double scale, double x[], double f[], int *ncall, double xnew[], int *status ) { /* * Name: * NewVertex * Purpose: * Locate a new vertex for a simplex. * Type: * Private function. * Synopsis: * #include "mapping.h" * double NewVertex( const MapData *mapdata, int lo, double scale, * double x[], double f[], int *ncall, double xnew[], int *status ); * Class Membership: * Mapping member function. * Description: * This function is provided for use during optimisation of a * Mapping function using the simplex method. It generates the * coordinates of a new simplex vertex and evaluates the Mapping * function at that point. If the function's value is better then * (i.e. larger than) the value at the previously worst vertex, * then it is used to replace that vertex. * Parameters: * mapdata * Pointer to a MapData structure which describes the Mapping * function to be used. * lo * The (zero-based) index of the simplex vertex which initially * has the worst (lowest) value. * scale * The scale factor to be used to generate the new vertex. The * distance of the worst vertex from the centre of the face * opposite it is scaled by this factor to give the new vertex * position. Negative factors result in reflection through this * opposite face. * x * An array of double containing the coordinates of the vertices * of the simplex. The coordinates of the first vertex are * stored first, then those of the second vertex, etc. This * array will be updated by this function if the new vertex is * used to replace an existing one. * f * An array of double containing the Mapping function values at * each vertex of the simplex. This array will be updated by * this function if the new vertex is used to replace an * existing one. * ncall * Pointer to an int containing a count of the number of times * the Mapping function has been invoked. This value will be * updated to reflect the actions of this function. * xnew * An array of double with one element for each input coordinate * of the Mapping function. This is used as workspace. * status * Pointer to the inherited status variable. * Returned Value: * The Mapping function value at the new vertex. This value is * returned whether or not the new vertex replaces an existing one. * Notes: * - A value of AST__BAD will be returned by this function if it is * invoked with the global error status set, or if it should fail * for any reason. * - A value of AST__BAD will also be returned if the new vertex * lies outside the constrained range of input coordinates * associated with the Mapping function (as specified in the * MapData structure supplied) or if any of the transformed output * coordinates produced by the underlying Mapping is bad. In either * case the new vertex will not be used to replace an existing one. */ /* Local Variables: */ double fnew; /* Function value at new vertex */ double xface; /* Coordinate of centre of magnification */ int coord; /* Loop counter for coordinates */ int ncoord; /* Number of coordinates */ int nvertex; /* Number of simplex vertices */ int vertex; /* Loop counter for vertices */ /* Initialise. */ fnew = AST__BAD; /* Check the global error status. */ if ( !astOK ) return fnew; /* Obtain the number of Mapping input coordinates from the MapData structure and calculate the number of simplex vertices. */ ncoord = mapdata->nin; nvertex = ncoord + 1; /* Loop to obtain each coordinate of the new vertex. */ for ( coord = 0; coord < ncoord; coord++ ) { /* Loop over all vertices except the lowest one and average their coordinates. This gives the coordinate of the centre of the face opposite the lowest vertex, which will act as the centre of magnification. */ xface = 0.0; for ( vertex = 0; vertex < nvertex; vertex++ ) { if ( vertex != lo ) { /* Divide each coordinate by the number of vertices as the sum is accumulated in order to minimise the risk of overflow. */ xface += x[ vertex * ncoord + coord ] / ( (double ) ( nvertex - 1 ) ); } } /* Magnify the lowest vertex's distance from this point by the required factor to give the coordinates of the new vertex. */ xnew[ coord ] = xface + ( x[ lo * ncoord + coord ] - xface ) * scale; } /* Evaluate the Mapping function at the new vertex. */ fnew = MapFunction( mapdata, xnew, ncall, status ); /* If the result is not bad and exceeds the previous value at the lowest vertex, then replace the lowest vertex with this new one. */ if ( astOK && ( fnew != AST__BAD ) && ( fnew > f[ lo ] ) ) { for ( coord = 0; coord < ncoord; coord++ ) { x[ lo * ncoord + coord ] = xnew[ coord ]; } f[ lo ] = fnew; } /* Return the value at the new vertex. */ return fnew; } static int QuadApprox( AstMapping *this, const double lbnd[2], const double ubnd[2], int nx, int ny, double *fit, double *rms, int *status ){ /* *++ * Name: c astQuadApprox f AST_QUADAPPROX * Purpose: * Obtain a quadratic approximation to a 2D Mapping. * Type: * Public virtual function. * Synopsis: c #include "mapping.h" c int QuadApprox( AstMapping *this, const double lbnd[2], c const double ubnd[2], int nx, int ny, double *fit, c double *rms ) f RESULT = AST_QUADAPPROX( THIS, LBND, UBND, NX, NY, FIT, RMS, STATUS ) * Class Membership: * Mapping function. * Description: * This function returns the co-efficients of a quadratic fit to the * supplied Mapping over the input area specified by c "lbnd" and "ubnd". f LBND and UBND. * The Mapping must have 2 inputs, but may have any number of outputs. * The i'th Mapping output is modelled as a quadratic function of the * 2 inputs (x,y): * * output_i = a_i_0 + a_i_1*x + a_i_2*y + a_i_3*x*y + a_i_4*x*x + * a_i_5*y*y * c The "fit" f The FIT * array is returned holding the values of the co-efficients a_0_0, * a_0_1, etc. * Parameters: c this f THIS = INTEGER (Given) * Pointer to the Mapping. c lbnd f LBND( * ) = DOUBLE PRECISION (Given) c Pointer to an array of doubles f An array * containing the lower bounds of a box defined within the input * coordinate system of the Mapping. The number of elements in this * array should equal the value of the Mapping's Nin attribute. This * box should specify the region over which the fit is to be * performed. c ubnd f UBND( * ) = DOUBLE PRECISION (Given) c Pointer to an array of doubles f An array * containing the upper bounds of the box specifying the region over * which the fit is to be performed. c nx f NX = INTEGER (Given) * The number of points to place along the first Mapping input. The * first point is at c "lbnd[0]" and the last is at "ubnd[0]". f LBND( 1 ) and the last is at UBND( 1 ). * If a value less than three is supplied a value of three will be used. c ny f NY = INTEGER (Given) * The number of points to place along the second Mapping input. The * first point is at c "lbnd[1]" and the last is at "ubnd[1]". f LBND( 2 ) and the last is at UBND( 2 ). * If a value less than three is supplied a value of three will be used. c fit f FIT( * ) = DOUBLE PRECISION (Returned) c Pointer to an array of doubles f An array * in which to return the co-efficients of the quadratic * approximation to the specified transformation. This array should * have at least "6*Nout", elements. The first 6 elements hold the * fit to the first Mapping output. The next 6 elements hold the * fit to the second Mapping output, etc. So if the Mapping has 2 * inputs and 2 outputs the quadratic approximation to the forward * transformation is: * c X_out = fit[0] + fit[1]*X_in + fit[2]*Y_in + fit[3]*X_in*Y_in + c fit[4]*X_in*X_in + fit[5]*Y_in*Y_in c Y_out = fit[6] + fit[7]*X_in + fit[8]*Y_in + fit[9]*X_in*Y_in + c fit[10]*X_in*X_in + fit[11]*Y_in*Y_in f X_out = fit(1) + fit(2)*X_in + fit(3)*Y_in + fit(4)*X_in*Y_in + f fit(5)*X_in*X_in + fit(6)*Y_in*Y_in f Y_out = fit(7) + fit(8)*X_in + fit(9)*Y_in + fit(10)*X_in*Y_in + f fit(11)*X_in*X_in + fit(12)*Y_in*Y_in * c rms f RMS = DOUBLE PRECISION (Returned) c Pointer to a double in which to return the f The * RMS residual between the fit and the Mapping, summed over all * Mapping outputs. f STATUS = INTEGER (Given and Returned) f The global status. * Returned Value: c astQuadApprox() f AST_QUADAPPROX = LOGICAL * If a quadratic approximation was created, c a non-zero value is returned. Otherwise zero is returned f .TRUE is returned. Otherwise .FALSE. is returned * and the fit co-efficients are set to AST__BAD. * Notes: * - This function fits the Mapping's forward transformation. To fit * the inverse transformation, the Mapping should be inverted using c astInvert f AST_INVERT * before invoking this function. c - A value of zero f - A value of .FALSE. * will be returned if this function is invoked * with the global error status set, or if it should fail for any * reason. *-- */ /* Local Variables: */ AstPointSet *pset1; AstPointSet *pset2; double **pdat1; double **pdat2; double *ofit; double *px; double *py; double *pz; double det; double dx; double dy; double mat[ 6*6 ]; double sx2; double sx2y2; double sx2y; double sx3; double sx3y; double sx4; double sx; double sxy2; double sxy3; double sxy; double sy2; double sy3; double sy4; double sy; double sz; double sz2; double szx2; double szx; double szxy; double szy2; double szy; double x; double xx; double xy; double y; double yy; double z; int i; int iout; int iw[ 6 ]; int ix; int iy; int n; int nin; int nout; int np; int ntot; int result; int sing; /* Initialise the returned values. */ result = 0; fit[ 0 ] = AST__BAD; *rms = AST__BAD; ntot = 0; /* Check the global error status. */ if( !astOK ) return result; /* Get the number of Mapping inputs and outputs. Report an error if not correct. */ nin = astGetI( this, "Nin" ); nout = astGetI( this, "Nout" ); if( nin != 2 && astOK ) { astError( AST__BADNI, "astQuadApprox(%s): Input Mapping has %d %s - " "it must have 2 inputs.", status, astGetClass( this ), nin, (nin==1)?"input":"inputs" ); } /* Ensure we are using at least 3 points on each of the two input axes. */ if( nx < 3 ) nx = 3; if( ny < 3 ) ny = 3; /* Get the total number of grid points. */ np = nx*ny; /* Create a PointSet to hold the 2D grid of input positions. */ pset1 = astPointSet( np, 2, " ", status ); pdat1 = astGetPoints( pset1 ); /* Create a PointSet to hold the N-D grid of output positions. */ pset2 = astPointSet( np, nout, " ", status ); pdat2 = astGetPoints( pset2 ); /* Check the memory allocation (and everything else) was succesful. */ if( astOK ) { /* Find the cell dimensions on X and Y input axes. */ dx = ( ubnd[ 0 ] - lbnd[ 0 ] )/( nx - 1 ); dy = ( ubnd[ 1 ] - lbnd[ 1 ] )/( ny - 1 ); /* Create a regular grid of input positions. */ px = pdat1[ 0 ]; py = pdat1[ 1 ]; for( iy = 0; iy < ny; iy++ ) { x = lbnd[ 0 ]; y = lbnd[ 1 ] + iy*dy; for( ix = 0; ix < nx; ix++ ) { *(px++) = x; *(py++) = y; x += dx; } } /* Use the supplied Mapping to transform this grid into the output space. */ (void) astTransform( this, pset1, 1, pset2 ); /* Assume the approximation can be created. */ result = 1; *rms = 0.0; /* Loop round each Mapping output. */ for( iout = 0; iout < nout && astOK; iout++ ) { /* Get a pointer to the first element of the fit array for this output. */ ofit = fit + 6*iout; /* Form the required sums. */ n = 0; sx = 0.0; sy = 0.0; sxy = 0.0; sx2 = 0.0; sy2 = 0.0; sx2y = 0.0; sx3 = 0.0; sxy2 = 0.0; sy3 = 0.0; sx2y2 = 0.0; sx3y = 0.0; sxy3 = 0.0; sx4 = 0.0; sy4 = 0.0; sz = 0.0; sz2 = 0.0; szx = 0.0; szy = 0.0; szxy = 0.0; szx2 = 0.0; szy2 = 0.0; px = pdat1[ 0 ]; py = pdat1[ 1 ]; pz = pdat2[ iout ]; for( i = 0; i < np; i++ ) { x = *(px++); y = *(py++); z = *(pz++); if( z != AST__BAD ) { xx = x*x; yy = y*y; xy = x*y; n++; sx += x; sy += y; sxy += xy; sx2 += xx; sy2 += yy; sx2y += xx*y; sx3 += xx*x; sxy2 += x*yy; sy3 += yy*y; sx2y2 += xx*yy; sx3y += xx*xy; sxy3 += xy*yy; sx4 += xx*xx; sy4 += yy*yy; sz += z; sz2 += z*z; szx += z*x; szy += z*y; szxy += z*xy; szx2 += z*xx; szy2 += z*yy; } } /* Form a matrix (M) and vector (V) such that M.X = V, where X is the solution vector holding the required best fit parameter values (V is stored in ofit). */ mat[ 0 ] = n; mat[ 1 ] = sx; mat[ 2 ] = sy; mat[ 3 ] = sxy; mat[ 4 ] = sx2; mat[ 5 ] = sy2; mat[ 6 ] = sx; mat[ 7 ] = sx2; mat[ 8 ] = sxy; mat[ 9 ] = sx2y; mat[ 10 ] = sx3; mat[ 11 ] = sxy2; mat[ 12 ] = sy; mat[ 13 ] = sxy; mat[ 14 ] = sy2; mat[ 15 ] = sxy2; mat[ 16 ] = sx2y; mat[ 17 ] = sy3; mat[ 18 ] = sxy; mat[ 19 ] = sx2y; mat[ 20 ] = sxy2; mat[ 21 ] = sx2y2; mat[ 22 ] = sx3y; mat[ 23 ] = sxy3; mat[ 24 ] = sx2; mat[ 25 ] = sx3; mat[ 26 ] = sx2y; mat[ 27 ] = sx3y; mat[ 28 ] = sx4; mat[ 29 ] = sx2y2; mat[ 30 ] = sy2; mat[ 31 ] = sxy2; mat[ 32 ] = sy3; mat[ 33 ] = sxy3; mat[ 34 ] = sx2y2; mat[ 35 ] = sy4; ofit[ 0 ] = sz; ofit[ 1 ] = szx; ofit[ 2 ] = szy; ofit[ 3 ] = szxy; ofit[ 4 ] = szx2; ofit[ 5 ] = szy2; /* Now find the solution vector (the solution over-writes teh current contents of "ofit"). */ palDmat( 6, mat, ofit, &det, &sing, iw ); /* If the fit failed, fill the coefficient array with bad values. */ if( sing != 0 ) { for( i = 0; i < 6; i++ ) ofit[ i ] = AST__BAD; result = 0; break; /* If the fit succeeded, update the summ of the squared residuals. */ } else { ntot += n; *rms += ofit[ 0 ]*ofit[ 0 ]*n + 2*ofit[ 0 ]*ofit[ 1 ]*sx + 2*ofit[ 0 ]*ofit[ 2 ]*sy + 2*( ofit[ 0 ]*ofit[ 3 ] + ofit[ 1 ]*ofit[ 2 ] )*sxy + ( 2*ofit[ 0 ]*ofit[ 4 ] + ofit[ 1 ]*ofit[ 1 ] )*sx2 + ( 2*ofit[ 0 ]*ofit[ 5 ] + ofit[ 2 ]*ofit[ 2 ] )*sy2 + 2*ofit[ 1 ]*ofit[ 4 ]*sx3 + 2*( ofit[ 1 ]*ofit[ 3 ] + ofit[ 2 ]*ofit[ 4 ] )*sx2y + 2*( ofit[ 1 ]*ofit[ 5 ] + ofit[ 2 ]*ofit[ 3 ] )*sxy2 + 2*ofit[ 2 ]*ofit[ 5 ]*sy3 + ofit[ 4 ]*ofit[ 4 ]*sx4 + 2*ofit[ 3 ]*ofit[ 4 ]*sx3y + ( 2*ofit[ 4 ]*ofit[ 5 ] + ofit[ 3 ]*ofit[ 3 ] )*sx2y2 + 2*ofit[ 3 ]*ofit[ 5 ]*sxy3 + ofit[ 5 ]*ofit[ 5 ]*sy4 + sz2 - 2*( ofit[ 0 ]*sz + ofit[ 1 ]*szx + ofit[ 2 ]*szy + ofit[ 3 ]*szxy + ofit[ 4 ]*szx2 + ofit[ 5 ]*szy2 ); } } } /* Free resources. */ pset1 = astAnnul( pset1 ); pset2 = astAnnul( pset2 ); /* Return AST__BAD if anything went wrong. */ if( !astOK || ntot == 0 ) { result = 0; fit[ 0 ] = AST__BAD; *rms = AST__BAD; /* Otherwise normalise the returned RMS. */ } else { if( *rms > 0.0 ) { *rms = sqrt( *rms/ntot ); } else { *rms = 0.0; } } /* Return result */ return result; } static double Random( long int *seed, int *status ) { /* * Name: * Random * Purpose: * Return a pseudo-random value in the range 0 to 1. * Type: * Private function. * Synopsis: * #include "mapping.h" * double Random( long int *seed, int *status ); * Class Membership: * Mapping member function. * Description: * This function returns a pseudo-random double value from a PDF * uniformly distributed in the range 0 to 1. It also updates a * seed value so that a sequence of pseudo-random values may be * obtained with successive invocations. * Parameters: * seed * Pointer to a long int which should initially contain a * non-zero seed value. This will be updated with a new seed * which may be supplied on the next invocation in order to * obtain a different pseudo-random value. * status * Pointer to the inherited status variable. * Returned Value: * The pseudo-random value. */ /* Local Variables: */ long int i; /* Temporary storage */ /* This a basic random number generator using constants given in Numerical Recipes (Press et al.). */ i = *seed / 127773; *seed = ( *seed - i * 127773 ) * 16807 - i * 2836; if ( *seed < 0 ) *seed += 2147483647; /* Return the result as a double value in the range 0 to 1. */ return ( (double) ( *seed - 1 ) ) / (double) 2147483646; } static double Rate( AstMapping *this, double *at, int ax1, int ax2, int *status ){ /* *+ * Name: * astRate * Purpose: * Calculate the rate of change of a Mapping output. * Type: * Protected virtual function. * Synopsis: * #include "mapping.h" * result = astRate( AstMapping *this, double *at, int ax1, int ax2 ) * Class Membership: * Mapping method. * Description: * This function evaluates the rate of change of a specified output of * the supplied Mapping with respect to a specified input, at a * specified input position. * * The result is the mean gradient within a small interval centred on * the supplied position. The interval size is selected automatically * to minimise the error on the returned value. For large intervals, * the error is dominated by changes in the gradient of the * transformation. For small intervals, the error is dominated by * rounding errors. The best interval is the one that gives the most * consistent measure of the gradient within the interval. To find this * consistency, each candidate interval is subdivided into eight * sub-intervals, the mean gradient within each sub-interval is found, * and the associated consistency measure is then the difference between * the maximum and minimum sub-interval gradient found within the interval. * Parameters: * this * Pointer to the Mapping to be applied. * at * The address of an array holding the axis values at the position * at which the rate of change is to be evaluated. The number of * elements in this array should equal the number of inputs to the * Mapping. * ax1 * The index of the Mapping output for which the rate of change is to * be found (output numbering starts at 0 for the first output). * ax2 * The index of the Mapping input which is to be varied in order to * find the rate of change (input numbering starts at 0 for the first * input). * Returned Value: * astRate() * The rate of change of Mapping output "ax1" with respect to input * "ax2", evaluated at "at", or AST__BAD if the value cannot be * calculated. * Notes: * - A value of AST__BAD 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 astRate method available * via the protected interface to the Mapping class. The public * interface to this method is provided by the astRateId_ * function. */ #define NN 50 /* Local Variables: */ double h0; double h; double mean; double minrange; double range0; double range; double ret; double x0; double y[2*NN+1]; double z[2*NN+1]; int ibot; int iin; int iret; int itop; int nin; int nout; /* Initialise */ ret = AST__BAD; /* Check the global error status. */ if ( !astOK ) return ret; /* Allocate resources */ RateFun( NULL, NULL, -1, 0, 0, NULL, NULL, status ); /* Obtain the numbers of input and output coordinates for the Mapping. */ nin = astGetNin( this ); nout = astGetNout( this ); /* Validate the output index. */ if ( astOK && ( ax1 < 0 || ax1 >= nout ) ) { astError( AST__AXIIN, "astRate(%s): The supplied Mapping output " "index (%d) is invalid; it should be in the range 1 to %d.", status, astGetClass( this ), ax1 + 1, nout ); } /* Validate the input index. */ if ( astOK && ( ax2 < 0 || ax2 >= nin ) ) { astError( AST__AXIIN, "astRate(%s): The supplied Mapping input " "index (%d) is invalid; it should be in the range 1 to %d.", status, astGetClass( this ), ax2 + 1, nin ); } /* Check the Mapping has a forward transformation. */ if ( astOK && !astGetTranForward( this ) ) { astError( AST__NODEF, "astRate(%s): The supplied Mapping does not " "have a defined forward transformation.", status, astGetClass( this ) ); } /* Save the central value on the Mapping input which is to be varied. */ x0 = at[ ax2 ]; /* If it is bad, return bad values. */ if( astOK && x0 != AST__BAD ) { /* The required derivative is formed by evaluating the transformation at two positions close to "x0", and dividing the change in y by the change in x. The complexity comes in deciding how close to "x0" the two points should be. If the points are too far apart, the gradient of the function may vary significantly between the two points and so we have little confidence that he mean gradient in the interval is a good estimate of the gradient at "x0". On the other hand if the points are too close together, rounding errors will make the gradient value unreliable. The optimal interval is found by testing a number of different intervals as follows. Each interval is split into NDIV equal sub-intervals, and the gradient in each sub-interval is found. The max and min gradient for any of these sub-intervals is found, and the difference between them is used as an estimate of the reliability of the mean gradient within the whole interval. The interval with the greatest reliability is used to define the returned gradient. The initial estimate of the interval size is a fixed small fraction of the supplied "x0" value, or 1.0 if "x0" is zero. */ h0 = ( x0 != 0.0 ) ? DBL_EPSILON*1.0E9*fabs( x0 ) : 1.0; /* Attempt to find the mean gradient, and the range of gradients, within an interval of size "h0" centred on "x0". If this cannot be done, increase "h0" by a factor fo ten repeatedly until it can be done, or a silly large interval size is reached. */ mean = AST__BAD; while( mean == AST__BAD && h0 < 1.0E-10*DBL_MAX ) { h0 *= 10; mean = FindGradient( this, at, ax1, ax2, x0, h0, &range0, status ); } /* If this was not successful, return AST__BAD as the function value. */ if( mean != AST__BAD ) { /* We now search through a range of larger interval sizes, to see if any produce a more reliable mean gradient estimate (i.e. have a smaller range of gradients within the interval ). After that we search through a range of smaller interval sizes. The gradient range and mean gradient for each interval size are stored in arrays "y" and "z" respectively. "iret" is the index of the most reliable interval found so far (i.e. the one with the smallest range of sub-interval gradients). The original interval "h0" is stored in the middle element of these arrays (index "NN"). Intervals are stored in monotonic order of interval size in the arrays. */ iret = NN; y[ NN ] = range0; z[ NN ] = mean; minrange = range0; /* itop is the index of the last array elements to store calculated values. */ itop = NN; /* Loop round increasing the interval size by a factor of four each time round. */ h = h0; for( iin = NN + 1; iin <= 2*NN && astOK; iin++ ){ h *= 4.0; /* Calculate the mean gradient, and the range of gradients, using the current interval size. */ mean = FindGradient( this, at, ax1, ax2, x0, h, &range, status ); /* If it could be done, store the values in the arrays. */ if( mean != AST__BAD ) { itop++; z[ itop ] = mean; y[ itop ] = range; /* Look for the smallest range, and note its index in the arrays. */ if( range < minrange ) { minrange = range; iret = itop; /* If a range of zero is encountered, we only believe it if the previous interval also had zero range. Otherwise, it's probably just a numerical fluke. If the previous interval also had a range of zero, we can forget the rest of the algorithm since the supplied transformation is linear and we now have its gradient. So leave the loop. */ } else if( range == 0.0 && y[ iin - 1 ] == 0 ) { iret = itop; break; } /* Stop looping when the interval range is 100 times the original interval range. */ if( range > 100*range0 ) break; } } /* Record the minimum range found so far. */ range0 = minrange; /* ibot is the index of the first array elements to store calculated values. */ ibot = NN; /* Loop round decreasing the interval size by a factor of four each time round. This is just like the last loop, but goes the other way, to lower indices. */ h = h0; for( iin = NN - 1; iin >= 0 && astOK; iin-- ){ h /= 4.0; mean = FindGradient( this, at, ax1, ax2, x0, h, &range, status ); if( mean != AST__BAD ) { ibot--; z[ ibot ] = mean; y[ ibot ] = range; if( range < minrange ) { minrange = range; iret = ibot; } else if( range == 0.0 && y[ iin + 1 ] == 0 ) { iret = ibot; break; } if( range > 100*range0 ) break; } } /* If the smallest gradient range in any interval was zero, we only believe it if the adjacent interval size also had zero range. */ if( minrange == 0.0 ) { if( ( iret > ibot && y[ iret - 1 ] == 0 ) || ( iret < itop && y[ iret + 1 ] == 0 ) ) { ret = z[ iret ]; /* Otherwise, search for the smallest gradient range, ignoring values exactly equal to zero, and return the corresponding mean interval gradient. */ } else { for( iin = ibot; iin <= itop; iin++ ){ if( y[ iin ] > 0.0 ){ if( minrange == 0 || y[ iin ] < minrange ) { minrange = y[ iin ]; ret = z[ iin ]; } } } } /* If the minimum range was non-zero, we can just return the corresponding mean gradient. */ } else { ret = z[ iret ]; } } } /* Free resources */ RateFun( NULL, NULL, -2, 0, 0, NULL, NULL, status ); /* Return the result. */ return ret; #undef NN } static void RateFun( AstMapping *map, double *at, int ax1, int ax2, int n, double *x, double *y, int *status ) { /* * Name: * RateFun * Purpose: * Find the value of the function currently being differentiated by the * astRate method. * Type: * Private function. * Synopsis: * #include "mapping.h" * void RateFun( AstMapping *map, double *at, int ax1, int ax2, * int n, double *x, double *y, int *status ) * Class Membership: * Mapping method. * Description: * This is a service function for the astRate method. It evaluates the * function being differentiated at specified axis values. * * This function uses static resources in order to avoid the overhead * of creating new PointSets each time this function is called. These * static resources which must be initialised before the first invocation * with a given Mapping, and must be released after the final invocation. * See "ax1". * Parameters: * map * Pointer to a Mapping which yields the value of the function at x. * The Mapping may have any number of inputs and outputs; the specific * output representing the function value, f, is specified by ax1 and * the specific input representing the argument, x, is specified by ax2. * at * A pointer to an array holding axis values at the position at which * the function is to be evaluated. The number of values supplied * must equal the number of inputs to the Mapping. The value supplied * for axis "ax2" is ignored (the value of "x" is used for axis "ax2"). * ax1 * The zero-based index of the Mapping output which is to be * differentiated. Set this to -1 to allocate, or -2 to release, * the static resources used by this function. * ax2 * The zero-based index of the Mapping input which is to be varied. * n * The number of elements in the "x" and "y" arrays. This should not * be greater than 2*RATE_ORDER. * x * The value of the Mapping input specified by ax2 at which the * function is to be evaluated. If "ax2" is set to -1, then the * supplied value is used as flag indicating if the static resources * used by this function should be initialised (if x >= 0 ) or * freed (if x < 0). * y * An array in which to return the function values at the positions * given in "x". * status * Pointer to the inherited status variable. */ /* Local Variables: */ astDECLARE_GLOBALS AstPointSet *pset1; AstPointSet *pset2; double **ptr1; double **ptr2; double *oldx; double *oldy; double *p; double xx; int i; int k; int nin; int nout; /* Check the global error status. */ if ( !astOK ) return; /* Get a pointer to the thread specific global data structure. */ astGET_GLOBALS(map); /* Initialise variables to avoid "used of uninitialised variable" messages from dumb compilers. */ pset2 = NULL; /* If required, initialise things. */ if( ax1 == -1 ) { for( i = 0; i < RATEFUN_MAX_CACHE; i++ ) { ratefun_pset_size[ i ] = 0; ratefun_pset1_cache[ i ] = NULL; ratefun_pset2_cache[ i ] = NULL; } ratefun_next_slot = 0; /* If required, clean up. */ } else if( ax1 == -2 ) { for( i = 0; i < RATEFUN_MAX_CACHE; i++ ) { ratefun_pset_size[ i ] = 0; if( ratefun_pset1_cache[ i ] ) ratefun_pset1_cache[ i ] = astAnnul( ratefun_pset1_cache[ i ] ); if( ratefun_pset2_cache[ i ] ) ratefun_pset2_cache[ i ] = astAnnul( ratefun_pset2_cache[ i ] ); } ratefun_next_slot = 0; /* Otherwise do the transformations. */ } else { /* See if we have already created PointSets of the correct size. */ pset1 = NULL; for( i = 0; i < RATEFUN_MAX_CACHE; i++ ) { if( ratefun_pset_size[ i ] == n ) { pset1 = ratefun_pset1_cache[ i ]; pset2 = ratefun_pset2_cache[ i ]; break; } } /* If we have not, create new PointSets now. */ if( pset1 == NULL ) { nin = astGetNin( map ); pset1 = astPointSet( n, nin, "", status ); ptr1 = astGetPoints( pset1 ); nout = astGetNout( map ); pset2 = astPointSet( n, nout, "", status ); ptr2 = astGetPoints( pset2 ); /* Store the input position in the input PointSet. */ for( i = 0; i < nin; i++ ) { xx = at[ i ]; p = ptr1[ i ]; for( k = 0; k < n; k++, p++ ) *p = xx; } /* Add these new PointSets to the cache, removing any existing PointSets. */ if( ratefun_pset_size[ ratefun_next_slot ] > 0 ) { (void) astAnnul( ratefun_pset1_cache[ ratefun_next_slot ] ); (void) astAnnul( ratefun_pset2_cache[ ratefun_next_slot ] ); } ratefun_pset1_cache[ ratefun_next_slot ] = pset1; ratefun_pset2_cache[ ratefun_next_slot ] = pset2; ratefun_pset_size[ ratefun_next_slot ] = n; if( ++ratefun_next_slot == RATEFUN_MAX_CACHE ) ratefun_next_slot = 0; /* If existing PointSets were found, get there data arrays. */ } else { ptr1 = astGetPoints( pset1 ); ptr2 = astGetPoints( pset2 ); } /* Store the input X values in the input PointSet data array. */ oldx = ptr1[ ax2 ]; ptr1[ ax2 ] = x; /* Store the output Y values in the output PointSet data array. */ oldy = ptr2[ ax1 ]; ptr2[ ax1 ] = y; /* Transform the positions. */ (void) astTransform( map, pset1, 1, pset2 ); /* Re-instate the original arrays in the PointSets. */ ptr1[ ax2 ] = oldx; ptr2[ ax1 ] = oldy; } } /* *++ * Name: c astRebin f AST_REBIN * Purpose: * Rebin a region of a data grid. * Type: * Public virtual function. * Synopsis: c #include "mapping.h" c void astRebin( AstMapping *this, double wlim, int ndim_in, c const int lbnd_in[], const int ubnd_in[], c const in[], const in_var[], c int spread, const double params[], int flags, c double tol, int maxpix, c badval, int ndim_out, c const int lbnd_out[], const int ubnd_out[], c const int lbnd[], const int ubnd[], c out[], out_var[] ); f CALL AST_REBIN( THIS, WLIM, NDIM_IN, LBND_IN, UBND_IN, IN, IN_VAR, f SPREAD, PARAMS, FLAGS, f TOL, MAXPIX, BADVAL, f NDIM_OUT, LBND_OUT, UBND_OUT, f LBND, UBND, OUT, OUT_VAR, STATUS ) * Class Membership: * Mapping method. * Description: * This is a set of functions for rebinning gridded data (e.g. an * image) under the control of a geometrical transformation, which * is specified by a Mapping. The functions operate on a pair of * data grids (input and output), each of which may have any number * of dimensions. Rebinning may be restricted to a specified * region of the input grid. An associated grid of error estimates * associated with the input data may also be supplied (in the form * of variance values), so as to produce error estimates for the * rebined output data. Propagation of missing data (bad pixels) * is supported. * * Note, if you will be rebining a sequence of input arrays and then * co-adding them into a single array, the alternative c astRebinSeq functions f AST_REBINSEQ routines * will in general be more efficient. * * You should use a rebinning function which matches the numerical * type of the data you are processing by replacing in c the generic function name astRebin by an appropriate 1- or f the generic function name AST_REBIN by an appropriate 1- or * 2-character type code. For example, if you are rebinning data c with type "float", you should use the function astRebinF (see f with type REAL, you should use the function AST_REBINR (see * the "Data Type Codes" section below for the codes appropriate to * other numerical types). * * Rebinning of the grid of input data is performed by transforming * the coordinates of the centre of each input grid element (or pixel) * into the coordinate system of the output grid. The input pixel * value is then divided up and assigned to the output pixels in the * neighbourhood of the central output coordinates. A choice of * schemes are provided for determining how each input pixel value is * divided up between the output pixels. In general, each output pixel * may be assigned values from more than one input pixel. All * contributions to a given output pixel are summed to produce the * final output pixel value. Output pixels can be set to the supplied * bad value if they receive contributions from an insufficient number * of input pixels. This is controlled by the c "wlim" parameter. f WLIM argument. * * Input pixel coordinates are transformed into the coordinate * system of the output grid using the forward transformation of the * Mapping which is supplied. This means that geometrical features * in the input data are subjected to the Mapping's forward * transformation as they are transferred from the input to the * output grid. * * In practice, transforming the coordinates of every pixel of a * large data grid can be time-consuming, especially if the Mapping * involves complicated functions, such as sky projections. To * improve performance, it is therefore possible to approximate * non-linear Mappings by a set of linear transformations which are * applied piece-wise to separate sub-regions of the data. This * approximation process is applied automatically by an adaptive * algorithm, under control of an accuracy criterion which * expresses the maximum tolerable geometrical distortion which may * be introduced, as a fraction of a pixel. * * This algorithm first attempts to approximate the Mapping with a * linear transformation applied over the whole region of the * input grid which is being used. If this proves to be * insufficiently accurate, the input region is sub-divided into * two along its largest dimension and the process is repeated * within each of the resulting sub-regions. This process of * sub-division continues until a sufficiently good linear * approximation is found, or the region to which it is being * applied becomes too small (in which case the original Mapping is * used directly). * Parameters: c this f THIS = INTEGER (Given) * Pointer to a Mapping, whose forward transformation will be * used to transform the coordinates of pixels in the input * grid into the coordinate system of the output grid. * * The number of input coordinates used by this Mapping (as * given by its Nin attribute) should match the number of input c grid dimensions given by the value of "ndim_in" f grid dimensions given by the value of NDIM_IN * below. Similarly, the number of output coordinates (Nout * attribute) should match the number of output grid dimensions c given by "ndim_out". f given by NDIM_OUT. c wlim f WLIM = DOUBLE PRECISION (Given) * Gives the required number of input pixel values which must contribute * to an output pixel in order for the output pixel value to be * considered valid. If the sum of the input pixel weights contributing * to an output pixel is less than the supplied c "wlim" f WLIM * value, then the output pixel value is returned set to the * supplied bad value. c ndim_in f NDIM_IN = INTEGER (Given) * The number of dimensions in the input grid. This should be at * least one. c lbnd_in f LBND_IN( NDIM_IN ) = INTEGER (Given) c Pointer to an array of integers, with "ndim_in" elements, f An array * containing the coordinates of the centre of the first pixel * in the input grid along each dimension. c ubnd_in f UBND_IN( NDIM_IN ) = INTEGER (Given) c Pointer to an array of integers, with "ndim_in" elements, f An array * containing the coordinates of the centre of the last pixel in * the input grid along each dimension. * c Note that "lbnd_in" and "ubnd_in" together define the shape f Note that LBND_IN and UBND_IN together define the shape * and size of the input grid, its extent along a particular c (j'th) dimension being ubnd_in[j]-lbnd_in[j]+1 (assuming the c index "j" to be zero-based). They also define f (J'th) dimension being UBND_IN(J)-LBND_IN(J)+1. They also define * the input grid's coordinate system, each pixel having unit * extent along each dimension with integral coordinate values * at its centre. c in f IN( * ) = (Given) c Pointer to an array, with one element for each pixel in the f An array, with one element for each pixel in the * input grid, containing the input data to be rebined. The * numerical type of this array should match the 1- or * 2-character type code appended to the function name (e.g. if c you are using astRebinF, the type of each array element c should be "float"). f you are using AST_REBINR, the type of each array element f should be REAL). * * The storage order of data within this array should be such * that the index of the first grid dimension varies most * rapidly and that of the final dimension least rapidly c (i.e. Fortran array indexing is used). f (i.e. normal Fortran array storage order). c in_var f IN_VAR( * ) = (Given) c An optional pointer to a second array with the same size and c type as the "in" array. If given, this should contain a set c of non-negative values which represent estimates of the c statistical variance associated with each element of the "in" c array. If this array is supplied (together with the c corresponding "out_var" array), then estimates of the c variance of the rebined output data will be calculated. c c If no input variance estimates are being provided, a NULL c pointer should be given. f An optional second array with the same size and type as the f IN array. If the AST__USEVAR flag is set via the FLAGS f argument (below), this array should contain a set of f non-negative values which represent estimates of the f statistical variance associated with each element of the IN f array. Estimates of the variance of the rebined output data f will then be calculated. f f If the AST__USEVAR flag is not set, no input variance f estimates are required and this array will not be used. A f dummy (e.g. one-element) array may then be supplied. c spread f SPREAD = INTEGER (Given) c This parameter specifies the scheme to be used for dividing f This argument specifies the scheme to be used for dividing * each input data value up amongst the corresponding output pixels. * It may be used to select * from a set of pre-defined schemes by supplying one of the * values described in the "Pixel Spreading Schemes" * section below. If a value of zero is supplied, then the * default linear spreading scheme is used (equivalent to * supplying the value AST__LINEAR). c params f PARAMS( * ) = DOUBLE PRECISION (Given) c An optional pointer to an array of double which should contain f An optional array which should contain * any additional parameter values required by the pixel * spreading scheme. If such parameters are required, this * will be noted in the "Pixel Spreading Schemes" * section below. * c If no additional parameters are required, this array is not c used and a NULL pointer may be given. f If no additional parameters are required, this array is not f used. A dummy (e.g. one-element) array may then be supplied. c flags f FLAGS = INTEGER (Given) c The bitwise OR of a set of flag values which may be used to f The sum of a set of flag values which may be used to * provide additional control over the rebinning operation. See * the "Control Flags" section below for a description of the * options available. If no flag values are to be set, a value * of zero should be given. c tol f TOL = DOUBLE PRECISION (Given) * The maximum tolerable geometrical distortion which may be * introduced as a result of approximating non-linear Mappings * by a set of piece-wise linear transformations. This should be * expressed as a displacement in pixels in the output grid's * coordinate system. * * If piece-wise linear approximation is not required, a value * of zero may be given. This will ensure that the Mapping is * used without any approximation, but may increase execution * time. * * If the value is too high, discontinuities between the linear * approximations used in adjacent panel will be higher, and may * cause the edges of the panel to be visible when viewing the output * image at high contrast. If this is a problem, reduce the * tolerance value used. c maxpix f MAXPIX = INTEGER (Given) * A value which specifies an initial scale size (in pixels) for * the adaptive algorithm which approximates non-linear Mappings * with piece-wise linear transformations. Normally, this should * be a large value (larger than any dimension of the region of * the input grid being used). In this case, a first attempt to * approximate the Mapping by a linear transformation will be * made over the entire input region. * * If a smaller value is used, the input region will first be c divided into sub-regions whose size does not exceed "maxpix" f divided into sub-regions whose size does not exceed MAXPIX * pixels in any dimension. Only at this point will attempts at * approximation commence. * * This value may occasionally be useful in preventing false * convergence of the adaptive algorithm in cases where the * Mapping appears approximately linear on large scales, but has * irregularities (e.g. holes) on smaller scales. A value of, * say, 50 to 100 pixels can also be employed as a safeguard in * general-purpose software, since the effect on performance is * minimal. * * If too small a value is given, it will have the effect of * inhibiting linear approximation altogether (equivalent to c setting "tol" to zero). Although this may degrade f setting TOL to zero). Although this may degrade * performance, accurate results will still be obtained. c badval f BADVAL = (Given) * This argument should have the same type as the elements of c the "in" array. It specifies the value used to flag missing f the IN array. It specifies the value used to flag missing * data (bad pixels) in the input and output arrays. * c If the AST__USEBAD flag is set via the "flags" parameter, f If the AST__USEBAD flag is set via the FLAGS argument, c then this value is used to test for bad pixels in the "in" c (and "in_var") array(s). f then this value is used to test for bad pixels in the IN f (and IN_VAR) array(s). * * In all cases, this value is also used to flag any output c elements in the "out" (and "out_var") array(s) for which f elements in the OUT (and OUT_VAR) array(s) for which * rebined values could not be obtained (see the "Propagation * of Missing Data" section below for details of the * circumstances under which this may occur). c ndim_out f NDIM_OUT = INTEGER (Given) * The number of dimensions in the output grid. This should be * at least one. It need not necessarily be equal to the number * of dimensions in the input grid. c lbnd_out f LBND_OUT( NDIM_OUT ) = INTEGER (Given) c Pointer to an array of integers, with "ndim_out" elements, f An array * containing the coordinates of the centre of the first pixel * in the output grid along each dimension. c ubnd_out f UBND_OUT( NDIM_OUT ) = INTEGER (Given) c Pointer to an array of integers, with "ndim_out" elements, f An array * containing the coordinates of the centre of the last pixel in * the output grid along each dimension. * c Note that "lbnd_out" and "ubnd_out" together define the f Note that LBND_OUT and UBND_OUT together define the * shape, size and coordinate system of the output grid in the c same way as "lbnd_in" and "ubnd_in" define the shape, size f same way as LBND_IN and UBND_IN define the shape, size * and coordinate system of the input grid. c lbnd f LBND( NDIM_IN ) = INTEGER (Given) c Pointer to an array of integers, with "ndim_in" elements, f An array * containing the coordinates of the first pixel in the region * of the input grid which is to be included in the rebined output * array. c ubnd f UBND( NDIM_IN ) = INTEGER (Given) c Pointer to an array of integers, with "ndim_in" elements, f An array * containing the coordinates of the last pixel in the region of * the input grid which is to be included in the rebined output * array. * c Note that "lbnd" and "ubnd" together define the shape and f Note that LBND and UBND together define the shape and * position of a (hyper-)rectangular region of the input grid * which is to be included in the rebined output array. This region * should lie wholly within the extent of the input grid (as c defined by the "lbnd_in" and "ubnd_in" arrays). Regions of f defined by the LBND_IN and UBND_IN arrays). Regions of * the input grid lying outside this region will not be used. c out f OUT( * ) = (Returned) c Pointer to an array, with one element for each pixel in the f An array, with one element for each pixel in the * output grid, in which the rebined data values will be * returned. The numerical type of this array should match that c of the "in" array, and the data storage order should be such f of the IN array, and the data storage order should be such * that the index of the first grid dimension varies most * rapidly and that of the final dimension least rapidly c (i.e. Fortran array indexing is used). f (i.e. normal Fortran array storage order). c out_var f OUT_VAR( * ) = (Returned) c An optional pointer to an array with the same type and size c as the "out" array. If given, this array will be used to c return variance estimates for the rebined data values. This c array will only be used if the "in_var" array has also been c supplied. f An optional array with the same type and size as the OUT f array. If the AST__USEVAR flag is set via the FLAGS argument, f this array will be used to return variance estimates for the f rebined data values. * * The output variance values will be calculated on the * assumption that errors on the input data values are * statistically independent and that their variance estimates * may simply be summed (with appropriate weighting factors) * when several input pixels contribute to an output data * value. If this assumption is not valid, then the output error * estimates may be biased. In addition, note that the * statistical errors on neighbouring output data values (as * well as the estimates of those errors) may often be * correlated, even if the above assumption about the input data * is correct, because of the pixel spreading schemes * employed. * c If no output variance estimates are required, a NULL pointer c should be given. f If the AST__USEVAR flag is not set, no output variance f estimates will be calculated and this array will not be f used. A dummy (e.g. one-element) array may then be supplied. f STATUS = INTEGER (Given and Returned) f The global status. * Data Type Codes: * To select the appropriate rebinning function, you should c replace in the generic function name astRebin with a f replace in the generic function name AST_REBIN with a * 1- or 2-character data type code, so as to match the numerical * type of the data you are processing, as follows: c - D: double c - F: float c - I: int c - B: byte (signed char) c - UB: unsigned byte (unsigned char) f - D: DOUBLE PRECISION f - R: REAL f - I: INTEGER f - B: BYTE (treated as signed) f - UB: BYTE (treated as unsigned) * c For example, astRebinD would be used to process "double" c data, while astRebinI would be used to process "int" c data, etc. f For example, AST_REBIND would be used to process DOUBLE f PRECISION data, while AST_REBINI would be used to process f integer data (stored in an INTEGER array), etc. * * Note that, unlike c astResample, the astRebin f AST_RESAMPLE, the AST_REBIN * set of functions does not yet support unsigned integer data types * or integers of different sizes. * Pixel Spreading Schemes: * The pixel spreading scheme specifies the Point Spread Function (PSF) * applied to each input pixel value as it is copied into the output * array. It can be thought of as the inverse of the sub-pixel * interpolation schemes used by the c astResample f AST_RESAMPLE * group of functions. That is, in a sub-pixel interpolation scheme the * kernel specifies the weight to assign to each input pixel when * forming the weighted mean of the input pixels, whereas the kernel in a * pixel spreading scheme specifies the fraction of the input data value * which is to be assigned to each output pixel. As for interpolation, the * choice of suitable pixel spreading scheme involves stricking a balance * between schemes which tend to degrade sharp features in the data by * smoothing them, and those which attempt to preserve sharp features but * which often tend to introduce unwanted artifacts. See the c astResample f AST_RESAMPLE * documentation for further discussion. * * The binning algorithm used has the ability to introduce artifacts * not seen when using a resampling algorithm. Particularly, when * viewing the output image at high contrast, systems of curves lines * covering the entire image may be visible. These are caused by a * beating effect between the input pixel positions and the output pixels * position, and their nature and strength depend critically upon the * nature of the Mapping and the spreading function being used. In * general, the nearest neighbour spreading function demonstrates this * effect more clearly than the other functions, and for this reason * should be used with caution. * * The following values (defined in the c "ast.h" header file) f AST_PAR include file) * may be assigned to the c "spread" f SPREAD * parameter. See the c astResample f AST_RESAMPLE * documentation for details of these schemes including the use of the c "fspread" and "params" parameters: f FSPREAD and PARAMS arguments: * * - AST__NEAREST * - AST__LINEAR * - AST__SINC * - AST__SINCSINC * - AST__SINCCOS * - AST__SINCGAUSS * - AST__SOMBCOS * * In addition, the following schemes can be used with f AST_REBIN but not with AST_RESAMPLE: c astRebin but not with astResample: * * - AST__GAUSS: This scheme uses a kernel of the form exp(-k*x*x), with k * a positive constant determined by the full-width at half-maximum (FWHM). * The FWHM should be supplied in units of output pixels by means of the c "params[1]" f PARAMS(2) * value and should be at least 0.1. The c "params[0]" f PARAMS(1) * value should be used to specify at what point the Gaussian is truncated * to zero. This should be given as a number of output pixels on either * side of the central output point in each dimension (the nearest integer * value is used). * Control Flags: c The following flags are defined in the "ast.h" header file and f The following flags are defined in the AST_PAR include file and * may be used to provide additional control over the rebinning * process. Having selected a set of flags, you should supply the c bitwise OR of their values via the "flags" parameter: f sum of their values via the FLAGS argument: * * - AST__USEBAD: Indicates that there may be bad pixels in the * input array(s) which must be recognised by comparing with the c value given for "badval" and propagated to the output array(s). f value given for BADVAL and propagated to the output array(s). * If this flag is not set, all input values are treated literally c and the "badval" value is only used for flagging output array f and the BADVAL value is only used for flagging output array * values. f - AST__USEVAR: Indicates that variance information should be f processed in order to provide estimates of the statistical error f associated with the rebined values. If this flag is not set, f no variance processing will occur and the IN_VAR and OUT_VAR f arrays will not be used. (Note that this flag is only available f in the Fortran interface to AST.) * Propagation of Missing Data: * Instances of missing data (bad pixels) in the output grid are c identified by occurrences of the "badval" value in the "out" f identified by occurrences of the BADVAL value in the OUT * array. These are produced if the sum of the weights of the * contributing input pixels is less than c "wlim". f WLIM. * * An input pixel is considered bad (and is consequently ignored) if * its c data value is equal to "badval" and the AST__USEBAD flag is c set via the "flags" parameter. f data value is equal to BADVAL and the AST__USEBAD flag is f set via the FLAGS argument. * * In addition, associated output variance estimates (if c calculated) may be declared bad and flagged with the "badval" c value in the "out_var" array for similar reasons. f calculated) may be declared bad and flagged with the BADVAL f value in the OUT_VAR array for similar reasons. *-- */ /* Define a macro to implement the function for a specific data type. */ #define MAKE_REBIN(X,Xtype,IntType) \ static void Rebin##X( AstMapping *this, double wlim, int ndim_in, \ const int lbnd_in[], const int ubnd_in[], \ const Xtype in[], const Xtype in_var[], \ int spread, const double params[], int flags, \ double tol, int maxpix, Xtype badval, \ int ndim_out, const int lbnd_out[], \ const int ubnd_out[], const int lbnd[], \ const int ubnd[], Xtype out[], Xtype out_var[], int *status ) { \ \ /* Local Variables: */ \ astDECLARE_GLOBALS /* Thread-specific data */ \ const char *badflag; /* Name of illegal flag */ \ AstMapping *simple; /* Pointer to simplified Mapping */ \ Xtype *d; /* Pointer to next output data value */ \ Xtype *v; /* Pointer to next output variance value */ \ double *w; /* Pointer to next weight value */ \ double *work; /* Pointer to weight array */ \ int idim; /* Loop counter for coordinate dimensions */ \ int ipix_out; /* Index into output array */ \ int nin; /* Number of Mapping input coordinates */ \ int nout; /* Number of Mapping output coordinates */ \ int npix; /* Number of pixels in input region */ \ int npix_out; /* Number of pixels in output array */ \ int64_t mpix; /* Number of pixels for testing */ \ \ /* Check the global error status. */ \ if ( !astOK ) return; \ \ /* Get a pointer to a structure holding thread-specific global data values */ \ astGET_GLOBALS(this); \ \ /* Obtain values for the Nin and Nout attributes of the Mapping. */ \ nin = astGetNin( this ); \ nout = astGetNout( this ); \ \ /* If OK, check that the number of input grid dimensions matches the \ number required by the Mapping and is at least 1. Report an error \ if necessary. */ \ if ( astOK && ( ( ndim_in != nin ) || ( ndim_in < 1 ) ) ) { \ astError( AST__NGDIN, "astRebin"#X"(%s): Bad number of input grid " \ "dimensions (%d).", status, astGetClass( this ), ndim_in ); \ if ( ndim_in != nin ) { \ astError( AST__NGDIN, "The %s given requires %d coordinate value%s " \ "to specify an input position.", status, \ astGetClass( this ), nin, ( nin == 1 ) ? "" : "s" ); \ } \ } \ \ /* If OK, also check that the number of output grid dimensions matches \ the number required by the Mapping and is at least 1. Report an \ error if necessary. */ \ if ( astOK && ( ( ndim_out != nout ) || ( ndim_out < 1 ) ) ) { \ astError( AST__NGDIN, "astRebin"#X"(%s): Bad number of output grid " \ "dimensions (%d).", status, astGetClass( this ), ndim_out ); \ if ( ndim_out != nout ) { \ astError( AST__NGDIN, "The %s given generates %s%d coordinate " \ "value%s for each output position.", status, astGetClass( this ), \ ( nout < ndim_out ) ? "only " : "", nout, \ ( nout == 1 ) ? "" : "s" ); \ } \ } \ \ /* Check that the lower and upper bounds of the input grid are \ consistent. Report an error if any pair is not. */ \ mpix = 1; \ if ( astOK ) { \ for ( idim = 0; idim < ndim_in; idim++ ) { \ if ( lbnd_in[ idim ] > ubnd_in[ idim ] ) { \ astError( AST__GBDIN, "astRebin"#X"(%s): Lower bound of " \ "input grid (%d) exceeds corresponding upper bound " \ "(%d).", status, astGetClass( this ), \ lbnd_in[ idim ], ubnd_in[ idim ] ); \ astError( AST__GBDIN, "Error in input dimension %d.", status, \ idim + 1 ); \ break; \ } else { \ mpix *= ubnd_in[ idim ] - lbnd_in[ idim ] + 1; \ } \ } \ } \ \ /* Report an error if there are too many pixels in the input. */ \ if ( astOK && (int) mpix != mpix ) { \ astError( AST__EXSPIX, "astRebin"#X"(%s): Supplied input array " \ "contains too many pixels (%g): must be fewer than %d.", \ status, astGetClass( this ), (double) mpix, INT_MAX ); \ } \ \ /* Check that the positional accuracy tolerance supplied is valid and \ report an error if necessary. */ \ if ( astOK && ( tol < 0.0 ) ) { \ astError( AST__PATIN, "astRebin"#X"(%s): Invalid positional " \ "accuracy tolerance (%.*g pixel).", status, \ astGetClass( this ), AST__DBL_DIG, tol ); \ astError( AST__PATIN, "This value should not be less than zero." , status); \ } \ \ /* Check that the initial scale size in pixels supplied is valid and \ report an error if necessary. */ \ if ( astOK && ( maxpix < 0 ) ) { \ astError( AST__SSPIN, "astRebin"#X"(%s): Invalid initial scale " \ "size in pixels (%d).", status, astGetClass( this ), maxpix ); \ astError( AST__SSPIN, "This value should not be less than zero." , status); \ } \ \ /* Check that the lower and upper bounds of the output grid are \ consistent. Report an error if any pair is not. */ \ mpix = 1; \ if ( astOK ) { \ for ( idim = 0; idim < ndim_out; idim++ ) { \ if ( lbnd_out[ idim ] > ubnd_out[ idim ] ) { \ astError( AST__GBDIN, "astRebin"#X"(%s): Lower bound of " \ "output grid (%d) exceeds corresponding upper bound " \ "(%d).", status, astGetClass( this ), \ lbnd_out[ idim ], ubnd_out[ idim ] ); \ astError( AST__GBDIN, "Error in output dimension %d.", status, \ idim + 1 ); \ break; \ } else { \ mpix *= ubnd_out[ idim ] - lbnd_out[ idim ] + 1; \ } \ } \ } \ \ /* Report an error if there are too many pixels in the output. */ \ if ( astOK && (int) mpix != mpix ) { \ astError( AST__EXSPIX, "astRebin"#X"(%s): Supplied output array " \ "contains too many pixels (%g): must be fewer than %d.", \ status, astGetClass( this ), (double) mpix, INT_MAX ); \ } \ \ /* Similarly check the bounds of the input region. */ \ mpix = 1; \ if ( astOK ) { \ for ( idim = 0; idim < ndim_out; idim++ ) { \ if ( lbnd[ idim ] > ubnd[ idim ] ) { \ astError( AST__GBDIN, "astRebin"#X"(%s): Lower bound of " \ "input region (%d) exceeds corresponding upper " \ "bound (%d).", status, astGetClass( this ), \ lbnd[ idim ], ubnd[ idim ] ); \ \ /* Also check that the input region lies wholly within the input \ grid. */ \ } else if ( lbnd[ idim ] < lbnd_in[ idim ] ) { \ astError( AST__GBDIN, "astRebin"#X"(%s): Lower bound of " \ "input region (%d) is less than corresponding " \ "bound of input grid (%d).", status, astGetClass( this ), \ lbnd[ idim ], lbnd_in[ idim ] ); \ } else if ( ubnd[ idim ] > ubnd_in[ idim ] ) { \ astError( AST__GBDIN, "astRebin"#X"(%s): Upper bound of " \ "input region (%d) exceeds corresponding " \ "bound of input grid (%d).", status, astGetClass( this ), \ ubnd[ idim ], ubnd_in[ idim ] ); \ } else { \ mpix *= ubnd[ idim ] - lbnd[ idim ] + 1; \ } \ \ /* Say which dimension produced the error. */ \ if ( !astOK ) { \ astError( AST__GBDIN, "Error in output dimension %d.", status, \ idim + 1 ); \ break; \ } \ } \ } \ \ /* Report an error if there are too many pixels in the input region. */ \ if ( astOK && (int) mpix != mpix ) { \ astError( AST__EXSPIX, "astRebin"#X"(%s): Supplied input region " \ "contains too many pixels (%g): must be fewer than %d.", \ status, astGetClass( this ), (double) mpix, INT_MAX ); \ } \ \ /* If OK, loop to determine how many input pixels are to be binned. */ \ simple = NULL; \ npix = 1; \ npix_out = 1; \ unsimplified_mapping = this; \ if ( astOK ) { \ for ( idim = 0; idim < ndim_in; idim++ ) { \ npix *= ubnd[ idim ] - lbnd[ idim ] + 1; \ } \ \ /* Loop to determine how many pixels the output array contains. */ \ for ( idim = 0; idim < ndim_out; idim++ ) { \ npix_out *= ubnd_out[ idim ] - lbnd_out[ idim ] + 1; \ } \ \ /* If there are sufficient pixels to make it worthwhile, simplify the \ Mapping supplied to improve performance. Otherwise, just clone the \ Mapping pointer. Note we have already saved a pointer to the original \ Mapping so that lower-level functions can use it if they need to report \ an error. */ \ if ( npix > 1024 ) { \ simple = astSimplify( this ); \ } else { \ simple = astClone( this ); \ } \ } \ \ /* Report an error if the forward transformation of this simplified \ Mapping is not defined. */ \ if ( !astGetTranForward( simple ) && astOK ) { \ astError( AST__TRNND, "astRebin"#X"(%s): An forward coordinate " \ "transformation is not defined by the %s supplied.", status, \ astGetClass( unsimplified_mapping ), \ astGetClass( unsimplified_mapping ) ); \ } \ \ /* Report an error if any illegal flags were supplied. */ \ if( flags & AST__REBININIT ) { \ badflag = "AST__REBININIT"; \ } else if( flags & AST__REBINEND ) { \ badflag = "AST__REBINEND"; \ } else if( flags & AST__GENVAR ) { \ badflag = "AST__GENVAR"; \ } else if( flags & AST__DISVAR ) { \ badflag = "AST__DISVAR"; \ } else if( flags & AST__VARWGT ) { \ badflag = "AST__VARWGT"; \ } else if( flags & AST__PARWGT ) { \ badflag = "AST__PARWGT"; \ } else if( flags & AST__NONORM ) { \ badflag = "AST__NONORM"; \ } else if( flags & AST__CONSERVEFLUX ) { \ badflag = "AST__CONSERVEFLUX"; \ } else if( flags & ~( AST__USEBAD + AST__USEVAR ) ) { \ badflag = "unknown"; \ } else { \ badflag = NULL; \ } \ if ( badflag && astOK ) { \ astError( AST__BADFLG, "astRebin"#X"(%s): An illegal flag (%s) " \ "was included in the 'flags' argument.", status, \ astGetClass( unsimplified_mapping ), badflag ); \ } \ \ /* If required, allocate work array to hold the sum of the weights \ contributing to each output pixel, and initialise it to zero. */ \ if( wlim > 0.0 ) { \ work = astMalloc( sizeof( double )*(size_t) npix_out ); \ if( work ) { \ w = work; \ for( ipix_out = 0; ipix_out < npix_out; ipix_out++ ) *(w++) = 0.0; \ } \ } else { \ work = NULL; \ } \ \ /* Initialise the output arrays to hold zeros. */ \ d = out; \ if( out_var ) { \ v = out_var; \ for( ipix_out = 0; ipix_out < npix_out; ipix_out++, d++, v++ ) { \ *d = 0; \ *v = 0; \ } \ } else { \ for( ipix_out = 0; ipix_out < npix_out; ipix_out++, d++ ) { \ *d = 0; \ } \ } \ \ /* Perform the rebinning. Note that we pass all gridded data, the \ spread function and the bad pixel value by means of pointer \ types that obscure the underlying data type. This is to avoid \ having to replicate functions unnecessarily for each data \ type. However, we also pass an argument that identifies the data \ type we have obscured. */ \ if( RebinAdaptively( simple, ndim_in, lbnd_in, ubnd_in, \ (const void *) in, (const void *) in_var, \ TYPE_##X, spread, \ params, flags, tol, maxpix, \ (const void *) &badval, \ ndim_out, lbnd_out, ubnd_out, \ lbnd, ubnd, npix_out, \ (void *) out, (void *) out_var, work, \ NULL, status ) && astOK ) { \ astError( AST__CNFLX, "astRebin"#X"(%s): Flux conservation was " \ "requested but could not be performed because the " \ "forward transformation of the supplied Mapping " \ "is too non-linear.", status, astGetClass( this ) ); \ } \ \ /* If required set output pixels bad if they have a total weight less \ than "wlim". */ \ if( work ) { \ w = work; \ d = out; \ if( out_var ) { \ v = out_var; \ for( ipix_out = 0; ipix_out < npix_out; ipix_out++, d++, w++, v++ ) { \ if( fabs( *w ) < wlim ) { \ *d = badval; \ *v = badval; \ } \ } \ } else { \ for( ipix_out = 0; ipix_out < npix_out; ipix_out++, d++, w++ ) { \ if( fabs( *w ) < wlim ) *d = badval; \ } \ } \ \ /* Free the work array. */ \ work = astFree( work ); \ } \ \ /* Annul the pointer to the simplified/cloned Mapping. */ \ simple = astAnnul( simple ); \ \ } /* Expand the above macro to generate a function for each required data type. */ #if HAVE_LONG_DOUBLE /* Not normally implemented */ MAKE_REBIN(LD,long double,0) #endif MAKE_REBIN(D,double,0) MAKE_REBIN(F,float,0) MAKE_REBIN(I,int,1) MAKE_REBIN(B,signed char,1) MAKE_REBIN(UB,unsigned char,1) /* Undefine the macro. */ #undef MAKE_REBIN static int RebinAdaptively( AstMapping *this, int ndim_in, const int *lbnd_in, const int *ubnd_in, const void *in, const void *in_var, DataType type, int spread, const double *params, int flags, double tol, int maxpix, const void *badval_ptr, int ndim_out, const int *lbnd_out, const int *ubnd_out, const int *lbnd, const int *ubnd, int npix_out, void *out, void *out_var, double *work, int64_t *nused, int *status ){ /* * Name: * RebinAdaptively * Purpose: * Rebin a section of a data grid adaptively. * Type: * Private function. * Synopsis: * #include "mapping.h" * int RebinAdaptively( AstMapping *this, int ndim_in, * const int *lbnd_in, const int *ubnd_in, * const void *in, const void *in_var, * DataType type, int spread, * const double *params, int flags, double tol, * int maxpix, const void *badval_ptr, * int ndim_out, const int *lbnd_out, * const int *ubnd_out, const int *lbnd, * const int *ubnd, int npix_out, void *out, * void *out_var, double *work, int64_t *nused, * int *status ) * Class Membership: * Mapping member function. * Description: * This function rebins a specified section of a rectangular grid of * data (with any number of dimensions) into another rectangular grid * (with a possibly different number of dimensions). The coordinate * transformation used to convert input pixel coordinates into positions * in the output grid is given by the forward transformation of the * Mapping which is supplied. Any pixel spreading scheme may be specified * for distributing the flux of an input pixel amongst the output * pixels. * * This function is very similar to RebinWithBlocking and RebinSection * which lie below it in the calling hierarchy. However, this function * also attempts to adapt to the Mapping supplied and to sub-divide the * section being rebinned into smaller sections within which a linear * approximation to the Mapping may be used. This reduces the number of * Mapping evaluations, thereby improving efficiency particularly when * complicated Mappings are involved. * Parameters: * this * Pointer to a Mapping, whose forward transformation may be * used to transform the coordinates of pixels in the input * grid into associated positions in the output grid. * * The number of input coordintes for the Mapping (Nin * attribute) should match the value of "ndim_in" (below), and * the number of output coordinates (Nout attribute) should * match the value of "ndim_out". * ndim_in * The number of dimensions in the input grid. This should be at * least one. * lbnd_in * Pointer to an array of integers, with "ndim_in" elements. * This should give the coordinates of the centre of the first * pixel in the input data grid along each dimension. * ubnd_in * Pointer to an array of integers, with "ndim_in" elements. * This should give the coordinates of the centre of the last * pixel in the input data grid along each dimension. * * Note that "lbnd_in" and "ubnd_in" together define the shape * and size of the input data grid, its extent along a * particular (i'th) dimension being (ubnd_in[i] - lbnd_in[i] + * 1). They also define the input grid's coordinate system, with * each pixel being of unit extent along each dimension with * integral coordinate values at its centre. * in * Pointer to the input array of data to be rebinned (with one * element for each pixel in the input grid). The numerical type * of these data should match the "type" value (below). The * storage order should be such that the coordinate of the first * dimension varies most rapidly and that of the final dimension * least rapidly (i.e. Fortran array storage order is used). * in_var * An optional pointer to a second array of positive numerical * values (with the same size and data type as the "in" array), * which represent estimates of the statistical variance * associated with each element of the "in" array. If this * second array is given (along with the corresponding "out_var" * array), then estimates of the variance of the rebinned data * will also be returned. * * If no variance estimates are required, a NULL pointer should * be given. * type * A value taken from the "DataType" enum, which specifies the * data type of the input and output arrays containing the * gridded data (and variance) values. * spread * A value selected from a set of pre-defined macros to identify * which pixel spread function should be used. * params * Pointer to an optional array of parameters that may be passed * to the pixel spread algorithm, if required. If no parameters * are required, a NULL pointer should be supplied. * flags * The bitwise OR of a set of flag values which provide additional * control over the resampling operation. * tol * The maximum permitted positional error in transforming input * pixel positions into the output grid in order to rebin * it. This should be expressed as a displacement in pixels in * the output grid's coordinate system. If the Mapping's forward * transformation can be approximated by piecewise linear functions * to this accuracy, then such functions may be used instead of the * Mapping in order to improve performance. Otherwise, every input * pixel position will be transformed individually using the Mapping. * * If linear approximation is not required, a "tol" value of * zero may be given. This will ensure that the Mapping is used * without any approximation. * maxpix * A value which specifies the largest scale size on which to * search for non-linearities in the Mapping supplied. This * value should be expressed as a number of pixels in the input * grid. The function will break the input section specified * into smaller sub-sections (if necessary), each no larger than * "maxpix" pixels in any dimension, before it attempts to * approximate the Mapping by a linear function over each * sub-section. * * If the value given is larger than the largest dimension of * the input section (the normal recommendation), the function * will initially search for non-linearity on a scale determined * by the size of the input section. This is almost always * satisfactory. Very occasionally, however, a Mapping may * appear linear on this scale but nevertheless have smaller * irregularities (e.g. "holes") in it. In such cases, "maxpix" * may be set to a suitably smaller value so as to ensure this * non-linearity is not overlooked. Typically, a value of 50 to * 100 pixels might be suitable and should have little effect on * performance. * * If too small a value is given, however, it will have the * effect of preventing linear approximation occurring at all * (equivalent to setting "tol" to zero). Although this may * degrade performance, accurate results will still be obtained. * badval_ptr * If the AST__USEBAD flag is set (above), this parameter is a * pointer to a value which is used to identify bad data and/or * variance values in the input array(s). The referenced value's * data type must match that of the "in" (and "in_var") * arrays. The same value will also be used to flag any output * array elements for which rebinned values could not be * obtained. The output arrays(s) may be flagged with this * value whether or not the AST__USEBAD flag is set (the * function return value indicates whether any such values have * been produced). * ndim_out * The number of dimensions in the output grid. This should be * at least one. * lbnd_out * Pointer to an array of integers, with "ndim_out" elements. * This should give the coordinates of the centre of the first * pixel in the output data grid along each dimension. * ubnd_out * Pointer to an array of integers, with "ndim_out" elements. * This should give the coordinates of the centre of the last * pixel in the output data grid along each dimension. * * Note that "lbnd_out" and "ubnd_out" together define the shape * and size of the output data grid in the same way as "lbnd_in" * and "ubnd_in" define the shape and size of the input grid * (see above). * lbnd * Pointer to an array of integers, with "ndim_in" elements. * This should give the coordinates of the first pixel in the * section of the input data grid which is to be rebinned. * ubnd * Pointer to an array of integers, with "ndim_in" elements. * This should give the coordinates of the last pixel in the * section of the input data grid which is to be rebinned. * * Note that "lbnd" and "ubnd" define the shape and position of * the section of the input grid which is to be rebinned. This section * should lie wholly within the extent of the input grid (as defined * by the "lbnd_out" and "ubnd_out" arrays). Regions of the input * grid lying outside this section will be ignored. * npix_out * The number of pixels in the output array. * out * Pointer to an array with the same data type as the "in" * array, into which the rebinned data will be returned. The * storage order should be such that the coordinate of the first * dimension varies most rapidly and that of the final dimension * least rapidly (i.e. Fortran array storage order is used). * out_var * An optional pointer to an array with the same data type and * size as the "out" array, into which variance estimates for * the rebinned values may be returned. This array will only be * used if the "in_var" array has been given. * * If no output variance estimates are required, a NULL pointer * should be given. * work * An optional pointer to a double array with the same size as * the "out" array. The contents of this array (if supplied) are * incremented by the accumulated weights assigned to each output pixel. * If no accumulated weights are required, a NULL pointer should be * given. * nused * An optional pointer to a int64_t which will be incremented by the * number of input values pasted into the output array. Ignored if NULL. * status * Pointer to the inherited status variable. * Returned Value: * A non-zero value is returned if "flags" included AST__CONSERVEFLUX (i.e. * flux conservation was requested), but the forward transformation of the * supplied Mapping had zero determinant everywhere within the region * being binned (no error is reported if this happens). Zero is returned * otherwise. */ /* Local Variables: */ double *flbnd; /* Array holding floating point lower bounds */ double *fubnd; /* Array holding floating point upper bounds */ double *linear_fit; /* Pointer to array of fit coefficients */ int *hi; /* Pointer to array of section upper bounds */ int *lo; /* Pointer to array of section lower bounds */ int coord_in; /* Loop counter for input coordinates */ int dim; /* Output section dimension size */ int dimx; /* Dimension with maximum section extent */ int divide; /* Sub-divide the output section? */ int i; /* Loop count */ int isLinear; /* Is the transformation linear? */ int mxdim; /* Largest output section dimension size */ int need_fit; /* Do we need to perform a linear fit? */ int npix; /* Number of pixels in output section */ int npoint; /* Number of points for obtaining a fit */ int nvertex; /* Number of vertices of output section */ int result; /* Returned value */ int res1; /* Flux conservation error in 1st section? */ int res2; /* Flux conservation error in 2nd section? */ int toobig; /* Section too big (must sub-divide)? */ int toosmall; /* Section too small to sub-divide? */ /* Initialise */ result = 0; /* Check the global error status. */ if ( !astOK ) return result; /* Further initialisation. */ npix = 1; mxdim = 0; dimx = 1; nvertex = 1; /* Loop through the input grid dimensions. */ for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { /* Obtain the extent in each dimension of the input section which is to be rebinned, and calculate the total number of pixels it contains. */ dim = ubnd[ coord_in ] - lbnd[ coord_in ] + 1; npix *= dim; /* Find the maximum dimension size of this input section and note which dimension has this size. */ if ( dim > mxdim ) { mxdim = dim; dimx = coord_in; } /* Calculate how many vertices the output section has. */ nvertex *= 2; } /* Calculate how many sample points will be needed (by the astLinearApprox function) to obtain a linear fit to the Mapping's forward transformation. */ npoint = 1 + 4 * ndim_in + 2 * nvertex; /* If the number of pixels in the input section is not at least 4 times this number, we will probably not save significant time by attempting to obtain a linear fit, so note that the input section is too small. */ toosmall = ( npix < ( 4 * npoint ) ); /* Note if the maximum dimension of the input section exceeds the user-supplied scale factor. */ toobig = ( maxpix < mxdim ); /* Indicate we do not yet have a linear fit. */ linear_fit = NULL; /* Initialise a flag indicating if we need to perform a linear fit. This is always the case if flux conservation was requested. */ need_fit = ( flags & AST__CONSERVEFLUX ); /* If the output section is too small to be worth obtaining a linear fit, or if the accuracy tolerance is zero, we will not sub-divide. This means that the Mapping will be used to transform each pixel's coordinates and no linear approximation will be used. */ if ( toosmall || ( tol == 0.0 ) ) { divide = 0; /* Otherwise, if the largest input section dimension exceeds the scale length given, we will sub-divide. This offers the possibility of obtaining a linear approximation to the Mapping over a reduced range of input coordinates (which will be handled by a recursive invocation of this function). */ } else if ( toobig ) { divide = 1; /* If neither of the above apply, we need to do a fit regardless of whether flux conservation was requested or not. Whether we divide or not will depend on whether the Mapping is linear or not. Assume for the moment that the Mapping is not linear and so we will divide. */ } else { need_fit = 1; divide = 1; } /* If required, attempt to fit a linear approximation to the Mapping's forward transformation over the range of coordinates covered by the input section. We need to temporarily copy the integer bounds into floating point arrays to use astLinearApprox. */ if( need_fit ) { /* Allocate memory for floating point bounds and for the coefficient array */ flbnd = astMalloc( sizeof( double )*(size_t) ndim_in ); fubnd = astMalloc( sizeof( double )*(size_t) ndim_in ); linear_fit = astMalloc( sizeof( double )* (size_t) ( ndim_out*( ndim_in + 1 ) ) ); if( astOK ) { /* Copy the bounds into these arrays, and change them so that they refer to the lower and upper edges of the cell rather than the centre. This is essential if one of the axes is spanned by a single cell, since otherwise the upper and lower bounds would be identical. */ for( i = 0; i < ndim_in; i++ ) { flbnd[ i ] = (double) lbnd[ i ] - 0.5; fubnd[ i ] = (double) ubnd[ i ] + 0.5; } /* Get the linear approximation to the forward transformation. */ isLinear = astLinearApprox( this, flbnd, fubnd, tol, linear_fit ); /* Free the coeff array if the inverse transformation is not linear. */ if( !isLinear ) linear_fit = astFree( linear_fit ); } else { linear_fit = astFree( linear_fit ); } /* Free resources */ flbnd = astFree( flbnd ); fubnd = astFree( fubnd ); /* If a linear fit was obtained, we will use it and therefore do not wish to sub-divide further. Otherwise, we sub-divide (unless the section is too small or too big as determined earlier) in the hope that this may result in a linear fit next time. */ if( linear_fit ) divide = 0; } /* If no sub-division is required, perform rebinning (in a memory-efficient manner, since the section we are rebinning might still be very large). This will use the linear fit, if obtained above. */ if ( astOK ) { if ( !divide ) { result = RebinWithBlocking( this, linear_fit, ndim_in, lbnd_in, ubnd_in, in, in_var, type, spread, params, flags, badval_ptr, ndim_out, lbnd_out, ubnd_out, lbnd, ubnd, npix_out, out, out_var, work, nused, status ); /* Otherwise, allocate workspace to perform the sub-division. */ } else { lo = astMalloc( sizeof( int ) * (size_t) ndim_in ); hi = astMalloc( sizeof( int ) * (size_t) ndim_in ); if ( astOK ) { /* Initialise the bounds of a new input section to match the original input section. */ for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { lo[ coord_in ] = lbnd[ coord_in ]; hi[ coord_in ] = ubnd[ coord_in ]; } /* Replace the upper bound of the section's largest dimension with the mid-point of the section along this dimension, rounded downwards. */ hi[ dimx ] = (int) floor( 0.5 * (double) ( lbnd[ dimx ] + ubnd[ dimx ] ) ); /* Rebin the resulting smaller section using a recursive invocation of this function. */ res1 = RebinAdaptively( this, ndim_in, lbnd_in, ubnd_in, in, in_var, type, spread, params, flags, tol, maxpix, badval_ptr, ndim_out, lbnd_out, ubnd_out, lo, hi, npix_out, out, out_var, work, nused, status ); /* Now set up a second section which covers the remaining half of the original input section. */ lo[ dimx ] = hi[ dimx ] + 1; hi[ dimx ] = ubnd[ dimx ]; /* If this section contains pixels, resample it in the same way, summing the returned values. */ if ( lo[ dimx ] <= hi[ dimx ] ) { res2 = RebinAdaptively( this, ndim_in, lbnd_in, ubnd_in, in, in_var, type, spread, params, flags, tol, maxpix, badval_ptr, ndim_out, lbnd_out, ubnd_out, lo, hi, npix_out, out, out_var, work, nused, status ); } else { res2 = 0; } /* If neither section could be rebinned because of an indeterminant mapping, return a result indicating this. */ result = ( res1 && res2 ); } /* Free the workspace. */ lo = astFree( lo ); hi = astFree( hi ); } } /* If coefficients for a linear fit were obtained, then free the space they occupy. */ if ( linear_fit ) linear_fit = astFree( linear_fit ); /* Retyurn a flag indicating if no part of the array could be binned because of an indeterminate Mapping. */ return result; } static void RebinSection( AstMapping *this, const double *linear_fit, int ndim_in, const int *lbnd_in, const int *ubnd_in, const void *in, const void *in_var, double infac, DataType type, int spread, const double *iparams, int flags, const void *badval_ptr, int ndim_out, const int *lbnd_out, const int *ubnd_out, const int *lbnd, const int *ubnd, int npix_out, void *out, void *out_var, double *work, int64_t *nused, int *status ) { /* * Name: * RebinSection * Purpose: * Rebin a section of a data grid. * Type: * Private function. * Synopsis: * #include "mapping.h" * void RebinSection( AstMapping *this, const double *linear_fit, * int ndim_in, const int *lbnd_in, const int *ubnd_in, * const void *in, const void *in_var, double infac, * DataType type, int spread, const double *iparams, * int flags, const void *badval_ptr, int ndim_out, * const int *lbnd_out, const int *ubnd_out, * const int *lbnd, const int *ubnd, int npix_out, * void *out, void *out_var, double *work, * int64_t *nused, int *status ) * Class Membership: * Mapping member function. * Description: * This function rebins a specified section of a rectangular grid of * data (with any number of dimensions) into another rectangular grid * (with a possibly different number of dimensions). The coordinate * transformation used to convert input pixel coordinates into positions * in the output grid is given by the forward transformation of the * Mapping which is supplied or, alternatively, by a linear approximation * fitted to a Mapping's forward transformation. Any pixel spreading scheme * may be specified for distributing the flux of an input pixel amongst * the output pixels. * Parameters: * this * Pointer to a Mapping, whose forward transformation may be * used to transform the coordinates of pixels in the input * grid into associated positions in the output grid. * * The number of input coordintes for the Mapping (Nin * attribute) should match the value of "ndim_in" (below), and * the number of output coordinates (Nout attribute) should * match the value of "ndim_out". * linear_fit * Pointer to an optional array of double which contains the * coefficients of a linear fit which approximates the above * Mapping's forward coordinate transformation. If this is * supplied, it will be used in preference to the above Mapping * when transforming coordinates. This may be used to enhance * performance in cases where evaluation of the Mapping's * forward transformation is expensive. If no linear fit is * available, a NULL pointer should be supplied. * * The way in which the fit coefficients are stored in this * array and the number of array elements are as defined by the * astLinearApprox function. * ndim_in * The number of dimensions in the input grid. This should be at * least one. * lbnd_in * Pointer to an array of integers, with "ndim_in" elements. * This should give the coordinates of the centre of the first * pixel in the input data grid along each dimension. * ubnd_in * Pointer to an array of integers, with "ndim_in" elements. * This should give the coordinates of the centre of the last * pixel in the input data grid along each dimension. * * Note that "lbnd_in" and "ubnd_in" together define the shape * and size of the input data grid, its extent along a * particular (i'th) dimension being (ubnd_in[i] - lbnd_in[i] + * 1). They also define the input grid's coordinate system, with * each pixel being of unit extent along each dimension with * integral coordinate values at its centre. * in * Pointer to the input array of data to be rebinned (with one * element for each pixel in the input grid). The numerical type * of these data should match the "type" value (below). The * storage order should be such that the coordinate of the first * dimension varies most rapidly and that of the final dimension * least rapidly (i.e. Fortran array storage order is used). * in_var * An optional pointer to a second array of positive numerical * values (with the same size and data type as the "in" array), * which represent estimates of the statistical variance * associated with each element of the "in" array. If this * second array is given (along with the corresponding "out_var" * array), then estimates of the variance of the rebinned data * will also be returned. * * If no variance estimates are required, a NULL pointer should * be given. * infac * A factor by which to multiply the input data values before use. * type * A value taken from the "DataType" enum, which specifies the * data type of the input and output arrays containing the * gridded data (and variance) values. * spread * A value selected from a set of pre-defined macros to identify * which pixel spread function should be used. * iparams * Pointer to an optional array of parameters that may be passed * to the pixel spread algorithm, if required. If no parameters * are required, a NULL pointer should be supplied. * flags * The bitwise OR of a set of flag values which provide additional * control over the resampling operation. * badval_ptr * If the AST__USEBAD flag is set (above), this parameter is a * pointer to a value which is used to identify bad data and/or * variance values in the input array(s). The referenced value's * data type must match that of the "in" (and "in_var") * arrays. The same value will also be used to flag any output * array elements for which rebinned values could not be * obtained. The output arrays(s) may be flagged with this * value whether or not the AST__USEBAD flag is set (the * function return value indicates whether any such values have * been produced). * ndim_out * The number of dimensions in the output grid. This should be * at least one. * lbnd_out * Pointer to an array of integers, with "ndim_out" elements. * This should give the coordinates of the centre of the first * pixel in the output data grid along each dimension. * ubnd_out * Pointer to an array of integers, with "ndim_out" elements. * This should give the coordinates of the centre of the last * pixel in the output data grid along each dimension. * * Note that "lbnd_out" and "ubnd_out" together define the shape * and size of the output data grid in the same way as "lbnd_in" * and "ubnd_in" define the shape and size of the input grid * (see above). * lbnd * Pointer to an array of integers, with "ndim_in" elements. * This should give the coordinates of the first pixel in the * section of the input data grid which is to be rebinned. * ubnd * Pointer to an array of integers, with "ndim_in" elements. * This should give the coordinates of the last pixel in the * section of the input data grid which is to be rebinned. * * Note that "lbnd" and "ubnd" define the shape and position of * the section of the input grid which is to be rebinned. This section * should lie wholly within the extent of the input grid (as defined * by the "lbnd_out" and "ubnd_out" arrays). Regions of the input * grid lying outside this section will be ignored. * npix_out * The number of pixels in the output array. * out * Pointer to an array with the same data type as the "in" * array, into which the rebinned data will be returned. The * storage order should be such that the coordinate of the first * dimension varies most rapidly and that of the final dimension * least rapidly (i.e. Fortran array storage order is used). * out_var * An optional pointer to an array with the same data type and * size as the "out" array, into which variance estimates for * the rebinned values may be returned. This array will only be * used if the "in_var" array has been given. * * If no output variance estimates are required, a NULL pointer * should be given. * work * An optional pointer to a double array with the same size as * the "out" array. The contents of this array (if supplied) are * incremented by the accumulated weights assigned to each output pixel. * If no accumulated weights are required, a NULL pointer should be * given. * nused * An optional pointer to a int64_t which will be incremented by the * number of input values pasted into the output array. Ignored if NULL. * Notes: * - This function does not take steps to limit memory usage if the * grids supplied are large. To resample large grids in a more * memory-efficient way, the ResampleWithBlocking function should * be used. */ /* Local Variables: */ astDECLARE_GLOBALS /* Thread-specific data */ AstPointSet *pset_in; /* Input PointSet for transformation */ AstPointSet *pset_out; /* Output PointSet for transformation */ const double *params; /* Pointer to spreading scheme parameters */ const double *grad; /* Pointer to gradient matrix of linear fit */ const double *zero; /* Pointer to zero point array of fit */ double **ptr_in; /* Pointer to input PointSet coordinates */ double **ptr_out; /* Pointer to output PointSet coordinates */ double *accum; /* Pointer to array of accumulated sums */ double conwgt; /* Constant weight for all pixels */ double x1; /* Interim x coordinate value */ double xx1; /* Initial x coordinate value */ double y1; /* Interim y coordinate value */ double yy1; /* Initial y coordinate value */ int *dim; /* Pointer to array of output pixel indices */ int *offset; /* Pointer to array of output pixel offsets */ int *stride; /* Pointer to array of output grid strides */ int coord_in; /* Loop counter for input dimensions */ int coord_out; /* Loop counter for output dimensions */ int done; /* All pixel indices done? */ int i1; /* Interim offset into "accum" array */ int i2; /* Final offset into "accum" array */ int idim; /* Loop counter for dimensions */ int ix; /* Loop counter for output x coordinate */ int iy; /* Loop counter for output y coordinate */ int neighb; /* Number of neighbouring pixels */ int npoint; /* Number of output points (pixels) */ int off1; /* Interim pixel offset into output array */ int off2; /* Interim pixel offset into output array */ int off; /* Final pixel offset into output array */ int point; /* Counter for output points (pixels ) */ int s; /* Temporary variable for strides */ const double *par; /* Pointer to parameter array */ double fwhm; /* Full width half max. of gaussian */ double lpar[ 1 ]; /* Local parameter array */ void (* kernel)( double, const double [], int, double *, int * ); /* Kernel fn. */ /* Check the global error status. */ if ( !astOK ) return; /* Get a pointer to a structure holding thread-specific global data values */ astGET_GLOBALS(this); /* Further initialisation. */ pset_in = NULL; ptr_in = NULL; ptr_out = NULL; pset_out = NULL; neighb = 0; kernel = NULL; /* If a constant weight is to be factored in to all pixels, it will have been supplied as the first value in the "params" array, with the remaining values being the actual parameters of the requested spreading scheme. Copy the constant weight into another value and modify the pointer to the start of the params array to exclude it. */ if( flags & AST__PARWGT ) { params = iparams + 1; conwgt = iparams[ 0 ]; } else { params = iparams; conwgt = 1.0; } /* Calculate the number of input points, as given by the product of the input grid dimensions. */ for ( npoint = 1, coord_in = 0; coord_in < ndim_in; coord_in++ ) { npoint *= ubnd[ coord_in ] - lbnd[ coord_in ] + 1; } /* Allocate workspace. */ offset = astMalloc( sizeof( int ) * (size_t) npoint ); stride = astMalloc( sizeof( int ) * (size_t) ndim_in ); if ( astOK ) { /* Calculate the stride for each input grid dimension. */ off = 0; s = 1; for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { stride[ coord_in ] = s; s *= ubnd_in[ coord_in ] - lbnd_in[ coord_in ] + 1; } /* A linear fit to the Mapping is available. */ /* ========================================= */ if ( linear_fit ) { /* If a linear fit to the Mapping has been provided, then obtain pointers to the array of gradients and zero-points comprising the fit. */ grad = linear_fit + ndim_out; zero = linear_fit; /* Create a PointSet to hold the output grid coordinates and obtain an array of pointers to its coordinate data. */ pset_out = astPointSet( npoint, ndim_out, "", status ); ptr_out = astGetPoints( pset_out ); if ( astOK ) { /* Initialise the count of input points. */ point = 0; /* Handle the 1-dimensional case optimally. */ /* ---------------------------------------- */ if ( ( ndim_in == 1 ) && ( ndim_out == 1 ) ) { /* Loop through the pixels of the input grid and transform their x coordinates into the output grid's coordinate system using the linear fit supplied. Store the results in the PointSet created above. */ off = lbnd[ 0 ] - lbnd_in[ 0 ]; xx1 = zero[ 0 ] + grad[ 0 ] * (double) lbnd[ 0 ]; for ( ix = lbnd[ 0 ]; ix <= ubnd[ 0 ]; ix++ ) { ptr_out[ 0 ][ point ] = xx1; xx1 += grad[ 0 ]; offset[ point++ ] = off++; } /* Handle the 2-dimensional case optimally. */ /* ---------------------------------------- */ } else if ( ( ndim_in == 2 ) && ( ndim_out == 2 ) ) { /* Loop through the range of y coordinates in the input grid and calculate interim values of the output coordinates using the linear fit supplied. */ x1 = zero[ 0 ] + grad[ 1 ] * (double) ( lbnd[ 1 ] - 1 ); y1 = zero[ 1 ] + grad[ 3 ] * (double) ( lbnd[ 1 ] - 1 ); off1 = stride[ 1 ] * ( lbnd[ 1 ] - lbnd_in[ 1 ] - 1 ) - lbnd_in[ 0 ]; for ( iy = lbnd[ 1 ]; iy <= ubnd[ 1 ]; iy++ ) { x1 += grad[ 1 ]; y1 += grad[ 3 ]; /* Also calculate an interim pixel offset into the input array. */ off1 += stride[ 1 ]; /* Now loop through the range of input x coordinates and calculate the final values of the input coordinates, storing the results in the PointSet created above. */ xx1 = x1 + grad[ 0 ] * (double) lbnd[ 0 ]; yy1 = y1 + grad[ 2 ] * (double) lbnd[ 0 ]; off = off1 + lbnd[ 0 ]; for ( ix = lbnd[ 0 ]; ix <= ubnd[ 0 ]; ix++ ) { ptr_out[ 0 ][ point ] = xx1; xx1 += grad[ 0 ]; ptr_out[ 1 ][ point ] = yy1; yy1 += grad[ 2 ]; /* Also calculate final pixel offsets into the input array. */ offset[ point++ ] = off++; } } /* Handle other numbers of dimensions. */ /* ----------------------------------- */ } else { /* Allocate workspace. */ accum = astMalloc( sizeof( double ) * (size_t) ( ndim_in * ndim_out ) ); dim = astMalloc( sizeof( int ) * (size_t) ndim_in ); if ( astOK ) { /* Initialise an array of pixel indices for the input grid which refer to the first pixel which we will rebin. Also calculate the offset of this pixel within the input array. */ off = 0; for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { dim[ coord_in ] = lbnd[ coord_in ]; off += stride[ coord_in ] * ( dim[ coord_in ] - lbnd_in[ coord_in ] ); } /* To calculate each output grid coordinate we must perform a matrix multiply on the input grid coordinates (using the gradient matrix) and then add the zero points. However, since we will usually only be altering one input coordinate at a time (the least significant), we can avoid the full matrix multiply by accumulating partial sums for the most significant input coordinates and only altering those sums which need to change each time. The zero points never change, so we first fill the "most significant" end of the "accum" array with these. */ for ( coord_out = 0; coord_out < ndim_out; coord_out++ ) { accum[ ( coord_out + 1 ) * ndim_in - 1 ] = zero[ coord_out ]; } coord_in = ndim_in - 1; /* Now loop to process each input pixel. */ for ( done = 0; !done; point++ ) { /* To generate the output coordinate that corresponds to the current input pixel, we work down from the most significant dimension whose index has changed since the previous pixel we considered (given by "coord_in"). For each affected dimension, we accumulate in "accum" the matrix sum (including the zero point) for that dimension and all higher input dimensions. We must accumulate a separate set of sums for each output coordinate we wish to produce. (Note that for the first pixel we process, all dimensions are considered "changed", so we start by initialising the whole "accum" array.) */ for ( coord_out = 0; coord_out < ndim_out; coord_out++ ) { /* ptr_out[ coord_out ][ point ] = zero[ coord_out ]; for ( idim = 0; idim < ndim_in; idim++ ) { ptr_out[ coord_out ][ point ] += grad[ idim + coord_out*ndim_in ] * dim[ idim ]; } */ i1 = coord_out * ndim_in; for ( idim = coord_in; idim >= 1; idim-- ) { i2 = i1 + idim; accum[ i2 - 1 ] = accum[ i2 ] + dim[ idim ] * grad[ i2 ]; } /* The output coordinate for each dimension is given by the accumulated sum for input dimension zero (giving the sum over all input dimensions). We do not store this in the "accum" array, but assign the result directly to the coordinate array of the PointSet created earlier. */ ptr_out[ coord_out ][ point ] = accum[ i1 ] + dim[ 0 ] * grad[ i1 ]; } /* Store the offset of the current pixel in the input array. */ offset[ point ] = off; /* Now update the array of pixel indices to refer to the next input pixel. */ coord_in = 0; do { /* The least significant index which currently has less than its maximum value is incremented by one. The offset into the input array is updated accordingly. */ if ( dim[ coord_in ] < ubnd[ coord_in ] ) { dim[ coord_in ]++; off += stride[ coord_in ]; break; /* Any less significant indices which have reached their maximum value are returned to their minimum value and the input pixel offset is decremented appropriately. */ } else { dim[ coord_in ] = lbnd[ coord_in ]; off -= stride[ coord_in ] * ( ubnd[ coord_in ] - lbnd[ coord_in ] ); /* All the output pixels have been processed once the most significant pixel index has been returned to its minimum value. */ done = ( ++coord_in == ndim_in ); } } while ( !done ); } } /* Free the workspace. */ accum = astFree( accum ); dim = astFree( dim ); } } /* No linear fit to the Mapping is available. */ /* ========================================== */ } else { /* Create a PointSet to hold the coordinates of the input pixels and obtain a pointer to its coordinate data. */ pset_in = astPointSet( npoint, ndim_in, "", status ); ptr_in = astGetPoints( pset_in ); if ( astOK ) { /* Initialise the count of input points. */ point = 0; /* Handle the 1-dimensional case optimally. */ /* ---------------------------------------- */ if ( ndim_in == 1 && ndim_out == 1 ) { /* Loop through the required range of input x coordinates, assigning the coordinate values to the PointSet created above. Also store a pixel offset into the input array. */ for ( ix = lbnd[ 0 ]; ix <= ubnd[ 0 ]; ix++ ) { ptr_in[ 0 ][ point ] = (double) ix; offset[ point++ ] = ix - lbnd_in[ 0 ]; } /* Handle the 2-dimensional case optimally. */ /* ---------------------------------------- */ } else if ( ndim_in == 2 && ndim_out == 2) { /* Loop through the required range of input y coordinates, calculating an interim pixel offset into the input array. */ off1 = stride[ 1 ] * ( lbnd[ 1 ] - lbnd_in[ 1 ] - 1 ) - lbnd_in[ 0 ]; for ( iy = lbnd[ 1 ]; iy <= ubnd[ 1 ]; iy++ ) { off1 += stride[ 1 ]; /* Loop through the required range of input x coordinates, assigning the coordinate values to the PointSet created above. Also store a final pixel offset into the input array. */ off2 = off1 + lbnd[ 0 ]; for ( ix = lbnd[ 0 ]; ix <= ubnd[ 0 ]; ix++ ) { ptr_in[ 0 ][ point ] = (double) ix; ptr_in[ 1 ][ point ] = (double) iy; offset[ point++ ] = off2++; } } /* Handle other numbers of dimensions. */ /* ----------------------------------- */ } else { /* Allocate workspace. */ dim = astMalloc( sizeof( int ) * (size_t) ndim_in ); if ( astOK ) { /* Initialise an array of pixel indices for the input grid which refer to the first pixel to be rebinned. Also calculate the offset of this pixel within the input array. */ off = 0; for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { dim[ coord_in ] = lbnd[ coord_in ]; off += stride[ coord_in ] * ( dim[ coord_in ] - lbnd_in[ coord_in ] ); } /* Loop to generate the coordinates of each input pixel. */ for ( done = 0; !done; point++ ) { /* Copy each pixel's coordinates into the PointSet created above. */ for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { ptr_in[ coord_in ][ point ] = (double) dim[ coord_in ]; } /* Store the offset of the pixel in the input array. */ offset[ point ] = off; /* Now update the array of pixel indices to refer to the next input pixel. */ coord_in = 0; do { /* The least significant index which currently has less than its maximum value is incremented by one. The offset into the input array is updated accordingly. */ if ( dim[ coord_in ] < ubnd[ coord_in ] ) { dim[ coord_in ]++; off += stride[ coord_in ]; break; /* Any less significant indices which have reached their maximum value are returned to their minimum value and the input pixel offset is decremented appropriately. */ } else { dim[ coord_in ] = lbnd[ coord_in ]; off -= stride[ coord_in ] * ( ubnd[ coord_in ] - lbnd[ coord_in ] ); /* All the input pixels have been processed once the most significant pixel index has been returned to its minimum value. */ done = ( ++coord_in == ndim_in ); } } while ( !done ); } } /* Free the workspace. */ dim = astFree( dim ); } /* When all the input pixel coordinates have been generated, use the Mapping's forward transformation to generate the output coordinates from them. Obtain an array of pointers to the resulting coordinate data. */ pset_out = astTransform( this, pset_in, 1, NULL ); ptr_out = astGetPoints( pset_out ); } /* Annul the PointSet containing the input coordinates. */ pset_in = astAnnul( pset_in ); } } /* Rebin the input grid. */ /* ------------------------ */ if( astOK ) { /* Identify the pixel spreading scheme to be used. */ /* Nearest pixel. */ /* -------------- */ switch ( spread ) { case AST__NEAREST: /* Define a macro to use a "case" statement to invoke the nearest-pixel spreading function appropriate to a given data type. */ #define CASE_NEAREST(X,Xtype) \ case ( TYPE_##X ): \ SpreadNearest##X( ndim_out, lbnd_out, ubnd_out, \ (Xtype *) in, (Xtype *) in_var, \ infac, npoint, offset, \ (const double *const *) ptr_out, \ conwgt, flags, *( (Xtype *) badval_ptr ), \ npix_out, (Xtype *) out, \ (Xtype *) out_var, work, nused, status ); \ break; /* Use the above macro to invoke the appropriate function. */ switch ( type ) { #if HAVE_LONG_DOUBLE /* Not normally implemented */ CASE_NEAREST(LD,long double) #endif CASE_NEAREST(D,double) CASE_NEAREST(F,float) CASE_NEAREST(I,int) CASE_NEAREST(B,signed char) CASE_NEAREST(UB,unsigned char) case ( TYPE_L ): break; case ( TYPE_K ): break; case ( TYPE_S ): break; case ( TYPE_UL ): break; case ( TYPE_UI ): break; case ( TYPE_UK ): break; case ( TYPE_US ): break; } break; /* Undefine the macro. */ #undef CASE_NEAREST /* Linear spreading. */ /* ----------------- */ /* Note this is also the default if zero is given. */ case AST__LINEAR: case 0: /* Define a macro to use a "case" statement to invoke the linear spreading function appropriate to a given data type. */ #define CASE_LINEAR(X,Xtype) \ case ( TYPE_##X ): \ SpreadLinear##X( ndim_out, lbnd_out, ubnd_out,\ (Xtype *) in, (Xtype *) in_var, \ infac, npoint, offset, \ (const double *const *) ptr_out, \ conwgt, flags, *( (Xtype *) badval_ptr ), \ npix_out, (Xtype *) out, \ (Xtype *) out_var, work, nused, status ); \ break; /* Use the above macro to invoke the appropriate function. */ switch ( type ) { #if HAVE_LONG_DOUBLE /* Not normally implemented */ CASE_LINEAR(LD,long double) #endif CASE_LINEAR(D,double) CASE_LINEAR(F,float) CASE_LINEAR(I,int) CASE_LINEAR(B,signed char) CASE_LINEAR(UB,unsigned char) case ( TYPE_L ): break; case ( TYPE_K ): break; case ( TYPE_S ): break; case ( TYPE_UL ): break; case ( TYPE_UI ): break; case ( TYPE_UK ): break; case ( TYPE_US ): break; } break; /* Undefine the macro. */ #undef CASE_LINEAR /* Spreading using a 1-d kernel. */ /* ----------------------------- */ case AST__SINC: case AST__SINCCOS: case AST__SINCGAUSS: case AST__GAUSS: case AST__SINCSINC: case AST__SOMB: case AST__SOMBCOS: /* Obtain a pointer to the appropriate 1-d kernel function (either internal or user-defined) and set up any parameters it may require. */ par = NULL; switch ( spread ) { /* sinc(pi*x) */ /* ---------- */ /* Assign the kernel function. */ case AST__SINC: kernel = Sinc; /* Calculate the number of neighbouring pixels to use. */ neighb = (int) floor( params[ 0 ] + 0.5 ); if ( neighb <= 0 ) { neighb = 2; } else { neighb = MaxI( 1, neighb, status ); } break; /* somb(pi*x) */ /* ---------- */ /* Assign the kernel function. */ case AST__SOMB: kernel = Somb; /* Calculate the number of neighbouring pixels to use. */ neighb = (int) floor( params[ 0 ] + 0.5 ); if ( neighb <= 0 ) { neighb = 2; } else { neighb = MaxI( 1, neighb, status ); } break; /* sinc(pi*x)*cos(k*pi*x) */ /* ---------------------- */ /* Assign the kernel function. */ case AST__SINCCOS: kernel = SincCos; /* Store the required value of "k" in a local parameter array and pass this array to the kernel function. */ lpar[ 0 ] = 0.5 / MaxD( 1.0, params[ 1 ], status ); par = lpar; /* Obtain the number of neighbouring pixels to use. If this is zero or less, the number will be calculated automatically below. */ neighb = (int) floor( params[ 0 ] + 0.5 ); if ( neighb <= 0 ) neighb = INT_MAX; /* Calculate the maximum number of neighbouring pixels required by the width of the kernel, and use this value if preferable. */ neighb = MinI( neighb, (int) ceil( MaxD( 1.0, params[ 1 ], status ) ), status ); break; /* sinc(pi*x)*exp(-k*x*x) */ /* ---------------------- */ /* Assign the kernel function. */ case AST__SINCGAUSS: kernel = SincGauss; /* Constrain the full width half maximum of the gaussian factor. */ fwhm = MaxD( 0.1, params[ 1 ], status ); /* Store the required value of "k" in a local parameter array and pass this array to the kernel function. */ lpar[ 0 ] = 4.0 * log( 2.0 ) / ( fwhm * fwhm ); par = lpar; /* Obtain the number of neighbouring pixels to use. If this is zero or less, use the number of neighbouring pixels required by the width of the kernel (out to where the gaussian term falls to 1% of its peak value). */ neighb = (int) floor( params[ 0 ] + 0.5 ); if ( neighb <= 0 ) neighb = (int) ceil( sqrt( -log( 0.01 ) / lpar[ 0 ] ) ); break; /* exp(-k*x*x) */ /* ----------- */ /* Assign the kernel function. */ case AST__GAUSS: kernel = Gauss; /* Constrain the full width half maximum of the gaussian. */ fwhm = MaxD( 0.1, params[ 1 ], status ); /* Store the required value of "k" in a local parameter array and pass this array to the kernel function. */ lpar[ 0 ] = 4.0 * log( 2.0 ) / ( fwhm * fwhm ); par = lpar; /* Obtain the number of neighbouring pixels to use. If this is zero or less, use the number of neighbouring pixels required by the width of the kernel (out to where the gaussian term falls to 1% of its peak value). */ neighb = (int) floor( params[ 0 ] + 0.5 ); if ( neighb <= 0 ) neighb = (int) ceil( sqrt( -log( 0.01 ) / lpar[ 0 ] ) ); break; /* somb(pi*x)*cos(k*pi*x) */ /* ---------------------- */ /* Assign the kernel function. */ case AST__SOMBCOS: kernel = SombCos; /* Store the required value of "k" in a local parameter array and pass this array to the kernel function. */ lpar[ 0 ] = 0.5 / MaxD( 1.0, params[ 1 ], status ); par = lpar; /* Obtain the number of neighbouring pixels to use. If this is zero or less, the number will be calculated automatically below. */ neighb = (int) floor( params[ 0 ] + 0.5 ); if ( neighb <= 0 ) neighb = INT_MAX; /* Calculate the maximum number of neighbouring pixels required by the width of the kernel, and use this value if preferable. */ neighb = MinI( neighb, (int) ceil( MaxD( 1.0, params[ 1 ], status ) ), status ); break; /* sinc(pi*x)*sinc(k*pi*x) */ /* ----------------------- */ /* Assign the kernel function. */ case AST__SINCSINC: kernel = SincSinc; /* Store the required value of "k" in a local parameter array and pass this array to the kernel function. */ lpar[ 0 ] = 0.5 / MaxD( 1.0, params[ 1 ], status ); par = lpar; /* Obtain the number of neighbouring pixels to use. If this is zero or less, the number will be calculated automatically below. */ neighb = (int) floor( params[ 0 ] + 0.5 ); if ( neighb <= 0 ) neighb = INT_MAX; /* Calculate the maximum number of neighbouring pixels required by the width of the kernel, and use this value if preferable. */ neighb = MinI( neighb, (int) ceil( MaxD( 1.0, params[ 1 ], status ) ), status ); break; } /* Define a macro to use a "case" statement to invoke the 1-d kernel interpolation function appropriate to a given data type, passing it the pointer to the kernel function obtained above. */ #define CASE_KERNEL1(X,Xtype) \ case ( TYPE_##X ): \ SpreadKernel1##X( this, ndim_out, lbnd_out, ubnd_out, \ (Xtype *) in, (Xtype *) in_var, \ infac, npoint, offset, \ (const double *const *) ptr_out, \ kernel, neighb, par, conwgt, flags, \ *( (Xtype *) badval_ptr ), \ npix_out, (Xtype *) out, \ (Xtype *) out_var, work, nused, \ status ); \ break; /* Use the above macro to invoke the appropriate function. */ switch ( type ) { #if HAVE_LONG_DOUBLE /* Not normally implemented */ CASE_KERNEL1(LD,long double) #endif CASE_KERNEL1(D,double) CASE_KERNEL1(F,float) CASE_KERNEL1(I,int) CASE_KERNEL1(B,signed char) CASE_KERNEL1(UB,unsigned char) case ( TYPE_L ): break; case ( TYPE_K ): break; case ( TYPE_S ): break; case ( TYPE_UL ): break; case ( TYPE_UI ): break; case ( TYPE_UK ): break; case ( TYPE_US ): break; } break; /* Undefine the macro. */ #undef CASE_KERNEL1 /* Error: invalid pixel spreading scheme specified. */ /* ------------------------------------------------ */ default: /* Define a macro to report an error message appropriate to a given data type. */ #define CASE_ERROR(X) \ case TYPE_##X: \ astError( AST__SISIN, "astRebin"#X"(%s): Invalid " \ "pixel spreading scheme (%d) specified.", status, \ astGetClass( unsimplified_mapping ), spread ); \ break; /* Use the above macro to report an appropriate error message. */ switch ( type ) { #if HAVE_LONG_DOUBLE /* Not normally implemented */ CASE_ERROR(LD) #endif CASE_ERROR(D) CASE_ERROR(F) CASE_ERROR(I) CASE_ERROR(B) CASE_ERROR(UB) case ( TYPE_L ): break; case ( TYPE_K ): break; case ( TYPE_S ): break; case ( TYPE_UL ): break; case ( TYPE_UI ): break; case ( TYPE_UK ): break; case ( TYPE_US ): break; } break; /* Undefine the macro. */ #undef CASE_ERROR } } /* Annul the PointSet used to hold output coordinates. */ pset_out = astAnnul( pset_out ); /* Free the workspace. */ offset = astFree( offset ); stride = astFree( stride ); } /* *++ * Name: c astRebinSeq f AST_REBINSEQ * Purpose: * Rebin a region of a sequence of data grids. * Type: * Public virtual function. * Synopsis: c #include "mapping.h" c void astRebinSeq( AstMapping *this, double wlim, int ndim_in, c const int lbnd_in[], const int ubnd_in[], c const in[], const in_var[], c int spread, const double params[], int flags, c double tol, int maxpix, badval, c int ndim_out, const int lbnd_out[], c const int ubnd_out[], const int lbnd[], c const int ubnd[], out[], out_var[], c double weights[], int64_t *nused ); f CALL AST_REBINSEQ( THIS, WLIM, NDIM_IN, LBND_IN, UBND_IN, IN, IN_VAR, f SPREAD, PARAMS, FLAGS, TOL, MAXPIX, BADVAL, f NDIM_OUT, LBND_OUT, UBND_OUT, LBND, UBND, OUT, f OUT_VAR, WEIGHTS, NUSED, STATUS ) * Class Membership: * Mapping method. * Description: * This set of c functions is identical to astRebin f routines is identical to AST_REBIN * except that the rebinned input data is added into the supplied * output arrays, rather than simply over-writing the contents of the * output arrays. Thus, by calling this c function f routine * repeatedly, a sequence of input arrays can be rebinned and accumulated * into a single output array, effectively forming a mosaic of the * input data arrays. * * In addition, the weights associated with each output pixel are * returned. The weight of an output pixel indicates the number of input * pixels which have been accumulated in that output pixel. If the entire * value of an input pixel is assigned to a single output pixel, then the * weight of that output pixel is incremented by one. If some fraction of * the value of an input pixel is assigned to an output pixel, then the * weight of that output pixel is incremented by the fraction used. * * The start of a new sequence is indicated by specifying the * AST__REBININIT flag via the c "flags" parameter. f FLAGS argument. * This causes the supplied arrays to be filled with zeros before the * rebinned input data is added into them. Subsequenct invocations * within the same sequence should omit the AST__REBININIT flag. * * The last call in a sequence is indicated by specifying the * AST__REBINEND flag. Depending on which flags are supplied, this may * cause the output data and variance arrays to be normalised before * being returned. This normalisation consists of dividing the data * array by the weights array, and can eliminate artifacts which may be * introduced into the rebinned data as a consequence of aliasing * between the input and output grids. This results in each output * pixel value being the weighted mean of the input pixel values that * fall in the neighbourhood of the output pixel (rather like c astResample). f AST_RESAMPLE). * Optionally, these normalised * values can then be multiplied by a scaling factor to ensure that the * total data sum in any small area is unchanged. This scaling factor * is equivalent to the number of input pixel values that fall into each * output pixel. In addition to * normalisation of the output data values, any output variances are * also appropriately normalised, and any output data values with * weight less than c "wlim" are set to "badval". f WLIM are set to BADVAL. * * Output variances can be generated in two ways; by rebinning the supplied * input variances with appropriate weights, or by finding the spread of * input data values contributing to each output pixel (see the AST__GENVAR * and AST__USEVAR flags). * Parameters: c this f THIS = INTEGER (Given) * Pointer to a Mapping, whose forward transformation will be * used to transform the coordinates of pixels in the input * grid into the coordinate system of the output grid. * * The number of input coordinates used by this Mapping (as * given by its Nin attribute) should match the number of input c grid dimensions given by the value of "ndim_in" f grid dimensions given by the value of NDIM_IN * below. Similarly, the number of output coordinates (Nout * attribute) should match the number of output grid dimensions c given by "ndim_out". f given by NDIM_OUT. c If "in" is NULL, the Mapping will not be used, but a valid c Mapping must still be supplied. c wlim f WLIM = DOUBLE PRECISION (Given) * This value is only used if the AST__REBINEND flag is specified * via the c "flags" parameter. f FLAGS argument. * It gives the required number of input pixel values which must * contribute to an output pixel (i.e. the output pixel weight) in * order for the output pixel value to be considered valid. If the sum * of the input pixel weights contributing to an output pixel is less * than the supplied c "wlim" f WLIM * value, then the output pixel value is returned set to the * supplied bad value. If the supplied value is less than 1.0E-10 * then 1.0E-10 is used instead. c ndim_in f NDIM_IN = INTEGER (Given) * The number of dimensions in the input grid. This should be at * least one. c Not used if "in" is NULL. c lbnd_in f LBND_IN( NDIM_IN ) = INTEGER (Given) c Pointer to an array of integers, with "ndim_in" elements, f An array * containing the coordinates of the centre of the first pixel * in the input grid along each dimension. c Not used if "in" is NULL. c ubnd_in f UBND_IN( NDIM_IN ) = INTEGER (Given) c Pointer to an array of integers, with "ndim_in" elements, f An array * containing the coordinates of the centre of the last pixel in * the input grid along each dimension. * c Note that "lbnd_in" and "ubnd_in" together define the shape f Note that LBND_IN and UBND_IN together define the shape * and size of the input grid, its extent along a particular c (j'th) dimension being ubnd_in[j]-lbnd_in[j]+1 (assuming the c index "j" to be zero-based). They also define f (J'th) dimension being UBND_IN(J)-LBND_IN(J)+1. They also define * the input grid's coordinate system, each pixel having unit * extent along each dimension with integral coordinate values * at its centre. c Not used if "in" is NULL. c in f IN( * ) = (Given) c Pointer to an array, with one element for each pixel in the f An array, with one element for each pixel in the * input grid, containing the input data to be rebined. The * numerical type of this array should match the 1- or * 2-character type code appended to the function name (e.g. if c you are using astRebinSeqF, the type of each array element c should be "float"). f you are using AST_REBINSEQR, the type of each array element f should be REAL). * * The storage order of data within this array should be such * that the index of the first grid dimension varies most * rapidly and that of the final dimension least rapidly c (i.e. Fortran array indexing is used). f (i.e. normal Fortran array storage order). c If a NULL pointer is supplied for "in", then no data is added to c the output arrays, but any initialisation or normalisation c requested by "flags" is still performed. c in_var f IN_VAR( * ) = (Given) * An optional c pointer to a * second array with the same size and type as the c "in" f IN * array. If given, this should contain a set of non-negative values * which represent estimates of the statistical variance associated * with each element of the c "in" f IN * array. * If neither the AST__USEVAR nor the AST__VARWGT flag is set, no * input variance estimates are required and this f array c pointer * will not be used. f A dummy (e.g. one-element) array c A NULL pointer * may then be supplied. c spread f SPREAD = INTEGER (Given) c This parameter specifies the scheme to be used for dividing f This argument specifies the scheme to be used for dividing * each input data value up amongst the corresponding output pixels. * It may be used to select * from a set of pre-defined schemes by supplying one of the * values described in the "Pixel Spreading Schemes" * section in the description of the c astRebin functions. f AST_REBIN routines. * If a value of zero is supplied, then the default linear spreading * scheme is used (equivalent to supplying the value AST__LINEAR). c Not used if "in" is NULL. c params f PARAMS( * ) = DOUBLE PRECISION (Given) c An optional pointer to an array of double which should contain f An optional array which should contain * any additional parameter values required by the pixel * spreading scheme. If such parameters are required, this * will be noted in the "Pixel Spreading Schemes" section in the * description of the c astRebin functions. f AST_REBIN routines. * c If no additional parameters are required, this array is not c used and a NULL pointer may be given. See also flag AST__PARWGT. f If no additional parameters are required, this array is not f used. A dummy (e.g. one-element) array may then be supplied. c Not used if "in" is NULL. c flags f FLAGS = INTEGER (Given) c The bitwise OR of a set of flag values which may be used to f The sum of a set of flag values which may be used to * provide additional control over the rebinning operation. See * the "Control Flags" section below for a description of the * options available. If no flag values are to be set, a value * of zero should be given. c tol f TOL = DOUBLE PRECISION (Given) * The maximum tolerable geometrical distortion which may be * introduced as a result of approximating non-linear Mappings * by a set of piece-wise linear transformations. This should be * expressed as a displacement in pixels in the output grid's * coordinate system. * * If piece-wise linear approximation is not required, a value * of zero may be given. This will ensure that the Mapping is * used without any approximation, but may increase execution * time. * * If the value is too high, discontinuities between the linear * approximations used in adjacent panel will be higher, and may * cause the edges of the panel to be visible when viewing the output * image at high contrast. If this is a problem, reduce the * tolerance value used. c Not used if "in" is NULL. c maxpix f MAXPIX = INTEGER (Given) * A value which specifies an initial scale size (in pixels) for * the adaptive algorithm which approximates non-linear Mappings * with piece-wise linear transformations. Normally, this should * be a large value (larger than any dimension of the region of * the input grid being used). In this case, a first attempt to * approximate the Mapping by a linear transformation will be * made over the entire input region. * * If a smaller value is used, the input region will first be c divided into sub-regions whose size does not exceed "maxpix" f divided into sub-regions whose size does not exceed MAXPIX * pixels in any dimension. Only at this point will attempts at * approximation commence. * * This value may occasionally be useful in preventing false * convergence of the adaptive algorithm in cases where the * Mapping appears approximately linear on large scales, but has * irregularities (e.g. holes) on smaller scales. A value of, * say, 50 to 100 pixels can also be employed as a safeguard in * general-purpose software, since the effect on performance is * minimal. * * If too small a value is given, it will have the effect of * inhibiting linear approximation altogether (equivalent to c setting "tol" to zero). Although this may degrade f setting TOL to zero). Although this may degrade * performance, accurate results will still be obtained. c Not used if "in" is NULL. c badval f BADVAL = (Given) * This argument should have the same type as the elements of c the "in" array. It specifies the value used to flag missing f the IN array. It specifies the value used to flag missing * data (bad pixels) in the input and output arrays. * c If the AST__USEBAD flag is set via the "flags" parameter, f If the AST__USEBAD flag is set via the FLAGS argument, c then this value is used to test for bad pixels in the "in" c (and "in_var") array(s). f then this value is used to test for bad pixels in the IN f (and IN_VAR) array(s). * * In all cases, this value is also used to flag any output c elements in the "out" (and "out_var") array(s) for which f elements in the OUT (and OUT_VAR) array(s) for which * rebined values could not be obtained (see the "Propagation * of Missing Data" section below for details of the * circumstances under which this may occur). c ndim_out f NDIM_OUT = INTEGER (Given) * The number of dimensions in the output grid. This should be * at least one. It need not necessarily be equal to the number * of dimensions in the input grid. c lbnd_out f LBND_OUT( NDIM_OUT ) = INTEGER (Given) c Pointer to an array of integers, with "ndim_out" elements, f An array * containing the coordinates of the centre of the first pixel * in the output grid along each dimension. c ubnd_out f UBND_OUT( NDIM_OUT ) = INTEGER (Given) c Pointer to an array of integers, with "ndim_out" elements, f An array * containing the coordinates of the centre of the last pixel in * the output grid along each dimension. * c Note that "lbnd_out" and "ubnd_out" together define the f Note that LBND_OUT and UBND_OUT together define the * shape, size and coordinate system of the output grid in the c same way as "lbnd_in" and "ubnd_in" define the shape, size f same way as LBND_IN and UBND_IN define the shape, size * and coordinate system of the input grid. c lbnd f LBND( NDIM_IN ) = INTEGER (Given) c Pointer to an array of integers, with "ndim_in" elements, f An array * containing the coordinates of the first pixel in the region * of the input grid which is to be included in the rebined output * array. c Not used if "in" is NULL. c ubnd f UBND( NDIM_IN ) = INTEGER (Given) c Pointer to an array of integers, with "ndim_in" elements, f An array * containing the coordinates of the last pixel in the region of * the input grid which is to be included in the rebined output * array. * c Note that "lbnd" and "ubnd" together define the shape and f Note that LBND and UBND together define the shape and * position of a (hyper-)rectangular region of the input grid * which is to be included in the rebined output array. This region * should lie wholly within the extent of the input grid (as c defined by the "lbnd_in" and "ubnd_in" arrays). Regions of f defined by the LBND_IN and UBND_IN arrays). Regions of * the input grid lying outside this region will not be used. c Not used if "in" is NULL. c out f OUT( * ) = (Given and Returned) c Pointer to an array, with one element for each pixel in the f An array, with one element for each pixel in the * output grid. The rebined data values will be added into the * original contents of this array. The numerical type of this array * should match that of the c "in" array, and the data storage order should be such f IN array, and the data storage order should be such * that the index of the first grid dimension varies most * rapidly and that of the final dimension least rapidly c (i.e. Fortran array indexing is used). f (i.e. normal Fortran array storage order). c out_var f OUT_VAR( * ) = (Given and Returned) * A c pointer to an * array with the same type and size as the c "out" f OUT * array. This c pointer f array * will only be used if the AST__USEVAR or AST__GENVAR flag is set f via the FLAGS argument, f via the "flags" parameter, * in which case variance estimates for the rebined data values will * be added into the array. If neither the AST__USEVAR flag nor the * AST__GENVAR flag is set, no output variance estimates will be * calculated and this c pointer f array * will not be used. A c NULL pointer f dummy (e.g. one-element) array * may then be supplied. c weights f WEIGHTS( * ) = DOUBLE PRECISION (Given and Returned) c Pointer to an array of double, f An array * with one or two elements for each pixel in the output grid, * depending on whether or not the AST__GENVAR flag has been supplied * via the c "flags" parameter. f FLAGS parameter. * If AST__GENVAR has not been specified then the array should have * one element for each output pixel, and it will be used to * accumulate the weight associated with each output pixel. * If AST__GENVAR has been specified then the array should have * two elements for each output pixel. The first half of the array * is again used to accumulate the weight associated with each output * pixel, and the second half is used to accumulate the square of * the weights. In each half, the data storage order should be such that * the index of the first grid dimension varies most rapidly and that of * the final dimension least rapidly c (i.e. Fortran array indexing is used). f (i.e. normal Fortran array storage order). c nused f NUSED = INTEGER*8 (Given and Returned) c A pointer to an int64_t containing the f The * number of input data values that have been added into the output * array so far. The supplied value is incremented on exit by the * number of input values used. The value is initially set to zero * if the AST__REBININIT flag is set in c "flags". f FLAGS. f STATUS = INTEGER (Given and Returned) f The global status. * Data Type Codes: * To select the appropriate rebinning function, you should c replace in the generic function name astRebinSeq with a f replace in the generic function name AST_REBINSEQ with a * 1- or 2-character data type code, so as to match the numerical * type of the data you are processing, as follows: c - D: double c - F: float c - I: int c - B: byte (signed char) c - UB: unsigned byte (unsigned char) f - D: DOUBLE PRECISION f - R: REAL f - I: INTEGER f - B: BYTE (treated as signed) f - UB: BYTE (treated as unsigned) * c For example, astRebinSeqD would be used to process "double" c data, while astRebinSeqI would be used to process "int" c data, etc. f For example, AST_REBIND would be used to process DOUBLE f PRECISION data, while AST_REBINI would be used to process f integer data (stored in an INTEGER array), etc. * * Note that, unlike c astResample, the astRebinSeq f AST_RESAMPLE, the AST_REBINSEQ * set of functions does not yet support unsigned integer data types * or integers of different sizes. * Control Flags: c The following flags are defined in the "ast.h" header file and f The following flags are defined in the AST_PAR include file and * may be used to provide additional control over the rebinning * process. Having selected a set of flags, you should supply the c bitwise OR of their values via the "flags" parameter: f sum of their values via the FLAGS argument: * * - AST__REBININIT: Used to mark the first call in a sequence. It indicates * that the supplied c "out", "out_var" and "weights" f OUT, OUT_VAR and WEIGHTS * arrays should be filled with zeros (thus over-writing any supplied * values) before adding the rebinned input data into them. This flag * should be used when rebinning the first input array in a sequence. * - AST__REBINEND: Used to mark the last call in a sequence. It causes * each value in the c "out" and "out_var" f OUT and OUT_VAR * arrays to be divided by a normalisation factor before being * returned. The normalisation factor for each output data value is just * the corresponding value from the weights array. The normalisation * factor for each output variance value is the square of the data value * normalisation factor (see also AST__CONSERVEFLUX). It also causes * output data values to be set bad if the corresponding weight is less * than the value supplied for c parameter "wlim". f argument WLIM. * It also causes any temporary values stored in the output variance array * (see flag AST__GENVAR below) to be converted into usable variance values. * Note, this flag is ignored if the AST__NONORM flag is set. * - AST__USEBAD: Indicates that there may be bad pixels in the * input array(s) which must be recognised by comparing with the c value given for "badval" and propagated to the output array(s). f value given for BADVAL and propagated to the output array(s). * If this flag is not set, all input values are treated literally c and the "badval" value is only used for flagging output array f and the BADVAL value is only used for flagging output array * values. * - AST__USEVAR: Indicates that output variance estimates should be * created by rebinning the supplied input variance estimates. An * error will be reported if both this flag and the AST__GENVAR flag * are supplied. * - AST__GENVAR: Indicates that output variance estimates should be * created based on the spread of input data values contributing to each * output pixel. An error will be reported if both this flag and the * AST__USEVAR flag are supplied. If the AST__GENVAR flag is specified, * the supplied output variance array is first used as a work array to * accumulate the temporary values needed to generate the output * variances. When the sequence ends (as indicated by the * AST__REBINEND flag), the contents of the output variance array are * converted into the required variance estimates. If the generation of * such output variances is required, this flag should be used on every * invocation of this c function f routine * within a sequence, and any supplied input variances will have no effect * on the output variances (although input variances will still be used * to weight the input data if the AST__VARWGT flag is also supplied). * The statistical meaning of these output varianes is determined by * the presence or absence of the AST__DISVAR flag (see below). * - AST__DISVAR: This flag is ignored unless the AST__GENVAR flag * has also been specified. It determines the statistical meaning of * the generated output variances. If AST__DISVAR is not specified, * generated variances represent variances on the output mean values. If * AST__DISVAR is specified, the generated variances represent the variance * of the distribution from which the input values were taken. Each output * variance created with AST__DISVAR will be larger than that created * without AST__DISVAR by a factor equal to the number of input samples * that contribute to the output sample. * - AST__VARWGT: Indicates that the input data should be weighted by * the reciprocal of the input variances. Otherwise, all input data are * given equal weight. If this flag is specified, the calculation of the * output variances (if any) is modified to take account of the * varying weights assigned to the input data values. See also AST__PARWGT. * - AST__PARWGT: Indicates that a constant weight should be used when * pasting each pixel of the supplied input array into the returned * arrays. This extra weight value should be inserted at the start of the c "params" f 'PARAMS * array (which should consequently be one element longer than specified in * the "Pixel Spreading Schemes" section in the description of the c astRebin functions). f AST_REBIN routines). * If the AST__VARWGT flag is also specified, the total weight for * each pixel is the product of the reciprocal of the pixel variance * and the value supplied in the last element of the c "params" array. f 'PARAMS array. * - AST__NONORM: If the simple unnormalised sum of all input data falling * in each output pixel is required, then this flag should be set on * each call in the sequence and the AST__REBINEND should not be used * on the last call. In this case c NULL pointers can be supplied for "weights" and "nused". f WEIGHTS and NUSED are ignored. * This flag cannot be used with the AST__CONSERVEFLUX, AST__GENVAR, * AST__PARWGT or AST__VARWGT flag. * - AST__CONSERVEFLUX: Indicates that the normalized output pixel values * generated by the AST__REBINEND flag should be scaled in such a way as * to preserve the total data value in a feature on the sky. Without this * flag, each normalised output pixel value represents a weighted mean * of the input data values around the corresponding input position. f (i.e. AST_REBINSEQ behaves similarly to AST_RESAMPLE). This f (i.e. AST_REBINSEQ behaves similarly to AST_RESAMPLE). This * is appropriate if the input data represents the spatial density of * some quantity (e.g. surface brightness in Janskys per square * arc-second) because the output pixel values will have the same * normalisation and units as the input pixel values. However, if the * input data values represent flux (or some other physical quantity) * per pixel, then the AST__CONSERVEFLUX flag could be of use. It causes * each output pixel value to be scaled by the ratio of the output pixel * size to the input pixel size. * * This flag can only be used if the Mapping is successfully approximated * by one or more linear transformations. Thus an error will be reported * if it used when the c "tol" parameter f TOL argument * is set to zero (which stops the use of linear approximations), or * if the Mapping is too non-linear to be approximated by a piece-wise * linear transformation. The ratio of output to input pixel size is * evaluated once for each panel of the piece-wise linear approximation to * the Mapping, and is assumed to be constant for all output pixels in the * panel. The scaling factors for adjacent panels will in general * differ slightly, and so the joints between panels may be visible when * viewing the output image at high contrast. If this is a problem, * reduce the value of the c "tol" parameter f TOL argument * until the difference between adjacent panels is sufficiently small * to be insignificant. * * This flag should normally be supplied on each invocation of c astRebinSeq f AST_REBINSEQ * within a given sequence. * * Note, this flag cannot be used in conjunction with the AST__NOSCALE * flag (an error will be reported if both flags are specified). * Propagation of Missing Data: * Instances of missing data (bad pixels) in the output grid are c identified by occurrences of the "badval" value in the "out" f identified by occurrences of the BADVAL value in the OUT * array. These are only produced if the AST__REBINEND flag is * specified and a pixel has zero weight. * * An input pixel is considered bad (and is consequently ignored) if * its c data value is equal to "badval" and the AST__USEBAD flag is c set via the "flags" parameter. f data value is equal to BADVAL and the AST__USEBAD flag is f set via the FLAGS argument. * * In addition, associated output variance estimates (if c calculated) may be declared bad and flagged with the "badval" c value in the "out_var" array for similar reasons. f calculated) may be declared bad and flagged with the BADVAL f value in the OUT_VAR array for similar reasons. *-- */ /* Define a macro to implement the function for a specific data type. */ #define MAKE_REBINSEQ(X,Xtype,IntType) \ static void RebinSeq##X( AstMapping *this, double wlim, int ndim_in, \ const int lbnd_in[], const int ubnd_in[], \ const Xtype in[], const Xtype in_var[], \ int spread, const double params[], int flags, \ double tol, int maxpix, Xtype badval, \ int ndim_out, const int lbnd_out[], \ const int ubnd_out[], const int lbnd[], \ const int ubnd[], Xtype out[], Xtype out_var[], \ double weights[], int64_t *nused, int *status ) { \ \ /* Local Variables: */ \ AstMapping *simple; /* Pointer to simplified Mapping */ \ Xtype *d; /* Pointer to next output data value */ \ Xtype *v; /* Pointer to next output variance value */ \ astDECLARE_GLOBALS /* Thread-specific data */ \ double *w; /* Pointer to next weight value */ \ double mwpip; /* Mean weight per input pixel */ \ double neff; /* Effective number of contributing input pixels */ \ double sw; /* Sum of weights at output pixel */ \ double wgt; /* Output pixel weight */ \ int i; /* Loop counter for output pixels */ \ int idim; /* Loop counter for coordinate dimensions */ \ int ipix_out; /* Index into output array */ \ int nin; /* Number of Mapping input coordinates */ \ int nout; /* Number of Mapping output coordinates */ \ int npix; /* Number of pixels in input region */ \ int npix_out; /* Number of pixels in output array */ \ int64_t mpix; /* Number of pixels for testing */ \ \ /* Check the global error status. */ \ if ( !astOK ) return; \ \ /* Get a pointer to a structure holding thread-specific global data values */ \ astGET_GLOBALS(this); \ \ /* Loop to determine how many pixels the output array contains. */ \ npix_out = 1; \ for ( idim = 0; idim < ndim_out; idim++ ) { \ npix_out *= ubnd_out[ idim ] - lbnd_out[ idim ] + 1; \ } \ \ /* Obtain values for the Nin and Nout attributes of the Mapping. */ \ nin = astGetNin( this ); \ nout = astGetNout( this ); \ \ /* If OK, also check that the number of output grid dimensions matches \ the number required by the Mapping and is at least 1. Report an \ error if necessary. */ \ if ( astOK && ( ( ndim_out != nout ) || ( ndim_out < 1 ) ) ) { \ astError( AST__NGDIN, "astRebinSeq"#X"(%s): Bad number of output grid " \ "dimensions (%d).", status, astGetClass( this ), ndim_out ); \ if ( ndim_out != nout ) { \ astError( AST__NGDIN, "The %s given generates %s%d coordinate " \ "value%s for each output position.", status, astGetClass( this ), \ ( nout < ndim_out ) ? "only " : "", nout, \ ( nout == 1 ) ? "" : "s" ); \ } \ } \ \ /* If no input data was supplied, jump to the normalisation section. */ \ simple = NULL; \ if( in ) { \ \ /* If OK, check that the number of input grid dimensions matches the \ number required by the Mapping and is at least 1. Report an error \ if necessary. */ \ if ( astOK && ( ( ndim_in != nin ) || ( ndim_in < 1 ) ) ) { \ astError( AST__NGDIN, "astRebinSeq"#X"(%s): Bad number of input grid " \ "dimensions (%d).", status, astGetClass( this ), ndim_in ); \ if ( ndim_in != nin ) { \ astError( AST__NGDIN, "The %s given requires %d coordinate value%s " \ "to specify an input position.", status, \ astGetClass( this ), nin, ( nin == 1 ) ? "" : "s" ); \ } \ } \ \ /* Check that the lower and upper bounds of the input grid are \ consistent. Report an error if any pair is not. */ \ mpix = 1; \ if ( astOK ) { \ for ( idim = 0; idim < ndim_in; idim++ ) { \ if ( lbnd_in[ idim ] > ubnd_in[ idim ] ) { \ astError( AST__GBDIN, "astRebinSeq"#X"(%s): Lower bound of " \ "input grid (%d) exceeds corresponding upper bound " \ "(%d).", status, astGetClass( this ), \ lbnd_in[ idim ], ubnd_in[ idim ] ); \ astError( AST__GBDIN, "Error in input dimension %d.", status, \ idim + 1 ); \ break; \ } else { \ mpix *= ubnd_in[ idim ] - lbnd_in[ idim ] + 1; \ } \ } \ } \ \ /* Report an error if there are too many pixels in the input. */ \ if ( astOK && (int) mpix != mpix ) { \ astError( AST__EXSPIX, "astRebinSeq"#X"(%s): Supplied input array " \ "contains too many pixels (%g): must be fewer than %d.", \ status, astGetClass( this ), (double) mpix, INT_MAX ); \ } \ \ /* Ensure any supplied "in_var" pointer is ignored if no input variances are \ needed. */ \ if( !( flags & AST__USEVAR ) && !( flags & AST__VARWGT ) ) { \ in_var = NULL; \ } \ \ /* Ensure any supplied "out_var" pointer is ignored if no output variances \ being created. */ \ if( !( flags & AST__USEVAR ) && !( flags & AST__GENVAR ) ) { \ out_var = NULL; \ } \ \ /* Check that the positional accuracy tolerance supplied is valid and \ report an error if necessary. */ \ if ( astOK && ( tol < 0.0 ) ) { \ astError( AST__PATIN, "astRebinSeq"#X"(%s): Invalid positional " \ "accuracy tolerance (%.*g pixel).", status, \ astGetClass( this ), AST__DBL_DIG, tol ); \ astError( AST__PATIN, "This value should not be less than zero." , status); \ } \ \ /* Check that the initial scale size in pixels supplied is valid and \ report an error if necessary. */ \ if ( astOK && ( maxpix < 0 ) ) { \ astError( AST__SSPIN, "astRebinSeq"#X"(%s): Invalid initial scale " \ "size in pixels (%d).", status, astGetClass( this ), maxpix ); \ astError( AST__SSPIN, "This value should not be less than zero." , status); \ } \ \ /* Check that the lower and upper bounds of the output grid are \ consistent. Report an error if any pair is not. */ \ mpix = 1; \ if ( astOK ) { \ for ( idim = 0; idim < ndim_out; idim++ ) { \ if ( lbnd_out[ idim ] > ubnd_out[ idim ] ) { \ astError( AST__GBDIN, "astRebinSeq"#X"(%s): Lower bound of " \ "output grid (%d) exceeds corresponding upper bound " \ "(%d).", status, astGetClass( this ), \ lbnd_out[ idim ], ubnd_out[ idim ] ); \ astError( AST__GBDIN, "Error in output dimension %d.", status, \ idim + 1 ); \ break; \ } else { \ mpix *= ubnd_out[ idim ] - lbnd_out[ idim ] + 1; \ } \ } \ } \ \ /* Report an error if there are too many pixels in the output. */ \ if ( astOK && (int) mpix != mpix ) { \ astError( AST__EXSPIX, "astRebinSeq"#X"(%s): Supplied output array " \ "contains too many pixels (%g): must be fewer than %d.", \ status, astGetClass( this ), (double) mpix, INT_MAX ); \ } \ \ /* Similarly check the bounds of the input region. */ \ mpix = 1; \ if ( astOK ) { \ for ( idim = 0; idim < ndim_in; idim++ ) { \ if ( lbnd[ idim ] > ubnd[ idim ] ) { \ astError( AST__GBDIN, "astRebinSeq"#X"(%s): Lower bound of " \ "input region (%d) exceeds corresponding upper " \ "bound (%d).", status, astGetClass( this ), \ lbnd[ idim ], ubnd[ idim ] ); \ \ /* Also check that the input region lies wholly within the input \ grid. */ \ } else if ( lbnd[ idim ] < lbnd_in[ idim ] ) { \ astError( AST__GBDIN, "astRebinSeq"#X"(%s): Lower bound of " \ "input region (%d) is less than corresponding " \ "bound of input grid (%d).", status, astGetClass( this ), \ lbnd[ idim ], lbnd_in[ idim ] ); \ } else if ( ubnd[ idim ] > ubnd_in[ idim ] ) { \ astError( AST__GBDIN, "astRebinSeq"#X"(%s): Upper bound of " \ "input region (%d) exceeds corresponding " \ "bound of input grid (%d).", status, astGetClass( this ), \ ubnd[ idim ], ubnd_in[ idim ] ); \ } else { \ mpix *= ubnd[ idim ] - lbnd[ idim ] + 1; \ } \ \ /* Say which dimension produced the error. */ \ if ( !astOK ) { \ astError( AST__GBDIN, "Error in output dimension %d.", status, \ idim + 1 ); \ break; \ } \ } \ } \ \ /* Report an error if there are too many pixels in the input region. */ \ if ( astOK && (int) mpix != mpix ) { \ astError( AST__EXSPIX, "astRebinSeq"#X"(%s): Supplied input region " \ "contains too many pixels (%g): must be fewer than %d.", \ status, astGetClass( this ), (double) mpix, INT_MAX ); \ } \ \ /* Check that only one of AST__USEVAR and ASR__GENVAR has been supplied. */ \ if( ( flags & AST__USEVAR ) && ( flags & AST__GENVAR ) ) { \ if( astOK ) { \ astError( AST__BDPAR, "astRebinSeq"#X"(%s): Incompatible flags " \ "AST__GENVAR and AST__USEVAR have been specified " \ "together (programming error).", status, astGetClass( this ) ); \ } \ } \ \ /* If AST__USEVAR or AST_VARWGT has been specified, check we have an \ input variance array. */ \ if( !in_var && astOK ) { \ if( ( flags & AST__USEVAR ) ) { \ astError( AST__BDPAR, "astRebinSeq"#X"(%s): The AST__USEVAR flag " \ "was specified but no input variance array was supplied " \ "(programming error).", status, astGetClass( this ) ); \ } else if( ( flags & AST__VARWGT ) ) { \ astError( AST__BDPAR, "astRebinSeq"#X"(%s): The AST__VARWGT flag " \ "was specified but no input variance array was supplied " \ "(programming error).", status, astGetClass( this ) ); \ } \ } \ \ /* If AST__USEVAR or AST_GENVAR has been specified, check we have an \ output variance array. */ \ if( !out_var && astOK ) { \ if( ( flags & AST__USEVAR ) ) { \ astError( AST__BDPAR, "astRebinSeq"#X"(%s): The AST__USEVAR flag " \ "was specified but no output variance array was supplied " \ "(programming error).", status, astGetClass( this ) ); \ } else if( ( flags & AST__GENVAR ) ) { \ astError( AST__BDPAR, "astRebinSeq"#X"(%s): The AST__GENVAR flag " \ "was specified but no output variance array was supplied " \ "(programming error).", status, astGetClass( this ) ); \ } \ } \ \ /* If the AST__NONORM flag has been supplied, check no incompatible flags have \ been specified. */ \ if( flags & AST__NONORM ) { \ if( ( flags & AST__GENVAR ) && astOK ) { \ astError( AST__BDPAR, "astRebinSeq"#X"(%s): Incompatible flags " \ "AST__GENVAR and AST__NONORM have been specified " \ "together (programming error).", status, astGetClass( this ) ); \ } else if( ( flags & AST__VARWGT ) && astOK ) { \ astError( AST__BDPAR, "astRebinSeq"#X"(%s): Incompatible flags " \ "AST__VARWGT and AST__NONORM have been specified " \ "together (programming error).", status, astGetClass( this ) ); \ } else if( ( flags & AST__PARWGT ) && astOK ) { \ astError( AST__BDPAR, "astRebinSeq"#X"(%s): Incompatible flags " \ "AST__PARWGT and AST__NONORM have been specified " \ "together (programming error).", status, astGetClass( this ) ); \ } else if( ( flags & AST__CONSERVEFLUX ) && astOK ) { \ astError( AST__BDPAR, "astRebinSeq"#X"(%s): Incompatible flags " \ "AST__CONSERVEFLUX and AST__NONORM have been specified " \ "together (programming error).", status, astGetClass( this ) ); \ } \ \ /* If the AST__NONORM flag has not been supplied, check that a weights array \ and nused pointer have been supplied. */ \ } else if( !weights ){ \ astError( AST__BDPAR, "astRebinSeq"#X"(%s): No weights array " \ "supplied (programming error).", status, \ astGetClass( this ) ); \ } else if( !nused ){ \ astError( AST__BDPAR, "astRebinSeq"#X"(%s): No 'nused' pointer " \ "supplied (programming error).", status, \ astGetClass( this ) ); \ } \ \ /* If OK, loop to determine how many input pixels are to be binned. */ \ npix = 1; \ unsimplified_mapping = this; \ if ( astOK ) { \ for ( idim = 0; idim < ndim_in; idim++ ) { \ npix *= ubnd[ idim ] - lbnd[ idim ] + 1; \ } \ \ /* If there are sufficient pixels to make it worthwhile, simplify the \ Mapping supplied to improve performance. Otherwise, just clone the \ Mapping pointer. Note we have already saved a pointer to the original \ Mapping so that lower-level functions can use it if they need to report \ an error. */ \ if ( npix > 1024 ) { \ simple = astSimplify( this ); \ } else { \ simple = astClone( this ); \ } \ } \ \ /* Report an error if the forward transformation of this simplified \ Mapping is not defined. */ \ if ( !astGetTranForward( simple ) && astOK ) { \ astError( AST__TRNND, "astRebinSeq"#X"(%s): An forward coordinate " \ "transformation is not defined by the %s supplied.", status, \ astGetClass( unsimplified_mapping ), \ astGetClass( unsimplified_mapping ) ); \ } \ \ /* If required, initialise the output arrays to hold zeros. */ \ if( flags & AST__REBININIT ) { \ d = out; \ if( out_var ) { \ v = out_var; \ for( ipix_out = 0; ipix_out < npix_out; ipix_out++, d++, v++ ) { \ *d = 0; \ *v = 0; \ } \ } else { \ for( ipix_out = 0; ipix_out < npix_out; ipix_out++, d++ ) { \ *d = 0; \ } \ } \ if( weights ) { \ w = weights; \ for( ipix_out = 0; ipix_out < npix_out; ipix_out++, w++ ) { \ *w = 0; \ } \ if( flags & AST__GENVAR ) { \ for( ipix_out = 0; ipix_out < npix_out; ipix_out++, w++ ) *w = 0; \ } \ } \ if( nused ) *nused = 0; \ } \ \ /* Paste the input values into the supplied output arrays. */ \ if( RebinAdaptively( simple, ndim_in, lbnd_in, ubnd_in, \ (const void *) in, (const void *) in_var, \ TYPE_##X, spread, params, flags, \ tol, maxpix, (const void *) &badval, \ ndim_out, lbnd_out, ubnd_out, lbnd, \ ubnd, npix_out, (void *) out, \ (void *) out_var, weights, nused, status ) ) { \ astError( AST__CNFLX, "astRebinSeq"#X"(%s): Flux conservation was " \ "requested but could not be performed because the " \ "forward transformation of the supplied Mapping " \ "is too non-linear.", status, astGetClass( this ) ); \ } \ \ /* Annul the pointer to the simplified/cloned Mapping. */ \ simple = astAnnul( simple ); \ \ } \ \ /* If required, finalise the sequence. */ \ if( ( flags & AST__REBINEND ) && !( flags & AST__NONORM ) && \ weights && nused ) { \ \ /* Ensure "wlim" is not zero. */ \ if( wlim < 1.0E-10 ) wlim = 1.0E-10; \ \ /* If it will be needed, find the average weight per input pixel. */ \ if( !( flags & AST__GENVAR ) && *nused > 0 ) { \ sw = 0.0; \ for( i = 0; i < npix_out; i++ ) { \ sw += weights[ i ]; \ } \ mwpip = sw/( *nused ); \ } else { \ mwpip = AST__BAD; \ } \ \ /* Normalise each output pixel. */ \ for( i = 0; i < npix_out; i++ ) { \ \ /* Find the effective number of input samples that contribute to the \ output sample. To do this properly requires the sum of the squared \ weights in each output pixel, but this is only available if AST__GENVAR \ flag is in use. In order to avoid changing the API for astRebinSeq, we \ honour this long-standing restriction, and use an approximation if \ AST__GENVAR is not in use. */ \ wgt = weights[ i ]; \ if( flags & AST__GENVAR ) { \ if( wgt > 0.0 && weights[ i + npix_out ] > 0 ) { \ neff = (wgt*wgt)/weights[ i + npix_out ]; \ } else { \ neff = 0.0; \ } \ \ /* If the sum of the squared weights is not available, compare the weight \ for this output pixel with the mean weight per input pixel. */ \ } else if( mwpip != AST__BAD ){ \ neff = wgt/mwpip; \ \ } else if( astOK ) { \ astError( AST__BADIN, "astRebinSeq"#X"(%s): The overlap " \ "between the %d-d input array and the %d-d output " \ "array contains no pixels with good data %svalues.", \ status, astGetClass( this ), nin, nout, \ in_var ? "and variance " : "" ); \ } \ \ /* Assign bad values to unused output pixels. */ \ if( neff < wlim || neff == 0.0 ) { \ out[ i ] = badval; \ if( out_var ) out_var[ i ] = badval; \ \ /* Otherwise, normalise the returned data value. No need to check "wgt" \ since it must be larger than zero since neff is larger than wlim. */ \ } else { \ out[ i ] /= wgt; \ \ /* Normalise the returned variance: propagated from input variances... */ \ if( out_var ) { \ if( flags & AST__USEVAR ) { \ out_var[ i ] /= wgt*wgt; \ \ /* Normalise the returned variance: from spread of input values... */ \ } else if( flags & AST__GENVAR && neff > 1.0 ) { \ out_var[ i ] /= wgt; \ out_var[ i ] -= out[ i ]*out[ i ]; \ if( out_var[ i ] < 0.0 ) out_var[ i ] = 0.0; \ \ /* If output variances are estimates of the variance of the distribution \ from which the input values were sampled... */ \ if( flags & AST__DISVAR ) { \ out_var[ i ] *= neff/( neff - 1.0 ); \ \ /* If output variances are estimates of the error on the mean data value... */ \ } else { \ out_var[ i ] *= 1.0/( neff - 1.0 ); \ } \ \ } else { \ out_var[ i ] = badval; \ } \ } \ } \ } \ } \ \ } /* Expand the above macro to generate a function for each required data type. */ #if HAVE_LONG_DOUBLE /* Not normally implemented */ MAKE_REBINSEQ(LD,long double,0) #endif MAKE_REBINSEQ(D,double,0) MAKE_REBINSEQ(F,float,0) MAKE_REBINSEQ(I,int,1) MAKE_REBINSEQ(B,signed char,1) MAKE_REBINSEQ(UB,unsigned char,1) /* Undefine the macro. */ #undef MAKE_REBINSEQ static int RebinWithBlocking( AstMapping *this, const double *linear_fit, int ndim_in, const int *lbnd_in, const int *ubnd_in, const void *in, const void *in_var, DataType type, int spread, const double *params, int flags, const void *badval_ptr, int ndim_out, const int *lbnd_out, const int *ubnd_out, const int *lbnd, const int *ubnd, int npix_out, void *out, void *out_var, double *work, int64_t *nused, int *status ) { /* * Name: * RebinWithBlocking * Purpose: * Rebin a section of a data grid in a memory-efficient way. * Type: * Private function. * Synopsis: * #include "mapping.h" * int RebinWithBlocking( AstMapping *this, const double *linear_fit, * int ndim_in, const int *lbnd_in, * const int *ubnd_in, const void *in, * const void *in_var, DataType type, * int spread, const double *params, int flags, * const void *badval_ptr, int ndim_out, * const int *lbnd_out, const int *ubnd_out, * const int *lbnd, const int *ubnd, int npix_out, * void *out, void *out_var, double *work, * int64_t *nused, int *status ) * Class Membership: * Mapping member function. * Description: * This function rebins a specified section of a rectangular grid of * data (with any number of dimensions) into another rectangular grid * (with a possibly different number of dimensions). The coordinate * transformation used to convert input pixel coordinates into positions * in the output grid is given by the forward transformation of the * Mapping which is supplied. Any pixel spreading scheme may be specified * for distributing the flux of an input pixel amongst the output * pixels. * * This function is very similar to RebinSection, except that in * order to limit memory usage and to ensure locality of reference, * it divides the input grid up into "blocks" which have a limited * extent along each input dimension. Each block, which will not * contain more than a pre-determined maximum number of pixels, is * then passed to RebinSection for resampling. * Parameters: * this * Pointer to a Mapping, whose forward transformation may be * used to transform the coordinates of pixels in the input * grid into associated positions in the output grid. * * The number of input coordintes for the Mapping (Nin * attribute) should match the value of "ndim_in" (below), and * the number of output coordinates (Nout attribute) should * match the value of "ndim_out". * linear_fit * Pointer to an optional array of double which contains the * coefficients of a linear fit which approximates the above * Mapping's forward coordinate transformation. If this is * supplied, it will be used in preference to the above Mapping * when transforming coordinates. This may be used to enhance * performance in cases where evaluation of the Mapping's * forward transformation is expensive. If no linear fit is * available, a NULL pointer should be supplied. * * The way in which the fit coefficients are stored in this * array and the number of array elements are as defined by the * astLinearApprox function. * ndim_in * The number of dimensions in the input grid. This should be at * least one. * lbnd_in * Pointer to an array of integers, with "ndim_in" elements. * This should give the coordinates of the centre of the first * pixel in the input data grid along each dimension. * ubnd_in * Pointer to an array of integers, with "ndim_in" elements. * This should give the coordinates of the centre of the last * pixel in the input data grid along each dimension. * * Note that "lbnd_in" and "ubnd_in" together define the shape * and size of the input data grid, its extent along a * particular (i'th) dimension being (ubnd_in[i] - lbnd_in[i] + * 1). They also define the input grid's coordinate system, with * each pixel being of unit extent along each dimension with * integral coordinate values at its centre. * in * Pointer to the input array of data to be rebinned (with one * element for each pixel in the input grid). The numerical type * of these data should match the "type" value (below). The * storage order should be such that the coordinate of the first * dimension varies most rapidly and that of the final dimension * least rapidly (i.e. Fortran array storage order is used). * in_var * An optional pointer to a second array of positive numerical * values (with the same size and data type as the "in" array), * which represent estimates of the statistical variance * associated with each element of the "in" array. If this * second array is given (along with the corresponding "out_var" * array), then estimates of the variance of the rebinned data * will also be returned. * * If no variance estimates are required, a NULL pointer should * be given. * type * A value taken from the "DataType" enum, which specifies the * data type of the input and output arrays containing the * gridded data (and variance) values. * spread * A value selected from a set of pre-defined macros to identify * which pixel spread function should be used. * params * Pointer to an optional array of parameters that may be passed * to the pixel spread algorithm, if required. If no parameters * are required, a NULL pointer should be supplied. * flags * The bitwise OR of a set of flag values which provide additional * control over the resampling operation. * badval_ptr * If the AST__USEBAD flag is set (above), this parameter is a * pointer to a value which is used to identify bad data and/or * variance values in the input array(s). The referenced value's * data type must match that of the "in" (and "in_var") * arrays. The same value will also be used to flag any output * array elements for which rebinned values could not be * obtained. The output arrays(s) may be flagged with this * value whether or not the AST__USEBAD flag is set (the * function return value indicates whether any such values have * been produced). * ndim_out * The number of dimensions in the output grid. This should be * at least one. * lbnd_out * Pointer to an array of integers, with "ndim_out" elements. * This should give the coordinates of the centre of the first * pixel in the output data grid along each dimension. * ubnd_out * Pointer to an array of integers, with "ndim_out" elements. * This should give the coordinates of the centre of the last * pixel in the output data grid along each dimension. * * Note that "lbnd_out" and "ubnd_out" together define the shape * and size of the output data grid in the same way as "lbnd_in" * and "ubnd_in" define the shape and size of the input grid * (see above). * lbnd * Pointer to an array of integers, with "ndim_in" elements. * This should give the coordinates of the first pixel in the * section of the input data grid which is to be rebinned. * ubnd * Pointer to an array of integers, with "ndim_in" elements. * This should give the coordinates of the last pixel in the * section of the input data grid which is to be rebinned. * * Note that "lbnd" and "ubnd" define the shape and position of * the section of the input grid which is to be rebinned. This section * should lie wholly within the extent of the input grid (as defined * by the "lbnd_out" and "ubnd_out" arrays). Regions of the input * grid lying outside this section will be ignored. * npix_out * The number of pixels in the output array. * out * Pointer to an array with the same data type as the "in" * array, into which the rebinned data will be returned. The * storage order should be such that the coordinate of the first * dimension varies most rapidly and that of the final dimension * least rapidly (i.e. Fortran array storage order is used). * out_var * An optional pointer to an array with the same data type and * size as the "out" array, into which variance estimates for * the rebinned values may be returned. This array will only be * used if the "in_var" array has been given. * * If no output variance estimates are required, a NULL pointer * should be given. * work * An optional pointer to a double array with the same size as * the "out" array. The contents of this array (if supplied) are * incremented by the accumulated weights assigned to each output pixel. * If no accumulated weights are required, a NULL pointer should be * given. * nused * An optional pointer to a int64_t which will be incremented by the * number of input values pasted into the output array. Ignored if NULL. * Returned Value: * A non-zero value is returned if "flags" included AST__CONSERVEFLUX (i.e. * flux conservation was requested), but the supplied linear fit to the * forward transformation of the Mapping had zero determinant (no error * is reported if this happens). Zero is returned otherwise. */ /* Local Constants: */ const int mxpix = 2 * 1024; /* Maximum number of pixels in a block (this relatively small number seems to give best performance) */ /* Local Variables: */ double factor; /* Flux conservation factor */ int *dim_block; /* Pointer to array of block dimensions */ int *lbnd_block; /* Pointer to block lower bound array */ int *ubnd_block; /* Pointer to block upper bound array */ int dim; /* Dimension size */ int done; /* All blocks rebinned? */ int hilim; /* Upper limit on maximum block dimension */ int idim; /* Loop counter for dimensions */ int lolim; /* Lower limit on maximum block dimension */ int mxdim_block; /* Maximum block dimension */ int npix; /* Number of pixels in block */ int result; /* Returned value */ /* Initialise */ result = 0; /* Check the global error status. */ if ( !astOK ) return result; /* Allocate workspace. */ lbnd_block = astMalloc( sizeof( int ) * (size_t) ndim_in ); ubnd_block = astMalloc( sizeof( int ) * (size_t) ndim_in ); dim_block = astMalloc( sizeof( int ) * (size_t) ndim_in ); if ( astOK ) { /* Find the optimum block size. */ /* ---------------------------- */ /* We first need to find the maximum extent which a block of input pixels may have in each dimension. We determine this by taking the input grid extent in each dimension and then limiting the maximum dimension size until the resulting number of pixels is sufficiently small. This approach allows the block shape to approximate (or match) the input grid shape when appropriate. */ /* First loop to calculate the total number of input pixels and the maximum input dimension size. */ npix = 1; mxdim_block = 0; for ( idim = 0; idim < ndim_in; idim++ ) { dim = ubnd[ idim ] - lbnd[ idim ] + 1; npix *= dim; if ( mxdim_block < dim ) mxdim_block = dim; } /* If the number of input pixels is too large for a single block, we perform iterations to determine the optimum upper limit on a block's dimension size. Initialise the limits on this result. */ if ( npix > mxpix ) { lolim = 1; hilim = mxdim_block; /* Loop to perform a binary chop, searching for the best result until the lower and upper limits on the result converge to adjacent values. */ while ( ( hilim - lolim ) > 1 ) { /* Form a new estimate from the mid-point of the previous limits. */ mxdim_block = ( hilim + lolim ) / 2; /* See how many pixels a block contains if its maximum dimension is limited to this new value. */ for ( npix = 1, idim = 0; idim < ndim_in; idim++ ) { dim = ubnd[ idim ] - lbnd[ idim ] + 1; npix *= ( dim < mxdim_block ) ? dim : mxdim_block; } /* Update the appropriate limit, according to whether the number of pixels is too large or too small. */ *( ( npix <= mxpix ) ? &lolim : &hilim ) = mxdim_block; } /* When iterations have converged, obtain the maximum limit on the dimension size of a block which results in no more than the maximum allowed number of pixels per block. However, ensure that all block dimensions are at least 2. */ mxdim_block = lolim; } if ( mxdim_block < 2 ) mxdim_block = 2; /* Calculate the block dimensions by applying this limit to the output grid dimensions. */ for ( idim = 0; idim < ndim_in; idim++ ) { dim = ubnd[ idim ] - lbnd[ idim ] + 1; dim_block[ idim ] = ( dim < mxdim_block ) ? dim : mxdim_block; /* Also initialise the lower and upper bounds of the first block of output grid pixels to be rebinned, ensuring that this does not extend outside the grid itself. */ lbnd_block[ idim ] = lbnd[ idim ]; ubnd_block[ idim ] = MinI( lbnd[ idim ] + dim_block[ idim ] - 1, ubnd[ idim ], status ); } /* Determine the flux conservation constant if needed. */ /* --------------------------------------------------- */ factor = 1.0; if( flags & AST__CONSERVEFLUX ) { if( linear_fit ) { factor = MatrixDet( ndim_out, ndim_in, linear_fit + ndim_out, status ); if( factor != 0.0 ) { factor = 1.0/factor; } else { result = 1; } } else { result = 1; } } /* Rebin each block of input pixels. */ /* --------------------------------- */ /* Loop to generate the extent of each block of input pixels and to rebin them. */ done = result; while ( !done && astOK ) { /* Rebin the current block, accumulating the sum of bad pixels produced. */ RebinSection( this, linear_fit, ndim_in, lbnd_in, ubnd_in, in, in_var, factor, type, spread, params, flags, badval_ptr, ndim_out, lbnd_out, ubnd_out, lbnd_block, ubnd_block, npix_out, out, out_var, work, nused, status ); /* Update the block extent to identify the next block of input pixels. */ idim = 0; do { /* We find the least significant dimension where the upper bound of the block has not yet reached the upper bound of the region of the input grid which we are rebinning. The block's position is then incremented by one block extent along this dimension, checking that the resulting extent does not go outside the region being rebinned. */ if ( ubnd_block[ idim ] < ubnd[ idim ] ) { lbnd_block[ idim ] = MinI( lbnd_block[ idim ] + dim_block[ idim ], ubnd[ idim ], status ); ubnd_block[ idim ] = MinI( lbnd_block[ idim ] + dim_block[ idim ] - 1, ubnd[ idim ], status ); break; /* If any less significant dimensions are found where the upper bound of the block has reached its maximum value, we reset the block to its lowest position. */ } else { lbnd_block[ idim ] = lbnd[ idim ]; ubnd_block[ idim ] = MinI( lbnd[ idim ] + dim_block[ idim ] - 1, ubnd[ idim ], status ); /* All the blocks have been processed once the position along the most significant dimension has been reset. */ done = ( ++idim == ndim_in ); } } while ( !done ); } } /* Free the workspace. */ lbnd_block = astFree( lbnd_block ); ubnd_block = astFree( ubnd_block ); dim_block = astFree( dim_block ); /* Return a flag indicating if there was an error conserving flux. */ return result; } static AstMapping *RemoveRegions( AstMapping *this, int *status ) { /* *++ * Name: c astRemoveRegions f AST_REMOVEREGIONS * Purpose: * Remove any Regions from a Mapping. * Type: * Public function. * Synopsis: c #include "mapping.h" c AstMapping *astRemoveRegions( AstMapping *this ) f RESULT = AST_REMOVEREGIONS( THIS, STATUS ) * Class Membership: * Mapping method. * Description: * This function searches the suppliedMapping (which may be a * compound Mapping such as a CmpMap) for any component Mappings * that are instances of the AST Region class. It then creates * a new Mapping from which all Regions have been removed. If * a Region cannot simply be removed (for instance, if it is a * component of a parallel CmpMap), then it is replaced with an * equivalent UnitMap in the returned Mapping. * Parameters: c this f THIS = INTEGER (Given) * Pointer to the original Mapping. f STATUS = INTEGER (Given and Returned) f The global status. * Returned Value: c astRemoveRegions() f AST_REMOVEREGIONS = INTEGER * A new pointer to the (possibly modified) Mapping. * Applicability: * CmpFrame * If the supplied Mapping is a CmpFrame, any component Frames * that are instances of the Region class are replaced by the * equivalent Frame. * FrameSet * If the supplied Mapping is a FrameSet, the returned Mapping * will be a copy of the supplied FrameSet in which Regions * have been removed from all the inter-Frame Mappings, and any * Frames which are instances of the Region class are replaced by * the equivalent Frame. * Mapping * This function applies to all Mappings. * Region * If the supplied Mapping is a Region, the returned Mapping will * be the equivalent Frame. * Notes: * - This function can safely be applied even to Mappings * which contain no Regions. If no Regions are found, it c behaves exactly like astClone and returns a pointer to the f behaves exactly like AST_CLONE and returns a pointer to the * original Mapping. * - The Mapping returned by this function may not be independent * of the original (even if some Regions were removed), and * modifying it may therefore result in indirect modification of * the original. If a completely independent result is required, a c copy should be made using astCopy. f copy should be made using AST_COPY. * - 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. *-- */ /* This base iplementation just returns a clone of the supplied Mapping pointer. Sub-classes should override it as necessary. */ return astClone( this ); } static void ReportPoints( AstMapping *this, int forward, AstPointSet *in_points, AstPointSet *out_points, int *status ) { /* *+ * Name: * astReportPoints * Purpose: * Report the effect of transforming a set of points using a Mapping. * Type: * Protected virtual function. * Synopsis: * #include "mapping.h" * void astReportPoints( AstMapping *this, int forward, * AstPointSet *in_points, AstPointSet *out_points ) * Class Membership: * Mapping method. * Description: * This function reports the coordinates of a set of points before * and after being transformed by a Mapping, by writing them to * standard output. * Parameters: * this * Pointer to the Mapping. * forward * A non-zero value indicates that the Mapping's forward * coordinate transformation has been applied, while a zero * value indicates the inverse transformation. * in_points * Pointer to a PointSet which is associated with the * coordinates of a set of points before the Mapping was * applied. * out_points * Pointer to a PointSet which is associated with the * coordinates of the same set of points after the Mapping has * been applied. * Notes: * - This method is provided as a development and debugging aid to * be invoked when coordinates are transformed by public Mapping * methods and under control of the "Report" Mapping attribute. * - Derived clases may over-ride this method in order to change * the way in which coordinates are formatted, etc. *- */ /* Local Variables: */ double **ptr_in; /* Pointer to array of input data pointers */ double **ptr_out; /* Pointer to array of output data pointers */ int coord; /* Loop counter for coordinates */ int ncoord_in; /* Number of input coordinates per point */ int ncoord_out; /* Number of output coordinates per point */ int npoint; /* Number of points to report */ int npoint_in; /* Number of input points */ int npoint_out; /* Number of output points */ int point; /* Loop counter for points */ /* Check the global error status. */ if ( !astOK ) return; /* Obtain the numbers of points and coordinates associated with each PointSet. */ npoint_in = astGetNpoint( in_points ); npoint_out = astGetNpoint( out_points ); ncoord_in = astGetNcoord( in_points ); ncoord_out = astGetNcoord( out_points ); /* Obtain the pointers that give access to the coordinate data associated with each PointSet. */ ptr_in = astGetPoints( in_points ); ptr_out = astGetPoints( out_points ); /* In the event that both PointSets don't contain equal numbers of points (this shouldn't actually happen), simply use the minimum number. */ npoint = ( npoint_in < npoint_out ) ? npoint_in : npoint_out; /* Loop to report the effect of the Mapping on each point in turn. */ for ( point = 0; point < npoint; point++ ) { /* Report the input coordinates (in parentheses and separated by commas). Replace coordinate values of AST__BAD with the string "" to indicate missing values. */ printf( "(" ); for ( coord = 0; coord < ncoord_in; coord++ ) { if ( ptr_in[ coord ][ point ] == AST__BAD ) { printf( "%s", coord ? ", " : "" ); } else { printf( "%s%.*g", coord ? ", " : "", AST__DBL_DIG, ptr_in[ coord ][ point ] ); } } /* Similarly report the output coordinates. */ printf( ") --> (" ); for ( coord = 0; coord < ncoord_out; coord++ ) { if ( ptr_out[ coord ][ point ] == AST__BAD ) { printf( "%s", coord ? ", " : "" ); } else { printf( "%s%.*g", coord ? ", " : "", AST__DBL_DIG, ptr_out[ coord ][ point ] ); } } printf( ")\n" ); } } /* *++ * Name: c astResample f AST_RESAMPLE * Purpose: * Resample a region of a data grid. * Type: * Public virtual function. * Synopsis: c #include "mapping.h" c int astResample( AstMapping *this, int ndim_in, c const int lbnd_in[], const int ubnd_in[], c const in[], const in_var[], c int interp, void (* finterp)( void ), c const double params[], int flags, c double tol, int maxpix, c badval, int ndim_out, c const int lbnd_out[], const int ubnd_out[], c const int lbnd[], const int ubnd[], c out[], out_var[] ); f RESULT = AST_RESAMPLE( THIS, NDIM_IN, LBND_IN, UBND_IN, IN, IN_VAR, f INTERP, FINTERP, PARAMS, FLAGS, f TOL, MAXPIX, BADVAL, f NDIM_OUT, LBND_OUT, UBND_OUT, f LBND, UBND, OUT, OUT_VAR, STATUS ) * Class Membership: * Mapping method. * Description: * This is a set of functions for resampling gridded data (e.g. an * image) under the control of a geometrical transformation, which * is specified by a Mapping. The functions operate on a pair of * data grids (input and output), each of which may have any number * of dimensions. Resampling may be restricted to a specified * region of the output grid. An associated grid of error estimates * associated with the input data may also be supplied (in the form * of variance values), so as to produce error estimates for the * resampled output data. Propagation of missing data (bad pixels) * is supported. * * You should use a resampling function which matches the numerical * type of the data you are processing by replacing in c the generic function name astResample by an appropriate 1- or f the generic function name AST_RESAMPLE by an appropriate 1- or * 2-character type code. For example, if you are resampling data c with type "float", you should use the function astResampleF (see f with type REAL, you should use the function AST_RESAMPLER (see * the "Data Type Codes" section below for the codes appropriate to * other numerical types). * * Resampling of the grid of input data is performed by * transforming the coordinates of the centre of each output grid * element (or pixel) into the coordinate system of the input grid. * Since the resulting coordinates will not, in general, coincide * with the centre of an input pixel, sub-pixel interpolation is * performed between the neighbouring input pixels. This produces a * resampled value which is then assigned to the output pixel. A * choice of sub-pixel interpolation schemes is provided, but you * may also implement your own. * * This algorithm samples the input data value, it does not integrate * it. Thus total data value in the input image will not, in general, * be conserved. However, an option is provided (see the "Control Flags" * section below) which can produce approximate flux conservation by * scaling the output values using the ratio of the output pixel size * to the input pixel size. However, if accurate flux conservation is * important to you, consder using the c astRebin or astRebinSeq family of functions f AST_REBIN or AST_REBINSEQ family of routines * instead. * * Output pixel coordinates are transformed into the coordinate * system of the input grid using the inverse transformation of the * Mapping which is supplied. This means that geometrical features * in the input data are subjected to the Mapping's forward * transformation as they are transferred from the input to the * output grid (although the Mapping's forward transformation is * not explicitly used). * * In practice, transforming the coordinates of every pixel of a * large data grid can be time-consuming, especially if the Mapping * involves complicated functions, such as sky projections. To * improve performance, it is therefore possible to approximate * non-linear Mappings by a set of linear transformations which are * applied piece-wise to separate sub-regions of the data. This * approximation process is applied automatically by an adaptive * algorithm, under control of an accuracy criterion which * expresses the maximum tolerable geometrical distortion which may * be introduced, as a fraction of a pixel. * * This algorithm first attempts to approximate the Mapping with a * linear transformation applied over the whole region of the * output grid which is being used. If this proves to be * insufficiently accurate, the output region is sub-divided into * two along its largest dimension and the process is repeated * within each of the resulting sub-regions. This process of * sub-division continues until a sufficiently good linear * approximation is found, or the region to which it is being * applied becomes too small (in which case the original Mapping is * used directly). * Parameters: c this f THIS = INTEGER (Given) * Pointer to a Mapping, whose inverse transformation will be * used to transform the coordinates of pixels in the output * grid into the coordinate system of the input grid. This * yields the positions which are used to obtain resampled * values by sub-pixel interpolation within the input grid. * * The number of input coordinates used by this Mapping (as * given by its Nin attribute) should match the number of input c grid dimensions given by the value of "ndim_in" f grid dimensions given by the value of NDIM_IN * below. Similarly, the number of output coordinates (Nout * attribute) should match the number of output grid dimensions c given by "ndim_out". f given by NDIM_OUT. c ndim_in f NDIM_IN = INTEGER (Given) * The number of dimensions in the input grid. This should be at * least one. c lbnd_in f LBND_IN( NDIM_IN ) = INTEGER (Given) c Pointer to an array of integers, with "ndim_in" elements, f An array * containing the coordinates of the centre of the first pixel * in the input grid along each dimension. c ubnd_in f UBND_IN( NDIM_IN ) = INTEGER (Given) c Pointer to an array of integers, with "ndim_in" elements, f An array * containing the coordinates of the centre of the last pixel in * the input grid along each dimension. * c Note that "lbnd_in" and "ubnd_in" together define the shape f Note that LBND_IN and UBND_IN together define the shape * and size of the input grid, its extent along a particular c (j'th) dimension being ubnd_in[j]-lbnd_in[j]+1 (assuming the c index "j" to be zero-based). They also define f (J'th) dimension being UBND_IN(J)-LBND_IN(J)+1. They also define * the input grid's coordinate system, each pixel having unit * extent along each dimension with integral coordinate values * at its centre. c in f IN( * ) = (Given) c Pointer to an array, with one element for each pixel in the f An array, with one element for each pixel in the * input grid, containing the input data to be resampled. The * numerical type of this array should match the 1- or * 2-character type code appended to the function name (e.g. if c you are using astResampleF, the type of each array element c should be "float"). f you are using AST_RESAMPLER, the type of each array element f should be REAL). * * The storage order of data within this array should be such * that the index of the first grid dimension varies most * rapidly and that of the final dimension least rapidly c (i.e. Fortran array indexing is used). f (i.e. normal Fortran array storage order). c in_var f IN_VAR( * ) = (Given) c An optional pointer to a second array with the same size and c type as the "in" array. If given, this should contain a set c of non-negative values which represent estimates of the c statistical variance associated with each element of the "in" c array. If this array is supplied (together with the c corresponding "out_var" array), then estimates of the c variance of the resampled output data will be calculated. c c If no input variance estimates are being provided, a NULL c pointer should be given. f An optional second array with the same size and type as the f IN array. If the AST__USEVAR flag is set via the FLAGS f argument (below), this array should contain a set of f non-negative values which represent estimates of the f statistical variance associated with each element of the IN f array. Estimates of the variance of the resampled output data f will then be calculated. f f If the AST__USEVAR flag is not set, no input variance f estimates are required and this array will not be used. A f dummy (e.g. one-element) array may then be supplied. c interp f INTERP = INTEGER (Given) c This parameter specifies the scheme to be used for sub-pixel f This argument specifies the scheme to be used for sub-pixel * interpolation within the input grid. It may be used to select * from a set of pre-defined schemes by supplying one of the * values described in the "Sub-Pixel Interpolation Schemes" * section below. If a value of zero is supplied, then the * default linear interpolation scheme is used (equivalent to * supplying the value AST__LINEAR). * * Alternatively, you may supply a value which indicates that c you will provide your own function to perform sub-pixel c interpolation by means of the "finterp " parameter. Again, see f you will provide your own routine to perform sub-pixel f interpolation by means of the FINTERP argument. Again, see * the "Sub-Pixel Interpolation Schemes" section below for * details. c finterp f FINTERP = SUBROUTINE (Given) c If the value given for the "interp" parameter indicates that c you will provide your own function for sub-pixel c interpolation, then a pointer to that function should be c given here. For details of the interface which the function c should have (several are possible, depending on the value of c "interp"), see the "Sub-Pixel Interpolation Schemes" section c below. f If the value given for the INTERP argument indicates that you f will provide your own routine for sub-pixel interpolation, f then the name of that routine should be given here (the name f should also appear in a Fortran EXTERNAL statement in the f routine which invokes AST_RESAMPLE). For details of the f interface which the routine should have (several are f possible, depending on the value of INTERP), see the f "Sub-Pixel Interpolation Schemes" section below. * c If the "interp" parameter has any other value, corresponding c to one of the pre-defined interpolation schemes, then this c function will not be used and you may supply a NULL pointer. f If the INTERP argument has any other value, corresponding to f one of the pre-defined interpolation schemes, then this f routine will not be used and you may supply the null routine f AST_NULL here (note only one underscore). No EXTERNAL f statement is required for this routine, so long as the AST_PAR f include file has been used. c params f PARAMS( * ) = DOUBLE PRECISION (Given) c An optional pointer to an array of double which should contain f An optional array which should contain * any additional parameter values required by the sub-pixel * interpolation scheme. If such parameters are required, this * will be noted in the "Sub-Pixel Interpolation Schemes" c section below (you may also use this array to pass values c to your own interpolation function). f section below (you may also use this array to pass values f to your own interpolation routine). * c If no additional parameters are required, this array is not c used and a NULL pointer may be given. f If no additional parameters are required, this array is not f used. A dummy (e.g. one-element) array may then be supplied. c flags f FLAGS = INTEGER (Given) c The bitwise OR of a set of flag values which may be used to f The sum of a set of flag values which may be used to * provide additional control over the resampling operation. See * the "Control Flags" section below for a description of the * options available. If no flag values are to be set, a value * of zero should be given. c tol f TOL = DOUBLE PRECISION (Given) * The maximum tolerable geometrical distortion which may be * introduced as a result of approximating non-linear Mappings * by a set of piece-wise linear transformations. This should be * expressed as a displacement in pixels in the input grid's * coordinate system. * * If piece-wise linear approximation is not required, a value * of zero may be given. This will ensure that the Mapping is * used without any approximation, but may increase execution * time. c maxpix f MAXPIX = INTEGER (Given) * A value which specifies an initial scale size (in pixels) for * the adaptive algorithm which approximates non-linear Mappings * with piece-wise linear transformations. Normally, this should * be a large value (larger than any dimension of the region of * the output grid being used). In this case, a first attempt to * approximate the Mapping by a linear transformation will be * made over the entire output region. * * If a smaller value is used, the output region will first be c divided into sub-regions whose size does not exceed "maxpix" f divided into sub-regions whose size does not exceed MAXPIX * pixels in any dimension. Only at this point will attempts at * approximation commence. * * This value may occasionally be useful in preventing false * convergence of the adaptive algorithm in cases where the * Mapping appears approximately linear on large scales, but has * irregularities (e.g. holes) on smaller scales. A value of, * say, 50 to 100 pixels can also be employed as a safeguard in * general-purpose software, since the effect on performance is * minimal. * * If too small a value is given, it will have the effect of * inhibiting linear approximation altogether (equivalent to c setting "tol" to zero). Although this may degrade f setting TOL to zero). Although this may degrade * performance, accurate results will still be obtained. c badval f BADVAL = (Given) * This argument should have the same type as the elements of c the "in" array. It specifies the value used to flag missing f the IN array. It specifies the value used to flag missing * data (bad pixels) in the input and output arrays. * c If the AST__USEBAD flag is set via the "flags" parameter, f If the AST__USEBAD flag is set via the FLAGS argument, c then this value is used to test for bad pixels in the "in" c (and "in_var") array(s). f then this value is used to test for bad pixels in the IN f (and IN_VAR) array(s). * c Unless the AST__NOBAD flag is set via the "flags" parameter, f Unless the AST__NOBAD flag is set via the FLAGS argument, * this value is also used to flag any output c elements in the "out" (and "out_var") array(s) for which f elements in the OUT (and OUT_VAR) array(s) for which * resampled values could not be obtained (see the "Propagation * of Missing Data" section below for details of the c circumstances under which this may occur). The astResample f circumstances under which this may occur). The AST_RESAMPLE * function return value indicates whether any such values have * been produced. If the AST__NOBAD flag is set. then output array * elements for which no resampled value could be obtained are * left set to the value they had on entry to this function. c ndim_out f NDIM_OUT = INTEGER (Given) * The number of dimensions in the output grid. This should be * at least one. It need not necessarily be equal to the number * of dimensions in the input grid. c lbnd_out f LBND_OUT( NDIM_OUT ) = INTEGER (Given) c Pointer to an array of integers, with "ndim_out" elements, f An array * containing the coordinates of the centre of the first pixel * in the output grid along each dimension. c ubnd_out f UBND_OUT( NDIM_OUT ) = INTEGER (Given) c Pointer to an array of integers, with "ndim_out" elements, f An array * containing the coordinates of the centre of the last pixel in * the output grid along each dimension. * c Note that "lbnd_out" and "ubnd_out" together define the f Note that LBND_OUT and UBND_OUT together define the * shape, size and coordinate system of the output grid in the c same way as "lbnd_in" and "ubnd_in" define the shape, size f same way as LBND_IN and UBND_IN define the shape, size * and coordinate system of the input grid. c lbnd f LBND( NDIM_OUT ) = INTEGER (Given) c Pointer to an array of integers, with "ndim_out" elements, f An array * containing the coordinates of the first pixel in the region * of the output grid for which a resampled value is to be * calculated. c ubnd f UBND( NDIM_OUT ) = INTEGER (Given) c Pointer to an array of integers, with "ndim_out" elements, f An array * containing the coordinates of the last pixel in the region of * the output grid for which a resampled value is to be * calculated. * c Note that "lbnd" and "ubnd" together define the shape and f Note that LBND and UBND together define the shape and * position of a (hyper-)rectangular region of the output grid * for which resampled values should be produced. This region * should lie wholly within the extent of the output grid (as c defined by the "lbnd_out" and "ubnd_out" arrays). Regions of f defined by the LBND_OUT and UBND_OUT arrays). Regions of * the output grid lying outside this region will not be * modified. c out f OUT( * ) = (Returned) c Pointer to an array, with one element for each pixel in the f An array, with one element for each pixel in the * output grid, into which the resampled data values will be * returned. The numerical type of this array should match that c of the "in" array, and the data storage order should be such f of the IN array, and the data storage order should be such * that the index of the first grid dimension varies most * rapidly and that of the final dimension least rapidly c (i.e. Fortran array indexing is used). f (i.e. normal Fortran array storage order). c out_var f OUT_VAR( * ) = (Returned) c An optional pointer to an array with the same type and size c as the "out" array. If given, this array will be used to c return variance estimates for the resampled data values. This c array will only be used if the "in_var" array has also been c supplied. f An optional array with the same type and size as the OUT f array. If the AST__USEVAR flag is set via the FLAGS argument, f this array will be used to return variance estimates for the f resampled data values. * * The output variance values will be calculated on the * assumption that errors on the input data values are * statistically independent and that their variance estimates * may simply be summed (with appropriate weighting factors) * when several input pixels contribute to an output data * value. If this assumption is not valid, then the output error * estimates may be biased. In addition, note that the * statistical errors on neighbouring output data values (as * well as the estimates of those errors) may often be * correlated, even if the above assumption about the input data * is correct, because of the sub-pixel interpolation schemes * employed. * c If no output variance estimates are required, a NULL pointer c should be given. f If the AST__USEVAR flag is not set, no output variance f estimates will be calculated and this array will not be f used. A dummy (e.g. one-element) array may then be supplied. f STATUS = INTEGER (Given and Returned) f The global status. * Returned Value: c astResample() f AST_RESAMPLE = INTEGER * The number of output pixels for which no valid resampled value * could be obtained. Thus, in the absence of any error, a returned * value of zero indicates that all the required output pixels * received valid resampled data values (and variances). See the c "badval" and "flags" parameters. f BADVAL and FLAGS arguments. * Notes: * - A value of zero will be returned if this function is invoked * with the global error status set, or if it should fail for any * reason. * Data Type Codes: * To select the appropriate resampling function, you should c replace in the generic function name astResample with a f replace in the generic function name AST_RESAMPLE with a * 1- or 2-character data type code, so as to match the numerical * type of the data you are processing, as follows: c - D: double c - F: float c - L: long int (may be 32 or 64 bit) c - K: 64 bit int c - UL: unsigned long int (may be 32 or 64 bit) c - UK: unsigned 64 bit int c - I: int c - UI: unsigned int c - S: short int c - US: unsigned short int c - B: byte (signed char) c - UB: unsigned byte (unsigned char) f - D: DOUBLE PRECISION f - R: REAL f - I: INTEGER f - UI: INTEGER (treated as unsigned) f - S: INTEGER*2 (short integer) f - US: INTEGER*2 (short integer, treated as unsigned) f - B: BYTE (treated as signed) f - UB: BYTE (treated as unsigned) * c For example, astResampleD would be used to process "double" c data, while astResampleS would be used to process "short int" c data, etc. f For example, AST_RESAMPLED would be used to process DOUBLE f PRECISION data, while AST_RESAMPLES would be used to process f short integer data (stored in an INTEGER*2 array), etc. f f For compatibility with other Starlink facilities, the codes W f and UW are provided as synonyms for S and US respectively (but f only in the Fortran interface to AST). * Sub-Pixel Interpolation Schemes: * There is no such thing as a perfect sub-pixel interpolation * scheme and, in practice, all resampling will result in some * degradation of gridded data. A range of schemes is therefore * provided, from which you can choose the one which best suits * your needs. * * In general, a balance must be struck between schemes which tend * to degrade sharp features in the data by smoothing them, and * those which attempt to preserve sharp features. The latter will * often tend to introduce unwanted oscillations, typically visible * as "ringing" around sharp features and edges, especially if the * data are under-sampled (i.e. if the sharpest features are less * than about two pixels across). In practice, a good interpolation * scheme is likely to be a compromise and may exhibit some aspects * of both these features. * * For under-sampled data, some interpolation schemes may appear to * preserve data resolution because they transform single input * pixels into single output pixels, rather than spreading their * data between several output pixels. While this may look * better cosmetically, it can result in a geometrical shift of * sharp features in the data. You should beware of this if you * plan to use such features (e.g.) for image alignment. * * The following are two easy-to-use sub-pixel interpolation * schemes which are generally applicable. They are selected by c supplying the appropriate value (defined in the "ast.h" header c file) via the "interp" parameter. In these cases, the "finterp" c and "params" parameters are not used: f supplying the appropriate value (defined in the AST_PAR include f file) via the INTERP argument. In these cases, the FINTERP f and PARAMS arguments are not used: * * - AST__NEAREST: This is the simplest possible scheme, in which * the value of the input pixel with the nearest centre to the * interpolation point is used. This is very quick to execute and * will preserve single-pixel features in the data, but may * displace them by up to half their width along each dimension. It * often gives a good cosmetic result, so is useful for quick-look * processing, but is unsuitable if accurate geometrical * transformation is required. * - AST__LINEAR: This is the default scheme, which uses linear * interpolation between the nearest neighbouring pixels in the * input grid (there are two neighbours in one dimension, four * neighbours in two dimensions, eight in three dimensions, * etc.). It is superior to the nearest-pixel scheme (above) in not * displacing features in the data, yet it still executes fairly * rapidly. It is generally a safe choice if you do not have any * particular reason to favour another scheme, since it cannot * introduce oscillations. However, it does introduce some spatial * smoothing which varies according to the distance of the * interpolation point from the neighbouring pixels. This can * degrade the shape of sharp features in the data in a * position-dependent way. It may also show in the output variance * grid (if used) as a pattern of stripes or fringes. * * An alternative set of interpolation schemes is based on forming * the interpolated value from the weighted sum of a set of * surrounding pixel values (not necessarily just the nearest * neighbours). This approach has its origins in the theory of * digital filtering, in which interpolated values are obtained by * conceptually passing the sampled data (represented by a grid of * delta functions) through a linear filter which implements a * convolution. Because the convolution kernel is continuous, the * convolution yields a continuous function which may then be * evaluated at fractional pixel positions. The (possibly * multi-dimensional) kernel is usually regarded as "separable" and * formed from the product of a set of identical 1-dimensional * kernel functions, evaluated along each dimension. Different * interpolation schemes are then distinguished by the choice of * this 1-dimensional interpolation kernel. The number of * surrounding pixels which contribute to the result may also be * varied. * * From a practical standpoint, it is useful to divide the weighted * sum of pixel values by the sum of the weights when determining * the interpolated value. Strictly, this means that a true * convolution is no longer being performed. However, the * distinction is rarely important in practice because (for * slightly subtle reasons) the sum of weights is always * approximately constant for good interpolation kernels. The * advantage of this technique, which is used here, is that it can * easily accommodate missing data and tends to minimise unwanted * oscillations at the edges of the data grid. * * In the following schemes, which are based on a 1-dimensional c interpolation kernel, the first element of the "params" array f interpolation kernel, the first element of the PARAMS array * should be used to specify how many pixels are to contribute to the * interpolated result on either side of the interpolation point in * each dimension (the nearest integer value is used). Execution time * increases rapidly with this number. Typically, a value of 2 is * appropriate and the minimum value used will be 1 (i.e. two pixels * altogether, one on either side of the interpolation point). c A value of zero or less may be given for "params[0]" f A value of zero or less may be given for PARAMS(1) * to indicate that a suitable number of pixels should be calculated * automatically. * c In each of these cases, the "finterp" parameter is not used: f In each of these cases, the FINTERP argument is not used: * * - AST__GAUSS: This scheme uses a kernel of the form exp(-k*x*x), with * k a positive constant. The full-width at half-maximum (FWHM) is * given by c "params[1]" f PARAMS(2) f value, which should be at least 0.1 (in addition, setting PARAMS(1) * to zero will select the number of contributing pixels so as to utilise * the width of the kernel out to where the envelope declines to 1% of its * maximum value). This kernel suppresses noise at the expense of * smoothing the output array. * - AST__SINC: This scheme uses a sinc(pi*x) kernel, where x is the * pixel offset from the interpolation point and sinc(z)=sin(z)/z. This * sometimes features as an "optimal" interpolation kernel in books on * image processing. Its supposed optimality depends on the assumption * that the data are band-limited (i.e. have no spatial frequencies above * a certain value) and are adequately sampled. In practice, astronomical * data rarely meet these requirements. In addition, high spatial * frequencies are often present due (e.g.) to image defects and cosmic * ray events. Consequently, substantial ringing can be experienced with * this kernel. The kernel also decays slowly with distance, so that * many surrounding pixels are required, leading to poor performance. * Abruptly truncating it, by using only a few neighbouring pixels, c improves performance and may reduce ringing (if "params[0]" is set to f improves performance and may reduce ringing (if PARAMS(1) is set to * zero, then only two pixels will be used on either side). However, a * more gradual truncation, as implemented by other kernels, is generally * to be preferred. This kernel is provided mainly so that you can * convince yourself not to use it! * - AST__SINCSINC: This scheme uses an improved kernel, of the form * sinc(pi*x).sinc(k*pi*x), with k a constant, out to the point where * sinc(k*pi*x) goes to zero, and zero beyond. The second sinc() factor * provides an "envelope" which gradually rolls off the normal sinc(pi*x) * kernel at large offsets. The width of this envelope is specified by * giving the number of pixels offset at which it goes to zero by means c of the "params[1]" value, which should be at least 1.0 (in addition, c setting "params[0]" to zero will select the number of contributing f of the PARAMS(2) value, which should be at least 1.0 (in addition, f setting PARAMS(1) to zero will select the number of contributing * pixels so as to utilise the full width of the kernel, out to where it c reaches zero). The case given by "params[0]=2, params[1]=2" is typically f reaches zero). The case given by PARAMS(1)=2, PARAMS(2)=2 is typically * a good choice and is sometimes known as the Lanczos kernel. This is a * valuable general-purpose interpolation scheme, intermediate in its * visual effect on images between the AST__NEAREST and AST__LINEAR * schemes. Although the kernel is slightly oscillatory, ringing is * adequately suppressed if the data are well sampled. * - AST__SINCCOS: This scheme uses a kernel of the form * sinc(pi*x).cos(k*pi*x), with k a constant, out to the point where * cos(k*pi*x) goes to zero, and zero beyond. As above, the cos() factor * provides an envelope which gradually rolls off the sinc() kernel * at large offsets. The width of this envelope is specified by giving * the number of pixels offset at which it goes to zero by means c of the "params[1]" value, which should be at least 1.0 (in addition, c setting "params[0]" to zero will select the number of contributing f of the PARAMS(2) value, which should be at least 1.0 (in addition, f setting PARAMS(1) to zero will select the number of contributing * pixels so as to utilise the full width of the kernel, out to where it * reaches zero). This scheme gives similar results to the * AST__SINCSINC scheme, which it resembles. * - AST__SINCGAUSS: This scheme uses a kernel of the form * sinc(pi*x).exp(-k*x*x), with k a positive constant. Here, the sinc() * kernel is rolled off using a Gaussian envelope which is specified by c giving its full-width at half-maximum (FWHM) by means of the "params[1]" c value, which should be at least 0.1 (in addition, setting "params[0]" f giving its full-width at half-maximum (FWHM) by means of the PARAMS(2) f value, which should be at least 0.1 (in addition, setting PARAMS(1) * to zero will select the number of contributing pixels so as to utilise * the width of the kernel out to where the envelope declines to 1% of its * maximum value). On astronomical images and spectra, good results are * often obtained by approximately matching the FWHM of the c envelope function, given by "params[1]", to the point spread function f envelope function, given by PARAMS(2), to the point spread function * of the input data. However, there does not seem to be any theoretical * reason for this. * - AST__SOMB: This scheme uses a somb(pi*x) kernel (a "sombrero" * function), where x is the pixel offset from the interpolation point * and somb(z)=2*J1(z)/z (J1 is a Bessel function of the first kind of * order 1). It is similar to the AST__SINC kernel, and has the same * parameter usage. * - AST__SOMBCOS: This scheme uses a kernel of the form * somb(pi*x).cos(k*pi*x), with k a constant, out to the point where * cos(k*pi*x) goes to zero, and zero beyond. It is similar to the * AST__SINCCOS kernel, and has the same parameter usage. * * In addition, the following schemes are provided which are not based * on a 1-dimensional kernel: * * - AST__BLOCKAVE: This scheme simply takes an average of all the * pixels on the input grid in a cube centred on the interpolation * point. The number of pixels in the cube is determined by the c value of the first element of the "params" array, which gives f value of the first element of the PARAMS array, which gives * the number of pixels in each dimension on either side of the c central point. Hence a block of (2 * params[0])^ndim_in f central point. Hence a block of (2 * PARAMS(1))**NDIM_IN * pixels in the input grid will be examined to determine the * value of the output pixel. If the variance is not being used c (var_in or var_out = NULL) then all valid pixels in this cube f (USEVAR = .FALSE.) then all valid pixels in this cube * will be averaged in to the result with equal weight. * If variances are being used, then each input pixel will be * weighted proportionally to the reciprocal of its variance; any * pixel without a valid variance will be discarded. This scheme * is suitable where the output grid is much coarser than the * input grid; if the ratio of pixel sizes is R then a suitable c value of params[0] may be R/2. f value of PARAMS(1) may be R/2. * c Finally, supplying the following values for "interp" allows you c to implement your own sub-pixel interpolation scheme by means of c your own function. You should supply a pointer to this function c via the "finterp" parameter: f Finally, supplying the following values for INTERP allows you to f implement your own sub-pixel interpolation scheme by means of f your own routine. You should supply the name of this routine via f the FINTERP argument: * c - AST__UKERN1: In this scheme, you supply a function to evaluate c your own 1-dimensional interpolation kernel, which is then used c to perform sub-pixel interpolation (as described above). The c function you supply should have the same interface as the c fictitious astUkern1 function (q.v.). In addition, a value c should be given via "params[0]" to specify the number of c neighbouring pixels which are to contribute to each interpolated c value (in the same way as for the pre-defined interpolation c schemes described above). Other elements of the "params" array c are available to pass values to your interpolation function. f - AST__UKERN1: In this scheme, you supply a routine to evaluate f your own 1-dimensional interpolation kernel, which is then used f to perform sub-pixel interpolation (as described above). The f routine you supply should have the same interface as the f fictitious AST_UKERN1 routine (q.v.). In addition, a value f should be given via PARAMS(1) to specify the number of f neighbouring pixels which are to contribute to each interpolated f value (in the same way as for the pre-defined interpolation f schemes described above). Other elements of the PARAMS array f are available to pass values to your interpolation routine. * c - AST__UINTERP: This is a completely general scheme, in which c your interpolation function has access to all of the input c data. This allows you to implement any interpolation algorithm c you choose, which could (for example) be non-linear, or c adaptive. In this case, the astResample functions play no c role in the sub-pixel interpolation process and simply handle c the geometrical transformation of coordinates and other c housekeeping. The function you supply should have the same c interface as the fictitious astUinterp function (q.v.). In this c case, the "params" parameter is not used by astResample, but c is available to pass values to your interpolation function. f - AST__UINTERP: This is a completely general scheme, in which f your interpolation routine has access to all of the input f data. This allows you to implement any interpolation algorithm f you choose, which could (for example) be non-linear, or f adaptive. In this case, the AST_RESAMPLE functions play no f role in the sub-pixel interpolation process and simply handle f the geometrical transformation of coordinates and other f housekeeping. The routine you supply should have the same f interface as the fictitious AST_UINTERP routine (q.v.). In this f case, the PARAMS argument is not used by AST_RESAMPLE, but f is available to pass values to your interpolation routine. * Control Flags: c The following flags are defined in the "ast.h" header file and f The following flags are defined in the AST_PAR include file and * may be used to provide additional control over the resampling * process. Having selected a set of flags, you should supply the c bitwise OR of their values via the "flags" parameter: f sum of their values via the FLAGS argument: * * - AST__NOBAD: Indicates that any output array elements for which no * resampled value could be obtained should be left set to the value * they had on entry to this function. If this flag is not supplied, * such output array elements are set to the value supplied for c parameter "badval". Note, this flag cannot be used in conjunction f argument BADVAL. Note, this flag cannot be used in conjunction * with the AST__CONSERVEFLUX flag (an error will be reported if both * flags are specified). * - AST__URESAMP1, 2, 3 & 4: A set of four flags which are * reserved for your own use. They may be used to pass private c information to any sub-pixel interpolation function which you f information to any sub-pixel interpolation routine which you * implement yourself. They are ignored by all the pre-defined * interpolation schemes. * - AST__USEBAD: Indicates that there may be bad pixels in the * input array(s) which must be recognised by comparing with the c value given for "badval" and propagated to the output array(s). f value given for BADVAL and propagated to the output array(s). * If this flag is not set, all input values are treated literally c and the "badval" value is only used for flagging output array f and the BADVAL value is only used for flagging output array * values. f - AST__USEVAR: Indicates that variance information should be f processed in order to provide estimates of the statistical error f associated with the resampled values. If this flag is not set, f no variance processing will occur and the IN_VAR and OUT_VAR f arrays will not be used. (Note that this flag is only available f in the Fortran interface to AST.) * - AST__CONSERVEFLUX: Indicates that the output pixel values should * be scaled in such a way as to preserve (approximately) the total data * value in a feature on the sky. Without this flag, each output pixel * value represents an instantaneous sample of the input data values at * the corresponding input position. This is appropriate if the input * data represents the spatial density of some quantity (e.g. surface * brightness in Janskys per square arc-second) because the output * pixel values will have the same normalisation and units as the * input pixel values. However, if the input data values represent * flux (or some other physical quantity) per pixel, then the * AST__CONSERVEFLUX flag could be used. This causes each output * pixel value to be scaled by the ratio of the output pixel size to * the input pixel size. * * This flag can only be used if the Mapping is successfully approximated * by one or more linear transformations. Thus an error will be reported * if it used when the c "tol" parameter f TOL argument * is set to zero (which stops the use of linear approximations), or * if the Mapping is too non-linear to be approximated by a piece-wise * linear transformation. The ratio of output to input pixel size is * evaluated once for each panel of the piece-wise linear approximation to * the Mapping, and is assumed to be constant for all output pixels in the * panel. The scaling factors for adjacent panels will in general * differ slightly, and so the joints between panels may be visible when * viewing the output image at high contrast. If this is a problem, * reduce the value of the c "tol" parameter f TOL argument * until the difference between adjacent panels is sufficiently small * to be insignificant. * * Note, this flag cannot be used in conjunction with the AST__NOBAD * flag (an error will be reported if both flags are specified). * Propagation of Missing Data: * Unless the AST__NOBAD flag is specified, instances of missing data * (bad pixels) in the output grid are c identified by occurrences of the "badval" value in the "out" f identified by occurrences of the BADVAL value in the OUT * array. These may be produced if any of the following happen: * * - The input position (the transformed position of the output * pixel's centre) lies outside the boundary of the grid of input * pixels. * - The input position lies inside the boundary of a bad input * pixel. In this context, an input pixel is considered bad if its c data value is equal to "badval" and the AST__USEBAD flag is c set via the "flags" parameter. f data value is equal to BADVAL and the AST__USEBAD flag is f set via the FLAGS argument. * (Positions which have half-integral coordinate values, and * therefore lie on a pixel boundary, are regarded as lying within * the pixel with the larger, i.e. more positive, index.) * - The set of neighbouring input pixels (excluding those which * are bad) is unsuitable for calculating an interpolated * value. Whether this is true may depend on the sub-pixel * interpolation scheme in use. * - The interpolated value lies outside the range which can be c represented using the data type of the "out" array. f represented using the data type of the OUT array. * * In addition, associated output variance estimates (if c calculated) may be declared bad and flagged with the "badval" c value in the "out_var" array under any of the following f calculated) may be declared bad and flagged with the BADVAL f value in the OUT_VAR array under any of the following * circumstances: * c - The associated resampled data value (in the "out" array) is bad. f - The associated resampled data value (in the OUT array) is bad. * - The set of neighbouring input pixels which contributed to the * output data value do not all have valid variance estimates * associated with them. In this context, an input variance * estimate may be regarded as bad either because it has the value c "badval" (and the AST__USEBAD flag is set), or because it is f BADVAL (and the AST__USEBAD flag is set), or because it is * negative. * - The set of neighbouring input pixels for which valid variance * values are available is unsuitable for calculating an overall * variance value. Whether this is true may depend on the sub-pixel * interpolation scheme in use. * - The variance value lies outside the range which can be c represented using the data type of the "out_var" array. f represented using the data type of the OUT_VAR array. * * If the AST__NOBAD flag is specified via c parameter "flags", f argument FLAGS, * then output array elements that would otherwise be set to c "badval" f BADVAL * are instead left holding the value they had on entry to this * function. The number of such array elements is returned as * the function value. *-- */ /* Define a macro to implement the function for a specific data type. */ #define MAKE_RESAMPLE(X,Xtype) \ static int Resample##X( AstMapping *this, int ndim_in, \ const int lbnd_in[], const int ubnd_in[], \ const Xtype in[], const Xtype in_var[], \ int interp, void (* finterp)( void ), \ const double params[], int flags, double tol, \ int maxpix, Xtype badval, \ int ndim_out, const int lbnd_out[], \ const int ubnd_out[], const int lbnd[], \ const int ubnd[], Xtype out[], Xtype out_var[], int *status ) { \ \ /* Local Variables: */ \ astDECLARE_GLOBALS /* Thread-specific data */ \ AstMapping *simple; /* Pointer to simplified Mapping */ \ int idim; /* Loop counter for coordinate dimensions */ \ int nin; /* Number of Mapping input coordinates */ \ int nout; /* Number of Mapping output coordinates */ \ int npix; /* Number of pixels in output region */ \ int result; /* Result value to return */ \ int64_t mpix; /* Number of pixels for testing */ \ \ /* Initialise. */ \ result = 0; \ \ /* Check the global error status. */ \ if ( !astOK ) return result; \ \ /* Get a pointer to a structure holding thread-specific global data values */ \ astGET_GLOBALS(this); \ \ /* Obtain values for the Nin and Nout attributes of the Mapping. */ \ nin = astGetNin( this ); \ nout = astGetNout( this ); \ \ /* If OK, check that the number of input grid dimensions matches the \ number required by the Mapping and is at least 1. Report an error \ if necessary. */ \ if ( astOK && ( ( ndim_in != nin ) || ( ndim_in < 1 ) ) ) { \ astError( AST__NGDIN, "astResample"#X"(%s): Bad number of input grid " \ "dimensions (%d).", status, astGetClass( this ), ndim_in ); \ if ( ndim_in != nin ) { \ astError( AST__NGDIN, "The %s given requires %d coordinate value%s " \ "to specify an input position.", status, \ astGetClass( this ), nin, ( nin == 1 ) ? "" : "s" ); \ } \ } \ \ /* If OK, also check that the number of output grid dimensions matches \ the number required by the Mapping and is at least 1. Report an \ error if necessary. */ \ if ( astOK && ( ( ndim_out != nout ) || ( ndim_out < 1 ) ) ) { \ astError( AST__NGDIN, "astResample"#X"(%s): Bad number of output grid " \ "dimensions (%d).", status, astGetClass( this ), ndim_out ); \ if ( ndim_out != nout ) { \ astError( AST__NGDIN, "The %s given generates %s%d coordinate " \ "value%s for each output position.", status, astGetClass( this ), \ ( nout < ndim_out ) ? "only " : "", nout, \ ( nout == 1 ) ? "" : "s" ); \ } \ } \ \ /* Check that the lower and upper bounds of the input grid are \ consistent. Report an error if any pair is not. Also get the number \ of pixels in the input grid. */ \ mpix = 1; \ if ( astOK ) { \ for ( idim = 0; idim < ndim_in; idim++ ) { \ if ( lbnd_in[ idim ] > ubnd_in[ idim ] ) { \ astError( AST__GBDIN, "astResample"#X"(%s): Lower bound of " \ "input grid (%d) exceeds corresponding upper bound " \ "(%d).", status, astGetClass( this ), \ lbnd_in[ idim ], ubnd_in[ idim ] ); \ astError( AST__GBDIN, "Error in input dimension %d.", status, \ idim + 1 ); \ break; \ } else { \ mpix *= ubnd_in[ idim ] - lbnd_in[ idim ] + 1; \ } \ } \ } \ \ /* Report an error if there are too many pixels in the input. */ \ if ( astOK && (int) mpix != mpix ) { \ astError( AST__EXSPIX, "astResample"#X"(%s): Supplied input array " \ "contains too many pixels (%g): must be fewer than %d.", \ status, astGetClass( this ), (double) mpix, INT_MAX ); \ } \ \ /* Check that the positional accuracy tolerance supplied is valid and \ report an error if necessary. */ \ if ( astOK && ( tol < 0.0 ) ) { \ astError( AST__PATIN, "astResample"#X"(%s): Invalid positional " \ "accuracy tolerance (%.*g pixel).", status, \ astGetClass( this ), AST__DBL_DIG, tol ); \ astError( AST__PATIN, "This value should not be less than zero." , status); \ } \ \ /* Check that the initial scale size in pixels supplied is valid and \ report an error if necessary. */ \ if ( astOK && ( maxpix < 0 ) ) { \ astError( AST__SSPIN, "astResample"#X"(%s): Invalid initial scale " \ "size in pixels (%d).", status, astGetClass( this ), maxpix ); \ astError( AST__SSPIN, "This value should not be less than zero." , status); \ } \ \ /* Check that the lower and upper bounds of the output grid are \ consistent. Report an error if any pair is not. Also get the \ number of pixels in the output array. */ \ mpix = 1; \ if ( astOK ) { \ for ( idim = 0; idim < ndim_out; idim++ ) { \ if ( lbnd_out[ idim ] > ubnd_out[ idim ] ) { \ astError( AST__GBDIN, "astResample"#X"(%s): Lower bound of " \ "output grid (%d) exceeds corresponding upper bound " \ "(%d).", status, astGetClass( this ), \ lbnd_out[ idim ], ubnd_out[ idim ] ); \ astError( AST__GBDIN, "Error in output dimension %d.", status, \ idim + 1 ); \ break; \ } else { \ mpix *= ubnd_out[ idim ] - lbnd_out[ idim ] + 1; \ } \ } \ } \ \ /* Report an error if there are too many pixels in the output. */ \ if ( astOK && (int) mpix != mpix ) { \ astError( AST__EXSPIX, "astResample"#X"(%s): Supplied output array " \ "contains too many pixels (%g): must be fewer than %d.", \ status, astGetClass( this ), (double) mpix, INT_MAX ); \ } \ \ /* Similarly check the bounds of the output region. */ \ mpix = 1; \ if ( astOK ) { \ for ( idim = 0; idim < ndim_out; idim++ ) { \ if ( lbnd[ idim ] > ubnd[ idim ] ) { \ astError( AST__GBDIN, "astResample"#X"(%s): Lower bound of " \ "output region (%d) exceeds corresponding upper " \ "bound (%d).", status, astGetClass( this ), \ lbnd[ idim ], ubnd[ idim ] ); \ \ /* Also check that the output region lies wholly within the output \ grid. */ \ } else if ( lbnd[ idim ] < lbnd_out[ idim ] ) { \ astError( AST__GBDIN, "astResample"#X"(%s): Lower bound of " \ "output region (%d) is less than corresponding " \ "bound of output grid (%d).", status, astGetClass( this ), \ lbnd[ idim ], lbnd_out[ idim ] ); \ } else if ( ubnd[ idim ] > ubnd_out[ idim ] ) { \ astError( AST__GBDIN, "astResample"#X"(%s): Upper bound of " \ "output region (%d) exceeds corresponding " \ "bound of output grid (%d).", status, astGetClass( this ), \ ubnd[ idim ], ubnd_out[ idim ] ); \ } else { \ mpix *= ubnd[ idim ] - lbnd[ idim ] + 1; \ } \ \ /* Say which dimension produced the error. */ \ if ( !astOK ) { \ astError( AST__GBDIN, "Error in output dimension %d.", status, \ idim + 1 ); \ break; \ } \ } \ } \ \ /* Report an error if there are too many pixels in the output region. */ \ if ( astOK && (int) mpix != mpix ) { \ astError( AST__EXSPIX, "astResample"#X"(%s): Supplied output region " \ "contains too many pixels (%g): must be fewer than %d.", \ status, astGetClass( this ), (double) mpix, INT_MAX ); \ } \ \ /* If we are conserving flux, check "tol" is not zero. */ \ if( ( flags & AST__CONSERVEFLUX ) && astOK ) { \ if( tol == 0.0 ) { \ astError( AST__CNFLX, "astResample"#X"(%s): Flux conservation was " \ "requested but cannot be performed because zero tolerance " \ "was also specified.", status, astGetClass( this ) ); \ \ /* Also check "nin" and "nout" are equal. */ \ } else if( nin != nout ) { \ astError( AST__CNFLX, "astResample"#X"(%s): Flux conservation was " \ "requested but cannot be performed because the Mapping " \ "has different numbers of inputs and outputs.", status, \ astGetClass( this ) ); \ } \ } \ \ /* If OK, loop to determine how many pixels require resampled values. */ \ simple = NULL; \ if ( astOK ) { \ npix = 1; \ for ( idim = 0; idim < ndim_out; idim++ ) { \ npix *= ubnd[ idim ] - lbnd[ idim ] + 1; \ } \ \ /* If there are sufficient pixels to make it worthwhile, simplify the \ Mapping supplied to improve performance. Otherwise, just clone the \ Mapping pointer. Note we save a pointer to the original Mapping so \ that lower-level functions can use it if they need to report an \ error. */ \ unsimplified_mapping = this; \ if ( npix > 1024 ) { \ simple = astSimplify( this ); \ } else { \ simple = astClone( this ); \ } \ } \ \ /* Report an error if the inverse transformation of this simplified \ Mapping is not defined. */ \ if ( !astGetTranInverse( simple ) && astOK ) { \ astError( AST__TRNND, "astResample"#X"(%s): An inverse coordinate " \ "transformation is not defined by the %s supplied.", status, \ astGetClass( unsimplified_mapping ), \ astGetClass( unsimplified_mapping ) ); \ } \ \ /* Perform the resampling. Note that we pass all gridded data, the \ interpolation function and the bad pixel value by means of pointer \ types that obscure the underlying data type. This is to avoid \ having to replicate functions unnecessarily for each data \ type. However, we also pass an argument that identifies the data \ type we have obscured. */ \ result = ResampleAdaptively( simple, ndim_in, lbnd_in, ubnd_in, \ (const void *) in, (const void *) in_var, \ TYPE_##X, interp, finterp, \ params, flags, tol, maxpix, \ (const void *) &badval, \ ndim_out, lbnd_out, ubnd_out, \ lbnd, ubnd, \ (void *) out, (void *) out_var, status ); \ \ /* Annul the pointer to the simplified/cloned Mapping. */ \ simple = astAnnul( simple ); \ \ /* If an error occurred, clear the returned result. */ \ if ( !astOK ) result = 0; \ \ /* Return the result. */ \ return result; \ } /* Expand the above macro to generate a function for each required data type. */ #if HAVE_LONG_DOUBLE /* Not normally implemented */ MAKE_RESAMPLE(LD,long double) #endif MAKE_RESAMPLE(D,double) MAKE_RESAMPLE(F,float) MAKE_RESAMPLE(L,long int) MAKE_RESAMPLE(UL,unsigned long int) MAKE_RESAMPLE(K,INT_BIG) MAKE_RESAMPLE(UK,UINT_BIG) MAKE_RESAMPLE(I,int) MAKE_RESAMPLE(UI,unsigned int) MAKE_RESAMPLE(S,short int) MAKE_RESAMPLE(US,unsigned short int) MAKE_RESAMPLE(B,signed char) MAKE_RESAMPLE(UB,unsigned char) /* Undefine the macro. */ #undef MAKE_RESAMPLE static int ResampleAdaptively( AstMapping *this, int ndim_in, const int *lbnd_in, const int *ubnd_in, const void *in, const void *in_var, DataType type, int interp, void (* finterp)( void ), const double *params, int flags, double tol, int maxpix, const void *badval_ptr, int ndim_out, const int *lbnd_out, const int *ubnd_out, const int *lbnd, const int *ubnd, void *out, void *out_var, int *status ) { /* * Name: * ResampleAdaptively * Purpose: * Resample a section of a data grid adaptively. * Type: * Private function. * Synopsis: * #include "mapping.h" * int ResampleAdaptively( AstMapping *this, int ndim_in, * const int *lbnd_in, const int *ubnd_in, * const void *in, const void *in_var, * DataType type, int interp, void (* finterp)( void ), * const double *params, int flags, double tol, * int maxpix, const void *badval_ptr, * int ndim_out, const int *lbnd_out, * const int *ubnd_out, const int *lbnd, * const int *ubnd, void *out, void *out_var ) * Class Membership: * Mapping member function. * Description: * This function resamples a rectangular grid of data (with any * number of dimensions) into a specified section of another * rectangular grid (with a possibly different number of * dimensions). The coordinate transformation used to convert * output pixel coordinates into positions in the input grid is * given by the inverse transformation of the Mapping which is * supplied. Any pixel interpolation scheme may be specified for * interpolating between the pixels of the input grid. * * This function is very similar to ResampleWithBlocking and * ResampleSection which lie below it in the calling * hierarchy. However, this function also attempts to adapt to the * Mapping supplied and to sub-divide the section being resampled * into smaller sections within which a linear approximation to the * Mapping may be used. This reduces the number of Mapping * evaluations, thereby improving efficiency particularly when * complicated Mappings are involved. * Parameters: * this * Pointer to a Mapping, whose inverse transformation may be * used to transform the coordinates of pixels in the output * grid into associated positions in the input grid, from which * the output pixel values should be derived (by interpolation * if necessary). * * The number of input coordintes for the Mapping (Nin * attribute) should match the value of "ndim_in" (below), and * the number of output coordinates (Nout attribute) should * match the value of "ndim_out". * ndim_in * The number of dimensions in the input grid. This should be at * least one. * lbnd_in * Pointer to an array of integers, with "ndim_in" elements. * This should give the coordinates of the centre of the first * pixel in the input data grid along each dimension. * ubnd_in * Pointer to an array of integers, with "ndim_in" elements. * This should give the coordinates of the centre of the last * pixel in the input data grid along each dimension. * * Note that "lbnd_in" and "ubnd_in" together define the shape * and size of the input data grid, its extent along a * particular (i'th) dimension being (ubnd_in[i] - lbnd_in[i] + * 1). They also define the input grid's coordinate system, with * each pixel being of unit extent along each dimension with * integral coordinate values at its centre. * in * Pointer to the input array of data to be resampled (with one * element for each pixel in the input grid). The numerical type * of these data should match the "type" value (below). The * storage order should be such that the coordinate of the first * dimension varies most rapidly and that of the final dimension * least rapidly (i.e. Fortran array storage order is used). * in_var * An optional pointer to a second array of positive numerical * values (with the same size and data type as the "in" array), * which represent estimates of the statistical variance * associated with each element of the "in" array. If this * second array is given (along with the corresponding "out_var" * array), then estimates of the variance of the resampled data * will also be returned. * * If no variance estimates are required, a NULL pointer should * be given. * type * A value taken from the "DataType" enum, which specifies the * data type of the input and output arrays containing the * gridded data (and variance) values. * interp * A value selected from a set of pre-defined macros to identify * which sub-pixel interpolation algorithm should be used. * finterp * If "interp" is set to a value which requires a user-supplied * function, then a pointer to that function shoild be given * here. Otherwise, this value is not used and may be a NULL * pointer. * params * Pointer to an optional array of parameters that may be passed * to the interpolation algorithm, if required. If no parameters * are required, a NULL pointer should be supplied. * flags * The bitwise OR of a set of flag values which provide * additional control over the resampling operation. * tol * The maximum permitted positional error in transforming output * pixel positions into the input grid in order to resample * it. This should be expressed as a displacement in pixels in * the input grid's coordinate system. If the Mapping's inverse * transformation can be approximated by piecewise linear * functions to this accuracy, then such functions may be used * instead of the Mapping in order to improve * performance. Otherwise, every output pixel position will be * transformed individually using the Mapping. * * If linear approximation is not required, a "tol" value of * zero may be given. This will ensure that the Mapping is used * without any approximation. * maxpix * A value which specifies the largest scale size on which to * search for non-linearities in the Mapping supplied. This * value should be expressed as a number of pixels in the output * grid. The function will break the output section specified * into smaller sub-sections (if necessary), each no larger than * "maxpix" pixels in any dimension, before it attempts to * approximate the Mapping by a linear function over each * sub-section. * * If the value given is larger than the largest dimension of * the output section (the normal recommendation), the function * will initially search for non-linearity on a scale determined * by the size of the output section. This is almost always * satisfactory. Very occasionally, however, a Mapping may * appear linear on this scale but nevertheless have smaller * irregularities (e.g. "holes") in it. In such cases, "maxpix" * may be set to a suitably smaller value so as to ensure this * non-linearity is not overlooked. Typically, a value of 50 to * 100 pixels might be suitable and should have little effect on * performance. * * If too small a value is given, however, it will have the * effect of preventing linear approximation occurring at all * (equivalent to setting "tol" to zero). Although this may * degrade performance, accurate results will still be obtained. * badval_ptr * If the AST__USEBAD flag is set (above), this parameter is a * pointer to a value which is used to identify bad data and/or * variance values in the input array(s). The referenced value's * data type must match that of the "in" (and "in_var") * arrays. Unless the AST__NOBAD flag is set, the same value will * also be used to flag any output array elements for which * resampled values could not be obtained. The output arrays(s) * may be flagged with this value whether or not the AST__USEBAD * flag is set (the function return value indicates whether any * such values have been produced). * ndim_out * The number of dimensions in the output grid. This should be * at least one. * lbnd_out * Pointer to an array of integers, with "ndim_out" elements. * This should give the coordinates of the centre of the first * pixel in the output data grid along each dimension. * ubnd_out * Pointer to an array of integers, with "ndim_out" elements. * This should give the coordinates of the centre of the last * pixel in the output data grid along each dimension. * * Note that "lbnd_out" and "ubnd_out" together define the shape * and size of the output data grid in the same way as "lbnd_in" * and "ubnd_in" define the shape and size of the input grid * (see above). * lbnd * Pointer to an array of integers, with "ndim_out" elements. * This should give the coordinates of the first pixel in the * section of the output data grid for which a value is * required. * ubnd * Pointer to an array of integers, with "ndim_out" elements. * This should give the coordinates of the last pixel in the * section of the output data grid for which a value is * required. * * Note that "lbnd" and "ubnd" define the shape and position of * the section of the output grid for which resampled values are * required. This section should lie wholly within the extent of * the output grid (as defined by the "lbnd_out" and "ubnd_out" * arrays). Regions of the output grid lying outside this section * will not be modified. * out * Pointer to an array with the same data type as the "in" * array, into which the resampled data will be returned. The * storage order should be such that the coordinate of the first * dimension varies most rapidly and that of the final dimension * least rapidly (i.e. Fortran array storage order is used). * out_var * An optional pointer to an array with the same data type and * size as the "out" array, into which variance estimates for * the resampled values may be returned. This array will only be * used if the "in_var" array has been given. * * If no output variance estimates are required, a NULL pointer * should be given. * Returned Value: * The number of output grid points for which no valid output value * could be obtained. * Notes: * - A value of zero will be returned if this function is invoked * with the global error status set, or if it should fail for any * reason. */ /* Local Variables: */ double *flbnd; /* Array holding floating point lower bounds */ double *fubnd; /* Array holding floating point upper bounds */ double *linear_fit; /* Pointer to array of fit coefficients */ int *hi; /* Pointer to array of section upper bounds */ int *lo; /* Pointer to array of section lower bounds */ int coord_out; /* Loop counter for output coordinates */ int dim; /* Output section dimension size */ int dimx; /* Dimension with maximum section extent */ int divide; /* Sub-divide the output section? */ int i; /* Loop count */ int isLinear; /* Is the transformation linear? */ int mxdim; /* Largest output section dimension size */ int npix; /* Number of pixels in output section */ int npoint; /* Number of points for obtaining a fit */ int nvertex; /* Number of vertices of output section */ int result; /* Result value to return */ int toobig; /* Section too big (must sub-divide)? */ int toosmall; /* Section too small to sub-divide? */ /* Initialise. */ result = 0; /* Check the global error status. */ if ( !astOK ) return result; /* Further initialisation. */ npix = 1; mxdim = 0; dimx = 1; nvertex = 1; /* Loop through the output grid dimensions. */ for ( coord_out = 0; coord_out < ndim_out; coord_out++ ) { /* Obtain the extent in each dimension of the output section which is to receive resampled values, and calculate the total number of pixels it contains. */ dim = ubnd[ coord_out ] - lbnd[ coord_out ] + 1; npix *= dim; /* Find the maximum dimension size of this output section and note which dimension has this size. */ if ( dim > mxdim ) { mxdim = dim; dimx = coord_out; } /* Calculate how many vertices the output section has. */ nvertex *= 2; } /* Calculate how many sample points will be needed (by the astLinearApprox function) to obtain a linear fit to the Mapping's inverse transformation. */ npoint = 1 + 4 * ndim_out + 2 * nvertex; /* If the number of pixels in the output section is not at least 4 times this number, we will probably not save significant time by attempting to obtain a linear fit, so note that the output section is too small. */ toosmall = ( npix < ( 4 * npoint ) ); /* Note if the maximum dimension of the output section exceeds the user-supplied scale factor. */ toobig = ( maxpix < mxdim ); /* Assume the Mapping is significantly non-linear before deciding whether to sub-divide the output section. */ linear_fit = NULL; /* If the output section is too small to be worth obtaining a linear fit, or if the accuracy tolerance is zero, we will not sub-divide. This means that the Mapping will be used to transform each pixel's coordinates and no linear approximation will be used. */ if ( toosmall || ( tol == 0.0 ) ) { divide = 0; /* Otherwise, if the largest output section dimension exceeds the scale length given, we will sub-divide. This offers the possibility of obtaining a linear approximation to the Mapping over a reduced range of output coordinates (which will be handled by a recursive invocation of this function). */ } else if ( toobig ) { divide = 1; /* If neither of the above apply, then attempt to fit a linear approximation to the Mapping's inverse transformation over the range of coordinates covered by the output section. We need to temporarily copy the integer bounds into floating point arrays to use astLinearApprox. */ } else { /* Allocate memory for floating point bounds and for the coefficient array */ flbnd = astMalloc( sizeof( double )*(size_t) ndim_out ); fubnd = astMalloc( sizeof( double )*(size_t) ndim_out ); linear_fit = astMalloc( sizeof( double )* (size_t) ( ndim_in*( ndim_out + 1 ) ) ); if( astOK ) { /* Copy the bounds into these arrays, and change them so that they refer to the lower and upper edges of the cell rather than the centre. This is essential if one of the axes is spanned by a single cell, since otherwise the upper and lower bounds would be identical. */ for( i = 0; i < ndim_out; i++ ) { flbnd[ i ] = (double) lbnd[ i ] - 0.5; fubnd[ i ] = (double) ubnd[ i ] + 0.5; } /* Get the linear approximation to the inverse transformation. The astLinearApprox function fits the forward transformation so temporarily invert the Mapping in order to get a fit to the inverse transformation. */ astInvert( this ); isLinear = astLinearApprox( this, flbnd, fubnd, tol, linear_fit ); astInvert( this ); /* Free the coeff array if the inverse transformation is not linear. */ if( !isLinear ) linear_fit = astFree( linear_fit ); } else { linear_fit = astFree( linear_fit ); } /* Free resources */ flbnd = astFree( flbnd ); fubnd = astFree( fubnd ); /* If a linear fit was obtained, we will use it and therefore do not wish to sub-divide further. Otherwise, we sub-divide in the hope that this may result in a linear fit next time. */ divide = !linear_fit; } /* If no sub-division is required, perform resampling (in a memory-efficient manner, since the section we are resampling might still be very large). This will use the linear fit, if obtained above. */ if ( astOK ) { if ( !divide ) { result = ResampleWithBlocking( this, linear_fit, ndim_in, lbnd_in, ubnd_in, in, in_var, type, interp, finterp, params, flags, badval_ptr, ndim_out, lbnd_out, ubnd_out, lbnd, ubnd, out, out_var, status ); /* Otherwise, allocate workspace to perform the sub-division. */ } else { lo = astMalloc( sizeof( int ) * (size_t) ndim_out ); hi = astMalloc( sizeof( int ) * (size_t) ndim_out ); if ( astOK ) { /* Initialise the bounds of a new output section to match the original output section. */ for ( coord_out = 0; coord_out < ndim_out; coord_out++ ) { lo[ coord_out ] = lbnd[ coord_out ]; hi[ coord_out ] = ubnd[ coord_out ]; } /* Replace the upper bound of the section's largest dimension with the mid-point of the section along this dimension, rounded downwards. */ hi[ dimx ] = (int) floor( 0.5 * (double) ( lbnd[ dimx ] + ubnd[ dimx ] ) ); /* Resample the resulting smaller section using a recursive invocation of this function. */ result = ResampleAdaptively( this, ndim_in, lbnd_in, ubnd_in, in, in_var, type, interp, finterp, params, flags, tol, maxpix, badval_ptr, ndim_out, lbnd_out, ubnd_out, lo, hi, out, out_var, status ); /* Now set up a second section which covers the remaining half of the original output section. */ lo[ dimx ] = hi[ dimx ] + 1; hi[ dimx ] = ubnd[ dimx ]; /* If this section contains pixels, resample it in the same way, summing the returned values. */ if ( lo[ dimx ] <= hi[ dimx ] ) { result += ResampleAdaptively( this, ndim_in, lbnd_in, ubnd_in, in, in_var, type, interp, finterp, params, flags, tol, maxpix, badval_ptr, ndim_out, lbnd_out, ubnd_out, lo, hi, out, out_var, status ); } } /* Free the workspace. */ lo = astFree( lo ); hi = astFree( hi ); } } /* If coefficients for a linear fit were obtained, then free the space they occupy. */ if ( linear_fit ) linear_fit = astFree( linear_fit ); /* If an error occurred, clear the returned result. */ if ( !astOK ) result = 0; /* Return the result. */ return result; } static int ResampleSection( AstMapping *this, const double *linear_fit, int ndim_in, const int *lbnd_in, const int *ubnd_in, const void *in, const void *in_var, DataType type, int interp, void (* finterp)( void ), const double *params, double factor, int flags, const void *badval_ptr, int ndim_out, const int *lbnd_out, const int *ubnd_out, const int *lbnd, const int *ubnd, void *out, void *out_var, int *status ) { /* * Name: * ResampleSection * Purpose: * Resample a section of a data grid. * Type: * Private function. * Synopsis: * #include "mapping.h" * int ResampleSection( AstMapping *this, const double *linear_fit, * int ndim_in, const int *lbnd_in, const int *ubnd_in, * const void *in, const void *in_var, * DataType type, int interp, void (* finterp)( void ), * const double *params, double factor, int flags, * const void *badval_ptr, int ndim_out, * const int *lbnd_out, const int *ubnd_out, * const int *lbnd, const int *ubnd, * void *out, void *out_var ) * Class Membership: * Mapping member function. * Description: * This function resamples a rectangular grid of data (with any * number of dimensions) into a specified section of another * rectangular grid (with a possibly different number of * dimensions). The coordinate transformation used is given by the * inverse transformation of the Mapping which is supplied or, * alternatively, by a linear approximation fitted to a Mapping's * inverse transformation. Any pixel interpolation scheme may be * specified for interpolating between the pixels of the input * grid. * Parameters: * this * Pointer to a Mapping, whose inverse transformation may be * used to transform the coordinates of pixels in the output * grid into associated positions in the input grid, from which * the output pixel values should be derived (by interpolation * if necessary). * * The number of input coordintes for the Mapping (Nin * attribute) should match the value of "ndim_in" (below), and * the number of output coordinates (Nout attribute) should * match the value of "ndim_out". * linear_fit * Pointer to an optional array of double which contains the * coefficients of a linear fit which approximates the above * Mapping's inverse coordinate transformation. If this is * supplied, it will be used in preference to the above Mapping * when transforming coordinates. This may be used to enhance * performance in cases where evaluation of the Mapping's * inverse transformation is expensive. If no linear fit is * available, a NULL pointer should be supplied. * * The way in which the fit coefficients are stored in this * array and the number of array elements are as defined by the * astLinearApprox function. * ndim_in * The number of dimensions in the input grid. This should be at * least one. * lbnd_in * Pointer to an array of integers, with "ndim_in" elements. * This should give the coordinates of the centre of the first * pixel in the input data grid along each dimension. * ubnd_in * Pointer to an array of integers, with "ndim_in" elements. * This should give the coordinates of the centre of the last * pixel in the input data grid along each dimension. * * Note that "lbnd_in" and "ubnd_in" together define the shape * and size of the input data grid, its extent along a * particular (i'th) dimension being (ubnd_in[i] - lbnd_in[i] + * 1). They also define the input grid's coordinate system, with * each pixel being of unit extent along each dimension with * integral coordinate values at its centre. * in * Pointer to the input array of data to be resampled (with one * element for each pixel in the input grid). The numerical type * of these data should match the "type" value (below). The * storage order should be such that the coordinate of the first * dimension varies most rapidly and that of the final dimension * least rapidly (i.e. Fortran array storage order is used). * in_var * An optional pointer to a second array of positive numerical * values (with the same size and data type as the "in" array), * which represent estimates of the statistical variance * associated with each element of the "in" array. If this * second array is given (along with the corresponding "out_var" * array), then estimates of the variance of the resampled data * will also be returned. * * If no variance estimates are required, a NULL pointer should * be given. * type * A value taken from the "DataType" enum, which specifies the * data type of the input and output arrays containing the * gridded data (and variance) values. * interp * A value selected from a set of pre-defined macros to identify * which sub-pixel interpolation algorithm should be used. * finterp * If "interp" is set to a value which requires a user-supplied * function, then a pointer to that function shoild be given * here. Otherwise, this value is not used and may be a NULL * pointer. * params * Pointer to an optional array of parameters that may be passed * to the interpolation algorithm, if required. If no parameters * are required, a NULL pointer should be supplied. * factor * A factor by which to scale the resampled output data values before * returning them. If flux is being conserved this should be set to * the ratio of the output pixel size to the input pixel size in the * section. Otherwise it should be set to 1.0. * flags * The bitwise OR of a set of flag values which provide * additional control over the resampling operation. * badval_ptr * If the AST__USEBAD flag is set (above), this parameter is a * pointer to a value which is used to identify bad data and/or * variance values in the input array(s). The referenced value's * data type must match that of the "in" (and "in_var") * arrays. Unless the AST__NOBAD flag is set, the same value will * also be used to flag any output array elements for which * resampled values could not be obtained. The output arrays(s) * may be flagged with this value whether or not the AST__USEBAD * flag is set (the function return value indicates whether any * such values have been produced). * ndim_out * The number of dimensions in the output grid. This should be * at least one. * lbnd_out * Pointer to an array of integers, with "ndim_out" elements. * This should give the coordinates of the centre of the first * pixel in the output data grid along each dimension. * ubnd_out * Pointer to an array of integers, with "ndim_out" elements. * This should give the coordinates of the centre of the last * pixel in the output data grid along each dimension. * * Note that "lbnd_out" and "ubnd_out" together define the shape * and size of the output data grid in the same way as "lbnd_in" * and "ubnd_in" define the shape and size of the input grid * (see above). * lbnd * Pointer to an array of integers, with "ndim_out" elements. * This should give the coordinates of the first pixel in the * section of the output data grid for which a value is * required. * ubnd * Pointer to an array of integers, with "ndim_out" elements. * This should give the coordinates of the last pixel in the * section of the output data grid for which a value is * required. * * Note that "lbnd" and "ubnd" define the shape and position of * the section of the output grid for which resampled values are * required. This section should lie wholly within the extent of * the output grid (as defined by the "lbnd_out" and "ubnd_out" * arrays). Regions of the output grid lying outside this section * will not be modified. * out * Pointer to an array with the same data type as the "in" * array, into which the resampled data will be returned. The * storage order should be such that the coordinate of the first * dimension varies most rapidly and that of the final dimension * least rapidly (i.e. Fortran array storage order is used). * out_var * An optional pointer to an array with the same data type and * size as the "out" array, into which variance estimates for * the resampled values may be returned. This array will only be * used if the "in_var" array has been given. * * If no output variance estimates are required, a NULL pointer * should be given. * Returned Value: * The number of output grid points for which no valid output value * could be obtained. * Notes: * - This function does not take steps to limit memory usage if the * grids supplied are large. To resample large grids in a more * memory-efficient way, the ResampleWithBlocking function should * be used. * - A value of zero will be returned if this function is invoked * with the global error status set, or if it should fail for any * reason. */ /* Local Variables: */ astDECLARE_GLOBALS /* Thread-specific data */ AstPointSet *pset_in; /* Input PointSet for transformation */ AstPointSet *pset_out; /* Output PointSet for transformation */ const double *grad; /* Pointer to gradient matrix of linear fit */ const double *par; /* Pointer to parameter array */ const double *zero; /* Pointer to zero point array of fit */ double **ptr_in; /* Pointer to input PointSet coordinates */ double **ptr_out; /* Pointer to output PointSet coordinates */ double *accum; /* Pointer to array of accumulated sums */ double fwhm; /* Full width half max. of gaussian */ double lpar[ 1 ]; /* Local parameter array */ double x1; /* Interim x coordinate value */ double y1; /* Interim y coordinate value */ int *dim; /* Pointer to array of output pixel indices */ int *offset; /* Pointer to array of output pixel offsets */ int *stride; /* Pointer to array of output grid strides */ int conserve; /* Conserve flux? */ int coord_in; /* Loop counter for input dimensions */ int coord_out; /* Loop counter for output dimensions */ int done; /* All pixel indices done? */ int i1; /* Interim offset into "accum" array */ int i2; /* Final offset into "accum" array */ int idim; /* Loop counter for dimensions */ int ix; /* Loop counter for output x coordinate */ int iy; /* Loop counter for output y coordinate */ int nbad; /* Number of pixels assigned a bad value */ int neighb; /* Number of neighbouring pixels */ int npoint; /* Number of output points (pixels) */ int off1; /* Interim pixel offset into output array */ int off; /* Final pixel offset into output array */ int point; /* Counter for output points (pixels ) */ int result; /* Result value to be returned */ int s; /* Temporary variable for strides */ int usevar; /* Process variance array? */ void (* gifunc)( void ); /* General interpolation function */ void (* kernel)( double, const double [], int, double *, int * ); /* Kernel fn. */ void (* fkernel)( double, const double [], int, double * ); /* User kernel fn. */ /* Initialise. */ result = 0; /* Get a pointer to a structure holding thread-specific global data values */ astGET_GLOBALS(this); /* Check the global error status. */ if ( !astOK ) return result; /* Further initialisation. */ pset_in = NULL; ptr_in = NULL; neighb = 0; gifunc = NULL; kernel = NULL; fkernel = NULL; /* See if we are conserving flux */ conserve = flags & AST__CONSERVEFLUX; /* If we are conserving flux, then we need some way to tell which output array elements have been assigned a value and which have not. If the AST__NOBAD flag has been specified then this is not possible to report an error. */ if( ( flags & AST__NOBAD ) && conserve ) { astError( AST__BADFLG, "astResample: Cannot use the AST__NOBAD and " "AST__CONSERVEFLUX flags together (programming error)." , status); } /* Calculate the number of output points, as given by the product of the output grid dimensions. */ for ( npoint = 1, coord_out = 0; coord_out < ndim_out; coord_out++ ) { npoint *= ubnd[ coord_out ] - lbnd[ coord_out ] + 1; } /* Allocate workspace. */ offset = astMalloc( sizeof( int ) * (size_t) npoint ); stride = astMalloc( sizeof( int ) * (size_t) ndim_out ); if ( astOK ) { /* Calculate the stride for each output grid dimension. */ off = 0; s = 1; for ( coord_out = 0; coord_out < ndim_out; coord_out++ ) { stride[ coord_out ] = s; s *= ubnd_out[ coord_out ] - lbnd_out[ coord_out ] + 1; } /* A linear fit to the Mapping is available. */ /* ========================================= */ if ( linear_fit ) { /* If a linear fit to the Mapping has been provided, then obtain pointers to the array of gradients and zero-points comprising the fit. */ grad = linear_fit + ndim_in; zero = linear_fit; /* Create a PointSet to hold the input grid coordinates and obtain an array of pointers to its coordinate data. */ pset_in = astPointSet( npoint, ndim_in, "", status ); ptr_in = astGetPoints( pset_in ); if ( astOK ) { /* Initialise the count of output points. */ point = 0; /* Handle the 1-dimensional case optimally. */ /* ---------------------------------------- */ if ( ( ndim_in == 1 ) && ( ndim_out == 1 ) ) { /* Loop through the pixels of the output grid and transform their x coordinates into the input grid's coordinate system using the linear fit supplied. Store the results in the PointSet created above. */ for ( ix = lbnd[ 0 ]; ix <= ubnd[ 0 ]; ix++ ) { ptr_in[ 0 ][ point ] = zero[ 0 ] + grad[ 0 ] * (double) ix; /* Calculate the offset of each pixel within the output array. */ offset[ point ] = ix - lbnd_out[ 0 ]; point++; } /* Handle the 2-dimensional case optimally. */ /* ---------------------------------------- */ } else if ( ( ndim_in == 2 ) && ( ndim_out == 2 ) ) { /* Loop through the range of y coordinates in the output grid and calculate interim values of the input coordinates using the linear fit supplied. */ for ( iy = lbnd[ 1 ]; iy <= ubnd[ 1 ]; iy++ ) { x1 = zero[ 0 ] + grad[ 1 ] * (double) iy; y1 = zero[ 1 ] + grad[ 3 ] * (double) iy; /* Also calculate an interim pixel offset into the output array. */ off1 = stride[ 1 ] * ( iy - lbnd_out[ 1 ] ) - lbnd_out[ 0 ]; /* Now loop through the range of output x coordinates and calculate the final values of the input coordinates, storing the results in the PointSet created above. */ for ( ix = lbnd[ 0 ]; ix <= ubnd[ 0 ]; ix++ ) { ptr_in[ 0 ][ point ] = x1 + grad[ 0 ] * (double) ix; ptr_in[ 1 ][ point ] = y1 + grad[ 2 ] * (double) ix; /* Also calculate final pixel offsets into the output array. */ offset[ point ] = off1 + ix; point++; } } /* Handle other numbers of dimensions. */ /* ----------------------------------- */ } else { /* Allocate workspace. */ accum = astMalloc( sizeof( double ) * (size_t) ( ndim_in * ndim_out ) ); dim = astMalloc( sizeof( int ) * (size_t) ndim_out ); if ( astOK ) { /* Initialise an array of pixel indices for the output grid which refer to the first pixel for which we require a value. Also calculate the offset of this pixel within the output array. */ off = 0; for ( coord_out = 0; coord_out < ndim_out; coord_out++ ) { dim[ coord_out ] = lbnd[ coord_out ]; off += stride[ coord_out ] * ( dim[ coord_out ] - lbnd_out[ coord_out ] ); } /* To calculate each input grid coordinate we must perform a matrix multiply on the output grid coordinates (using the gradient matrix) and then add the zero points. However, since we will usually only be altering one output coordinate at a time (the least significant), we can avoid the full matrix multiply by accumulating partial sums for the most significant output coordinates and only altering those sums which need to change each time. The zero points never change, so we first fill the "most significant" end of the "accum" array with these. */ for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { accum[ ( coord_in + 1 ) * ndim_out - 1 ] = zero[ coord_in ]; } coord_out = ndim_out - 1; /* Now loop to process each output pixel. */ for ( done = 0; !done; point++ ) { /* To generate the input coordinate that corresponds to the current output pixel, we work down from the most significant dimension whose index has changed since the previous pixel we considered (given by "coord_out"). For each affected dimension, we accumulate in "accum" the matrix sum (including the zero point) for that dimension and all higher output dimensions. We must accumulate a separate set of sums for each input coordinate we wish to produce. (Note that for the first pixel we process, all dimensions are considered "changed", so we start by initialising the whole "accum" array.) */ for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { i1 = coord_in * ndim_out; for ( idim = coord_out; idim >= 1; idim-- ) { i2 = i1 + idim; accum[ i2 - 1 ] = accum[ i2 ] + dim[ idim ] * grad[ i2 ]; } /* The input coordinate for each dimension is given by the accumulated sum for output dimension zero (giving the sum over all output dimensions). We do not store this in the "accum" array, but assign the result directly to the coordinate array of the PointSet created earlier. */ ptr_in[ coord_in ][ point ] = accum[ i1 ] + dim[ 0 ] * grad[ i1 ]; } /* Store the offset of the current pixel in the output array. */ offset[ point ] = off; /* Now update the array of pixel indices to refer to the next output pixel. */ coord_out = 0; do { /* The least significant index which currently has less than its maximum value is incremented by one. The offset into the output array is updated accordingly. */ if ( dim[ coord_out ] < ubnd[ coord_out ] ) { dim[ coord_out ]++; off += stride[ coord_out ]; break; /* Any less significant indices which have reached their maximum value are returned to their minimum value and the output pixel offset is decremented appropriately. */ } else { dim[ coord_out ] = lbnd[ coord_out ]; off -= stride[ coord_out ] * ( ubnd[ coord_out ] - lbnd[ coord_out ] ); /* All the output pixels have been processed once the most significant pixel index has been returned to its minimum value. */ done = ( ++coord_out == ndim_out ); } } while ( !done ); } } /* Free the workspace. */ accum = astFree( accum ); dim = astFree( dim ); } } /* No linear fit to the Mapping is available. */ /* ========================================== */ } else { /* If flux conseravtion was requested, report an error, since we can only conserve flux if a linear approximation is available. */ if( conserve && astOK ) { astError( AST__CNFLX, "astResampleSection(%s): Flux conservation " "was requested but cannot be performed because either the Mapping " "is too non-linear, or the requested tolerance is too small.", status, astGetClass( this ) ); } /* Create a PointSet to hold the coordinates of the output pixels and obtain a pointer to its coordinate data. */ pset_out = astPointSet( npoint, ndim_out, "", status ); ptr_out = astGetPoints( pset_out ); if ( astOK ) { /* Initialise the count of output points. */ point = 0; /* Handle the 1-dimensional case optimally. */ /* ---------------------------------------- */ if ( ndim_out == 1 && ndim_in == 1 ) { /* Loop through the required range of output x coordinates, assigning the coordinate values to the PointSet created above. Also store a pixel offset into the output array. */ for ( ix = lbnd[ 0 ]; ix <= ubnd[ 0 ]; ix++ ) { ptr_out[ 0 ][ point ] = (double) ix; offset[ point ] = ix - lbnd_out[ 0 ]; /* Increment the count of output pixels. */ point++; } /* Handle the 2-dimensional case optimally. */ /* ---------------------------------------- */ } else if ( ndim_out == 2 && ndim_in == 2 ) { /* Loop through the required range of output y coordinates, calculating an interim pixel offset into the output array. */ for ( iy = lbnd[ 1 ]; iy <= ubnd[ 1 ]; iy++ ) { off1 = stride[ 1 ] * ( iy - lbnd_out[ 1 ] ) - lbnd_out[ 0 ]; /* Loop through the required range of output x coordinates, assigning the coordinate values to the PointSet created above. Also store a final pixel offset into the output array. */ for ( ix = lbnd[ 0 ]; ix <= ubnd[ 0 ]; ix++ ) { ptr_out[ 0 ][ point ] = (double) ix; ptr_out[ 1 ][ point ] = (double) iy; offset[ point ] = off1 + ix; /* Increment the count of output pixels. */ point++; } } /* Handle other numbers of dimensions. */ /* ----------------------------------- */ } else { /* Allocate workspace. */ dim = astMalloc( sizeof( int ) * (size_t) ndim_out ); if ( astOK ) { /* Initialise an array of pixel indices for the output grid which refer to the first pixel for which we require a value. Also calculate the offset of this pixel within the output array. */ off = 0; for ( coord_out = 0; coord_out < ndim_out; coord_out++ ) { dim[ coord_out ] = lbnd[ coord_out ]; off += stride[ coord_out ] * ( dim[ coord_out ] - lbnd_out[ coord_out ] ); } /* Loop to generate the coordinates of each output pixel. */ for ( done = 0; !done; point++ ) { /* Copy each pixel's coordinates into the PointSet created above. */ for ( coord_out = 0; coord_out < ndim_out; coord_out++ ) { ptr_out[ coord_out ][ point ] = (double) dim[ coord_out ]; } /* Store the offset of the pixel in the output array. */ offset[ point ] = off; /* Now update the array of pixel indices to refer to the next output pixel. */ coord_out = 0; do { /* The least significant index which currently has less than its maximum value is incremented by one. The offset into the output array is updated accordingly. */ if ( dim[ coord_out ] < ubnd[ coord_out ] ) { dim[ coord_out ]++; off += stride[ coord_out ]; break; /* Any less significant indices which have reached their maximum value are returned to their minimum value and the output pixel offset is decremented appropriately. */ } else { dim[ coord_out ] = lbnd[ coord_out ]; off -= stride[ coord_out ] * ( ubnd[ coord_out ] - lbnd[ coord_out ] ); /* All the output pixels have been processed once the most significant pixel index has been returned to its minimum value. */ done = ( ++coord_out == ndim_out ); } } while ( !done ); } } /* Free the workspace. */ dim = astFree( dim ); } /* When all the output pixel coordinates have been generated, use the Mapping's inverse transformation to generate the input coordinates from them. Obtain an array of pointers to the resulting coordinate data. */ pset_in = astTransform( this, pset_out, 0, NULL ); ptr_in = astGetPoints( pset_in ); } /* Annul the PointSet containing the output coordinates. */ pset_out = astAnnul( pset_out ); } } /* Resample the input grid. */ /* ------------------------ */ /* Determine if a variance array is to be processed. */ usevar = ( in_var && out_var ); /* If the input coordinates have been produced successfully, identify the input grid resampling method to be used. */ if ( astOK ) { /* Nearest pixel. */ /* -------------- */ switch ( interp ) { case AST__NEAREST: /* Define a macro to use a "case" statement to invoke the nearest-pixel interpolation function appropriate to a given data type. */ #define CASE_NEAREST(X,Xtype) \ case ( TYPE_##X ): \ result = \ InterpolateNearest##X( ndim_in, lbnd_in, ubnd_in, \ (Xtype *) in, (Xtype *) in_var, \ npoint, offset, \ (const double *const *) ptr_in, \ flags, *( (Xtype *) badval_ptr ), \ (Xtype *) out, (Xtype *) out_var, status ); \ break; /* Use the above macro to invoke the appropriate function. */ switch ( type ) { #if HAVE_LONG_DOUBLE /* Not normally implemented */ CASE_NEAREST(LD,long double) #endif CASE_NEAREST(D,double) CASE_NEAREST(F,float) CASE_NEAREST(L,long int) CASE_NEAREST(UL,unsigned long int) CASE_NEAREST(K,INT_BIG) CASE_NEAREST(UK,UINT_BIG) CASE_NEAREST(I,int) CASE_NEAREST(UI,unsigned int) CASE_NEAREST(S,short int) CASE_NEAREST(US,unsigned short int) CASE_NEAREST(B,signed char) CASE_NEAREST(UB,unsigned char) } break; /* Undefine the macro. */ #undef CASE_NEAREST /* Linear interpolation. */ /* --------------------- */ /* Note this is also the default if zero is given. */ case AST__LINEAR: case 0: /* Define a macro to use a "case" statement to invoke the linear interpolation function appropriate to a given data type. */ #define CASE_LINEAR(X,Xtype) \ case ( TYPE_##X ): \ result = \ InterpolateLinear##X( ndim_in, lbnd_in, ubnd_in,\ (Xtype *) in, (Xtype *) in_var, \ npoint, offset, \ (const double *const *) ptr_in, \ flags, *( (Xtype *) badval_ptr ), \ (Xtype *) out, (Xtype *) out_var, status ); \ break; /* Use the above macro to invoke the appropriate function. */ switch ( type ) { #if HAVE_LONG_DOUBLE /* Not normally implemented */ CASE_LINEAR(LD,long double) #endif CASE_LINEAR(D,double) CASE_LINEAR(F,float) CASE_LINEAR(L,long int) CASE_LINEAR(UL,unsigned long int) CASE_LINEAR(K,INT_BIG) CASE_LINEAR(UK,UINT_BIG) CASE_LINEAR(I,int) CASE_LINEAR(UI,unsigned int) CASE_LINEAR(S,short int) CASE_LINEAR(US,unsigned short int) CASE_LINEAR(B,signed char) CASE_LINEAR(UB,unsigned char) } break; /* Undefine the macro. */ #undef CASE_LINEAR /* Interpolation using a 1-d kernel. */ /* --------------------------------- */ case AST__GAUSS: case AST__SINC: case AST__SINCCOS: case AST__SINCGAUSS: case AST__SINCSINC: case AST__SOMB: case AST__SOMBCOS: case AST__UKERN1: /* User-supplied 1-d kernel function */ /* Obtain a pointer to the appropriate 1-d kernel function (either internal or user-defined) and set up any parameters it may require. */ par = NULL; switch ( interp ) { /* sinc(pi*x) interpolation. */ /* ------------------------- */ /* Assign the kernel function. */ case AST__SINC: kernel = Sinc; /* Calculate the number of neighbouring pixels to use. */ neighb = (int) floor( params[ 0 ] + 0.5 ); if ( neighb <= 0 ) { neighb = 2; } else { neighb = MaxI( 1, neighb, status ); } break; /* sinc(pi*x)*cos(k*pi*x) interpolation. */ /* ------------------------------------- */ /* Assign the kernel function. */ case AST__SINCCOS: kernel = SincCos; /* Store the required value of "k" in a local parameter array and pass this array to the kernel function. */ lpar[ 0 ] = 0.5 / MaxD( 1.0, params[ 1 ], status ); par = lpar; /* Obtain the number of neighbouring pixels to use. If this is zero or less, the number will be calculated automatically below. */ neighb = (int) floor( params[ 0 ] + 0.5 ); if ( neighb <= 0 ) neighb = INT_MAX; /* Calculate the maximum number of neighbouring pixels required by the width of the kernel, and use this value if preferable. */ neighb = MinI( neighb, (int) ceil( MaxD( 1.0, params[ 1 ], status ) ), status ); break; /* somb(pi*x) interpolation. */ /* ------------------------- */ /* Assign the kernel function. */ case AST__SOMB: kernel = Somb; /* Calculate the number of neighbouring pixels to use. */ neighb = (int) floor( params[ 0 ] + 0.5 ); if ( neighb <= 0 ) { neighb = 2; } else { neighb = MaxI( 1, neighb, status ); } break; /* somb(pi*x)*cos(k*pi*x) interpolation. */ /* ------------------------------------- */ /* Assign the kernel function. */ case AST__SOMBCOS: kernel = SombCos; /* Store the required value of "k" in a local parameter array and pass this array to the kernel function. */ lpar[ 0 ] = 0.5 / MaxD( 1.0, params[ 1 ], status ); par = lpar; /* Obtain the number of neighbouring pixels to use. If this is zero or less, the number will be calculated automatically below. */ neighb = (int) floor( params[ 0 ] + 0.5 ); if ( neighb <= 0 ) neighb = INT_MAX; /* Calculate the maximum number of neighbouring pixels required by the width of the kernel, and use this value if preferable. */ neighb = MinI( neighb, (int) ceil( MaxD( 1.0, params[ 1 ], status ) ), status ); break; /* sinc(pi*x)*exp(-k*x*x) interpolation. */ /* ------------------------------------- */ /* Assign the kernel function. */ case AST__SINCGAUSS: kernel = SincGauss; /* Constrain the full width half maximum of the gaussian factor. */ fwhm = MaxD( 0.1, params[ 1 ], status ); /* Store the required value of "k" in a local parameter array and pass this array to the kernel function. */ lpar[ 0 ] = 4.0 * log( 2.0 ) / ( fwhm * fwhm ); par = lpar; /* Obtain the number of neighbouring pixels to use. If this is zero or less, use the number of neighbouring pixels required by the width of the kernel (out to where the gaussian term falls to 1% of its peak value). */ neighb = (int) floor( params[ 0 ] + 0.5 ); if ( neighb <= 0 ) neighb = (int) ceil( sqrt( -log( 0.01 ) / lpar[ 0 ] ) ); break; /* exp(-k*x*x) interpolation. */ /* -------------------------- */ /* Assign the kernel function. */ case AST__GAUSS: kernel = Gauss; /* Constrain the full width half maximum of the gaussian. */ fwhm = MaxD( 0.1, params[ 1 ], status ); /* Store the required value of "k" in a local parameter array and pass this array to the kernel function. */ lpar[ 0 ] = 4.0 * log( 2.0 ) / ( fwhm * fwhm ); par = lpar; /* Obtain the number of neighbouring pixels to use. If this is zero or less, use the number of neighbouring pixels required by the width of the kernel (out to where the gaussian term falls to 1% of its peak value). */ neighb = (int) floor( params[ 0 ] + 0.5 ); if ( neighb <= 0 ) neighb = (int) ceil( sqrt( -log( 0.01 ) / lpar[ 0 ] ) ); break; /* sinc(pi*x)*sinc(k*pi*x) interpolation. */ /* -------------------------------------- */ /* Assign the kernel function. */ case AST__SINCSINC: kernel = SincSinc; /* Store the required value of "k" in a local parameter array and pass this array to the kernel function. */ lpar[ 0 ] = 0.5 / MaxD( 1.0, params[ 1 ], status ); par = lpar; /* Obtain the number of neighbouring pixels to use. If this is zero or less, the number will be calculated automatically below. */ neighb = (int) floor( params[ 0 ] + 0.5 ); if ( neighb <= 0 ) neighb = INT_MAX; /* Calculate the maximum number of neighbouring pixels required by the width of the kernel, and use this value if preferable. */ neighb = MinI( neighb, (int) ceil( MaxD( 1.0, params[ 1 ], status ) ), status ); break; /* User-supplied kernel. */ /* --------------------- */ /* Assign the kernel function. */ case AST__UKERN1: fkernel = (void (*)( double, const double [], int, double * )) finterp; /* Calculate the number of neighbouring pixels to use. */ neighb = MaxI( 1, (int) floor( params[ 0 ] + 0.5 ), status ); /* Pass a pointer to the "params" array. */ par = params; break; } /* Define a macro to use a "case" statement to invoke the 1-d kernel interpolation function appropriate to a given data type, passing it the pointer to the kernel function obtained above. */ #define CASE_KERNEL1(X,Xtype) \ case ( TYPE_##X ): \ result = \ InterpolateKernel1##X( this, ndim_in, lbnd_in, ubnd_in, \ (Xtype *) in, (Xtype *) in_var, \ npoint, offset, \ (const double *const *) ptr_in, \ kernel, fkernel, neighb, par, flags, \ *( (Xtype *) badval_ptr ), \ (Xtype *) out, (Xtype *) out_var, status ); \ break; /* Use the above macro to invoke the appropriate function. */ switch ( type ) { #if HAVE_LONG_DOUBLE /* Not normally implemented */ CASE_KERNEL1(LD,long double) #endif CASE_KERNEL1(D,double) CASE_KERNEL1(F,float) CASE_KERNEL1(L,long int) CASE_KERNEL1(UL,unsigned long int) CASE_KERNEL1(K,INT_BIG) CASE_KERNEL1(UK,UINT_BIG) CASE_KERNEL1(I,int) CASE_KERNEL1(UI,unsigned int) CASE_KERNEL1(S,short int) CASE_KERNEL1(US,unsigned short int) CASE_KERNEL1(B,signed char) CASE_KERNEL1(UB,unsigned char) } break; /* Undefine the macro. */ #undef CASE_KERNEL1 /* General sub-pixel interpolation function. */ /* ----------------------------------------- */ case AST__BLOCKAVE: case AST__UINTERP: /* Define a macro to use a "case" statement to invoke the general sub-pixel interpolation function appropriate to a given type and the selected value of the interp variable. */ #define CASE_GINTERP(X,Xtype) \ case ( TYPE_##X ): \ \ /* Obtain a pointer to the appropriate general interpolation function \ (either internal or user-defined) and set up any parameters it may \ require. */ \ switch ( interp ) { \ \ /* Block averaging interpolation. */ \ /* ------------------------------ */ \ case AST__BLOCKAVE: \ gifunc = (void (*)( void )) InterpolateBlockAverage##X; \ break; \ \ /* User-supplied sub-pixel interpolation function. */ \ /* ----------------------------------------------- */ \ case AST__UINTERP: \ gifunc = (void (*)( void )) finterp; \ break; \ } \ \ /* Invoke the general interpolation function. It has to be cast to the \ right type (i.e. a function with the correctly typed arguments) \ to prevent default promotion (to int or double) of its arguments. \ The cast here corresponds to the declaration of ast_resample_uinterp##Xtype. */ \ ( *( (void (*)( int, const int[], const int[], \ const Xtype[], \ const Xtype[], \ int, const int[], \ const double *const[], \ const double[], int, \ Xtype, \ Xtype *, \ Xtype *, \ int * )) \ gifunc ) )( ndim_in, lbnd_in, ubnd_in, \ (Xtype *) in, \ (Xtype *) ( usevar ? in_var : NULL ), \ npoint, offset, \ (const double *const *) ptr_in, \ params, flags, \ *( (Xtype *) badval_ptr ), \ (Xtype *) out, \ (Xtype *) ( usevar ? out_var : NULL ), \ &nbad ); \ if ( astOK ) { \ result += nbad; \ } else { \ astError( astStatus, "astResample"#X"(%s): Error " \ "signalled by user-supplied sub-pixel " \ "interpolation function.", status, \ astGetClass( unsimplified_mapping ) ); \ } \ break; /* Use the above macro to invoke the function. */ switch ( type ) { #if HAVE_LONG_DOUBLE /* Not normally implemented */ CASE_GINTERP(LD,long double) #endif CASE_GINTERP(D,double) CASE_GINTERP(F,float) CASE_GINTERP(L,long int) CASE_GINTERP(UL,unsigned long int) CASE_GINTERP(K,INT_BIG) CASE_GINTERP(UK,UINT_BIG) CASE_GINTERP(I,int) CASE_GINTERP(UI,unsigned int) CASE_GINTERP(S,short int) CASE_GINTERP(US,unsigned short int) CASE_GINTERP(B,signed char) CASE_GINTERP(UB,unsigned char) } break; /* Undefine the macro. */ #undef CASE_GINTERP /* Error: invalid interpolation scheme specified. */ /* ---------------------------------------------- */ default: /* Define a macro to report an error message appropriate to a given data type. */ #define CASE_ERROR(X) \ case TYPE_##X: \ astError( AST__SISIN, "astResample"#X"(%s): Invalid " \ "sub-pixel interpolation scheme (%d) specified.", status, \ astGetClass( unsimplified_mapping ), interp ); \ break; /* Use the above macro to report an appropriate error message. */ switch ( type ) { #if HAVE_LONG_DOUBLE /* Not normally implemented */ CASE_ERROR(LD) #endif CASE_ERROR(D) CASE_ERROR(F) CASE_ERROR(L) CASE_ERROR(UL) CASE_ERROR(K) CASE_ERROR(UK) CASE_ERROR(I) CASE_ERROR(UI) CASE_ERROR(S) CASE_ERROR(US) CASE_ERROR(B) CASE_ERROR(UB) } break; /* Undefine the macro. */ #undef CASE_ERROR } } /* Now scale the output values to conserve flux if required. */ if( conserve ) { /* Define a macro to use a "case" statement to invoke the function appropriate to a given data type. These simply multiple the output data value by the factor, and the output variance by the square of the factor. */ #define CASE_CONSERVE(X,Xtype) \ case ( TYPE_##X ): \ ConserveFlux##X( factor, npoint, offset, *( (Xtype *) badval_ptr ), \ (Xtype *) out, \ (Xtype *) ( usevar ? out_var : NULL ), status ); \ break; /* Use the above macro to invoke the appropriate function. */ switch ( type ) { #if HAVE_LONG_DOUBLE /* Not normally implemented */ CASE_CONSERVE(LD,long double) #endif CASE_CONSERVE(D,double) CASE_CONSERVE(F,float) CASE_CONSERVE(L,long int) CASE_CONSERVE(UL,unsigned long int) CASE_CONSERVE(K,INT_BIG) CASE_CONSERVE(UK,UINT_BIG) CASE_CONSERVE(I,int) CASE_CONSERVE(UI,unsigned int) CASE_CONSERVE(S,short int) CASE_CONSERVE(US,unsigned short int) CASE_CONSERVE(B,signed char) CASE_CONSERVE(UB,unsigned char) } /* Undefine the macro. */ #undef CASE_CONSERVE } /* Annul the PointSet used to hold input coordinates. */ pset_in = astAnnul( pset_in ); /* Free the workspace. */ offset = astFree( offset ); stride = astFree( stride ); /* If an error occurred, clear the returned value. */ if ( !astOK ) result = 0; /* Return the result. */ return result; } static int ResampleWithBlocking( AstMapping *this, const double *linear_fit, int ndim_in, const int *lbnd_in, const int *ubnd_in, const void *in, const void *in_var, DataType type, int interp, void (* finterp)( void ), const double *params, int flags, const void *badval_ptr, int ndim_out, const int *lbnd_out, const int *ubnd_out, const int *lbnd, const int *ubnd, void *out, void *out_var, int *status ) { /* * Name: * ResampleWithBlocking * Purpose: * Resample a section of a data grid in a memory-efficient way. * Type: * Private function. * Synopsis: * #include "mapping.h" * int ResampleWithBlocking( AstMapping *this, const double *linear_fit, * int ndim_in, * const int *lbnd_in, const int *ubnd_in, * const void *in, const void *in_var, * DataType type, int interp, void (* finterp)( void ), * const double *params, int flags, * const void *badval_ptr, int ndim_out, * const int *lbnd_out, const int *ubnd_out, * const int *lbnd, const int *ubnd, * void *out, void *out_var, int *status ) * Class Membership: * Mapping member function. * Description: * This function resamples a rectangular grid of data (with any * number of dimensions) into a specified section of another * rectangular grid (with a possibly different number of * dimensions). The coordinate transformation used is given by the * inverse transformation of the Mapping which is supplied or, * alternatively, by a linear approximation fitted to a Mapping's * inverse transformation. Any pixel interpolation scheme may be * specified for interpolating between the pixels of the input * grid. * * This function is very similar to ResampleSection, except that in * order to limit memory usage and to ensure locality of reference, * it divides the output grid up into "blocks" which have a limited * extent along each output dimension. Each block, which will not * contain more than a pre-determined maximum number of pixels, is * then passed to ResampleSection for resampling. * Parameters: * this * Pointer to a Mapping, whose inverse transformation may be * used to transform the coordinates of pixels in the output * grid into associated positions in the input grid, from which * the output pixel values should be derived (by interpolation * if necessary). * * The number of input coordintes for the Mapping (Nin * attribute) should match the value of "ndim_in" (below), and * the number of output coordinates (Nout attribute) should * match the value of "ndim_out". * linear_fit * Pointer to an optional array of double which contains the * coefficients of a linear fit which approximates the above * Mapping's inverse coordinate transformation. If this is * supplied, it will be used in preference to the above Mapping * when transforming coordinates. This may be used to enhance * performance in cases where evaluation of the Mapping's * inverse transformation is expensive. If no linear fit is * available, a NULL pointer should be supplied. * * The way in which the fit coefficients are stored in this * array and the number of array elements are as defined by the * astLinearApprox function. * ndim_in * The number of dimensions in the input grid. This should be at * least one. * lbnd_in * Pointer to an array of integers, with "ndim_in" elements. * This should give the coordinates of the centre of the first * pixel in the input data grid along each dimension. * ubnd_in * Pointer to an array of integers, with "ndim_in" elements. * This should give the coordinates of the centre of the last * pixel in the input data grid along each dimension. * * Note that "lbnd_in" and "ubnd_in" together define the shape * and size of the input data grid, its extent along a * particular (i'th) dimension being (ubnd_in[i] - lbnd_in[i] + * 1). They also define the input grid's coordinate system, with * each pixel being of unit extent along each dimension with * integral coordinate values at its centre. * in * Pointer to the input array of data to be resampled (with one * element for each pixel in the input grid). The numerical type * of these data should match the "type" value (below). The * storage order should be such that the coordinate of the first * dimension varies most rapidly and that of the final dimension * least rapidly (i.e. Fortran array storage order is used). * in_var * An optional pointer to a second array of positive numerical * values (with the same size and data type as the "in" array), * which represent estimates of the statistical variance * associated with each element of the "in" array. If this * second array is given (along with the corresponding "out_var" * array), then estimates of the variance of the resampled data * will also be returned. * * If no variance estimates are required, a NULL pointer should * be given. * type * A value taken from the "DataType" enum, which specifies the * data type of the input and output arrays containing the * gridded data (and variance) values. * interp * A value selected from a set of pre-defined macros to identify * which sub-pixel interpolation algorithm should be used. * finterp * If "interp" is set to a value which requires a user-supplied * function, then a pointer to that function shoild be given * here. Otherwise, this value is not used and may be a NULL * pointer. * params * Pointer to an optional array of parameters that may be passed * to the interpolation algorithm, if required. If no parameters * are required, a NULL pointer should be supplied. * flags * The bitwise OR of a set of flag values which provide * additional control over the resampling operation. * badval_ptr * If the AST__USEBAD flag is set (above), this parameter is a * pointer to a value which is used to identify bad data and/or * variance values in the input array(s). The referenced value's * data type must match that of the "in" (and "in_var") * arrays. Unless the AST__NOBAD flag is set, the same value will * also be used to flag any output array elements for which * resampled values could not be obtained. The output arrays(s) * may be flagged with this value whether or not the AST__USEBAD * flag is set (the function return value indicates whether any * such values have been produced). * ndim_out * The number of dimensions in the output grid. This should be * at least one. * lbnd_out * Pointer to an array of integers, with "ndim_out" elements. * This should give the coordinates of the centre of the first * pixel in the output data grid along each dimension. * ubnd_out * Pointer to an array of integers, with "ndim_out" elements. * This should give the coordinates of the centre of the last * pixel in the output data grid along each dimension. * * Note that "lbnd_out" and "ubnd_out" together define the shape * and size of the output data grid in the same way as "lbnd_in" * and "ubnd_in" define the shape and size of the input grid * (see above). * lbnd * Pointer to an array of integers, with "ndim_out" elements. * This should give the coordinates of the first pixel in the * section of the output data grid for which a value is * required. * ubnd * Pointer to an array of integers, with "ndim_out" elements. * This should give the coordinates of the last pixel in the * section of the output data grid for which a value is * required. * * Note that "lbnd" and "ubnd" define the shape and position of * the section of the output grid for which resampled values are * required. This section should lie wholly within the extent of * the output grid (as defined by the "lbnd_out" and "ubnd_out" * arrays). Regions of the output grid lying outside this section * will not be modified. * out * Pointer to an array with the same data type as the "in" * array, into which the resampled data will be returned. The * storage order should be such that the coordinate of the first * dimension varies most rapidly and that of the final dimension * least rapidly (i.e. Fortran array storage order is used). * out_var * An optional pointer to an array with the same data type and * size as the "out" array, into which variance estimates for * the resampled values may be returned. This array will only be * used if the "in_var" array has been given. * * If no output variance estimates are required, a NULL pointer * should be given. * status * Pointer to the inherited status variable. * Returned Value: * The number of output grid points for which no valid output value * could be obtained. * Notes: * - A value of zero will be returned if this function is invoked * with the global error status set, or if it should fail for any * reason. */ /* Local Constants: */ const int mxpix = 2 * 1024; /* Maximum number of pixels in a block (this relatively small number seems to give best performance) */ /* Local Variables: */ double factor; /* Flux conservation factor */ int *dim_block; /* Pointer to array of block dimensions */ int *lbnd_block; /* Pointer to block lower bound array */ int *ubnd_block; /* Pointer to block upper bound array */ int dim; /* Dimension size */ int done; /* All blocks resampled? */ int hilim; /* Upper limit on maximum block dimension */ int idim; /* Loop counter for dimensions */ int lolim; /* Lower limit on maximum block dimension */ int mxdim_block; /* Maximum block dimension */ int npix; /* Number of pixels in block */ int result; /* Result value to return */ /* Initialise. */ result = 0; /* Check the global error status. */ if ( !astOK ) return result; /* Allocate workspace. */ lbnd_block = astMalloc( sizeof( int ) * (size_t) ndim_out ); ubnd_block = astMalloc( sizeof( int ) * (size_t) ndim_out ); dim_block = astMalloc( sizeof( int ) * (size_t) ndim_out ); if ( astOK ) { /* Find the optimum block size. */ /* ---------------------------- */ /* We first need to find the maximum extent which a block of output pixels may have in each dimension. We determine this by taking the output grid extent in each dimension and then limiting the maximum dimension size until the resulting number of pixels is sufficiently small. This approach allows the block shape to approximate (or match) the output grid shape when appropriate. */ /* First loop to calculate the total number of output pixels and the maximum output dimension size. */ npix = 1; mxdim_block = 0; for ( idim = 0; idim < ndim_out; idim++ ) { dim = ubnd[ idim ] - lbnd[ idim ] + 1; npix *= dim; if ( mxdim_block < dim ) mxdim_block = dim; } /* If the number of output pixels is too large for a single block, we perform iterations to determine the optimum upper limit on a block's dimension size. Initialise the limits on this result. */ if ( npix > mxpix ) { lolim = 1; hilim = mxdim_block; /* Loop to perform a binary chop, searching for the best result until the lower and upper limits on the result converge to adjacent values. */ while ( ( hilim - lolim ) > 1 ) { /* Form a new estimate from the mid-point of the previous limits. */ mxdim_block = ( hilim + lolim ) / 2; /* See how many pixels a block contains if its maximum dimension is limited to this new value. */ for ( npix = 1, idim = 0; idim < ndim_out ; idim++ ) { dim = ubnd[ idim ] - lbnd[ idim ] + 1; npix *= ( dim < mxdim_block ) ? dim : mxdim_block; } /* Update the appropriate limit, according to whether the number of pixels is too large or too small. */ *( ( npix <= mxpix ) ? &lolim : &hilim ) = mxdim_block; } /* When iterations have converged, obtain the maximum limit on the dimension size of a block which results in no more than the maximum allowed number of pixels per block. However, ensure that all block dimensions are at least 2. */ mxdim_block = lolim; } if ( mxdim_block < 2 ) mxdim_block = 2; /* Calculate the block dimensions by applying this limit to the output grid dimensions. */ for ( idim = 0; idim < ndim_out ; idim++ ) { dim = ubnd[ idim ] - lbnd[ idim ] + 1; dim_block[ idim ] = ( dim < mxdim_block ) ? dim : mxdim_block; /* Also initialise the lower and upper bounds of the first block of output grid pixels to be resampled, ensuring that this does not extend outside the grid itself. */ lbnd_block[ idim ] = lbnd[ idim ]; ubnd_block[ idim ] = MinI( lbnd[ idim ] + dim_block[ idim ] - 1, ubnd[ idim ], status ); } /* Determine the flux conservation constant if needed. */ /* --------------------------------------------------- */ if( ( flags & AST__CONSERVEFLUX ) && linear_fit ) { factor = MatrixDet( ndim_in, ndim_out, linear_fit + ndim_in, status ); } else { factor = 1.0; } /* Resample each block of output pixels. */ /* ------------------------------------- */ /* Loop to generate the extent of each block of output pixels and to resample them. */ done = 0; while ( !done && astOK ) { /* Resample the current block, accumulating the sum of bad pixels produced. */ result += ResampleSection( this, linear_fit, ndim_in, lbnd_in, ubnd_in, in, in_var, type, interp, finterp, params, factor, flags, badval_ptr, ndim_out, lbnd_out, ubnd_out, lbnd_block, ubnd_block, out, out_var, status ); /* Update the block extent to identify the next block of output pixels. */ idim = 0; do { /* We find the least significant dimension where the upper bound of the block has not yet reached the upper bound of the region of the output grid which we are resampling. The block's position is then incremented by one block extent along this dimension, checking that the resulting extent does not go outside the region being resampled. */ if ( ubnd_block[ idim ] < ubnd[ idim ] ) { lbnd_block[ idim ] = MinI( lbnd_block[ idim ] + dim_block[ idim ], ubnd[ idim ], status ); ubnd_block[ idim ] = MinI( lbnd_block[ idim ] + dim_block[ idim ] - 1, ubnd[ idim ], status ); break; /* If any less significant dimensions are found where the upper bound of the block has reached its maximum value, we reset the block to its lowest position. */ } else { lbnd_block[ idim ] = lbnd[ idim ]; ubnd_block[ idim ] = MinI( lbnd[ idim ] + dim_block[ idim ] - 1, ubnd[ idim ], status ); /* All the blocks have been processed once the position along the most significant dimension has been reset. */ done = ( ++idim == ndim_out ); } } while ( !done ); } } /* Free the workspace. */ lbnd_block = astFree( lbnd_block ); ubnd_block = astFree( ubnd_block ); dim_block = astFree( dim_block ); /* If an error occurred, clear the returned value. */ if ( !astOK ) result = 0; /* Return the result. */ return result; } static void SetAttrib( AstObject *this_object, const char *setting, int *status ) { /* * Name: * SetAttrib * Purpose: * Set an attribute value for a Mapping. * Type: * Private function. * Synopsis: * #include "mapping.h" * void SetAttrib( AstObject *this, const char *setting ) * Class Membership: * Mapping member function (over-rides the astSetAttrib protected * method inherited from the Object class). * Description: * This function assigns an attribute value for a Mapping, the * attribute and its value being specified by means of a string of * the form: * * "attribute= value " * * Here, "attribute" specifies the attribute name and should be in * lower case with no white space present. The value to the right * of the "=" should be a suitable textual representation of the * value to be assigned and this will be interpreted according to * the attribute's data type. White space surrounding the value is * only significant for string attributes. * Parameters: * this * Pointer to the Mapping. * setting * Pointer to a null terminated string specifying the new attribute * value. */ /* Local Variables: */ AstMapping *this; /* Pointer to the Mapping structure */ int invert; /* Invert attribute value */ int len; /* Length of setting string */ int nc; /* Number of characters read by astSscanf */ int report; /* Report attribute value */ /* Check the global error status. */ if ( !astOK ) return; /* Obtain a pointer to the Mapping structure. */ this = (AstMapping *) this_object; /* Obtain the length of the setting string. */ len = (int) strlen( setting ); /* Test for each recognised attribute in turn, using "astSscanf" to parse the setting string and extract the attribute value (or an offset to it in the case of string values). In each case, use the value set in "nc" to check that the entire string was matched. Once a value has been obtained, use the appropriate method to set it. */ /* Invert. */ /* ------- */ if ( nc = 0, ( 1 == astSscanf( setting, "invert= %d %n", &invert, &nc ) ) && ( nc >= len ) ) { astSetInvert( this, invert ); /* Report. */ /* ------- */ } else if ( nc = 0, ( 1 == astSscanf( setting, "report= %d %n", &report, &nc ) ) && ( nc >= len ) ) { astSetReport( this, report ); /* Define a macro to see if the setting string matches any of the read-only attributes of this class. */ #define MATCH(attrib) \ ( nc = 0, ( 0 == astSscanf( setting, attrib "=%*[^\n]%n", &nc ) ) && \ ( nc >= len ) ) /* If the attribute was not recognised, use this macro to report an error if a read-only attribute has been specified. */ } else if ( MATCH( "nin" ) || MATCH( "nout" ) || MATCH( "islinear" ) || MATCH( "issimple" ) || MATCH( "tranforward" ) || MATCH( "traninverse" ) ) { astError( AST__NOWRT, "astSet: The setting \"%s\" is invalid for a %s.", status, setting, astGetClass( this ) ); astError( AST__NOWRT, "This is a read-only attribute." , status); /* If the attribute is still not recognised, pass it on to the parent method for further interpretation. */ } else { (*parent_setattrib)( this_object, setting, status ); } /* Undefine macros local to this function. */ #undef MATCH } static void Sinc( double offset, const double params[], int flags, double *value, int *status ) { /* * Name: * Sinc * Purpose: * 1-dimensional sinc(pi*x) interpolation kernel. * Type: * Private function. * Synopsis: * #include "mapping.h" * void Sinc( double offset, const double params[], int flags, * double *value, int *status ) * Class Membership: * Mapping member function. * Description: * This function calculates the value of a 1-dimensional sub-pixel * interpolation kernel. The function used is sinc(pi*x), where * sinc(z)=sin(z)/z. * Parameters: * offset * The offset of a pixel from the interpolation point, measured * in pixels. * params * Not used. * flags * Not used. * value * Pointer to a double to receive the calculated kernel value. * status * Pointer to the inherited status variable. * Notes: * - This function does not perform error checking and does not * generate errors. */ /* Local Variables: */ static double pi; /* Value of pi */ static int init = 0; /* Initialisation flag */ /* On the first invocation, initialise a local value for pi. Do this only once. */ if ( !init ) { pi = acos( -1.0 ); init = 1; } /* Scale the offset. */ offset *= pi; /* Evaluate the function. */ *value = ( offset != 0.0 ) ? ( sin( offset ) / offset ) : 1.0; } static void SincCos( double offset, const double params[], int flags, double *value, int *status ) { /* * Name: * SincCos * Purpose: * 1-dimensional sinc(pi*x)*cos(k*pi*x) interpolation kernel. * Type: * Private function. * Synopsis: * #include "mapping.h" * void SincCos( double offset, const double params[], int flags, * double *value, int *status ) * Class Membership: * Mapping member function. * Description: * This function calculates the value of a 1-dimensional sub-pixel * interpolation kernel. The function used is sinc(pi*x)*cos(k*pi*x) * out to the point where cos(k*pi*x) = 0, and zero beyond. Here, * sinc(z)=sin(z)/z. * Parameters: * offset * The offset of a pixel from the interpolation point, measured * in pixels. * params * The first element of this array should give a value for "k" * in the cos(k*pi*x) term. * flags * Not used. * value * Pointer to a double to receive the calculated kernel value. * status * Pointer to the inherited status variable. * Notes: * - This function does not perform error checking and does not * generate errors. */ /* Local Variables: */ double offset_k; /* Scaled offset */ static double halfpi; /* Value of pi/2 */ static double pi; /* Value of pi */ static int init = 0; /* Initialisation flag */ /* On the first invocation, initialise local values for pi and pi/2. Do this only once. */ if ( !init ) { pi = acos( -1.0 ); halfpi = 0.5 * pi; init = 1; } /* Multiply the offset by pi and remove its sign. */ offset = pi * fabs( offset ); /* Find the offset scaled by the "k" factor. */ offset_k = offset * params[ 0 ]; /* If the cos(k*pi*x) term has not reached zero, calculate the result. */ if ( offset_k < halfpi ) { *value = ( ( offset != 0.0 ) ? ( sin( offset ) / offset ) : 1.0 ) * cos( offset_k ); /* Otherwise, the result is zero. */ } else { *value = 0.0; } } static void SincGauss( double offset, const double params[], int flags, double *value, int *status ) { /* * Name: * SincGauss * Purpose: * 1-dimensional sinc(pi*x)*exp(-k*x*x) interpolation kernel. * Type: * Private function. * Synopsis: * #include "mapping.h" * void SincGauss( double offset, const double params[], int flags, * double *value, int *status ) * Class Membership: * Mapping member function. * Description: * This function calculates the value of a 1-dimensional sub-pixel * interpolation kernel. The function used is sinc(pi*x)*exp(-k*x*x), * where sinc(z)=sin(z)/z. * Parameters: * offset * The offset of a pixel from the interpolation point, measured * in pixels. * params * The first element of this array should give a value for "k" * in the exp(-k*x*x) term. * flags * Not used. * value * Pointer to a double to receive the calculated kernel value. * status * Pointer to the inherited status variable. * Notes: * - This function does not perform error checking and does not * generate errors. */ /* Local Variables: */ double offset_pi; /* Offset multiplied by pi */ static double pi; /* Value of pi */ static int init = 0; /* Initialisation flag */ /* On the first invocation, initialise a local value for pi. Do this only once. */ if ( !init ) { pi = acos( -1.0 ); init = 1; } /* Find the offset scaled by pi. */ offset_pi = pi * offset; /* Calculate the result. */ *value = ( ( offset_pi != 0.0 ) ? ( sin( offset_pi ) / offset_pi ) : 1.0 ) * exp( -params[ 0 ] * offset * offset ); } static void SincSinc( double offset, const double params[], int flags, double *value, int *status ) { /* * Name: * SincSinc * Purpose: * 1-dimensional sinc(pi*x)*sinc(k*pi*x) interpolation kernel. * Type: * Private function. * Synopsis: * #include "mapping.h" * void SincSinc( double offset, const double params[], int flags, * double *value, int *status ) * Class Membership: * Mapping member function. * Description: * This function calculates the value of a 1-dimensional sub-pixel * interpolation kernel. The function used is sinc(pi*x)*sinc(k*pi*x), * out to the point where sinc(k*pi*x)=0, and zero beyond. Here, * sinc(z)=sin(z)/z. * Parameters: * offset * The offset of a pixel from the interpolation point, measured * in pixels. * params * The first element of this array should give a value for "k" * in the sinc(k*pi*x) term. * flags * Not used. * value * Pointer to a double to receive the calculated kernel value. * status * Pointer to the inherited status variable. * Notes: * - This function does not perform error checking and does not * generate errors. */ /* Local Variables: */ double offset_k; /* Scaled offset */ static double halfpi; /* Value of pi/2 */ static double pi; /* Value of pi */ static int init = 0; /* Initialisation flag */ /* On the first invocation, initialise local values for pi and pi/2. Do this only once. */ if ( !init ) { pi = acos( -1.0 ); halfpi = 0.5 * pi; init = 1; } /* Multiply the offset by pi and remove its sign. */ offset = pi * fabs( offset ); /* Find the offset scaled by the "k" factor. */ offset_k = offset * params[ 0 ]; /* If the sinc(k*pi*x) term has not reached zero, calculate the result. */ if ( offset_k < halfpi ) { *value = ( ( offset != 0.0 ) ? ( sin( offset ) / offset ) : 1.0 ) * ( ( offset_k != 0.0 ) ? ( sin( offset_k ) / offset_k ) : 1.0 ); /* Otherwise, the result is zero. */ } else { *value = 0.0; } } static AstMapping *Simplify( AstMapping *this, int *status ) { /* *++ * Name: c astSimplify f AST_SIMPLIFY * Purpose: * Simplify a Mapping. * Type: * Public function. * Synopsis: c #include "mapping.h" c AstMapping *astSimplify( AstMapping *this ) f RESULT = AST_SIMPLIFY( THIS, STATUS ) * Class Membership: * Mapping method. * Description: * This function simplifies a Mapping (which may be a compound * Mapping such as a CmpMap) to eliminate redundant computational * steps, or to merge separate steps which can be performed more * efficiently in a single operation. * * As a simple example, a Mapping which multiplied coordinates by * 5, and then multiplied the result by 10, could be simplified to * a single step which multiplied by 50. Similarly, a Mapping which * multiplied by 5, and then divided by 5, could be reduced to a * simple copying operation. * * This function should typically be applied to Mappings which have * undergone substantial processing or have been formed by merging * other Mappings. It is of potential benefit, for example, in * reducing execution time if applied before using a Mapping to * transform a large number of coordinates. * Parameters: c this f THIS = INTEGER (Given) * Pointer to the original Mapping. f STATUS = INTEGER (Given and Returned) f The global status. * Returned Value: c astSimplify() f AST_SIMPLIFY = INTEGER * A new pointer to the (possibly simplified) Mapping. * Applicability: * Mapping * This function applies to all Mappings. * FrameSet * If the supplied Mapping is a FrameSet, the returned Mapping * will be a copy of the supplied FrameSet in which all the * inter-Frame Mappings have been simplified. * Notes: * - Mappings that have a set value for their Ident attribute are * left unchanged after simplification. This is so that their * individual identity is preserved. This restriction does not * apply to the simplification of Frames. * - This function can safely be applied even to Mappings which * cannot be simplified. If no simplification is possible, it c behaves exactly like astClone and returns a pointer to the f behaves exactly like AST_CLONE and returns a pointer to the * original Mapping. * - The Mapping returned by this function may not be independent * of the original (even if simplification was possible), and * modifying it may therefore result in indirect modification of * the original. If a completely independent result is required, a c copy should be made using astCopy. f copy should be made using AST_COPY. * - A null Object pointer (AST__NULL) will be returned if this c function is invoked with the AST error status set, or if it f function is invoked with STATUS set to an error value, or if it * should fail for any reason. *-- */ /* Local Variables: */ AstMapping **map_list; /* Pointer to array of Mapping pointers */ AstMapping *map; /* Cloned pointer to nominated Mapping */ AstMapping *result; /* Pointer to result Mapping */ int *invert_list; /* Pointer to array of invert flags */ int imap; /* Loop counter for Mappings */ int modified; /* Index of first modified element */ int nmap; /* Number of Mappings */ int simpler; /* Simplification achieved? */ /* Initialise. */ result = NULL; /* Check the inherited status. */ if ( !astOK ) return result; /* Initialise dynamic arrays of Mapping pointers and associated invert flags. */ nmap = 0; map_list = NULL; invert_list = NULL; /* Build a Mapping list to contain this Mapping (the list should only have 1 element). */ astMapList( this, 1, astGetInvert( this ), &nmap, &map_list, &invert_list ); /* Pass the list repeatedly to the "astMapMerge" method for simplification. */ simpler = 0; while ( astOK ) { map = astClone( map_list[ 0 ] ); modified = astMapMerge( map, 0, 1, &nmap, &map_list, &invert_list ); map = astAnnul( map ); /* Quit looping if the number of Mappings increases above 1, or if no further change occurs. Note if any simplification was achieved. */ if ( ( nmap > 1 ) || ( modified < 0 ) ) break; simpler = 1; } /* Check whether simplification has occurred. If not, simply clone the original Mapping pointer. This is what will normally happen for Mapping classes which inherit the default (null) "astMapMerge" method from this class and do not define one of their own. */ if ( astOK ) { if ( !simpler || ( nmap > 1 ) ) { result = astClone( this ); /* If simplification occurred, test if the resulting Mapping has the Invert attribute value we want. If so, we can simply clone a pointer to it. */ } else { if ( invert_list[ 0 ] == astGetInvert( map_list[ 0 ] ) ) { result = astClone( map_list[ 0 ] ); /* If not, we must make a copy. */ } else { result = astCopy( map_list[ 0 ] ); /* Either clear the copy's Invert attribute, or set it to 1, as required. */ if ( invert_list[ 0 ] ) { astSetInvert( result, 1 ); } else { astClearInvert( result ); } } } } /* Loop to annul all the pointers in the Mapping list. */ for ( imap = 0; imap < nmap; imap++ ) { map_list[ imap ] = astAnnul( map_list[ imap ] ); } /* Free the dynamic arrays. */ map_list = astFree( map_list ); invert_list = astFree( invert_list ); /* If an error occurred, annul the returned Mapping. */ if ( !astOK ) result = astAnnul( result ); /* Return the result. */ return result; } static void Somb( double offset, const double params[], int flags, double *value, int *status ) { /* * Name: * Somb * Purpose: * 1-dimensional somb(pi*x) interpolation kernel. * Type: * Private function. * Synopsis: * #include "mapping.h" * void Somb( double offset, const double params[], int flags, * double *value, int *status ) * Class Membership: * Mapping member function. * Description: * This function calculates the value of a 1-dimensional sub-pixel * interpolation kernel. The function used is somb(pi*x), where * somb(z)=2*J1(z)/z (J1 is a Bessel function of the first kind of * order 1). * Parameters: * offset * The offset of a pixel from the interpolation point, measured * in pixels. * params * Not used. * flags * Not used. * value * Pointer to a double to receive the calculated kernel value. * status * Pointer to the inherited status variable. * Notes: * - This function does not perform error checking and does not * generate errors. */ /* Local Variables: */ static double pi; /* Value of pi */ static int init = 0; /* Initialisation flag */ /* On the first invocation, initialise a local value for pi. Do this only once. */ if ( !init ) { pi = acos( -1.0 ); init = 1; } /* Scale the offset. */ offset *= pi; /* Evaluate the function. */ *value = ( offset != 0.0 ) ? ( 2.0*J1Bessel( offset, status ) / offset ) : 1.0; } static void SombCos( double offset, const double params[], int flags, double *value, int *status ) { /* * Name: * SombCos * Purpose: * 1-dimensional somb(pi*x)*cos(k*pi*x) interpolation kernel. * Type: * Private function. * Synopsis: * #include "mapping.h" * void SombCos( double offset, const double params[], int flags, * double *value, int *status ) * Class Membership: * Mapping member function. * Description: * This function calculates the value of a 1-dimensional sub-pixel * interpolation kernel. The function used is somb(pi*x)*cos(k*pi*x) * out to the point where cos(k*pi*x) = 0, and zero beyond. Here, * somb(z)=2*J1(z)/z (J1 is a Bessel function of the first kind of * order 1). * Parameters: * offset * The offset of a pixel from the interpolation point, measured * in pixels. * params * The first element of this array should give a value for "k" * in the cos(k*pi*x) term. * flags * Not used. * value * Pointer to a double to receive the calculated kernel value. * status * Pointer to the inherited status variable. * Notes: * - This function does not perform error checking and does not * generate errors. */ /* Local Variables: */ double offset_k; /* Scaled offset */ static double halfpi; /* Value of pi/2 */ static double pi; /* Value of pi */ static int init = 0; /* Initialisation flag */ /* On the first invocation, initialise local values for pi and pi/2. Do this only once. */ if ( !init ) { pi = acos( -1.0 ); halfpi = 0.5 * pi; init = 1; } /* Multiply the offset by pi and remove its sign. */ offset = pi * fabs( offset ); /* Find the offset scaled by the "k" factor. */ offset_k = offset * params[ 0 ]; /* If the cos(k*pi*x) term has not reached zero, calculate the result. */ if ( offset_k < halfpi ) { *value = ( ( offset != 0.0 ) ? ( J1Bessel( offset, status ) / offset ) : 1.0 ) * cos( offset_k ); /* Otherwise, the result is zero. */ } else { *value = 0.0; } } static int SpecialBounds( const MapData *mapdata, double *lbnd, double *ubnd, double xl[], double xu[], int *status ) { /* * Name: * SpecialBounds * Purpose: * Estimate coordinate bounds using special points. * Type: * Private function. * Synopsis: * #include "mapping.h" * int SpecialBounds( const MapData *mapdata, double *lbnd, double *ubnd, * double xl[], double xu[], int *status ); * Class Membership: * Mapping member function. * Description: * This function makes a rough estimate of the lower and upper * bounds of a Mapping function over a constrained region of its * input coordinate space by transforming a set of special test * points. The points used lie at the corners of the constrained * region, at the centre of each of its faces, at its centroid, and * (if within the coordinate constraints) the origin. * * In many practical cases, the true extrema may actually lie at * one or other of these points, in which case the true bounds will * be found. In other cases, this function only provides an * approximate limit on each bound (there is no way of telling if * this is the case, however). In either case, having these initial * estimates can speed subsequent searches to find the global * extrema as well as making that search more secure * Parameters: * mapdata * Pointer to a MapData structure describing the Mapping * function, its coordinate constraints, etc. * lbnd * Pointer to a double. On entry, this should contain a * previously-obtained upper limit on the lower bound, or * AST__BAD if no such limit is available. On exit, it will be * updated with a new estimate of the lower bound, if a better * one has been found. * ubnd * Pointer to a double. On entry, this should contain a * previously-obtained lower limit on the upper bound, or * AST__BAD if no such limit is available. On exit, it will be * updated with a new estimate of the upper bound, if a better * one has been found. * xl * Pointer to an array of double, with one element for each * input coordinate, in which to return the position of a (not * necessarily unique) input point at which the lower output * bound is reached. This array is not altered if an improved * estimate of the lower bound cannot be found. * xu * Pointer to an array of double, with one element for each * input coordinate, in which to return the position of a (not * necessarily unique) input point at which the upper output * bound is reached. This array is not altered if an improved * estimate of the upper bound cannot be found. * status * Pointer to the inherited status variable. * Returned: * A flag indicating if the returned values can be refined. */ /* Local Variables: */ AstPointSet *pset_in; /* PointSet for input coordinates */ AstPointSet *pset_out; /* PointSet for output coordinates */ double **ptr_in; /* Pointer to input coordinates */ double **ptr_out; /* Pointer to output coordinates */ double *sxl; /* Secondary xl values */ double *sxu; /* Secondary xu values */ double f; /* Output coordinate value */ double slbnd; /* Secondary lbnd value */ double subnd; /* Secondary lbnd value */ int *limit; /* Workspace for lower/upper limit flags */ int bad; /* Output coordinate bad? */ int coord; /* Loop counter for coordinates */ int done; /* All corners done? */ int face; /* Loop counter for faces */ int ic; /* Index of corner */ int icen; /* Index of centroid point */ int ncorner; /* Number of corners */ int ncoord; /* Number of input coordinates */ int npoint; /* Number of points */ int origin; /* Origin lies within bounds? */ int point; /* Loop counter for points */ int result; /* Returned flag */ /* Initialise */ result = 1; /* Initialise variables to avoid "used of uninitialised variable" messages from dumb compilers. */ pset_out = NULL; /* Obtain the number of coordinate axes and calculate the number of points required in order to place one at every corner of the constrained region of the coordinate space. */ ncoord = mapdata->nin; for ( npoint = 1, coord = 0; coord < ncoord; coord++ ) npoint *= 2; /* Also include a second point at each corner,offset slightly from the corner towards the centroid */ ncorner = npoint; npoint *= 2; /* Also include placing one at the centre of every face and one at the centroid of the constrained coordinate space. */ npoint += 2 * ncoord + 1; /* Determine if the origin lies within the bounds. If so, include it as a further point. */ origin = 1; for ( coord = 0; coord < ncoord; coord++ ) { if ( ( mapdata->lbnd[ coord ] > 0.0 ) || ( mapdata->ubnd[ coord ] < 0.0 ) ) { origin = 0; break; } } if ( origin ) npoint++; /* Initialise secondary bounds to be the supplied primary bounds */ slbnd = *lbnd; subnd = *ubnd; /* Create workspace for ssecondary xl xu values */ sxl = astMalloc( sizeof(double)*(size_t) ncoord ); sxu = astMalloc( sizeof(double)*(size_t) ncoord ); /* Create a PointSet to hold the coordinates and obtain a pointer to its coordinate values. Also allocate workspace for calculating the corner coordinates. */ pset_in = astPointSet( npoint, ncoord, "", status ); ptr_in = astGetPoints( pset_in ); limit = astMalloc( sizeof( int ) * (size_t) ncoord ); if ( astOK ) { /* Initialise the workspace. */ for ( coord = 0; coord < ncoord; coord++ ) limit[ coord ] = 0; /* Loop to visit every corner. */ point = 0; done = 0; do { /* At each corner, translate the contents of the "limit" array (containing zeros and ones) into the lower or upper bound on the corresponding axis. This gives the coordinates of the corner, which we store in the input PointSet. */ for ( coord = 0; coord < ncoord; coord++ ) { ptr_in[ coord ][ point ] = limit[ coord ] ? mapdata->ubnd[ coord ] : mapdata->lbnd[ coord ]; } /* Increment the count of points (i.e. corners). */ point++; /* Now update the limit array to identify the next corner. */ coord = 0; do { /* Flip the first zero found to become a one. This gives a new corner. */ if ( !limit[ coord ] ) { limit[ coord ] = 1; break; /* However, first flip any previous ones to become zeros and then examine the next element. We have processed all corners once the array is entirely filled with ones. */ } else { limit[ coord ] = 0; done = ( ++coord == ncoord ); } } while ( !done ); } while ( !done ); /* Once the corners have been processed, loop to consider the centre of each face. */ for ( face = 0; face < ( 2 * ncoord ); face++ ) { /* First calculate the centroid value for each coordinate. Then set one of these coordinates to the bound where the face lies. */ for ( coord = 0; coord < ncoord; coord++ ) { ptr_in[ coord ][ point ] = 0.5 * ( mapdata->lbnd[ coord ] + mapdata->ubnd[ coord ] ); } ptr_in[ face / 2 ][ point ] = ( face % 2 ) ? mapdata->lbnd[ face / 2 ] : mapdata->ubnd[ face / 2 ]; /* Increment the count of points. */ point++; } /* Place a point at the centroid of the constrained coordinate space. */ for ( coord = 0; coord < ncoord; coord++ ) { ptr_in[ coord ][ point ] = 0.5 * ( mapdata->lbnd[ coord ] + mapdata->ubnd[ coord ] ); } icen = point++; /* Add a set of positions which are offset slightly from each corner towards the centroid. */ for ( ic = 0; ic < ncorner; ic++ ) { for ( coord = 0; coord < ncoord; coord++ ) { ptr_in[ coord ][ point ] = 0.999*ptr_in[ coord ][ ic ] + 0.001*ptr_in[ coord ][ icen ]; } point++; } /* Finally, add the origin, if it lies within the constraints. */ if ( origin ) { for ( coord = 0; coord < ncoord; coord++ ) { ptr_in[ coord ][ point ] = 0.0; } } /* Once all the input coordinates have been calculated, transform them and obtain a pointer to the resulting coordinate values. */ pset_out = astTransform( mapdata->mapping, pset_in, mapdata->forward, NULL ); ptr_out = astGetPoints( pset_out ); if ( astOK ) { /* Loop through each point and test if any of its transformed coordinates is bad. */ for ( point = 0; point < npoint; point++ ) { bad = 0; for ( coord = 0; coord < mapdata->nout; coord++ ) { if ( ptr_out[ coord ][ point ] == AST__BAD ) { bad = 1; break; } } /* If so, we ignore the point. Otherwise, extract the required coordinate. */ f = ptr_out[ mapdata->coord ][ point ]; if ( !bad ) { /* Use this to update the lower and upper bounds we are seeking. If either bound is updated, also store the coordinates of the corresponding input point. */ if ( ( *lbnd == AST__BAD ) || ( f < *lbnd ) ) { *lbnd = f; for ( coord = 0; coord < ncoord; coord++ ) { xl[ coord ] = ptr_in[ coord ][ point ]; } } if ( ( *ubnd == AST__BAD ) || ( f > *ubnd ) ) { *ubnd = f; for ( coord = 0; coord < ncoord; coord++ ) { xu[ coord ] = ptr_in[ coord ][ point ]; } } /* If this point has a bad coord value, it may still be useful if the required coord value is not bad. In this case, extract the required coordinate. */ } else if ( f != AST__BAD ) { /* Use this to update secondary lower and upper bounds we are seeking. These will be returned if no primary values are found via the previous code block. */ if ( ( slbnd == AST__BAD ) || ( f < slbnd ) ) { slbnd = f; for ( coord = 0; coord < ncoord; coord++ ) { sxl[ coord ] = ptr_in[ coord ][ point ]; } } if ( ( subnd == AST__BAD ) || ( f > subnd ) ) { subnd = f; for ( coord = 0; coord < ncoord; coord++ ) { sxu[ coord ] = ptr_in[ coord ][ point ]; } } } } /* If no primary values could be found, use secondary values. */ if( *lbnd == AST__BAD && *ubnd == AST__BAD ) { *lbnd = slbnd; *ubnd = subnd; for ( coord = 0; coord < ncoord; coord++ ) { xu[ coord ] = sxu[ coord ]; xl[ coord ] = sxl[ coord ]; } result = ( slbnd == AST__BAD || subnd == AST__BAD ); } } } /* Free workspace */ sxl = astFree( sxl ); sxu = astFree( sxu ); /* Annul the temporary PointSets and free the workspace. */ pset_in = astAnnul( pset_in ); pset_out = astAnnul( pset_out ); limit = astFree( limit ); return result; } /* * Name: * SpreadKernel1 * Purpose: * Rebin a data grid, using a 1-d interpolation kernel. * Type: * Private function. * Synopsis: * #include "mapping.h" * void SpreadKernel1( AstMapping *this, int ndim_out, * const int *lbnd_out, const int *ubnd_out, * const *in, const *in_var, * double infac, int npoint, const int *offset, * const double *const *coords, * void (* kernel)( double, const double [], int, * double *, int * ), * int neighb, const double *params, double conwgt, * int flags, badval, int npix_out, *out, * *out_var, double *work, int64_t *nused, * int *status ) * Class Membership: * Mapping member function. * Description: * This is a set of functions which rebins a rectangular region of an * input grid of data (and, optionally, associated statistical variance * values) so as to place them into a new output grid. Each input * grid point may be mapped on to a position in the output grid in * an arbitrary way. The input and output grids may have any number * of dimensions, not necessarily equal. * * Where the input positions given do not correspond with a pixel centre * in the output grid, the each input pixel value is spread out between the * surrounding output pixels using weights determined by a separable kernel * which is the product of a 1-dimensional kernel function evaluated along * each output dimension. A pointer should be supplied to the 1-dimensional * kernel function to be used. * Parameters: * this * Pointer to the Mapping being used in the rebinning operation * (this is only used for constructing error messages). * ndim_out * The number of dimensions in the output grid. This should be at * least one. * lbnd_out * Pointer to an array of integers, with "ndim_out" elements. * This should give the coordinates of the centre of the first * pixel in the output grid along each dimension. * ubnd_out * Pointer to an array of integers, with "ndim_out" elements. * This should give the coordinates of the centre of the last * pixel in the output grid along each dimension. * * Note that "lbnd_out" and "ubnd_out" together define the shape * and size of the output grid, its extent along a particular * (i'th) dimension being ubnd_out[i]-lbnd_out[i]+1 (assuming "i" * is zero-based). They also define the output grid's coordinate * system, with each pixel being of unit extent along each * dimension with integral coordinate values at its centre. * in * Pointer to the array of data to be rebinned. The numerical type * of these data should match the function used, as given by the * suffix on the function name. Note that details of how the input * grid maps on to this array (e.g. the storage order, number of * dimensions, etc.) is arbitrary and is specified entirely by means * of the "offset" array. The "in" array should therefore contain * sufficient elements to accommodate the "offset" values supplied. * There is no requirement that all elements of the "in" array * should be rebinned, and any which are not addressed by the * contents of the "offset" array will be ignored. * in_var * An optional pointer to a second array of positive numerical * values (with the same size and type as the "in" array), which * represent estimates of the statistical variance associated * with each element of the "in" array. If this second array is * given (along with the corresponding "out_var" array), then * estimates of the variance of the resampled data will also be * returned. It is addressed in exactly the same way (via the * "offset" array) as the "in" array. * * If no variance estimates are required, a NULL pointer should * be given. * infac * A factor by which to multiply the input data values before use. * npoint * The number of input points which are to be rebinned. * offset * Pointer to an array of integers with "npoint" elements. For * each input point, this array should contain the zero-based * offset in the input array(s) (i.e. the "in" and, optionally, * the "in_var" arrays) from which the value to be rebinned should * be obtained. * coords * An array of pointers to double, with "ndim_out" elements. * Element "coords[coord]" should point at the first element of * an array of double (with "npoint" elements) which contains the * values of coordinate number "coord" for each point being * rebinned. The value of coordinate number "coord" for * rebinning point number "point" is therefore given by * "coords[coord][point]" (assuming both indices are * zero-based). If any point has a coordinate value of AST__BAD * associated with it, then the corresponding input data (and * variance) value will be ignored. * kernel * Pointer to the 1-dimensional kernel function to be used. * neighb * The number of neighbouring pixels in each dimension (on each * side of the interpolation position) which are to receive * contributions from the input pixel value. This value should be at * least 1. * params * Pointer to an optional array of parameter values to be passed * to the kernel function. If no parameters are required by this * function, then a NULL pointer may be supplied. * conwgt * The initial weight to use for all pixels (typically 1.0). Other * weights (e.g. interpolation weights, variance weights, etc) are * applied as factors to this initial value. * flags * The bitwise OR of a set of flag values which control the * operation of the function. These are chosend from: * * - AST__USEBAD: indicates whether there are "bad" (i.e. missing) data * in the input array(s) which must be recognised. If this flag is not * set, all input values are treated literally. * - AST__GENVAR: Indicates that output variances should be generated * from the spread of values contributing to each output pixel. * - AST__USEVAR: Indicates that output variances should be generated * by rebinning the input variances. * - AST__VARWGT: Indicates that input variances should be used to * create weights for the input data values. * * Only one of AST__GENVAR and AST__USEVAR should be supplied. * badval * If the AST__USEBAD flag is set in the "flags" value (above), * this parameter specifies the value which is used to identify * bad data and/or variance values in the input array(s). Its * numerical type must match that of the "in" (and "in_var") * arrays. The same value will also be used to flag any output * array elements for which resampled values could not be * obtained. The output arrays(s) may be flagged with this * value whether or not the AST__USEBAD flag is set (the * function return value indicates whether any such values have * been produced). * npix_out * Number of pixels in output array. * out * Pointer to an array with the same data type as the "in" * array, into which the rebinned data will be returned. The * storage order should be such that the index of the first grid * dimension varies most rapidly and that of the final dimension * least rapidly (i.e. Fortran array storage order). * out_var * An optional pointer to an array with the same data type and * size as the "out" array, into which variance estimates for * the rebinned values may be returned. This array will only be * used if the "in_var" array has been given. The values returned * are estimates of the statistical variance of the corresponding * values in the "out" array, on the assumption that all errors in * input grid values (in the "in" array) are statistically independent * and that their variance estimates (in the "in_var" array) may * simply be summed (with appropriate weighting factors). * * If no output variance estimates are required, a NULL pointer * should be given. * work * A pointer to an array with the same data type and size as the "out" * array which is used as work space. The values in the supplied * array are incremented on exit by the sum of the weights used * with each output pixel. * nused * An optional pointer to a int64_t which will be incremented by the * number of input values pasted into the output array. Ignored if NULL. * Notes: * - There is a separate function for each numerical type of * gridded data, distinguished by replacing the in the function * name by the appropriate 1- or 2-character suffix. */ /* Define macros to implement the function for a specific data type. */ #define MAKE_SPREAD_KERNEL1(X,Xtype,IntType) \ static void SpreadKernel1##X( AstMapping *this, int ndim_out, \ const int *lbnd_out, const int *ubnd_out, \ const Xtype *in, const Xtype *in_var, \ double infac, int npoint, const int *offset, \ const double *const *coords, \ void (* kernel)( double, const double [], \ int, double *, int * ), \ int neighb, const double *params, \ double conwgt, int flags, Xtype badval, int npix_out, \ Xtype *out, Xtype *out_var, double *work, \ int64_t *nused, int *status ) { \ \ /* Local Variables: */ \ astDECLARE_GLOBALS /* Thread-specific data */ \ Xtype c; \ Xtype in_val; /* Input pixel value */ \ double **wtptr; /* Pointer to array of weight pointers */ \ double **wtptr_last; /* Array of highest weight pointer values */ \ double *filter; /* Pointer to Nd array of filter values */ \ double *kp; /* Pointer to next weight values */ \ double *kstart; /* Pointer to next kernel value */ \ double *kval; /* Pointer to 1d array of kernel values */ \ double *wtprod; /* Accumulated weight value array pointer */ \ double *xfilter; /* Pointer to 1d array of x axis filter values */ \ double *xnl; /* Pointer to previous ofset array (n-d) */ \ double pfac; /* Input weight with extra supplied factor */ \ double pixwt; /* Weight to apply to individual pixel */ \ double sum; /* Sum of all filter values */ \ double wgt; /* Weight for input value */ \ double x; /* x coordinate value */ \ double xn; /* Coordinate value (n-d) */ \ double xx; /* X offset */ \ double xxl; /* Previous X offset */ \ double xxn; \ double y; /* y coordinate value */ \ double yy; /* Y offset */ \ double yyl; /* Previous Y offset */ \ int *hi; /* Pointer to array of upper indices */ \ int *jhi; /* Pointer to array of filter upper indices */ \ int *jlo; /* Pointer to array of filter lower indices */ \ int *lo; /* Pointer to array of lower indices */ \ int *stride; /* Pointer to array of dimension strides */ \ int bad; /* Output pixel bad? */ \ int done; /* All pixel indices done? */ \ int genvar; /* Generate output variances? */ \ int hi_ix; /* Upper output pixel index (x dimension) */ \ int hi_iy; /* Upper output pixel index (y dimension) */ \ int hi_jx; /* Upper filter pixel index (x dimension) */ \ int hi_jy; /* Upper filter pixel index (y dimension) */ \ int idim; /* Loop counter for dimensions */ \ int ii; /* Loop counter for dimensions */ \ int ix; /* Pixel index in output grid x dimension */ \ int iy; /* Pixel index in output grid y dimension */ \ int jjx; /* Reflected pixel index in filter grid x dimension */ \ int jjy; /* Reflected pixel index in filter grid y dimension */ \ int jx; /* Pixel index in filter grid x dimension */ \ int jxn; \ int jy; /* Pixel index in filter grid y dimension */ \ int kerror; /* Error signalled by kernel function? */ \ int lo_ix; /* Lower output pixel index (x dimension) */ \ int lo_iy; /* Lower output pixel index (y dimension) */ \ int lo_jx; /* Lower filter pixel index (x dimension) */ \ int lo_jy; /* Lower filter pixel index (y dimension) */ \ int nb2; /* The total number of neighbouring pixels */ \ int nf; /* Number of pixels in filter array */ \ int nwx; /* Used X width of kernel function (*2) */ \ int nwy; /* Used Y width of kernel function (*2) */ \ int off1; /* Input pixel offset due to y index */ \ int off_in; /* Offset to input pixel */ \ int off_out; /* Offset to output pixel */ \ int off_xedge; /* Does filter box overlap array edge on the X axis? */ \ int off_yedge; /* Does filter box overlap array edge on the Y axis? */ \ int point; /* Loop counter for output points */ \ int s; /* Temporary variable for strides */ \ int usebad; /* Use "bad" input pixel values? */ \ int usevar; /* Process variance array? */ \ int varwgt; /* Use input variances as weights? */ \ int ystride; /* Stride along input grid y dimension */ \ \ /* Check the global error status. */ \ if ( !astOK ) return; \ \ /* Get a pointer to a structure holding thread-specific global data values */ \ astGET_GLOBALS(this); \ \ /* Further initialisation. */ \ kerror = 0; \ sum = 0.0; \ bad = 0; \ \ /* Find the total number of pixels in the filter used to spread a single \ input pixel into the output image. */ \ nb2 = 2*neighb; \ nf = 1; \ for ( idim = 0; idim < ndim_out; idim++ ) nf *= nb2; \ \ /* Allocate workspace to hold the filter values. */ \ filter = astMalloc( sizeof( double ) * (size_t) nf ); \ if ( astOK ) { \ \ /* Determine if we are processing bad pixels or variances. */ \ usebad = flags & AST__USEBAD; \ usevar = 0; \ genvar = 0; \ if( flags & AST__GENVAR ) { \ genvar = out_var && work; \ } else if( flags & AST__USEVAR ) { \ usevar = in_var && out_var; \ } \ varwgt = ( flags & AST__VARWGT ) && in_var && work; \ \ /* Handle the 1-dimensional case optimally. */ \ /* ---------------------------------------- */ \ if ( ndim_out == 1 ) { \ \ /* Identify eight cases, according to whether bad pixels and/or variances \ are being processed and/or used. In each case we assign constant values \ (0 or 1) to the "Usebad", "Usevar" and "Varwgt" flags so that code for \ handling bad pixels and variances can be eliminated by the compiler's \ optimisation system when not required. */ \ if( varwgt ) { \ if ( usebad ) { \ if ( usevar ) { \ KERNEL_1D(X,Xtype,1,1,0,IntType,1) \ } else if ( genvar ) { \ KERNEL_1D(X,Xtype,1,0,1,IntType,1) \ } else { \ KERNEL_1D(X,Xtype,1,0,0,IntType,1) \ } \ } else { \ if ( usevar ) { \ KERNEL_1D(X,Xtype,0,1,0,IntType,1) \ } else if ( genvar ) { \ KERNEL_1D(X,Xtype,0,0,1,IntType,1) \ } else { \ KERNEL_1D(X,Xtype,0,0,0,IntType,1) \ } \ } \ } else { \ if ( usebad ) { \ if ( usevar ) { \ KERNEL_1D(X,Xtype,1,1,0,IntType,0) \ } else if ( genvar ) { \ KERNEL_1D(X,Xtype,1,0,1,IntType,0) \ } else { \ KERNEL_1D(X,Xtype,1,0,0,IntType,0) \ } \ } else { \ if ( usevar ) { \ KERNEL_1D(X,Xtype,0,1,0,IntType,0) \ } else if ( genvar ) { \ KERNEL_1D(X,Xtype,0,0,1,IntType,0) \ } else { \ KERNEL_1D(X,Xtype,0,0,0,IntType,0) \ } \ } \ } \ \ /* Exit point on error in kernel function */ \ Kernel_SError_1d: ; \ \ /* Handle the 2-dimensional case optimally. */ \ /* ---------------------------------------- */ \ } else if ( ndim_out == 2 ) { \ \ /* Allocate workspace to hold the X axis filter values. */ \ xfilter = astMalloc( sizeof( double ) * (size_t) nb2 ); \ \ /* Calculate the stride along the y dimension of the output grid. */ \ ystride = ubnd_out[ 0 ] - lbnd_out[ 0 ] + 1; \ \ /* Identify eight cases, according to whether bad pixels and/or variances \ are being processed and/or used. In each case we assign constant values \ (0 or 1) to the "Usebad", "Usevar" and "Varwgt" flags so that code for \ handling bad pixels and variances can be eliminated by the compiler's \ optimisation system when not required. */ \ if( varwgt ) { \ if ( usebad ) { \ if ( usevar ) { \ KERNEL_2D(X,Xtype,1,1,0,IntType,1) \ } else if ( genvar ) { \ KERNEL_2D(X,Xtype,1,0,1,IntType,1) \ } else { \ KERNEL_2D(X,Xtype,1,0,0,IntType,1) \ } \ } else { \ if ( usevar ) { \ KERNEL_2D(X,Xtype,0,1,0,IntType,1) \ } else if ( genvar ) { \ KERNEL_2D(X,Xtype,0,0,1,IntType,1) \ } else { \ KERNEL_2D(X,Xtype,0,0,0,IntType,1) \ } \ } \ } else { \ if ( usebad ) { \ if ( usevar ) { \ KERNEL_2D(X,Xtype,1,1,0,IntType,0) \ } else if ( genvar ) { \ KERNEL_2D(X,Xtype,1,0,1,IntType,0) \ } else { \ KERNEL_2D(X,Xtype,1,0,0,IntType,0) \ } \ } else { \ if ( usevar ) { \ KERNEL_2D(X,Xtype,0,1,0,IntType,0) \ } else if ( genvar ) { \ KERNEL_2D(X,Xtype,0,0,1,IntType,0) \ } else { \ KERNEL_2D(X,Xtype,0,0,0,IntType,0) \ } \ } \ } \ \ /* Free work space */ \ xfilter = astFree( xfilter ); \ \ /* Exit point on error in kernel function */ \ Kernel_SError_2d: ; \ \ /* Handle other numbers of dimensions. */ \ /* ----------------------------------- */ \ } else { \ \ /* Allocate workspace. */ \ hi = astMalloc( sizeof( int ) * (size_t) ndim_out ); \ lo = astMalloc( sizeof( int ) * (size_t) ndim_out ); \ jhi = astMalloc( sizeof( int ) * (size_t) ndim_out ); \ jlo = astMalloc( sizeof( int ) * (size_t) ndim_out ); \ stride = astMalloc( sizeof( int ) * (size_t) ndim_out ); \ xnl = astMalloc( sizeof( double ) * (size_t) ndim_out ); \ kval = astMalloc( sizeof( double ) * (size_t) \ ( nb2 * ndim_out ) ); \ wtprod = astMalloc( sizeof( double ) * (size_t) ndim_out ); \ wtptr = astMalloc( sizeof( double * ) * (size_t) ndim_out ); \ wtptr_last = astMalloc( sizeof( double * ) * (size_t) ndim_out ); \ if ( astOK ) { \ \ /* Calculate the stride along each dimension of the output grid. */ \ for ( s = 1, idim = 0; idim < ndim_out; idim++ ) { \ stride[ idim ] = s; \ s *= ubnd_out[ idim ] - lbnd_out[ idim ] + 1; \ xnl[ idim ] = AST__BAD; \ } \ \ /* Identify eight cases, according to whether bad pixels and/or variances \ are being processed and/or used. In each case we assign constant values \ (0 or 1) to the "Usebad", "Usevar" and "Varwgt" flags so that code for \ handling bad pixels and variances can be eliminated by the compiler's \ optimisation system when not required. */ \ if( varwgt ) { \ if ( usebad ) { \ if ( usevar ) { \ KERNEL_ND(X,Xtype,1,1,0,IntType,1) \ } else if ( genvar ) { \ KERNEL_ND(X,Xtype,1,0,1,IntType,1) \ } else { \ KERNEL_ND(X,Xtype,1,0,0,IntType,1) \ } \ } else { \ if ( usevar ) { \ KERNEL_ND(X,Xtype,0,1,0,IntType,1) \ } else if ( genvar ) { \ KERNEL_ND(X,Xtype,0,0,1,IntType,1) \ } else { \ KERNEL_ND(X,Xtype,0,0,0,IntType,1) \ } \ } \ } else { \ if ( usebad ) { \ if ( usevar ) { \ KERNEL_ND(X,Xtype,1,1,0,IntType,0) \ } else if ( genvar ) { \ KERNEL_ND(X,Xtype,1,0,1,IntType,0) \ } else { \ KERNEL_ND(X,Xtype,1,0,0,IntType,0) \ } \ } else { \ if ( usevar ) { \ KERNEL_ND(X,Xtype,0,1,0,IntType,0) \ } else if ( genvar ) { \ KERNEL_ND(X,Xtype,0,0,1,IntType,0) \ } else { \ KERNEL_ND(X,Xtype,0,0,0,IntType,0) \ } \ } \ } \ \ /* Exit point on error in kernel function */ \ Kernel_SError_Nd: ;\ } \ \ /* Free the workspace. */ \ hi = astFree( hi ); \ lo = astFree( lo ); \ jhi = astFree( jhi ); \ jlo = astFree( jlo ); \ stride = astFree( stride ); \ xnl = astFree( xnl ); \ kval = astFree( kval ); \ wtprod = astFree( wtprod ); \ wtptr = astFree( wtptr ); \ wtptr_last = astFree( wtptr_last ); \ } \ filter = astFree( filter ); \ }\ \ /* If an error occurred in the kernel function, then report a \ contextual error message. */ \ if ( kerror ) { \ astError( astStatus, "astRebin"#X"(%s): Error signalled by " \ "user-supplied 1-d interpolation kernel.", status, \ astGetClass( unsimplified_mapping ) ); \ } \ \ } #define KERNEL_1D(X,Xtype,Usebad,Usevar,Genvar,IntType,Varwgt) \ \ /* We do not yet have a previous filter position. */ \ xxl = AST__BAD; \ \ /* Loop round all input points which are to be rebinned. */ \ for( point = 0; point < npoint; point++ ) { \ \ /* Obtain the input data value which is to be added into the output array. */ \ off_in = offset[ point ]; \ in_val = in[ off_in ]; \ \ /* If necessary, test if the input data value or variance is bad. If we \ are using the reciprocal of the input variances as weights, then \ variance values of zero are also effectively bad (but we can use input \ variances of zero otherwise). */ \ if ( Usebad ) { \ bad = ( in_val == badval ); \ if ( Varwgt ) { \ bad = bad || ( in_var[ off_in ] == badval ) \ || ( in_var[ off_in ] <= 0.0 ); \ } else if ( Usevar ) { \ bad = bad || ( in_var[ off_in ] == badval ); \ } \ } else { \ if ( Varwgt ) { \ bad = ( in_var[ off_in ] <= 0.0 ); \ } else { \ bad = 0; \ } \ } \ \ /* Obtain the x coordinate of the current point and test if it is bad. \ Also test that the central point falls within the output array. */ \ x = coords[ 0 ][ point ]; \ ix = (int) floor( x + 0.5 ); \ if( ix < lbnd_out[ 0 ] || ix > ubnd_out[ 0 ] ) bad = 1; \ bad = bad || ( x == AST__BAD ); \ \ /* If OK, calculate the lowest and highest indices (in the x \ dimension) of the region of neighbouring output pixels that will \ receive contributions from the current input pixel. Constrain these \ values to lie within the output grid. */ \ if ( !bad ) { \ ix = (int) floor( x ) - neighb + 1; \ lo_ix = MaxI( ix, lbnd_out[ 0 ], status ); \ hi_ix = MinI( ix + nb2 - 1, ubnd_out[ 0 ], status ); \ \ /* Skip to the next input point if the current input point makes no \ contribution to any output pixel. */ \ if( lo_ix <= hi_ix ) { \ \ /* Increment the number of input pixels pasted into the output array. */ \ if( nused ) (*nused)++; \ \ /* Convert these output indices to the corresponding indices \ within a box [ 0, 2*neighb ] holding the kernel values. */ \ lo_jx = lo_ix - ix; \ hi_jx = hi_ix - ix; \ \ /* See if the kernel extends off the edge of the output array. */ \ nwx = hi_jx - lo_jx + 1; \ off_xedge = ( nwx < nb2 ); \ \ /* Use the kernel function to fill the work array with weights for all output \ pixels whether or not they fall within the output array. At the same \ time find the sum of all the factors. */ \ xx = (double) ix - x; \ if( xx != xxl || off_xedge ) { \ sum = 0.0; \ \ /* First handle cases where the kernel box overlaps an edge of the output \ array. In these cases, in order to conserve flux, the bit of the \ kernel function that is off the edge is reflected back onto the array. \ Care must be taken since the reflected part of the kernel may itself \ overlap the opposite edge of the array, in which case the overlapping \ part must again be reflected back onto the array. This iterative \ reflection is implemented using a fractional division (%) operator. */ \ if( off_xedge ) { \ nwx *= 2; \ xxl = AST__BAD; \ for( jx = 0; jx < nb2; jx++ ) filter[ jx ] = 0.0; \ \ for ( jx = 0; jx < nb2; jx++ ) { \ ( *kernel )( xx, params, flags, &pixwt, status ); \ if ( !astOK ) { \ kerror = 1; \ goto Kernel_SError_1d; \ } \ \ jjx = ( jx - lo_jx ) % nwx + lo_jx; \ if( jjx < lo_jx ) jjx += nwx; \ if( jjx > hi_jx ) jjx = 2*hi_jx - jjx + 1; \ \ filter[ jjx ] += pixwt; \ sum += pixwt; \ xx += 1.0; \ } \ \ /* Now handle cases where the kernel box is completely within the output \ array. */ \ } else { \ xxl = xx; \ \ for ( jx = 0; jx < nb2; jx++ ) { \ ( *kernel )( xx, params, flags, &pixwt, status ); \ \ /* Check for errors arising in the kernel function. */ \ if ( !astOK ) { \ kerror = 1; \ goto Kernel_SError_1d; \ } \ \ /* Store the kernel factor and increment the sum of all factors. */ \ filter[ jx ] = pixwt; \ sum += pixwt; \ xx += 1.0; \ } \ \ } \ \ /* Ensure we do not divide by zero. */ \ if( sum == 0.0 ) sum = 1.0; \ } \ \ /* If we are using the input data variances as weights, calculate the \ total weight, incorporating the normalisation factor for the kernel. */ \ if( Varwgt ) { \ wgt = conwgt/(sum*in_var[ off_in ]); \ \ /* If we are not using input variances as weights, the weight is just the \ kernel normalisation factor. */ \ } else { \ wgt = conwgt/sum; \ } \ \ /* Loop round all the output pixels which receive contributions from this \ input pixel, calculating the offset of each pixel from the start of the \ input array. */ \ off_out = lo_ix - lbnd_out[ 0 ]; \ for ( jx = lo_jx; jx <= hi_jx; jx++, off_out++ ) { \ \ /* Retrieve the weight for the current output pixel and normalise it. */ \ pixwt = wgt*filter[ jx ]; \ pfac = pixwt*infac; \ \ /* Update the output pixel with the required fraction of the input pixel \ value. */ \ c = CONV(IntType,pfac*in_val); \ \ if( work ) { \ out[ off_out ] += c; \ work[ off_out ] += pixwt; \ } else {\ out[ off_out ] += c; \ } \ \ if ( Usevar ) { \ out_var[ off_out ] += CONV(IntType,in_var[ off_in ]*pfac*pfac); \ } else if ( Genvar && pixwt != 0.0 ) { \ out_var[ off_out ] += c*c/pixwt; \ work[ off_out + npix_out ] += pixwt*pixwt; \ } \ \ } \ } \ } \ } #define KERNEL_2D(X,Xtype,Usebad,Usevar,Genvar,IntType,Varwgt) \ \ /* We do not yet have a previous filter position. */ \ xxl = AST__BAD; \ yyl = AST__BAD; \ \ /* Loop round all input points which are to be rebinned. */ \ for( point = 0; point < npoint; point++ ) { \ \ /* Obtain the input data value which is to be added into the output array. */ \ off_in = offset[ point ]; \ in_val = in[ off_in ]; \ \ /* If necessary, test if the input data value or variance is bad. If we \ are using the reciprocal of the input variances as weights, then \ variance values of zero are also effectively bad (but we can use input \ variances of zero otherwise). */ \ if ( Usebad ) { \ bad = ( in_val == badval ); \ if ( Varwgt ) { \ bad = bad || ( in_var[ off_in ] == badval ) \ || ( in_var[ off_in ] <= 0.0 ); \ } else if ( Usevar ) { \ bad = bad || ( in_var[ off_in ] == badval ); \ } \ } else { \ if ( Varwgt ) { \ bad = ( in_var[ off_in ] <= 0.0 ); \ } else { \ bad = 0; \ } \ } \ \ /* Obtain the x coordinate of the current point and test if it is bad. \ Also test that the central point falls within the output array. */ \ x = coords[ 0 ][ point ]; \ ix = (int) floor( x + 0.5 ); \ if( ix < lbnd_out[ 0 ] || ix > ubnd_out[ 0 ] ) bad = 1; \ bad = bad || ( x == AST__BAD ); \ if ( !bad ) { \ \ /* Similarly obtain and test the y coordinate. */ \ y = coords[ 1 ][ point ]; \ iy = (int) floor( y + 0.5 ); \ if( iy < lbnd_out[ 1 ] || iy > ubnd_out[ 1 ] ) bad = 1; \ bad = bad || ( y == AST__BAD ); \ if ( !bad ) { \ \ /* If OK, calculate the lowest and highest indices (in each dimension) \ of the region of neighbouring output pixels which will receive \ contributions from the current input pixel. Constrain these values \ to lie within the input grid. */ \ ix = (int) floor( x ) - neighb + 1; \ lo_ix = MaxI( ix, lbnd_out[ 0 ], status ); \ hi_ix = MinI( ix + nb2 - 1, ubnd_out[ 0 ], status ); \ iy = (int) floor( y ) - neighb + 1; \ lo_iy = MaxI( iy, lbnd_out[ 1 ], status ); \ hi_iy = MinI( iy + nb2 - 1, ubnd_out[ 1 ], status ); \ \ /* Skip to the next input point if the current input point makes no \ contribution to any output pixel. */ \ if( lo_ix <= hi_ix && lo_iy <= hi_iy ) { \ \ /* Increment the number of input pixels pasted into the output array. */ \ if( nused ) (*nused)++; \ \ /* Convert these output indices to the corresponding indices \ within a box [ 0:2*neighb, 0:2*neighb ] holding the kernel values. */ \ lo_jx = lo_ix - ix; \ hi_jx = hi_ix - ix; \ lo_jy = lo_iy - iy; \ hi_jy = hi_iy - iy; \ \ /* See if the kernel extends off the edge of the output array on either \ axis. */ \ nwx = hi_jx - lo_jx + 1; \ nwy = hi_jy - lo_jy + 1; \ off_xedge = ( nwx < nb2 ); \ off_yedge = ( nwy < nb2 ); \ \ /* Loop to evaluate the kernel function along the y dimension, storing \ the resulting weight values in all elements of each associated row \ in the kvar array. The function's argument is the offset of the \ output pixel (along this dimension) from the central output \ position. */ \ yy = (double) iy - y; \ xx = (double) ix - x; \ if( xx != xxl || yy != yyl || off_xedge || off_yedge ) { \ \ /* First handle cases where the kernel box extends beyond the top or \ bottom edge of the output array. In these cases, in order to conserve \ flux, the bit of the kernel function that is off the edge is reflected \ back onto the array. Care must be taken since the reflected part of the \ kernel may itself overlap the opposite edge of the array, in which \ case the overlapping part must again be reflected back onto the \ array. This iterative reflection is implemented using a fractional \ division (%) operator. */ \ if( off_yedge ) { \ nwy *= 2; \ xxl = AST__BAD; \ yyl = AST__BAD; \ for( jy = 0; jy < nb2*nb2; jy++ ) filter[ jy ] = 0.0; \ \ for ( jy = 0; jy < nb2; jy++ ) { \ ( *kernel )( yy, params, flags, &pixwt, status ); \ if ( !astOK ) { \ kerror = 1; \ goto Kernel_SError_2d; \ } \ \ jjy = ( jy - lo_jy ) % nwy + lo_jy; \ if( jjy < lo_jy ) jjy += nwy; \ if( jjy > hi_jy ) jjy = 2*hi_jy - jjy + 1; \ \ kp = filter + jjy*nb2; \ for( jx = 0; jx < nb2; jx++ ) *(kp++) += pixwt; \ yy += 1.0; \ } \ \ /* Now handles cases where the kernel does not overlap the top or bottom edge \ of the output array. */ \ } else { \ xxl = xx; \ yyl = yy; \ kp = filter; \ for ( jy = 0; jy < nb2; jy++ ) { \ ( *kernel )( yy, params, flags, &pixwt, status ); \ \ /* Check for errors arising in the kernel function. */ \ if ( !astOK ) { \ kerror = 1; \ goto Kernel_SError_2d; \ } \ \ /* Store the kernel factor in all elements of the current row. */ \ for( jx = 0; jx < nb2; jx++ ) *(kp++) = pixwt; \ \ /* Move on to the next row. */ \ yy += 1.0; \ } \ } \ \ /* Loop to evaluate the kernel function along the x dimension, multiplying \ the resulting weight values by the values already stored in the the \ associated column in the kvar array. The function's argument is the \ offset of the output pixel (along this dimension) from the central output \ position. Also form the total data sum in the filter array. First \ handle cases where the kernel overlaps the left or right edge of the \ output array. */ \ sum = 0.0; \ \ /* First deal with cases where the kernel extends beyond the left or \ right edge of the output array. */ \ if( off_xedge ) { \ nwx *= 2; \ xxl = AST__BAD; \ for( jx = 0; jx < nb2; jx++ ) xfilter[ jx ] = 0.0; \ \ for ( jx = 0; jx < nb2; jx++ ) { \ ( *kernel )( xx, params, flags, &pixwt, status ); \ if ( !astOK ) { \ kerror = 1; \ goto Kernel_SError_2d; \ } \ \ jjx = ( jx - lo_jx ) % nwx + lo_jx; \ if( jjx < lo_jx ) jjx += nwx; \ if( jjx > hi_jx ) jjx = 2*hi_jx - jjx + 1; \ \ xfilter[ jjx ] += pixwt; \ xx += 1.0; \ } \ \ for ( jx = 0; jx < nb2; jx++ ) { \ kp = filter + jx; \ for( jy = 0; jy < nb2; jy++, kp += nb2 ) { \ *kp *= xfilter[ jx ]; \ sum += *kp; \ } \ } \ \ /* Now deal with cases where the kernel does not extends beyond the left or \ right edge of the output array. */ \ } else { \ \ for ( jx = 0; jx < nb2; jx++ ) { \ ( *kernel )( xx, params, flags, &pixwt, status ); \ \ /* Check for errors arising in the kernel function. */ \ if ( !astOK ) { \ kerror = 1; \ goto Kernel_SError_2d; \ } \ \ /* Multiply the kernel factor by all elements of the current column. */ \ kp = filter + jx; \ for( jy = 0; jy < nb2; jy++, kp += nb2 ) { \ *kp *= pixwt; \ sum += *kp; \ } \ \ /* Move on to the next column. */ \ xx += 1.0; \ } \ } \ \ /* Ensure we do not divide by zero. */ \ if( sum == 0.0 ) sum = 1.0; \ } \ \ /* If we are using the input data variances as weights, calculate the \ total weight, incorporating the normalisation factor for the kernel. */ \ if( Varwgt ) { \ wgt = conwgt/(sum*in_var[ off_in ]); \ \ /* If we are not using input variances as weights, the weight is just the \ kernel normalisation factor. */ \ } else { \ wgt = conwgt/sum; \ } \ \ /* Find the offset into the output array at the first modified output pixel \ in the first modified row. */ \ off1 = lo_ix - lbnd_out[ 0 ] + ystride * ( lo_iy - lbnd_out[ 1 ] ); \ \ /* Loop over the affected output rows again. */ \ for ( jy = lo_jy; jy <= hi_jy; jy++, off1 += ystride ) { \ \ /* Save the offset of the first output pixel to be modified in the \ current row. */ \ off_out = off1; \ \ /* Get a pointer to the first weight value which will be used. */ \ kp = filter + lo_jx + jy*nb2; \ \ /* Loop over the affected output columns again. */ \ for ( jx = lo_jx; jx <= hi_jx; jx++, off_out++, kp++ ) { \ \ /* Calculate the weight for this output pixel and normalise it. */ \ pixwt = wgt*( *kp ); \ \ /* Update the output pixel with the required fraction of the input pixel \ value. */ \ pfac = pixwt*infac; \ c = CONV(IntType,pfac*in_val); \ \ out[ off_out ] += c; \ if( work ) work[ off_out ] += pixwt; \ \ if ( Usevar ) { \ out_var[ off_out ] += CONV(IntType,in_var[ off_in ]*pfac*pfac); \ } else if ( Genvar && pixwt != 0.0 ) { \ out_var[ off_out ] += c*c/pixwt; \ work[ off_out + npix_out ] += pixwt*pixwt; \ } \ } \ } \ } \ } \ } \ } #define KERNEL_ND(X,Xtype,Usebad,Usevar,Genvar,IntType,Varwgt) \ \ /* We do not yet have a normalising factor */ \ sum = AST__BAD; \ \ /* Loop round all input points which are to be rebinned. */ \ for( point = 0; point < npoint; point++ ) { \ \ /* Obtain the input data value which is to be added into the output array. */ \ off_in = offset[ point ]; \ in_val = in[ off_in ]; \ \ /* If necessary, test if the input data value or variance is bad. If we \ are using the reciprocal of the input variances as weights, then \ variance values of zero are also effectively bad (but we can use input \ variances of zero otherwise). */ \ if ( Usebad ) { \ bad = ( in_val == badval ); \ if ( Varwgt ) { \ bad = bad || ( in_var[ off_in ] == badval ) \ || ( in_var[ off_in ] <= 0.0 ); \ } else if ( Usevar ) { \ bad = bad || ( in_var[ off_in ] == badval ); \ } \ } else { \ if ( Varwgt ) { \ bad = ( in_var[ off_in ] <= 0.0 ); \ } else { \ bad = 0; \ } \ } \ \ /* Initialise offsets into the output array. Then loop to obtain each \ coordinate associated with the current output point. Set a flag \ indicating if any output pixel will be modified. */ \ if( !bad ) { \ off_out = 0; \ for ( idim = 0; idim < ndim_out; idim++ ) { \ xn = coords[ idim ][ point ]; \ \ /* Test if the coordinate is bad. If true, the corresponding output pixel \ value will be bad, so give up on this point. */ \ ix = (int) floor( xn + 0.5 ); \ if( ix < lbnd_out[ idim ] || ix > ubnd_out[ idim ] ) bad = 1; \ bad = bad || ( xn == AST__BAD ); \ if ( bad ) break; \ \ /* Calculate the lowest and highest indices (in the current dimension) \ of the region of neighbouring output pixels that will be modified. \ Constrain these values to lie within the output grid. */ \ ix = (int) floor( xn ) - neighb + 1; \ lo[ idim ] = MaxI( ix, lbnd_out[ idim ], status ); \ hi[ idim ] = MinI( ix + nb2 - 1, ubnd_out[ idim ], status ); \ jlo[ idim ] = lo[ idim ] - ix; \ jhi[ idim ] = hi[ idim ] - ix; \ \ /* Check there is some overlap with the output array on this axis. */ \ if( lo[ idim ] > hi[ idim ] ) { \ bad = 1; \ break; \ } \ \ /* Accumulate the offset (from the start of the output array) of the \ modified output pixel which has the lowest index in each dimension. */ \ off_out += stride[ idim ] * ( lo[ idim ] - lbnd_out[ idim ] ); \ \ /* Set up an array of pointers to locate the first filter pixel (stored in the \ "kval" array) for each dimension. */ \ wtptr[ idim ] = kval + nb2*idim; \ wtptr_last[ idim ] = wtptr[ idim ] + nb2 - 1; \ \ /* See if the kernel extends off the edge of the output array on the current \ axis. */ \ lo_jx = jlo[ idim ]; \ hi_jx = jhi[ idim ]; \ nwx = hi_jx - lo_jx + 1; \ off_xedge = ( nwx < nb2 ); \ \ /* Loop to evaluate the kernel function along each dimension, storing \ the resulting values. The function's argument is the offset of the \ output pixel (along the relevant dimension) from the central output \ point. */ \ xxn = (double) ix - xn; \ if( xxn != xnl[ idim ] || off_xedge ) { \ sum = AST__BAD; \ \ /* First handle cases where the kernel box overlaps an edge of the output \ array. In these cases, in order to conserve flux, the bit of the \ kernel function that is off the edge is reflected back onto the array. \ Care must be taken since the reflected part of the kernel may itself \ overlap the opposite edge of the array, in which case the overlapping \ part must again be reflected back onto the array. This iterative \ reflection is implemented using a fractional division (%) operator. */ \ if( off_xedge ) { \ nwx *= 2; \ xnl[ idim ] = AST__BAD; \ kp = wtptr[ idim ]; \ for( jx = 0; jx < nb2; jx++ ) *(kp++) = 0.0; \ \ kp = wtptr[ idim ]; \ for ( jx = 0; jx < nb2; jx++ ) { \ ( *kernel )( xxn, params, flags, &pixwt, status ); \ if ( !astOK ) { \ kerror = 1; \ goto Kernel_SError_1d; \ } \ \ jjx = ( jx - lo_jx ) % nwx + lo_jx; \ if( jjx < lo_jx ) jjx += nwx; \ if( jjx > hi_jx ) jjx = 2*hi_jx - jjx + 1; \ \ kp[ jjx ] += pixwt; \ xxn += 1.0; \ } \ \ /* Now handle cases where the kernel box is completely within the output \ array. */ \ } else { \ xnl[ idim ] = xxn; \ for ( jxn = 0; jxn < nb2; jxn++ ) { \ ( *kernel )( xxn, params, flags, wtptr[ idim ] + jxn, status ); \ \ /* Check for errors arising in the kernel function. */ \ if ( !astOK ) { \ kerror = 1; \ goto Kernel_SError_Nd; \ } \ \ /* Increment the kernel position. */ \ xxn += 1.0; \ } \ } \ } \ } \ \ /* If OK... */ \ if ( !bad ) { \ \ /* We only need to modify the normalising factor if the weight values \ have changed. */ \ if( sum == AST__BAD ) { \ \ /* The kernel value to use for each output pixel is the product of the \ kernel values for each individual axis at that point. To conserve \ flux we need to make sure that the sum of these kernel products is unity. \ So loop over the values now to find the total sum of all kernel values. */ \ idim = ndim_out - 1; \ wtprod[ idim ] = 1.0; \ done = 0; \ sum = 0; \ do { \ \ /* Each modified output pixel has a weight equal to the product of the kernel \ weight factors evaluated along each input dimension. However, since \ we typically only change the index of one dimension at a time, we \ can avoid forming this product repeatedly by retaining an array of \ accumulated products for all higher dimensions. We need then only \ update the lower elements in this array, corresponding to those \ dimensions whose index has changed. We do this here, "idim" being \ the index of the most significant dimension to have changed. Note \ that on the first pass, all dimensions are considered changed, \ causing this array to be initialised. */ \ for ( ii = idim; ii >= 1; ii-- ) { \ wtprod[ ii - 1 ] = wtprod[ ii ] * *( wtptr[ ii ] ); \ } \ \ /* Obtain the weight of each pixel from the accumulated product of \ weights. Also multiply by the weight for dimension zero, which is not \ included in the "wtprod" array). Increment the sum of all weights. */ \ sum += wtprod[ 0 ] * *( wtptr[ 0 ] ); \ \ /* Now update the weight value pointers and pixel offset to refer to \ the next output pixel to be considered. */ \ idim = 0; \ do { \ \ /* The first input dimension whose weight value pointer has not yet \ reached its final value has this pointer incremented. */ \ if ( wtptr[ idim ] != wtptr_last[ idim ] ) { \ wtptr[ idim ]++; \ break; \ \ /* Any earlier dimensions (which have reached the final pointer value) \ have this pointer returned to its lowest value. */ \ } else { \ wtptr[ idim ] -= nb2 - 1; \ done = ( ++idim == ndim_out ); \ } \ } while ( !done ); \ } while ( !done ); \ \ /* Ensure we do not divide by zero. */ \ if( sum == 0.0 ) sum = 1.0; \ } \ \ /* Re-initialise the weights pointers to refer to the first and last \ filter pixels which overlaps the output array. */ \ kstart = kval; \ for ( idim = 0; idim < ndim_out; idim++ ) { \ wtptr[ idim ] = kstart + jlo[ idim ]; \ wtptr_last[ idim ] = kstart + jhi[ idim ]; \ kstart += nb2; \ } \ \ /* If we are using the input data variances as weights, calculate the \ total weight, incorporating the normalisation factor for the kernel. */ \ if( Varwgt ) { \ wgt = conwgt/(sum*in_var[ off_in ]); \ \ /* If we are not using input variances as weights, the weight is just the \ kernel normalisation factor. */ \ } else { \ wgt = conwgt/sum; \ } \ \ /* Increment the number of input pixels pasted into the output array. */ \ if( nused ) (*nused)++; \ \ /* Initialise, and loop over the neighbouring output pixels to divide up \ the input pixel value between them. */ \ idim = ndim_out - 1; \ wtprod[ idim ] = 1.0; \ done = 0; \ do { \ \ /* Each modified output pixel has a weight equal to the product of the kernel \ weight factors evaluated along each input dimension. However, since \ we typically only change the index of one dimension at a time, we \ can avoid forming this product repeatedly by retaining an array of \ accumulated products for all higher dimensions. We need then only \ update the lower elements in this array, corresponding to those \ dimensions whose index has changed. We do this here, "idim" being \ the index of the most significant dimension to have changed. Note \ that on the first pass, all dimensions are considered changed, \ causing this array to be initialised. */ \ for ( ii = idim; ii >= 1; ii-- ) { \ wtprod[ ii - 1 ] = wtprod[ ii ] * *( wtptr[ ii ] ); \ } \ \ /* Obtain the weight of each pixel from the accumulated \ product of weights. Also multiply by the weight for dimension zero, \ which is not included in the "wtprod" array). */ \ pixwt = ( wtprod[ 0 ] * *( wtptr[ 0 ] ) )*wgt; \ \ /* Update the output pixel with the required fraction of the input pixel \ value. */ \ pfac = pixwt*infac; \ c = CONV(IntType,pfac*in_val); \ \ if( work ) { \ out[ off_out ] += c; \ work[ off_out ] += pixwt; \ } else {\ out[ off_out ] += c; \ } \ \ if ( Usevar ) { \ out_var[ off_out ] += CONV(IntType,in_var[ off_in ]*pfac*pfac); \ } else if ( Genvar && pixwt != 0.0 ) { \ out_var[ off_out ] += c*c/pixwt; \ work[ off_out + npix_out ] += pixwt*pixwt; \ } \ \ /* Now update the weight value pointers and pixel offset to refer to \ the next output pixel to be considered. */ \ idim = 0; \ do { \ \ /* The first input dimension whose weight value pointer has not yet \ reached its final value has this pointer incremented, and the pixel \ offset into the input array is updated accordingly. */ \ if ( wtptr[ idim ] != wtptr_last[ idim ] ) { \ wtptr[ idim ]++; \ off_out += stride[ idim ]; \ break; \ \ /* Any earlier dimensions (which have reached the final pointer value) \ have this pointer returned to its lowest value. Again, the pixel \ offset into the input image is updated accordingly. */ \ } else { \ wtptr[ idim ] -= ( hi[ idim ] - lo[ idim ] ); \ off_out -= stride[ idim ] * \ ( hi[ idim ] - lo[ idim ] ); \ done = ( ++idim == ndim_out ); \ } \ } while ( !done ); \ } while ( !done ); \ } \ } \ } /* Expand the main macro above to generate a function for each required signed data type. */ #if HAVE_LONG_DOUBLE /* Not normally implemented */ MAKE_SPREAD_KERNEL1(LD,long double,0) #endif MAKE_SPREAD_KERNEL1(D,double,0) MAKE_SPREAD_KERNEL1(F,float,0) MAKE_SPREAD_KERNEL1(I,int,1) MAKE_SPREAD_KERNEL1(B,signed char,1) MAKE_SPREAD_KERNEL1(UB,unsigned char,1) /* Undefine the macros used above. */ #undef KERNEL_ND #undef KERNEL_2D #undef KERNEL_1D #undef MAKE_SPREAD_KERNEL1 /* * Name: * SpreadLinear * Purpose: * Rebin a data grid, using the linear spreading scheme. * Type: * Private function. * Synopsis: * #include "mapping.h" * void SpreadLinear( int ndim_out, * const int *lbnd_out, const int *ubnd_out, * const *in, const *in_var, * double infac, int npoint, const int *offset, * const double *const *coords, double conwgt, int flags, * badval, int npix_out, *out, * *out_var, double *work, int64_t *nused ) * Class Membership: * Mapping member function. * Description: * This is a set of functions which rebins a rectangular region of an * input grid of data (and, optionally, associated statistical variance * values) so as to place them into a new output grid. Each input * grid point may be mapped on to a position in the output grid in * an arbitrary way. Where the positions given do not correspond * with a pixel centre in the input grid, the spreading scheme * used divides the input pixel value up linearly between the * nearest neighbouring output pixels in each dimension (there are 2 * nearest neighbours in 1 dimension, 4 in 2 dimensions, 8 in 3 * dimensions, etc.). * Parameters: * ndim_out * The number of dimensions in the output grid. This should be at * least one. * lbnd_out * Pointer to an array of integers, with "ndim_out" elements. * This should give the coordinates of the centre of the first * pixel in the output grid along each dimension. * ubnd_out * Pointer to an array of integers, with "ndim_out" elements. * This should give the coordinates of the centre of the last * pixel in the output grid along each dimension. * * Note that "lbnd_out" and "ubnd_out" together define the shape * and size of the output grid, its extent along a particular * (i'th) dimension being ubnd_out[i]-lbnd_out[i]+1 (assuming "i" * is zero-based). They also define the output grid's coordinate * system, with each pixel being of unit extent along each * dimension with integral coordinate values at its centre. * in * Pointer to the array of data to be rebinned. The numerical type * of these data should match the function used, as given by the * suffix on the function name. Note that details of how the input * grid maps on to this array (e.g. the storage order, number of * dimensions, etc.) is arbitrary and is specified entirely by means * of the "offset" array. The "in" array should therefore contain * sufficient elements to accommodate the "offset" values supplied. * There is no requirement that all elements of the "in" array * should be rebinned, and any which are not addressed by the * contents of the "offset" array will be ignored. * in_var * An optional pointer to a second array of positive numerical * values (with the same size and type as the "in" array), which * represent estimates of the statistical variance associated * with each element of the "in" array. If this second array is * given (along with the corresponding "out_var" array), then * estimates of the variance of the resampled data will also be * returned. It is addressed in exactly the same way (via the * "offset" array) as the "in" array. * * If no variance estimates are required, a NULL pointer should * be given. * infac * A factor by which to multiply the input data values before use. * npoint * The number of input points which are to be rebinned. * offset * Pointer to an array of integers with "npoint" elements. For * each input point, this array should contain the zero-based * offset in the input array(s) (i.e. the "in" and, optionally, * the "in_var" arrays) from which the value to be rebinned should * be obtained. * coords * An array of pointers to double, with "ndim_out" elements. * Element "coords[coord]" should point at the first element of * an array of double (with "npoint" elements) which contains the * values of coordinate number "coord" for each point being * rebinned. The value of coordinate number "coord" for * rebinning point number "point" is therefore given by * "coords[coord][point]" (assuming both indices are * zero-based). If any point has a coordinate value of AST__BAD * associated with it, then the corresponding input data (and * variance) value will be ignored. * The bitwise OR of a set of flag values which control the * operation of the function. These are chosend from: * * - AST__USEBAD: indicates whether there are "bad" (i.e. missing) data * in the input array(s) which must be recognised. If this flag is not * set, all input values are treated literally. * - AST__GENVAR: Indicates that any input variances are to be * ignored, and that the output variances should be generated from * the spread of values contributing to each output pixel. * conwgt * The initial weight to use for all pixels (typically 1.0). Other * weights (e.g. interpolation weights, variance weights, etc) are * applied as factors to this initial value. * flags * The bitwise OR of a set of flag values which control the * operation of the function. * badval * If the AST__USEBAD flag is set in the "flags" value (above), * this parameter specifies the value which is used to identify * bad data and/or variance values in the input array(s). Its * numerical type must match that of the "in" (and "in_var") * arrays. The same value will also be used to flag any output * array elements for which resampled values could not be * obtained. The output arrays(s) may be flagged with this * value whether or not the AST__USEBAD flag is set (the * function return value indicates whether any such values have * been produced). * npix_out * Number of pixels in output array. * out * Pointer to an array with the same data type as the "in" * array, into which the rebinned data will be returned. The * storage order should be such that the index of the first grid * dimension varies most rapidly and that of the final dimension * least rapidly (i.e. Fortran array storage order). * out_var * An optional pointer to an array with the same data type and * size as the "out" array, into which variance estimates for * the rebinned values may be returned. This array will only be * used if the "in_var" array has been given. The values returned * are estimates of the statistical variance of the corresponding * values in the "out" array, on the assumption that all errors in * input grid values (in the "in" array) are statistically independent * and that their variance estimates (in the "in_var" array) may * simply be summed (with appropriate weighting factors). * * If no output variance estimates are required, a NULL pointer * should be given. * work * An optional pointer to a double array with the same size as * the "out" array. The contents of this array (if supplied) are * incremented by the accumulated weights assigned to each output pixel. * If no accumulated weights are required, a NULL pointer should be * given. * nused * An optional pointer to a int64_t which will be incremented by the * number of input values pasted into the output array. Ignored if NULL. * Notes: * - There is a separate function for each numerical type of * gridded data, distinguished by replacing the in the function * name by the appropriate 1- or 2-character suffix. */ /* Define macros to implement the function for a specific data type. */ #define MAKE_SPREAD_LINEAR(X,Xtype,IntType) \ static void SpreadLinear##X( int ndim_out, \ const int *lbnd_out, const int *ubnd_out, \ const Xtype *in, const Xtype *in_var, \ double infac, int npoint, const int *offset, \ const double *const *coords, double conwgt, int flags, \ Xtype badval, int npix_out, Xtype *out, \ Xtype *out_var, double *work, int64_t *nused, \ int *status ) { \ \ /* Local Variables: */ \ Xtype c; /* Contribution to output value */ \ Xtype in_val; /* Input value */ \ double *frac_hi; /* Pointer to array of weights */ \ double *frac_lo; /* Pointer to array of weights */ \ double *wt; /* Pointer to array of weights */ \ double *wtprod; /* Array of accumulated weights pointer */ \ double *xn_max; /* Pointer to upper limits array (n-d) */ \ double *xn_min; /* Pointer to lower limits array (n-d) */ \ double frac_hi_x; /* Pixel weight (x dimension) */ \ double frac_hi_y; /* Pixel weight (y dimension) */ \ double frac_lo_x; /* Pixel weight (x dimension) */ \ double frac_lo_y; /* Pixel weight (y dimension) */ \ double pfac; /* Scaled pixel weight */ \ double pixwt; /* Total pixel weight */ \ double wgt; /* Weight for input value */ \ double x; /* x coordinate value */ \ double xmax; /* x upper limit */ \ double xmin; /* x lower limit */ \ double xn; /* Coordinate value (n-d) */ \ double y; /* y coordinate value */ \ double ymax; /* y upper limit */ \ double ymin; /* y lower limit */ \ int *dim; /* Pointer to array of pixel indices */ \ int *hi; /* Pointer to array of upper indices */ \ int *lo; /* Pointer to array of lower indices */ \ int *stride; /* Pointer to array of dimension strides */ \ int bad; /* Output pixel bad? */ \ int done; /* All pixel indices done? */ \ int genvar; /* Generate output variances? */ \ int hi_x; /* Upper pixel index (x dimension) */ \ int hi_y; /* Upper pixel index (y dimension) */ \ int idim; /* Loop counter for dimensions */ \ int ii; /* Loop counter for weights */ \ int ixn; /* Pixel index (n-d) */ \ int lo_x; /* Lower pixel index (x dimension) */ \ int lo_y; /* Lower pixel index (y dimension) */ \ int off; /* Total offset to input pixel */ \ int off_in; /* Offset to input pixel */ \ int off_lo; /* Offset to "first" input pixel */ \ int off_out; /* Offset to output pixel */ \ int point; /* Loop counter for output points */ \ int s; /* Temporary variable for strides */ \ int usebad; /* Use "bad" input pixel values? */ \ int usevar; /* Process variance array? */ \ int varwgt; /* Use input variances as weights? */ \ int ystride; /* Stride along input grid y dimension */ \ \ /* Check the global error status. */ \ if ( !astOK ) return; \ \ /* Initialise variables to avoid "used of uninitialised variable" \ messages from dumb compilers. */ \ bad = 0; \ \ /* Determine if we are processing bad pixels or variances. */ \ usebad = flags & AST__USEBAD; \ usevar = 0; \ genvar = 0; \ if( flags & AST__GENVAR ) { \ genvar = out_var && work; \ } else if( flags & AST__USEVAR ) { \ usevar = in_var && out_var; \ } \ varwgt = ( flags & AST__VARWGT ) && in_var && work; \ \ /* Handle the 1-dimensional case optimally. */ \ /* ---------------------------------------- */ \ if ( ndim_out == 1 ) { \ \ /* Calculate the coordinate limits of the input grid. */ \ xmin = (double) lbnd_out[ 0 ] - 0.5; \ xmax = (double) ubnd_out[ 0 ] + 0.5; \ \ /* Identify eight cases, according to whether bad pixels and/or variances \ are being processed and/or used. In each case we assign constant values \ (0 or 1) to the "Usebad", "Usevar" and "Varwgt" flags so that code for \ handling bad pixels and variances can be eliminated by the compiler's \ optimisation system when not required. */ \ if( varwgt ) { \ if ( usebad ) { \ if ( usevar ) { \ LINEAR_1D(X,Xtype,1,1,0,IntType,1) \ } else if ( genvar ) { \ LINEAR_1D(X,Xtype,1,0,1,IntType,1) \ } else { \ LINEAR_1D(X,Xtype,1,0,0,IntType,1) \ } \ } else { \ if ( usevar ) { \ LINEAR_1D(X,Xtype,0,1,0,IntType,1) \ } else if ( genvar ) { \ LINEAR_1D(X,Xtype,0,0,1,IntType,1) \ } else { \ LINEAR_1D(X,Xtype,0,0,0,IntType,1) \ } \ } \ } else { \ if ( usebad ) { \ if ( usevar ) { \ LINEAR_1D(X,Xtype,1,1,0,IntType,0) \ } else if ( genvar ) { \ LINEAR_1D(X,Xtype,1,0,1,IntType,0) \ } else { \ LINEAR_1D(X,Xtype,1,0,0,IntType,0) \ } \ } else { \ if ( usevar ) { \ LINEAR_1D(X,Xtype,0,1,0,IntType,0) \ } else if ( genvar ) { \ LINEAR_1D(X,Xtype,0,0,1,IntType,0) \ } else { \ LINEAR_1D(X,Xtype,0,0,0,IntType,0) \ } \ } \ } \ \ /* Handle the 2-dimensional case optimally. */ \ /* ---------------------------------------- */ \ } else if ( ndim_out == 2 ) { \ \ /* Calculate the stride along the y dimension of the output grid. */ \ ystride = ubnd_out[ 0 ] - lbnd_out[ 0 ] + 1; \ \ /* Calculate the coordinate limits of the output grid in each \ dimension. */ \ xmin = (double) lbnd_out[ 0 ] - 0.5; \ xmax = (double) ubnd_out[ 0 ] + 0.5; \ ymin = (double) lbnd_out[ 1 ] - 0.5; \ ymax = (double) ubnd_out[ 1 ] + 0.5; \ \ /* Identify eight cases, according to whether bad pixels and/or variances \ are being processed and/or used. In each case we assign constant values \ (0 or 1) to the "Usebad", "Usevar" and "Varwgt" flags so that code for \ handling bad pixels and variances can be eliminated by the compiler's \ optimisation system when not required. */ \ if( varwgt ) { \ if ( usebad ) { \ if ( usevar ) { \ LINEAR_2D(X,Xtype,1,1,0,IntType,1) \ } else if ( genvar ) { \ LINEAR_2D(X,Xtype,1,0,1,IntType,1) \ } else { \ LINEAR_2D(X,Xtype,1,0,0,IntType,1) \ } \ } else { \ if ( usevar ) { \ LINEAR_2D(X,Xtype,0,1,0,IntType,1) \ }else if ( genvar ) { \ LINEAR_2D(X,Xtype,0,0,1,IntType,1) \ } else { \ LINEAR_2D(X,Xtype,0,0,0,IntType,1) \ } \ } \ } else { \ if ( usebad ) { \ if ( usevar ) { \ LINEAR_2D(X,Xtype,1,1,0,IntType,0) \ } else if ( genvar ) { \ LINEAR_2D(X,Xtype,1,0,1,IntType,0) \ } else { \ LINEAR_2D(X,Xtype,1,0,0,IntType,0) \ } \ } else { \ if ( usevar ) { \ LINEAR_2D(X,Xtype,0,1,0,IntType,0) \ }else if ( genvar ) { \ LINEAR_2D(X,Xtype,0,0,1,IntType,0) \ } else { \ LINEAR_2D(X,Xtype,0,0,0,IntType,0) \ } \ } \ } \ \ /* Handle other numbers of dimensions. */ \ /* ----------------------------------- */ \ } else { \ \ /* Allocate workspace. */ \ dim = astMalloc( sizeof( int ) * (size_t) ndim_out ); \ frac_hi = astMalloc( sizeof( double ) * (size_t) ndim_out ); \ frac_lo = astMalloc( sizeof( double ) * (size_t) ndim_out ); \ hi = astMalloc( sizeof( int ) * (size_t) ndim_out ); \ lo = astMalloc( sizeof( int ) * (size_t) ndim_out ); \ stride = astMalloc( sizeof( int ) * (size_t) ndim_out ); \ wt = astMalloc( sizeof( double ) * (size_t) ndim_out ); \ wtprod = astMalloc( sizeof( double ) * (size_t) ndim_out ); \ xn_max = astMalloc( sizeof( double ) * (size_t) ndim_out ); \ xn_min = astMalloc( sizeof( double ) * (size_t) ndim_out ); \ if ( astOK ) { \ \ /* Calculate the stride along each dimension of the output grid. */ \ for ( s = 1, idim = 0; idim < ndim_out; idim++ ) { \ stride[ idim ] = s; \ s *= ubnd_out[ idim ] - lbnd_out[ idim ] + 1; \ \ /* Calculate the coordinate limits of the output grid in each \ dimension. */ \ xn_min[ idim ] = (double) lbnd_out[ idim ] - 0.5; \ xn_max[ idim ] = (double) ubnd_out[ idim ] + 0.5; \ } \ \ /* Identify eight cases, according to whether bad pixels and/or variances \ are being processed and/or used. In each case we assign constant values \ (0 or 1) to the "Usebad", "Usevar" and "Varwgt" flags so that code for \ handling bad pixels and variances can be eliminated by the compiler's \ optimisation system when not required. */ \ if( varwgt ) { \ if ( usebad ) { \ if ( usevar ) { \ LINEAR_ND(X,Xtype,1,1,0,IntType,1) \ } else if ( genvar ) { \ LINEAR_ND(X,Xtype,1,0,1,IntType,1) \ } else { \ LINEAR_ND(X,Xtype,1,0,0,IntType,1) \ } \ } else { \ if ( usevar ) { \ LINEAR_ND(X,Xtype,0,1,0,IntType,1) \ } else if ( genvar ) { \ LINEAR_ND(X,Xtype,0,0,1,IntType,1) \ } else { \ LINEAR_ND(X,Xtype,0,0,0,IntType,1) \ } \ } \ } else { \ if ( usebad ) { \ if ( usevar ) { \ LINEAR_ND(X,Xtype,1,1,0,IntType,0) \ } else if ( genvar ) { \ LINEAR_ND(X,Xtype,1,0,1,IntType,0) \ } else { \ LINEAR_ND(X,Xtype,1,0,0,IntType,0) \ } \ } else { \ if ( usevar ) { \ LINEAR_ND(X,Xtype,0,1,0,IntType,0) \ } else if ( genvar ) { \ LINEAR_ND(X,Xtype,0,0,1,IntType,0) \ } else { \ LINEAR_ND(X,Xtype,0,0,0,IntType,0) \ } \ } \ } \ } \ \ /* Free the workspace. */ \ dim = astFree( dim ); \ frac_hi = astFree( frac_hi ); \ frac_lo = astFree( frac_lo ); \ hi = astFree( hi ); \ lo = astFree( lo ); \ stride = astFree( stride ); \ wt = astFree( wt ); \ wtprod = astFree( wtprod ); \ xn_max = astFree( xn_max ); \ xn_min = astFree( xn_min ); \ } \ \ } #define LINEAR_1D(X,Xtype,Usebad,Usevar,Genvar,IntType,Varwgt) \ \ /* Loop round all input points which are to be rebinned. */ \ for( point = 0; point < npoint; point++ ) { \ \ /* Obtain the input data value which is to be added into the output array. */ \ off_in = offset[ point ]; \ in_val = in[ off_in ]; \ \ /* If necessary, test if the input data value or variance is bad. If we \ are using the reciprocal of the input variances as weights, then \ variance values of zero are also effectively bad (but we can use input \ variances of zero otherwise). */ \ if ( Usebad ) { \ bad = ( in_val == badval ); \ if ( Varwgt ) { \ bad = bad || ( in_var[ off_in ] == badval ) \ || ( in_var[ off_in ] <= 0.0 ); \ } else if ( Usevar ) { \ bad = bad || ( in_var[ off_in ] == badval ); \ } \ } else { \ if ( Varwgt ) { \ bad = ( in_var[ off_in ] <= 0.0 ); \ } else { \ bad = 0; \ } \ } \ \ /* Obtain the x coordinate of the current point and test if it lies \ outside the output grid. Also test if it is bad. */ \ x = coords[ 0 ][ point ]; \ bad = bad || ( x < xmin ) || ( x >= xmax ) || ( x == AST__BAD ); \ \ /* If OK, obtain the indices along the output grid x dimension of the \ two adjacent output pixels which will receive contributions from the \ input pixel. Also obtain the fractional weight to be applied to each of \ these pixels. */ \ if ( !bad ) { \ lo_x = (int) floor( x ); \ hi_x = lo_x + 1; \ frac_lo_x = (double) hi_x - x; \ frac_hi_x = 1.0 - frac_lo_x; \ \ /* Increment the number of input pixels pasted into the output array. */ \ if( nused ) (*nused)++; \ \ /* Obtain the offset within the output array of the first pixel to be \ updated (the one with the smaller index). */ \ off_lo = lo_x - lbnd_out[ 0 ]; \ \ /* If we are using the input data variances as weights, calculate the \ weight, and scale the fractions of each input pixel by the weight. */ \ wgt = conwgt; \ if( Varwgt ) wgt *= 1.0/in_var[ off_in ]; \ frac_lo_x *= wgt; \ frac_hi_x *= wgt; \ \ /* For each of the two pixels which may be updated, test if the pixel index \ lies within the output grid. Where it does, update the output pixel \ with the required fraction of the input pixel value. */ \ if ( lo_x >= lbnd_out[ 0 ] ) { \ pfac = frac_lo_x*infac; \ c = CONV(IntType,pfac*in_val); \ out[ off_lo ] += CONV(IntType, c ); \ if( work ) work[ off_lo ] += frac_lo_x; \ if ( Usevar ) { \ out_var[ off_lo ] += CONV(IntType,in_var[ off_in ]*pfac*pfac); \ } else if ( Genvar && frac_lo_x != 0.0 ) { \ out_var[ off_lo ] += c*c/frac_lo_x; \ work[ off_lo + npix_out ] += frac_lo_x*frac_lo_x; \ } \ } \ if ( hi_x <= ubnd_out[ 0 ] ) { \ pfac = frac_hi_x*infac; \ c = CONV(IntType,pfac*in_val); \ out[ off_lo + 1 ] += CONV(IntType, c ); \ if( work ) work[ off_lo + 1 ] += frac_hi_x; \ if ( Usevar ) { \ out_var[ off_lo + 1 ] += CONV(IntType,in_var[ off_in ]*pfac*pfac); \ } else if ( Genvar && frac_hi_x != 0.0 ) { \ out_var[ off_lo + 1 ] += c*c/frac_hi_x; \ work[ off_lo + 1 + npix_out ] += frac_hi_x*frac_hi_x; \ } \ } \ } \ } #define LINEAR_2D(X,Xtype,Usebad,Usevar,Genvar,IntType,Varwgt) \ \ /* Loop round all input points which are to be rebinned. */ \ for( point = 0; point < npoint; point++ ) { \ \ /* Obtain the input data value which is to be added into the output array. */ \ off_in = offset[ point ]; \ in_val = in[ off_in ]; \ \ /* If necessary, test if the input data value or variance is bad. If we \ are using the reciprocal of the input variances as weights, then \ variance values of zero are also effectively bad (but we can use input \ variances of zero otherwise). */ \ if ( Usebad ) { \ bad = ( in_val == badval ); \ if ( Varwgt ) { \ bad = bad || ( in_var[ off_in ] == badval ) \ || ( in_var[ off_in ] <= 0.0 ); \ } else if ( Usevar ) { \ bad = bad || ( in_var[ off_in ] == badval ); \ } \ } else { \ if ( Varwgt ) { \ bad = ( in_var[ off_in ] <= 0.0 ); \ } else { \ bad = 0; \ } \ } \ \ /* Obtain the x coordinate of the current point and test if it lies \ outside the output grid. Also test if it is bad. */ \ y = coords[ 1 ][ point ]; \ bad = bad || ( y < ymin ) || ( y >= ymax ) || ( y == AST__BAD ); \ if ( !bad ) { \ \ /* Similarly obtain and test the y coordinate. */ \ x = coords[ 0 ][ point ]; \ bad = bad || ( x < xmin ) || ( x >= xmax ) || ( x == AST__BAD ); \ if ( !bad ) { \ \ /* Increment the number of input pixels pasted into the output array. */ \ if( nused ) (*nused)++; \ \ /* If OK, obtain the indices along the output grid x dimension of the \ two adjacent pixels which recieve contributions from the input pixel. \ Also obtain the fractional weight to be applied to each of \ these pixels. */ \ lo_x = (int) floor( x ); \ hi_x = lo_x + 1; \ frac_lo_x = (double) hi_x - x; \ frac_hi_x = 1.0 - frac_lo_x; \ \ /* Repeat this process for the y dimension. */ \ lo_y = (int) floor( y ); \ hi_y = lo_y + 1; \ frac_lo_y = (double) hi_y - y; \ frac_hi_y = 1.0 - frac_lo_y; \ \ /* If we are using the input data variances as weights, calculate the \ weight, and scale the fractions of each input pixel by the weight. \ Since the product of two fractions is always used to scale the input \ data values, we use the square root of the reciprocal of the variance \ as the weight (so that when the product of two fractions is taken, \ the square roots multiply together to give the required 1/variance \ weight). */ \ wgt = conwgt; \ if( Varwgt ) wgt *= 1.0/sqrt( in_var[ off_in ] ); \ frac_lo_x *= wgt; \ frac_hi_x *= wgt; \ frac_lo_y *= wgt; \ frac_hi_y *= wgt; \ \ /* Obtain the offset within the output array of the first pixel to be \ updated (the one with the smaller index along both dimensions). */ \ off_lo = lo_x - lbnd_out[ 0 ] + ystride * ( lo_y - lbnd_out[ 1 ] ); \ \ /* For each of the four pixels which may be updated, test if the pixel indices \ lie within the output grid. Where they do, update the output pixel \ with the required fraction of the input pixel value. */ \ if ( lo_y >= lbnd_out[ 1 ] ) { \ if ( lo_x >= lbnd_out[ 0 ] ) { \ pixwt = frac_lo_x * frac_lo_y; \ pfac = pixwt*infac; \ c = CONV(IntType,pfac*in_val); \ out[ off_lo ] += CONV(IntType, c ); \ if( work ) work[ off_lo ] += pixwt; \ if ( Usevar ) { \ out_var[ off_lo ] += CONV(IntType,in_var[ off_in ]*pfac*pfac); \ } else if ( Genvar && pixwt != 0.0 ) { \ out_var[ off_lo ] += c*c/pixwt; \ work[ off_lo + npix_out ] += pixwt*pixwt; \ } \ } \ if ( hi_x <= ubnd_out[ 0 ] ) { \ off = off_lo + 1; \ pixwt = frac_hi_x * frac_lo_y; \ pfac = pixwt*infac; \ c = CONV(IntType,pfac*in_val); \ out[ off ] += CONV(IntType, c ); \ if( work ) work[ off ] += pixwt; \ if ( Usevar ) { \ out_var[ off ] += CONV(IntType,in_var[ off_in ]*pfac*pfac); \ } else if ( Genvar && pixwt != 0.0 ) { \ out_var[ off ] += c*c/pixwt; \ work[ off + npix_out ] += pixwt*pixwt; \ } \ } \ } \ if ( hi_y <= ubnd_out[ 1 ] ) { \ if ( lo_x >= lbnd_out[ 0 ] ) { \ off = off_lo + ystride; \ pixwt = frac_lo_x * frac_hi_y; \ pfac = pixwt*infac; \ c = CONV(IntType,pfac*in_val); \ out[ off ] += CONV(IntType, c ); \ if( work ) work[ off ] += pixwt; \ if ( Usevar ) { \ out_var[ off ] += CONV(IntType,in_var[ off_in ]*pfac*pfac); \ } else if ( Genvar && pixwt != 0.0 ) { \ out_var[ off ] += c*c/pixwt; \ work[ off + npix_out ] += pixwt*pixwt; \ } \ } \ if ( hi_x <= ubnd_out[ 0 ] ) { \ off = off_lo + ystride + 1; \ pixwt = frac_hi_x * frac_hi_y; \ pfac = pixwt*infac; \ c = CONV(IntType,pfac*in_val); \ out[ off ] += CONV(IntType, c ); \ if( work ) work[ off ] += pixwt; \ if ( Usevar ) { \ out_var[ off ] += CONV(IntType,in_var[ off_in ]*pfac*pfac); \ } else if ( Genvar && pixwt != 0.0 ) { \ out_var[ off ] += c*c/pixwt; \ work[ off + npix_out ] += pixwt*pixwt; \ } \ } \ } \ } \ } \ } #define LINEAR_ND(X,Xtype,Usebad,Usevar,Genvar,IntType,Varwgt) \ \ /* Loop round all input points which are to be rebinned. */ \ for( point = 0; point < npoint; point++ ) { \ \ /* Obtain the input data value which is to be added into the output array. */ \ off_in = offset[ point ]; \ in_val = in[ off_in ]; \ \ /* If necessary, test if the input data value or variance is bad. If we \ are using the reciprocal of the input variances as weights, then \ variance values of zero are also effectively bad (but we can use input \ variances of zero otherwise). */ \ if ( Usebad ) { \ bad = ( in_val == badval ); \ if ( Varwgt ) { \ bad = bad || ( in_var[ off_in ] == badval ) \ || ( in_var[ off_in ] <= 0.0 ); \ } else if ( Usevar ) { \ bad = bad || ( in_var[ off_in ] == badval ); \ } \ } else { \ if ( Varwgt ) { \ bad = ( in_var[ off_in ] <= 0.0 ); \ } else { \ bad = 0; \ } \ } \ \ /* Initialise offsets into the output array. Then loop to obtain each \ coordinate associated with the current output point. */ \ if( !bad ) { \ off_out = 0; \ for ( idim = 0; idim < ndim_out; idim++ ) { \ xn = coords[ idim ][ point ]; \ \ /* Test if the coordinate lies outside the output grid. Also test if \ it is bad. If either is true, the corresponding output pixel value \ will be bad, so give up on this point. */ \ bad = ( xn < xn_min[ idim ] ) || ( xn >= xn_max[ idim ] ) || \ ( xn == AST__BAD ); \ if ( bad ) break; \ \ /* Obtain the indices along the current dimension of the output grid of \ the two (usually adjacent) pixels which will be updated. If necessary, \ however, restrict each index to ensure it does not lie outside the \ input grid. Also calculate the fractional weight to be given to each \ pixel in order to divide the input value linearly between them. */ \ ixn = (int) floor( xn ); \ lo[ idim ] = MaxI( ixn, lbnd_out[ idim ], status ); \ hi[ idim ] = MinI( ixn + 1, ubnd_out[ idim ], status ); \ frac_lo[ idim ] = 1.0 - fabs( xn - (double) lo[ idim ] ); \ frac_hi[ idim ] = 1.0 - fabs( xn - (double) hi[ idim ] ); \ \ /* Store the lower index involved in spreading along each \ dimension and accumulate the offset from the start of the output \ array of the pixel which has these indices. */ \ dim[ idim ] = lo[ idim ]; \ off_out += stride[ idim ] * ( lo[ idim ] - lbnd_out[ idim ] ); \ \ /* Also store the fractional weight associated with the lower pixel \ along each dimension. */ \ wt[ idim ] = frac_lo[ idim ]; \ } \ \ /* If we are using the input data variances as weights, calculate the \ weight, and scale the fractions of each input pixel by the weight. */ \ wgt = conwgt; \ if( Varwgt ) wgt *= pow( in_var[ off_in ], -1.0/(double)ndim_out ); \ for ( idim = 0; idim < ndim_out; idim++ ) { \ frac_lo[ idim ] *= wgt; \ frac_hi[ idim ] *= wgt; \ wt[ idim ] = frac_lo[ idim ]; \ } \ \ /* If OK, increment the number of input pixels pasted into the output array. */ \ if ( !bad ) { \ if( nused ) (*nused)++; \ \ /* Loop over adjacent output pixels to divide up the input value. */ \ idim = ndim_out - 1; \ wtprod[ idim ] = 1.0; \ done = 0; \ do { \ \ /* Each pixel pixel to be updated has a total weight equal to the product \ of the weights which account for the displacement of its centre from \ the required position along each dimension. However, since we typically \ only change the index of one dimension at a time, we can avoid forming \ this product repeatedly by retaining an array of accumulated weight \ products for all higher dimensions. We need then only update the \ lower elements in this array, corresponding to those dimensions \ whose index has changed. We do this here, "idim" being the index of \ the most significant dimension to have changed. Note that on the \ first pass, all dimensions are considered changed, causing this \ array to be initialised. */ \ for ( ii = idim; ii >= 1; ii-- ) { \ wtprod[ ii - 1 ] = wtprod[ ii ] * wt[ ii ]; \ } \ \ /* Update the relevent output pixel. The pixel weight is formed by including \ the weight factor for dimension zero, since this is not included in \ the "wtprod" array. */ \ pixwt = wtprod[ 0 ] * wt[ 0 ]; \ pfac = pixwt*infac; \ c = CONV(IntType,pfac*in_val); \ out[ off_out ] += CONV(IntType, c ); \ if( work ) work[ off_out ] += pixwt; \ if ( Usevar ) { \ out_var[ off_out ] += CONV(IntType,in_var[ off_in ]*pfac*pfac); \ } else if ( Genvar && pixwt != 0.0 ) { \ out_var[ off_out ] += c*c/pixwt; \ work[ off_out + npix_out ] += pixwt*pixwt; \ } \ \ /* Now update the indices, offset and weight factors to refer to the \ next output pixel to be updated. */ \ idim = 0; \ do { \ \ /* The first input dimension which still refers to the pixel with the \ lower of the two possible indices is switched to refer to the other \ pixel (with the higher index). The offset into the output array and \ the fractional weight factor for this dimension are also updated \ accordingly. */ \ if ( dim[ idim ] != hi[ idim ] ) { \ dim[ idim ] = hi[ idim ]; \ off_out += stride[ idim ]; \ wt[ idim ] = frac_hi[ idim ]; \ break; \ \ /* Any earlier dimensions (referring to the higher index) are switched \ back to the lower index, if not already there, before going on to \ consider the next dimension. (This process is the same as \ incrementing a binary number and propagating overflows up through \ successive digits, except that dimensions where the "lo" and "hi" \ values are the same can only take one value.) The process stops at \ the first attempt to return the final dimension to the lower \ index. */ \ } else { \ if ( dim[ idim ] != lo[ idim ] ) { \ dim[ idim ] = lo[ idim ]; \ off_out -= stride[ idim ]; \ wt[ idim ] = frac_lo[ idim ]; \ } \ done = ( ++idim == ndim_out ); \ } \ } while ( !done ); \ } while ( !done ); \ } \ } \ } /* Expand the main macro above to generate a function for each required signed data type. */ #if HAVE_LONG_DOUBLE /* Not normally implemented */ MAKE_SPREAD_LINEAR(LD,long double,0) #endif MAKE_SPREAD_LINEAR(D,double,0) MAKE_SPREAD_LINEAR(F,float,0) MAKE_SPREAD_LINEAR(I,int,1) MAKE_SPREAD_LINEAR(B,signed char,1) MAKE_SPREAD_LINEAR(UB,unsigned char,1) /* Undefine the macros used above. */ #undef LINEAR_1D #undef LINEAR_2D #undef LINEAR_ND #undef MAKE_SPREAD_LINEAR /* * Name: * SpreadNearest * Purpose: * Rebin a data grid, using the nearest-pixel spreading scheme. * Type: * Private function. * Synopsis: * #include "mapping.h" * void SpreadNearest( int ndim_out, const int *lbnd_out, * const int *ubnd_out, const *in, * const *in_var, double infac, int npoint, * const int *offset, const double *const *coords, * double conwgt, int flags, badval, int npix_out, *out, * *out_var, double *work, int64_t *nused, * int *status ) * Class Membership: * Mapping member function. * Description: * This is a set of functions which rebins a rectangular region of an * input grid of data (and, optionally, associated statistical variance * values) so as to place them into a new output grid. Each input * grid point may be mapped on to a position in the output grid in * an arbitrary way. Where the positions given do not correspond * with a pixel centre in the output grid, the spreading scheme * used is simply to select the nearest pixel (i.e. the one whose * bounds contain the supplied position). * Parameters: * ndim_out * The number of dimensions in the output grid. This should be at * least one. * lbnd_out * Pointer to an array of integers, with "ndim_out" elements. * This should give the coordinates of the centre of the first * pixel in the output grid along each dimension. * ubnd_out * Pointer to an array of integers, with "ndim_out" elements. * This should give the coordinates of the centre of the last * pixel in the output grid along each dimension. * * Note that "lbnd_out" and "ubnd_out" together define the shape * and size of the output grid, its extent along a particular * (i'th) dimension being ubnd_out[i]-lbnd_out[i]+1 (assuming "i" * is zero-based). They also define the output grid's coordinate * system, with each pixel being of unit extent along each * dimension with integral coordinate values at its centre. * in * Pointer to the array of data to be rebinned. The numerical type * of these data should match the function used, as given by the * suffix on the function name. Note that details of how the input * grid maps on to this array (e.g. the storage order, number of * dimensions, etc.) is arbitrary and is specified entirely by means * of the "offset" array. The "in" array should therefore contain * sufficient elements to accommodate the "offset" values supplied. * There is no requirement that all elements of the "in" array * should be rebinned, and any which are not addressed by the * contents of the "offset" array will be ignored. * in_var * An optional pointer to a second array of positive numerical * values (with the same size and type as the "in" array), which * represent estimates of the statistical variance associated * with each element of the "in" array. If this second array is * given (along with the corresponding "out_var" array), then * estimates of the variance of the resampled data will also be * returned. It is addressed in exactly the same way (via the * "offset" array) as the "in" array. * * If no variance estimates are required, a NULL pointer should * be given. * infac * A factor by which to multiply the input data values before use. * npoint * The number of input points which are to be rebinned. * offset * Pointer to an array of integers with "npoint" elements. For * each input point, this array should contain the zero-based * offset in the input array(s) (i.e. the "in" and, optionally, * the "in_var" arrays) from which the value to be rebinned should * be obtained. * coords * An array of pointers to double, with "ndim_out" elements. * Element "coords[coord]" should point at the first element of * an array of double (with "npoint" elements) which contains the * values of coordinate number "coord" for each point being * rebinned. The value of coordinate number "coord" for * rebinning point number "point" is therefore given by * "coords[coord][point]" (assuming both indices are * zero-based). If any point has a coordinate value of AST__BAD * associated with it, then the corresponding input data (and * variance) value will be ignored. * conwgt * The initial weight to use for all pixels (typically 1.0). Other * weights (e.g. interpolation weights, variance weights, etc) are * applied as factors to this initial value. * flags * The bitwise OR of a set of flag values which control the * operation of the function. These are chosen from: * * - AST__USEBAD: indicates whether there are "bad" (i.e. missing) data * in the input array(s) which must be recognised. If this flag is not * set, all input values are treated literally. * - AST__GENVAR: Indicates that output variances should be generated * from the spread of values contributing to each output pixel. * - AST__USEVAR: Indicates that output variances should be generated * by rebinning the input variances. * - AST__VARWGT: Indicates that input variances should be used to * create weights for the input data values. * * Only one of AST__GENVAR and AST__USEVAR should be supplied. * badval * If the AST__USEBAD flag is set in the "flags" value (above), * this parameter specifies the value which is used to identify * bad data and/or variance values in the input array(s). Its * numerical type must match that of the "in" (and "in_var") * arrays. The same value will also be used to flag any output * array elements for which resampled values could not be * obtained. The output arrays(s) may be flagged with this * value whether or not the AST__USEBAD flag is set (the * function return value indicates whether any such values have * been produced). * npix_out * Number of pixels in output array. * out * Pointer to an array with the same data type as the "in" * array, into which the rebinned data will be returned. The * storage order should be such that the index of the first grid * dimension varies most rapidly and that of the final dimension * least rapidly (i.e. Fortran array storage order). * out_var * An optional pointer to an array with the same data type and * size as the "out" array, into which variance estimates for * the rebinned values may be returned. This array will only be * used if the "in_var" array has been given. The values returned * are estimates of the statistical variance of the corresponding * values in the "out" array, on the assumption that all errors in * input grid values (in the "in" array) are statistically independent * and that their variance estimates (in the "in_var" array) may * simply be summed (with appropriate weighting factors). * * If no output variance estimates are required, a NULL pointer * should be given. * work * A pointer to an array with the same data type and size as the "out" * array which is used as work space. The values in the supplied * array are incremented on exit by the sum of the weights used * with each output pixel. * nused * An optional pointer to a size_t which will be incremented by the * number of input values pasted into the output array. Ignored if NULL. * Notes: * - There is a separate function for each numerical type of * gridded data, distinguished by replacing the in the function * name by the appropriate 1- or 2-character suffix. */ /* Define a macro to implement the function for a specific data type. */ #define MAKE_SPREAD_NEAREST(X,Xtype,IntType) \ static void SpreadNearest##X( int ndim_out, \ const int *lbnd_out, const int *ubnd_out, \ const Xtype *in, const Xtype *in_var, \ double infac, int npoint, const int *offset, \ const double *const *coords, double conwgt, int flags, \ Xtype badval, int npix_out, Xtype *out, \ Xtype *out_var, double *work, int64_t *nused, \ int *status ) { \ \ /* Local Variables: */ \ Xtype c; /* Contribution to output value */ \ Xtype in_val; /* Input data value */ \ double *xn_max; /* Pointer to upper limits array (n-d) */ \ double *xn_min; /* Pointer to lower limits array (n-d) */ \ double pfac; /* Input weight with extra supplied factor */ \ double pixwt; /* Weight for input value */ \ double x; /* x coordinate value */ \ double xmax; /* x upper limit */ \ double xmin; /* x lower limit */ \ double xn; /* Coordinate value (n-d) */ \ double y; /* y coordinate value */ \ double ymax; /* y upper limit */ \ double ymin; /* y lower limit */ \ int *stride; /* Pointer to array of dimension strides */ \ int bad; /* Output pixel bad? */ \ int genvar; /* Generate output variances? */ \ int idim; /* Loop counter for dimensions */ \ int ix; /* Number of pixels offset in x direction */ \ int ixn; /* Number of pixels offset (n-d) */ \ int iy; /* Number of pixels offset in y direction */ \ int off_in; /* Pixel offset into input array */ \ int off_out; /* Pixel offset into output array */ \ int point; /* Loop counter for output points */ \ int s; /* Temporary variable for strides */ \ int usebad; /* Use "bad" input pixel values? */ \ int usevar; /* Process variance array? */ \ int varwgt; /* Use input variances as weights? */ \ int ystride; /* Stride along input grid y direction */ \ \ /* Check the global error status. */ \ if ( !astOK ) return; \ \ /* Determine if we are processing bad pixels or variances. */ \ usebad = flags & AST__USEBAD; \ usevar = 0; \ genvar = 0; \ if( flags & AST__GENVAR ) { \ genvar = out_var && work; \ } else if( flags & AST__USEVAR ) { \ usevar = in_var && out_var; \ } \ varwgt = ( flags & AST__VARWGT ) && in_var && work; \ \ /* Handle the 1-dimensional case optimally. */ \ /* ---------------------------------------- */ \ if ( ndim_out == 1 ) { \ \ /* Calculate the coordinate limits of the output array. */ \ xmin = (double) lbnd_out[ 0 ] - 0.5; \ xmax = (double) ubnd_out[ 0 ] + 0.5; \ \ /* Identify eight cases, according to whether bad pixels and/or variances \ are being processed and/or used. In each case we assign constant values \ (0 or 1) to the "Usebad", "Usevar" and "Varwgt" flags so that code for \ handling bad pixels and variances can be eliminated by the compiler's \ optimisation system when not required. */ \ if( varwgt ) { \ if ( usebad ) { \ if ( usevar ) { \ NEAR_1D(X,Xtype,1,1,0,IntType,1) \ } else if ( genvar ) { \ NEAR_1D(X,Xtype,1,0,1,IntType,1) \ } else { \ NEAR_1D(X,Xtype,1,0,0,IntType,1) \ } \ } else { \ if ( usevar ) { \ NEAR_1D(X,Xtype,0,1,0,IntType,1) \ } else if ( genvar ) { \ NEAR_1D(X,Xtype,0,0,1,IntType,1) \ } else { \ NEAR_1D(X,Xtype,0,0,0,IntType,1) \ } \ } \ } else { \ if ( usebad ) { \ if ( usevar ) { \ NEAR_1D(X,Xtype,1,1,0,IntType,0) \ } else if ( genvar ) { \ NEAR_1D(X,Xtype,1,0,1,IntType,0) \ } else { \ NEAR_1D(X,Xtype,1,0,0,IntType,0) \ } \ } else { \ if ( usevar ) { \ NEAR_1D(X,Xtype,0,1,0,IntType,0) \ } else if ( genvar ) { \ NEAR_1D(X,Xtype,0,0,1,IntType,0) \ } else { \ NEAR_1D(X,Xtype,0,0,0,IntType,0) \ } \ } \ } \ \ /* Handle the 2-dimensional case optimally. */ \ /* ---------------------------------------- */ \ } else if ( ndim_out == 2 ) { \ \ /* Calculate the stride along the y dimension of the output grid. */ \ ystride = ubnd_out[ 0 ] - lbnd_out[ 0 ] + 1; \ \ /* Calculate the coordinate limits of the output array in each \ dimension. */ \ xmin = (double) lbnd_out[ 0 ] - 0.5; \ xmax = (double) ubnd_out[ 0 ] + 0.5; \ ymin = (double) lbnd_out[ 1 ] - 0.5; \ ymax = (double) ubnd_out[ 1 ] + 0.5; \ \ /* Identify eight cases, according to whether bad pixels and/or variances \ are being processed and/or used. In each case we assign constant values \ (0 or 1) to the "Usebad", "Usevar" and "Varwgt" flags so that code for \ handling bad pixels and variances can be eliminated by the compiler's \ optimisation system when not required. */ \ if( varwgt ) { \ if ( usebad ) { \ if ( usevar ) { \ NEAR_2D(X,Xtype,1,1,0,IntType,1) \ } else if ( genvar ) { \ NEAR_2D(X,Xtype,1,0,1,IntType,1) \ } else { \ NEAR_2D(X,Xtype,1,0,0,IntType,1) \ } \ } else { \ if ( usevar ) { \ NEAR_2D(X,Xtype,0,1,0,IntType,1) \ } else if ( genvar ) { \ NEAR_2D(X,Xtype,0,0,1,IntType,1) \ } else { \ NEAR_2D(X,Xtype,0,0,0,IntType,1) \ } \ } \ } else { \ if ( usebad ) { \ if ( usevar ) { \ NEAR_2D(X,Xtype,1,1,0,IntType,0) \ } else if ( genvar ) { \ NEAR_2D(X,Xtype,1,0,1,IntType,0) \ } else { \ NEAR_2D(X,Xtype,1,0,0,IntType,0) \ } \ } else { \ if ( usevar ) { \ NEAR_2D(X,Xtype,0,1,0,IntType,0) \ } else if ( genvar ) { \ NEAR_2D(X,Xtype,0,0,1,IntType,0) \ } else { \ NEAR_2D(X,Xtype,0,0,0,IntType,0) \ } \ } \ } \ \ /* Handle other numbers of dimensions. */ \ /* ----------------------------------- */ \ } else { \ \ /* Allocate workspace. */ \ stride = astMalloc( sizeof( int ) * (size_t) ndim_out ); \ xn_max = astMalloc( sizeof( double ) * (size_t) ndim_out ); \ xn_min = astMalloc( sizeof( double ) * (size_t) ndim_out ); \ if ( astOK ) { \ \ /* Calculate the stride along each dimension of the output grid. */ \ for ( s = 1, idim = 0; idim < ndim_out; idim++ ) { \ stride[ idim ] = s; \ s *= ubnd_out[ idim ] - lbnd_out[ idim ] + 1; \ \ /* Calculate the coordinate limits of the output grid in each \ dimension. */ \ xn_min[ idim ] = (double) lbnd_out[ idim ] - 0.5; \ xn_max[ idim ] = (double) ubnd_out[ idim ] + 0.5; \ } \ \ /* Identify eight cases, according to whether bad pixels and/or variances \ are being processed and/or used. In each case we assign constant values \ (0 or 1) to the "Usebad", "Usevar" and "Varwgt" flags so that code for \ handling bad pixels and variances can be eliminated by the compiler's \ optimisation system when not required. */ \ if( varwgt ) { \ if ( usebad ) { \ if ( usevar ) { \ NEAR_ND(X,Xtype,1,1,0,IntType,1) \ } else if ( genvar ) { \ NEAR_ND(X,Xtype,1,0,1,IntType,1) \ } else { \ NEAR_ND(X,Xtype,1,0,0,IntType,1) \ } \ } else { \ if ( usevar ) { \ NEAR_ND(X,Xtype,0,1,0,IntType,1) \ } else if ( genvar ) { \ NEAR_ND(X,Xtype,0,0,1,IntType,1) \ } else { \ NEAR_ND(X,Xtype,0,0,0,IntType,1) \ } \ } \ } else { \ if ( usebad ) { \ if ( usevar ) { \ NEAR_ND(X,Xtype,1,1,0,IntType,0) \ } else if ( genvar ) { \ NEAR_ND(X,Xtype,1,0,1,IntType,0) \ } else { \ NEAR_ND(X,Xtype,1,0,0,IntType,0) \ } \ } else { \ if ( usevar ) { \ NEAR_ND(X,Xtype,0,1,0,IntType,0) \ } else if ( genvar ) { \ NEAR_ND(X,Xtype,0,0,1,IntType,0) \ } else { \ NEAR_ND(X,Xtype,0,0,0,IntType,0) \ } \ } \ } \ } \ \ /* Free the workspace. */ \ stride = astFree( stride ); \ xn_max = astFree( xn_max ); \ xn_min = astFree( xn_min ); \ } \ \ } #define NEAR_1D(X,Xtype,Usebad,Usevar,Genvar,IntType,Varwgt) \ \ /* Loop round all input points which are to be rebinned. */ \ for( point = 0; point < npoint; point++ ) { \ \ /* Obtain the input data value which is to be added into the output array. */ \ off_in = offset[ point ]; \ in_val = in[ off_in ]; \ \ /* If necessary, test if the input data value or variance is bad. If we \ are using the reciprocal of the input variances as weights, then \ variance values of zero are also effectively bad (but we can use input \ variances of zero otherwise). */ \ if ( Usebad ) { \ bad = ( in_val == badval ); \ if ( Varwgt ) { \ bad = bad || ( in_var[ off_in ] == badval ) \ || ( in_var[ off_in ] <= 0.0 ); \ } else if ( Usevar ) { \ bad = bad || ( in_var[ off_in ] == badval ); \ } \ } else { \ if ( Varwgt ) { \ bad = ( in_var[ off_in ] <= 0.0 ); \ } else { \ bad = 0; \ } \ } \ \ /* Obtain the output x coordinate corresponding to the centre of the \ current input pixel and test if it lies outside the output grid, or \ is bad. */ \ x = coords[ 0 ][ point ]; \ bad = bad || ( x < xmin ) || ( x >= xmax ) || ( x == AST__BAD ); \ if ( !bad ) { \ \ /* Increment the number of input pixels pasted into the output array. */ \ if( nused ) (*nused)++; \ \ /* If not, then obtain the offset within the output grid of the pixel \ which contains the current input point. */ \ off_out = (int) floor( x + 0.5 ) - lbnd_out[ 0 ]; \ \ /* If we are using the input data variances as weights, calculate the \ weight. */ \ pixwt = conwgt; \ if( Varwgt ) pixwt *= 1.0/in_var[ off_in ]; \ \ /* Get the weighted input data value, including any extra scaling. */ \ pfac = pixwt*infac; \ c = CONV(IntType,pfac*in_val); \ \ /* Increment the value of this output pixel by the weighted input pixel \ value, and increment the sum of the weights. */ \ out[ off_out ] += CONV(IntType, c ); \ if( work ) work[ off_out ] += pixwt; \ \ /* If output variances are being calculated on the basis of the input \ variances, then we also store the required sum in "out_var". */ \ if( Usevar ) { \ out_var[ off_out ] += CONV(IntType,in_var[ off_in ]*pfac*pfac); \ \ /* If output variances are being calculated on the basis of the spread of \ input values, we need the sum of the squared weighted data values, the \ sum of the weights (already in the first half of the "work" array), and \ the sum of the squared weights. */ \ } else if( Genvar && pixwt != 0.0 ) { \ out_var[ off_out ] += c*c/pixwt; \ work[ off_out + npix_out ] += pixwt*pixwt; \ } \ } \ } #define NEAR_2D(X,Xtype,Usebad,Usevar,Genvar,IntType,Varwgt) \ \ /* Loop round all input points which are to be rebinned. */ \ for( point = 0; point < npoint; point++ ) { \ \ /* Obtain the input data value which is to be added into the output array. */ \ off_in = offset[ point ]; \ in_val = in[ off_in ]; \ \ /* If necessary, test if the input data value or variance is bad. If we \ are using the reciprocal of the input variances as weights, then \ variance values of zero are also effectively bad (but we can use input \ variances of zero otherwise). */ \ if ( Usebad ) { \ bad = ( in_val == badval ); \ if ( Varwgt ) { \ bad = bad || ( in_var[ off_in ] == badval ) \ || ( in_var[ off_in ] <= 0.0 ); \ } else if ( Usevar ) { \ bad = bad || ( in_var[ off_in ] == badval ); \ } \ } else { \ if ( Varwgt ) { \ bad = ( in_var[ off_in ] <= 0.0 ); \ } else { \ bad = 0; \ } \ } \ \ /* Obtain the output y coordinate corresponding to the centre of the \ current input pixel and test if it lies outside the output grid, or \ is bad. */ \ y = coords[ 1 ][ point ]; \ bad = bad || ( y < ymin ) || ( y >= ymax ) || ( y == AST__BAD ); \ if ( !bad ) { \ \ /* Obtain the output x coordinate corresponding to the centre of the \ current input pixel and test if it lies outside the output grid, or \ is bad. */ \ x = coords[ 0 ][ point ]; \ bad = bad || ( x < xmin ) || ( x >= xmax ) || ( x == AST__BAD ); \ if ( !bad ) { \ \ /* Increment the number of input pixels pasted into the output array. */ \ if( nused ) (*nused)++; \ \ /* Obtain the offsets along each output grid dimension of the output \ pixel which is to receive the input pixel value. */ \ ix = (int) floor( x + 0.5 ) - lbnd_out[ 0 ]; \ iy = (int) floor( y + 0.5 ) - lbnd_out[ 1 ]; \ \ /* Calculate this pixel's offset from the start of the output array. */ \ off_out = ix + ystride * iy; \ \ /* If we are using the input data variances as weights, calculate the \ weight. */ \ pixwt = conwgt; \ if( Varwgt ) pixwt *= 1.0/in_var[ off_in ]; \ \ /* Get the weighted input data value, including any extra scaling. */ \ pfac = pixwt*infac; \ c = CONV(IntType,pfac*in_val); \ \ /* Increment the value of this output pixel by the weighted input pixel \ value, and increment the sum of the weights. */ \ out[ off_out ] += CONV(IntType, c ); \ if( work ) work[ off_out ] += pixwt; \ \ /* If output variances are being calculated on the basis of the input \ variances, then we also store the required sum in "out_var". */ \ if( Usevar ) { \ out_var[ off_out ] += CONV(IntType,in_var[ off_in ]*pfac*pfac); \ \ /* If output variances are being calculated on the basis of the spread of \ input values, we need the sum of the squared weighted data values, the \ sum of the weights (already in the first half of the "work" array), and \ the sum of the squared weights. */ \ } else if( Genvar && pixwt != 0.0 ) { \ out_var[ off_out ] += c*c/pixwt; \ work[ off_out + npix_out ] += pixwt*pixwt; \ } \ } \ } \ } #define NEAR_ND(X,Xtype,Usebad,Usevar,Genvar,IntType,Varwgt) \ \ /* Loop round all input points which are to be rebinned. */ \ for( point = 0; point < npoint; point++ ) { \ \ /* Obtain the input data value which is to be added into the output array. */ \ off_in = offset[ point ]; \ in_val = in[ off_in ]; \ \ /* If necessary, test if the input data value or variance is bad. If we \ are using the reciprocal of the input variances as weights, then \ variance values of zero are also effectively bad (but we can use input \ variances of zero otherwise). */ \ if ( Usebad ) { \ bad = ( in_val == badval ); \ if ( Varwgt ) { \ bad = bad || ( in_var[ off_in ] == badval ) \ || ( in_var[ off_in ] <= 0.0 ); \ } else if ( Usevar ) { \ bad = bad || ( in_var[ off_in ] == badval ); \ } \ } else { \ if ( Varwgt ) { \ bad = ( in_var[ off_in ] <= 0.0 ); \ } else { \ bad = 0; \ } \ } \ \ if( !bad ) { \ \ /* Initialise the offset into the output array. Then loop to obtain \ each coordinate associated with the current output point. */ \ off_out = 0; \ for ( idim = 0; idim < ndim_out; idim++ ) { \ xn = coords[ idim ][ point ]; \ \ /* Test if the coordinate lies outside the output grid, or is bad. If \ either is true, the corresponding input pixel value will be ignored, \ so give up on this point. */ \ bad = ( xn < xn_min[ idim ] ) || ( xn >= xn_max[ idim ] ) || \ ( xn == AST__BAD ); \ if ( bad ) { \ break; \ } \ \ /* Obtain the offset along the current output grid dimension of the \ output pixel which is to receive the input pixel value. */ \ ixn = (int) floor( xn + 0.5 ) - lbnd_out[ idim ]; \ \ /* Accumulate this pixel's offset from the start of the output array. */ \ off_out += ixn * stride[ idim ]; \ } \ \ if( !bad ) { \ \ /* Increment the number of input pixels pasted into the output array. */ \ if( nused ) (*nused)++; \ \ /* If we are using the input data variances as weights, calculate the \ weight. */ \ pixwt = conwgt; \ if( Varwgt ) pixwt *= 1.0/in_var[ off_in ]; \ \ /* Get the weighted input data value, including any extra scaling. */ \ pfac = pixwt*infac; \ c = CONV(IntType,pfac*in_val); \ \ /* Increment the value of this output pixel by the weighted input pixel \ value, and increment the sum of the weights. */ \ out[ off_out ] += CONV(IntType, c ); \ if( work ) work[ off_out ] += pixwt; \ \ /* If output variances are being calculated on the basis of the input \ variances, then we also store the required sum in "out_var". */ \ if( Usevar ) { \ out_var[ off_out ] += CONV(IntType,in_var[ off_in ]*pfac*pfac); \ \ /* If output variances are being calculated on the basis of the spread of \ input values, we need the sum of the squared weighted data values, the \ sum of the weights (already in the first half of the "work" array), and \ the sum of the squared weights. */ \ } else if( Genvar && pixwt != 0.0 ) { \ out_var[ off_out ] += c*c/pixwt; \ work[ off_out + npix_out ] += pixwt*pixwt; \ } \ } \ } \ } /* Expand the main macro above to generate a function for each required signed data type. */ #if HAVE_LONG_DOUBLE /* Not normally implemented */ MAKE_SPREAD_NEAREST(LD,long double,0) #endif MAKE_SPREAD_NEAREST(D,double,0) MAKE_SPREAD_NEAREST(F,float,0) MAKE_SPREAD_NEAREST(I,int,1) MAKE_SPREAD_NEAREST(B,signed char,1) MAKE_SPREAD_NEAREST(UB,unsigned char,1) /* Undefine the macros used above. */ #undef NEAR_ND #undef NEAR_2D #undef NEAR_1D #undef MAKE_SPREAD_NEAREST static int TestAttrib( AstObject *this_object, const char *attrib, int *status ) { /* * Name: * TestAttrib * Purpose: * Test if a specified attribute value is set for a Mapping. * Type: * Private function. * Synopsis: * #include "mapping.h" * int TestAttrib( AstObject *this, const char *attrib, int *status ) * Class Membership: * Mapping member function (over-rides the astTestAttrib protected * method inherited from the Object class). * Description: * This function returns a boolean result (0 or 1) to indicate whether * a value has been set for one of a Mapping's attributes. * Parameters: * this * Pointer to the Mapping. * attrib * Pointer to a null terminated string specifying the attribute * name. This should be in lower case with no surrounding white * space. * status * Pointer to the inherited status variable. * Returned Value: * One if a value has been set, otherwise zero. * Notes: * - A value of zero will be returned if this function is invoked * with the global status set, or if it should fail for any reason. */ /* Local Variables: */ AstMapping *this; /* Pointer to the Mapping structure */ int result; /* Result value to return */ /* Initialise. */ result = 0; /* Check the global error status. */ if ( !astOK ) return result; /* Obtain a pointer to the Mapping structure. */ this = (AstMapping *) this_object; /* Check the attribute name and test the appropriate attribute. */ /* Invert. */ /* ------- */ if ( !strcmp( attrib, "invert" ) ) { result = astTestInvert( this ); /* Report. */ /* ------- */ } else if ( !strcmp( attrib, "report" ) ) { result = astTestReport( this ); /* If the name is not recognised, test if it matches any of the read-only attributes of this class. If it does, then return zero. */ } else if ( !strcmp( attrib, "nin" ) || !strcmp( attrib, "islinear" ) || !strcmp( attrib, "issimple" ) || !strcmp( attrib, "nout" ) || !strcmp( attrib, "tranforward" ) || !strcmp( attrib, "traninverse" ) ) { result = 0; /* If the attribute is still not recognised, pass it on to the parent method for further interpretation. */ } else { result = (*parent_testattrib)( this_object, attrib, status ); } /* Return the result, */ return result; } static void Tran1( AstMapping *this, int npoint, const double xin[], int forward, double xout[], int *status ) { /* *++ * Name: c astTran1 f AST_TRAN1 * Purpose: * Transform 1-dimensional coordinates. * Type: * Public virtual function. * Synopsis: c #include "mapping.h" c void astTran1( AstMapping *this, int npoint, const double xin[], c int forward, double xout[] ) f CALL AST_TRAN1( THIS, NPOINT, XIN, FORWARD, XOUT, STATUS ) * Class Membership: * Mapping method. * Description: c This function applies a Mapping to transform the coordinates of f This routine applies a Mapping to transform the coordinates of * a set of points in one dimension. * Parameters: c this f THIS = INTEGER (Given) * Pointer to the Mapping to be applied. c npoint f NPOINT = INTEGER (Given) * The number of points to be transformed. c xin f XIN( NPOINT ) = DOUBLE PRECISION (Given) c An array of "npoint" coordinate values for the input f An array of coordinate values for the input * (untransformed) points. c forward f FORWARD = LOGICAL (Given) c A non-zero value indicates that the Mapping's forward c coordinate transformation is to be applied, while a zero c value indicates that the inverse transformation should be c used. f A .TRUE. value indicates that the Mapping's forward f coordinate transformation is to be applied, while a .FALSE. f value indicates that the inverse transformation should be f used. c xout f XOUT( NPOINT ) = DOUBLE PRECISION (Returned) c An array (with "npoint" elements) into which the f An array into which the * coordinates of the output (transformed) points will be written. f STATUS = INTEGER (Given and Returned) f The global status. * Notes: * - The Mapping supplied must have the value 1 for both its Nin * and Nout attributes. *-- */ /* Local Variables: */ AstPointSet *in_points; /* Pointer to input PointSet */ AstPointSet *out_points; /* Pointer to output PointSet */ const double *in_ptr[ 1 ]; /* Array of input data pointers */ double *out_ptr[ 1 ]; /* Array of output data pointers */ /* Check the global error status. */ if ( !astOK ) return; /* Validate the Mapping and numbers of points/coordinates. */ ValidateMapping( this, forward, npoint, 1, 1, "astTran1", status ); /* Set up pointers to the input and output coordinate arrays. */ if ( astOK ) { in_ptr[ 0 ] = xin; out_ptr[ 0 ] = xout; /* Create PointSets to describe the input and output points. */ in_points = astPointSet( npoint, 1, "", status ); out_points = astPointSet( npoint, 1, "", status ); /* Associate the data pointers with the PointSets (note we must explicitly remove the "const" qualifier from the input data here, although they will not be modified). */ astSetPoints( in_points, (double **) in_ptr ); astSetPoints( out_points, out_ptr ); /* Apply the required transformation to the coordinates. */ (void) astTransform( this, in_points, forward, out_points ); /* If the Mapping's Report attribute is set, report the effect the Mapping has had on the coordinates. */ if ( astGetReport( this ) ) astReportPoints( this, forward, in_points, out_points ); /* Delete the two PointSets. */ in_points = astDelete( in_points ); out_points = astDelete( out_points ); } } static void Tran2( AstMapping *this, int npoint, const double xin[], const double yin[], int forward, double xout[], double yout[], int *status ) { /* *++ * Name: c astTran2 f AST_TRAN2 * Purpose: * Transform 2-dimensional coordinates. * Type: * Public virtual function. * Synopsis: c #include "mapping.h" c void astTran2( AstMapping *this, c int npoint, const double xin[], const double yin[], c int forward, double xout[], double yout[] ) f CALL AST_TRAN2( THIS, NPOINT, XIN, YIN, FORWARD, XOUT, YOUT, STATUS ) * Class Membership: * Mapping method. * Description: c This function applies a Mapping to transform the coordinates of f This routine applies a Mapping to transform the coordinates of * a set of points in two dimensions. * Parameters: c this f THIS = INTEGER (Given) * Pointer to the Mapping to be applied. c npoint f NPOINT = INTEGER (Given) * The number of points to be transformed. c xin f XIN( NPOINT ) = DOUBLE PRECISION (Given) c An array of "npoint" X-coordinate values for the input f An array of X-coordinate values for the input * (untransformed) points. c yin f YIN( NPOINT ) = DOUBLE PRECISION (Given) c An array of "npoint" Y-coordinate values for the input f An array of Y-coordinate values for the input * (untransformed) points. c forward f FORWARD = LOGICAL (Given) c A non-zero value indicates that the Mapping's forward c coordinate transformation is to be applied, while a zero c value indicates that the inverse transformation should be c used. f A .TRUE. value indicates that the Mapping's forward f coordinate transformation is to be applied, while a .FALSE. f value indicates that the inverse transformation should be f used. c xout f XOUT( NPOINT ) = DOUBLE PRECISION (Returned) c An array (with "npoint" elements) into which the f An array into which the * X-coordinates of the output (transformed) points will be written. c yout f YOUT( NPOINT ) = DOUBLE PRECISION (Returned) c An array (with "npoint" elements) into which the f An array into which the * Y-coordinates of the output (transformed) points will be written. f STATUS = INTEGER (Given and Returned) f The global status. * Notes: * - The Mapping supplied must have the value 2 for both its Nin * and Nout attributes. *-- */ /* Local Variables: */ AstPointSet *in_points; /* Pointer to input PointSet */ AstPointSet *out_points; /* Pointer to output PointSet */ const double *in_ptr[ 2 ]; /* Array of input data pointers */ double *out_ptr[ 2 ]; /* Array of output data pointers */ /* Check the global error status. */ if ( !astOK ) return; /* Validate the Mapping and the numbers of points/coordinates. */ ValidateMapping( this, forward, npoint, 2, 2, "astTran2", status ); /* Set up pointers to the input and output coordinate arrays. */ if ( astOK ) { in_ptr[ 0 ] = xin; in_ptr[ 1 ] = yin; out_ptr[ 0 ] = xout; out_ptr[ 1 ] = yout; /* Create PointSets to describe the input and output points. */ in_points = astPointSet( npoint, 2, "", status ); out_points = astPointSet( npoint, 2, "", status ); /* Associate the data pointers with the PointSets (note we must explicitly remove the "const" qualifier from the input data here, although they will not be modified). */ astSetPoints( in_points, (double **) in_ptr ); astSetPoints( out_points, out_ptr ); /* Apply the required transformation to the coordinates. */ (void) astTransform( this, in_points, forward, out_points ); /* If the Mapping's Report attribute is set, report the effect the Mapping has had on the coordinates. */ if ( astGetReport( this ) ) astReportPoints( this, forward, in_points, out_points ); /* Delete the two PointSets. */ in_points = astDelete( in_points ); out_points = astDelete( out_points ); } } static void TranGrid( AstMapping *this, int ncoord_in, const int lbnd[], const int ubnd[], double tol, int maxpix, int forward, int ncoord_out, int outdim, double *out, int *status ) { /* *++ * Name: c astTranGrid f AST_TRANGRID * Purpose: * Transform a grid of positions * Type: * Public virtual function. * Synopsis: c #include "mapping.h" c void astTranGrid( AstMapping *this, int ncoord_in, c const int lbnd[], const int ubnd[], c double tol, int maxpix, int forward, c int ncoord_out, int outdim, double *out ); f CALL AST_TRANGRID( THIS, NCOORD_IN, LBND, UBND, TOL, MAXPIX, f FORWARD, NCOORD_OUT, OUTDIM, OUT, STATUS ) * Class Membership: * Mapping method. * Description: * This function uses the supplied Mapping to transforms a regular square * grid of points covering a specified box. It attempts to do this * quickly by first approximating the Mapping with a linear transformation * applied over the whole region of the input grid which is being used. * If this proves to be insufficiently accurate, the input region is * sub-divided into two along its largest dimension and the process is * repeated within each of the resulting sub-regions. This process of * sub-division continues until a sufficiently good linear approximation * is found, or the region to which it is being applied becomes too small * (in which case the original Mapping is used directly). * Parameters: c this f THIS = INTEGER (Given) * Pointer to the Mapping to be applied. c ncoord_in f NCOORD_IN = INTEGER (Given) * The number of coordinates being supplied for each box corner * (i.e. the number of dimensions of the space in which the * input points reside). c lbnd f LBND( NCOORD_IN ) = INTEGER (Given) c Pointer to an array of integers, with "ncoord_in" elements, f An array * containing the coordinates of the centre of the first pixel * in the input grid along each dimension. c ubnd f UBND( NCOORD_IN ) = INTEGER (Given) c Pointer to an array of integers, with "ncoord_in" elements, f An array * containing the coordinates of the centre of the last pixel in * the input grid along each dimension. * c Note that "lbnd" and "ubnd" together define the shape f Note that LBND and UBND together define the shape * and size of the input grid, its extent along a particular c (j'th) dimension being ubnd[j]-lbnd[j]+1 (assuming the c index "j" to be zero-based). They also define f (J'th) dimension being UBND(J)-LBND(J)+1. They also define * the input grid's coordinate system, each pixel having unit * extent along each dimension with integral coordinate values * at its centre. c tol f TOL = DOUBLE PRECISION (Given) * The maximum tolerable geometrical distortion which may be * introduced as a result of approximating non-linear Mappings * by a set of piece-wise linear transformations. This should be * expressed as a displacement within the output coordinate system * of the Mapping. * * If piece-wise linear approximation is not required, a value * of zero may be given. This will ensure that the Mapping is * used without any approximation, but may increase execution * time. * * If the value is too high, discontinuities between the linear * approximations used in adjacent panel will be higher. If this * is a problem, reduce the tolerance value used. c maxpix f MAXPIX = INTEGER (Given) * A value which specifies an initial scale size (in input grid points) * for the adaptive algorithm which approximates non-linear Mappings * with piece-wise linear transformations. Normally, this should * be a large value (larger than any dimension of the region of * the input grid being used). In this case, a first attempt to * approximate the Mapping by a linear transformation will be * made over the entire input region. * * If a smaller value is used, the input region will first be c divided into sub-regions whose size does not exceed "maxpix" f divided into sub-regions whose size does not exceed MAXPIX * grid points in any dimension. Only at this point will attempts * at approximation commence. * * This value may occasionally be useful in preventing false * convergence of the adaptive algorithm in cases where the * Mapping appears approximately linear on large scales, but has * irregularities (e.g. holes) on smaller scales. A value of, * say, 50 to 100 grid points can also be employed as a safeguard * in general-purpose software, since the effect on performance is * minimal. * * If too small a value is given, it will have the effect of * inhibiting linear approximation altogether (equivalent to c setting "tol" to zero). Although this may degrade f setting TOL to zero). Although this may degrade * performance, accurate results will still be obtained. c forward f FORWARD = LOGICAL (Given) c A non-zero value indicates that the Mapping's forward c coordinate transformation is to be applied, while a zero c value indicates that the inverse transformation should be c used. f A .TRUE. value indicates that the Mapping's forward f coordinate transformation is to be applied, while a .FALSE. f value indicates that the inverse transformation should be f used. c ncoord_out f NCOORD_OUT = INTEGER (Given) * The number of coordinates being generated by the Mapping for * each output point (i.e. the number of dimensions of the * space in which the output points reside). This need not be c the same as "ncoord_in". f the same as NCOORD_IN. c outdim f OUTDIM = INTEGER (Given) c The number of elements along the second dimension of the "out" f The number of elements along the first dimension of the OUT * array (which will contain the output coordinates). The value * given should not be less than the number of points in the grid. c out f OUT( OUTDIM, NCOORD_OUT ) = DOUBLE PRECISION (Returned) c The address of the first element in a 2-dimensional array of c shape "[ncoord_out][outdim]", into c which the coordinates of the output (transformed) points will c be written. These will be stored such that the value of c coordinate number "coord" for output point number "point" c will be found in element "out[coord][point]". f An array into which the coordinates of the output f (transformed) points will be written. These will be stored f such that the value of coordinate number COORD for output f point number POINT will be found in element OUT(POINT,COORD). * The points are ordered such that the first axis of the input * grid changes most rapidly. For example, if the input grid is * 2-dimensional and extends from (2,-1) to (3,1), the output * points will be stored in the order (2,-1), (3, -1), (2,0), (3,0), * (2,1), (3,1). f STATUS = INTEGER (Given and Returned) f The global status. * Notes: c - If the forward coordinate transformation is being applied, the c Mapping supplied must have the value of "ncoord_in" for its Nin c attribute and the value of "ncoord_out" for its Nout attribute. If c the inverse transformation is being applied, these values should c be reversed. f - If the forward coordinate transformation is being applied, the f Mapping supplied must have the value of NCOORD_IN for its Nin f attribute and the value of NCOORD_OUT for its Nout attribute. If f the inverse transformation is being applied, these values should f be reversed. *-- */ /* Local Variables: */ astDECLARE_GLOBALS /* Thread-specific data */ AstMapping *simple; /* Pointer to simplified Mapping */ double **out_ptr; /* Pointer to array of output data pointers */ int coord; /* Loop counter for coordinates */ int idim; /* Loop counter for coordinate dimensions */ int npoint; /* Number of points in the grid */ int64_t mpix; /* Number of points for testing */ /* Check the global error status. */ if ( !astOK ) return; /* Get a pointer to a structure holding thread-specific global data values */ astGET_GLOBALS(this); /* Calculate the number of points in the grid, and check that the lower and upper bounds of the input grid are consistent. Report an error if any pair is not. */ mpix = 1; for ( idim = 0; idim < ncoord_in; idim++ ) { if ( lbnd[ idim ] > ubnd[ idim ] ) { astError( AST__GBDIN, "astTranGrid(%s): Lower bound of " "input grid (%d) exceeds corresponding upper bound " "(%d).", status, astGetClass( this ), lbnd[ idim ], ubnd[ idim ] ); astError( AST__GBDIN, "Error in input dimension %d.", status, idim + 1 ); break; } else { mpix *= ubnd[ idim ] - lbnd[ idim ] + 1; } } /* Report an error if there are too many pixels in the input. */ npoint = mpix; if ( astOK && npoint != mpix ) { astError( AST__EXSPIX, "astTranGrid(%s): Supplied grid " "contains too many points (%g): must be fewer than %d.", status, astGetClass( this ), (double) mpix, INT_MAX/ncoord_out ); } mpix = outdim*ncoord_out; if ( astOK && (int) mpix != mpix ) { astError( AST__EXSPIX, "astTranGrid(%s): Supplied output array " "contains too many pixels (%g): must be fewer than %d.", status, astGetClass( this ), (double) mpix, INT_MAX ); } /* Validate the mapping and numbers of points/coordinates. */ ValidateMapping( this, forward, npoint, ncoord_in, ncoord_out, "astTranGrid", status ); /* Check that the positional accuracy tolerance supplied is valid and report an error if necessary. */ if ( astOK && ( tol < 0.0 ) ) { astError( AST__PATIN, "astTranGrid(%s): Invalid positional " "accuracy tolerance (%.*g pixel).", status, astGetClass( this ), AST__DBL_DIG, tol ); astError( AST__PATIN, "This value should not be less than zero." , status); } /* Check that the initial scale size in grid points supplied is valid and report an error if necessary. */ if ( astOK && ( maxpix < 0 ) ) { astError( AST__SSPIN, "astTranGrid(%s): Invalid initial scale " "size in grid points (%d).", status, astGetClass( this ), maxpix ); astError( AST__SSPIN, "This value should not be less than zero." , status); } /* Validate the output array dimension argument. */ if ( astOK && ( outdim < npoint ) ) { astError( AST__DIMIN, "astTranGrid(%s): The output array dimension value " "(%d) is invalid.", status, astGetClass( this ), outdim ); astError( AST__DIMIN, "This should not be less than the number of " "grid points being transformed (%d).", status, npoint ); } /* If there are sufficient pixels to make it worthwhile, simplify the Mapping supplied to improve performance. Otherwise, just clone the Mapping pointer. Note we save a pointer to the original Mapping so that lower-level functions can use it if they need to report an error. */ simple = NULL; unsimplified_mapping = this; if ( astOK ) { if ( npoint > 1024 ) { simple = astSimplify( this ); /* Report an error if the required transformation of this simplified Mapping is not defined. */ if( astOK ) { if ( forward && !astGetTranForward( simple ) ) { astError( AST__TRNND, "astTranGrid(%s): A forward coordinate " "transformation is not defined by the %s supplied.", status, astGetClass( unsimplified_mapping ), astGetClass( unsimplified_mapping ) ); } else if ( !forward && !astGetTranInverse( simple ) ) { astError( AST__TRNND, "astTranGrid(%s): An inverse coordinate " "transformation is not defined by the %s supplied.", status, astGetClass( unsimplified_mapping ), astGetClass( unsimplified_mapping ) ); } } } else { simple = astClone( this ); } /* Allocate memory to hold the array of output data pointers. */ out_ptr = astMalloc( sizeof( double * ) * (size_t) ncoord_out ); /* Initialise the output data pointers to point into the "out" array. */ if ( astOK ) { for ( coord = 0; coord < ncoord_out; coord++ ) { out_ptr[ coord ] = out + coord * outdim; } /* If required, temporarily invert the Mapping. */ if( !forward ) astInvert( simple ); /* Perform the transformation. */ TranGridAdaptively( simple, ncoord_in, lbnd, ubnd, lbnd, ubnd, tol, maxpix, ncoord_out, out_ptr, status ); /* If required, uninvert the Mapping. */ if( !forward ) astInvert( simple ); } /* Free the memory used for the data pointers. */ out_ptr = astFree( out_ptr ); /* Annul the pointer to the simplified/cloned Mapping. */ simple = astAnnul( simple ); } } static void TranGridAdaptively( AstMapping *this, int ncoord_in, const int *lbnd_in, const int *ubnd_in, const int lbnd[], const int ubnd[], double tol, int maxpix, int ncoord_out, double *out[], int *status ){ /* * Name: * TranGridAdaptively * Purpose: * Transform grid positions adaptively. * Type: * Private function. * Synopsis: * #include "mapping.h" * void TranGridAdaptively( AstMapping *this, int ncoord_in, * const int *lbnd_in, const int *ubnd_in, * const int lbnd[], const int ubnd[], * double tol, int maxpix, int ncoord_out, * double *out[] ) * Class Membership: * Mapping member function. * Description: * This function transforms grid points within a specified section of a * rectangular grid (with any number of dimensions) using the forward * transformation of the specified Mapping. * * This function is very similar to TranGridWithBlocking and TranGridSection * which lie below it in the calling hierarchy. However, this function * also attempts to adapt to the Mapping supplied and to sub-divide the * section being transformed into smaller sections within which a linear * approximation to the Mapping may be used. This reduces the number of * Mapping evaluations, thereby improving efficiency particularly when * complicated Mappings are involved. * Parameters: * this * Pointer to the Mapping to be applied. The forward transformation * is used. * ncoord_in * The number of coordinates being supplied for each box corner * (i.e. the number of dimensions of the space in which the * input points reside). * lbnd_in * Pointer to an array of integers, with "ndim_in" elements. * This should give the coordinates of the centre of the first * pixel in the input grid along each dimension. * ubnd_in * Pointer to an array of integers, with "ndim_in" elements. * This should give the coordinates of the centre of the last * pixel in the input grid along each dimension. * * Note that "lbnd_in" and "ubnd_in" together define the shape * and size of the whole input grid, its extent along a * particular (i'th) dimension being (ubnd_in[i] - lbnd_in[i] + * 1). They also define the input grid's coordinate system, with * each pixel being of unit extent along each dimension with * integral coordinate values at its centre. * lbnd * Pointer to an array of integers, with "ncoord_in" elements, * containing the coordinates of the centre of the first pixel * in the input grid along each dimension. * ubnd * Pointer to an array of integers, with "ncoord_in" elements, * containing the coordinates of the centre of the last pixel in * the input grid along each dimension. * * Note that "lbnd" and "ubnd" together define the shape * and size of the input grid, its extent along a particular * (j'th) dimension being ubnd[j]-lbnd[j]+1 (assuming the * index "j" to be zero-based). They also define * the input grid's coordinate system, each pixel having unit * extent along each dimension with integral coordinate values * at its centre. * tol * The maximum tolerable geometrical distortion which may be * introduced as a result of approximating non-linear Mappings * by a set of piece-wise linear transformations. This should be * expressed as a displacement in pixels in the output grid's * coordinate system. * * If piece-wise linear approximation is not required, a value * of zero may be given. This will ensure that the Mapping is * used without any approximation, but may increase execution * time. * * If the value is too high, discontinuities between the linear * approximations used in adjacent panel will be higher. If this * is a problem, reduce the tolerance value used. * maxpix * A value which specifies an initial scale size (in grid points) * for the adaptive algorithm which approximates non-linear Mappings * with piece-wise linear transformations. Normally, this should * be a large value (larger than any dimension of the region of * the input grid being used). In this case, a first attempt to * approximate the Mapping by a linear transformation will be * made over the entire input region. * * If a smaller value is used, the input region will first be * divided into sub-regions whose size does not exceed "maxpix" * grid points in any dimension. Only at this point will attempts * at approximation commence. * * This value may occasionally be useful in preventing false * convergence of the adaptive algorithm in cases where the * Mapping appears approximately linear on large scales, but has * irregularities (e.g. holes) on smaller scales. A value of, * say, 50 to 100 grid points can also be employed as a safeguard * in general-purpose software, since the effect on performance is * minimal. * * If too small a value is given, it will have the effect of * inhibiting linear approximation altogether (equivalent to * setting "tol" to zero). Although this may degrade * performance, accurate results will still be obtained. * ncoord_out * The number of dimensions of the space in which the output points * reside. * out * Pointer to an array with "ndim_out" elements. Element [i] of * this array is a pointer to an array in which to store the * transformed values for output axis "i". The points are ordered * such that the first axis of the input grid changes most rapidly. * For example, if the input grid is 2-dimensional and extends from * (2,-1) to (3,1), the output points will be stored in the order * (2,-1), (3, -1), (2,0), (3,0), (2,1), (3,1). */ /* Local Variables: */ double *flbnd; /* Array holding floating point lower bounds */ double *fubnd; /* Array holding floating point upper bounds */ double *linear_fit; /* Pointer to array of fit coefficients */ int *hi; /* Pointer to array of section upper bounds */ int *lo; /* Pointer to array of section lower bounds */ int coord_in; /* Loop counter for input coordinates */ int dim; /* Output section dimension size */ int dimx; /* Dimension with maximum section extent */ int divide; /* Sub-divide the output section? */ int i; /* Loop count */ int isLinear; /* Is the transformation linear? */ int mxdim; /* Largest output section dimension size */ int npix; /* Number of pixels in output section */ int npoint; /* Number of points for obtaining a fit */ int nvertex; /* Number of vertices of output section */ int toobig; /* Section too big (must sub-divide)? */ int toosmall; /* Section too small to sub-divide? */ /* Check the global error status. */ if ( !astOK ) return; /* Further initialisation. */ npix = 1; mxdim = 0; dimx = 1; nvertex = 1; /* Loop through the input grid dimensions. */ for ( coord_in = 0; coord_in < ncoord_in; coord_in++ ) { /* Obtain the extent in each dimension of the input section which is to be rebinned, and calculate the total number of pixels it contains. */ dim = ubnd[ coord_in ] - lbnd[ coord_in ] + 1; npix *= dim; /* Find the maximum dimension size of this input section and note which dimension has this size. */ if ( dim > mxdim ) { mxdim = dim; dimx = coord_in; } /* Calculate how many vertices the output section has. */ nvertex *= 2; } /* Calculate how many sample points will be needed (by the astLinearApprox function) to obtain a linear fit to the Mapping's forward transformation. */ npoint = 1 + 4 * ncoord_in + 2 * nvertex; /* If the number of pixels in the input section is not at least 4 times this number, we will probably not save significant time by attempting to obtain a linear fit, so note that the input section is too small. */ toosmall = ( npix < ( 4 * npoint ) ); /* Note if the maximum dimension of the input section exceeds the user-supplied scale factor. */ toobig = ( maxpix < mxdim ); /* Assume the Mapping is significantly non-linear before deciding whether to sub-divide the output section. */ linear_fit = NULL; /* If the output section is too small to be worth obtaining a linear fit, or if the accuracy tolerance is zero, we will not sub-divide. This means that the Mapping will be used to transform each pixel's coordinates and no linear approximation will be used. */ if ( toosmall || ( tol == 0.0 ) ) { divide = 0; /* Otherwise, if the largest input section dimension exceeds the scale length given, we will sub-divide. This offers the possibility of obtaining a linear approximation to the Mapping over a reduced range of input coordinates (which will be handled by a recursive invocation of this function). */ } else if ( toobig ) { divide = 1; /* If neither of the above apply, then attempt to fit a linear approximation to the forward transformation of the Mapping over the range of coordinates covered by the input section. We need to temporarily copy the integer bounds into floating point arrays to use astLinearApprox. */ } else { /* Allocate memory for floating point bounds and for the coefficient array */ flbnd = astMalloc( sizeof( double )*(size_t) ncoord_in ); fubnd = astMalloc( sizeof( double )*(size_t) ncoord_in ); linear_fit = astMalloc( sizeof( double )* (size_t) ( ncoord_out*( ncoord_in + 1 ) ) ); if( astOK ) { /* Copy the bounds into these arrays, and change them so that they refer to the lower and upper edges of the cell rather than the centre. This is essential if one of the axes is spanned by a single cell, since otherwise the upper and lower bounds would be identical. */ for( i = 0; i < ncoord_in; i++ ) { flbnd[ i ] = (double) lbnd[ i ] - 0.5; fubnd[ i ] = (double) ubnd[ i ] + 0.5; } /* Get the linear approximation to the forward transformation. */ isLinear = astLinearApprox( this, flbnd, fubnd, tol, linear_fit ); /* Free the coeff array if the inverse transformation is not linear. */ if( !isLinear ) linear_fit = astFree( linear_fit ); } else { linear_fit = astFree( linear_fit ); } /* Free resources */ flbnd = astFree( flbnd ); fubnd = astFree( fubnd ); /* If a linear fit was obtained, we will use it and therefore do not wish to sub-divide further. Otherwise, we sub-divide in the hope that this may result in a linear fit next time. */ divide = !linear_fit; } /* If no sub-division is required, perform the transformation (in a memory-efficient manner, since the section we are rebinning might still be very large). This will use the linear fit, if obtained above. */ if ( astOK ) { if ( !divide ) { TranGridWithBlocking( this, linear_fit, ncoord_in, lbnd_in, ubnd_in, lbnd, ubnd, ncoord_out, out, status ); /* Otherwise, allocate workspace to perform the sub-division. */ } else { lo = astMalloc( sizeof( int ) * (size_t) ncoord_in ); hi = astMalloc( sizeof( int ) * (size_t) ncoord_in ); if ( astOK ) { /* Initialise the bounds of a new input section to match the original input section. */ for ( coord_in = 0; coord_in < ncoord_in; coord_in++ ) { lo[ coord_in ] = lbnd[ coord_in ]; hi[ coord_in ] = ubnd[ coord_in ]; } /* Replace the upper bound of the section's largest dimension with the mid-point of the section along this dimension, rounded downwards. */ hi[ dimx ] = (int) floor( 0.5 * (double) ( lbnd[ dimx ] + ubnd[ dimx ] ) ); /* Rebin the resulting smaller section using a recursive invocation of this function. */ TranGridAdaptively( this, ncoord_in, lbnd_in, ubnd_in, lo, hi, tol, maxpix, ncoord_out, out, status ); /* Now set up a second section which covers the remaining half of the original input section. */ lo[ dimx ] = hi[ dimx ] + 1; hi[ dimx ] = ubnd[ dimx ]; /* If this section contains pixels, transform it in the same way. */ if ( lo[ dimx ] <= hi[ dimx ] ) { TranGridAdaptively( this, ncoord_in, lbnd_in, ubnd_in, lo, hi, tol, maxpix, ncoord_out, out, status ); } } /* Free the workspace. */ lo = astFree( lo ); hi = astFree( hi ); } } /* If coefficients for a linear fit were obtained, then free the space they occupy. */ if ( linear_fit ) linear_fit = astFree( linear_fit ); } static void TranGridSection( AstMapping *this, const double *linear_fit, int ndim_in, const int *lbnd_in, const int *ubnd_in, const int *lbnd, const int *ubnd, int ndim_out, double *out[], int *status ){ /* * Name: * TranGridSection * Purpose: * Transform grid points within a section of a rectangular grid. * Type: * Private function. * Synopsis: * #include "mapping.h" * void TranGridSection( AstMapping *this, const double *linear_fit, * int ndim_in, const int *lbnd_in, * const int *ubnd_in, const int *lbnd, * const int *ubnd, int ndim_out, double *out[] ) * Class Membership: * Mapping member function. * Description: * This function transforms grid points within a specified section of a * rectangular grid (with any number of dimensions) using a specified * Mapping or, alternatively, a linear approximation fitted to the * Mapping's forward transformation. * Parameters: * this * Pointer to a Mapping, whose forward transformation may be * used to transform the coordinates of points in the input * grid. * * The number of input coordintes for the Mapping (Nin * attribute) should match the value of "ndim_in" (below), and * the number of output coordinates (Nout attribute) should * match the value of "ndim_out". * linear_fit * Pointer to an optional array of double which contains the * coefficients of a linear fit which approximates the above * Mapping's forward coordinate transformation. If this is * supplied, it will be used in preference to the above Mapping * when transforming coordinates. This may be used to enhance * performance in cases where evaluation of the Mapping's * forward transformation is expensive. If no linear fit is * available, a NULL pointer should be supplied. * * The way in which the fit coefficients are stored in this * array and the number of array elements are as defined by the * astLinearApprox function. * ndim_in * The number of dimensions in the input grid. This should be at * least one. * lbnd_in * Pointer to an array of integers, with "ndim_in" elements. * This should give the coordinates of the centre of the first * pixel in the input data grid along each dimension. * ubnd_in * Pointer to an array of integers, with "ndim_in" elements. * This should give the coordinates of the centre of the last * pixel in the input data grid along each dimension. * * Note that "lbnd_in" and "ubnd_in" together define the shape * and size of the input data grid, its extent along a * particular (i'th) dimension being (ubnd_in[i] - lbnd_in[i] + * 1). They also define the input grid's coordinate system, with * each pixel being of unit extent along each dimension with * integral coordinate values at its centre. * lbnd * Pointer to an array of integers, with "ndim_in" elements. * This should give the coordinates of the first pixel in the * section of the input data grid which is to be rebinned. * ubnd * Pointer to an array of integers, with "ndim_in" elements. * This should give the coordinates of the last pixel in the * section of the input data grid which is to be rebinned. * * Note that "lbnd" and "ubnd" define the shape and position of * the section of the input grid which is to be rebinned. This section * should lie wholly within the extent of the input grid (as defined * by the "lbnd_out" and "ubnd_out" arrays). Regions of the input * grid lying outside this section will be ignored. * ndim_out * The number of dimensions in the output grid. This should be * at least one. * out * Pointer to an array with "ndim_out" elements. Element [i] of * this array is a pointer to an array in which to store the * transformed values for output axis "i". The points are ordered * such that the first axis of the input grid changes most rapidly. * For example, if the input grid is 2-dimensional and extends from * (2,-1) to (3,1), the output points will be stored in the order * (2,-1), (3, -1), (2,0), (3,0), (2,1), (3,1). * Notes: * - This function does not take steps to limit memory usage if the * grids supplied are large. To resample large grids in a more * memory-efficient way, the ResampleWithBlocking function should * be used. */ /* Local Variables: */ AstPointSet *pset_in; /* Input PointSet for transformation */ AstPointSet *pset_out; /* Output PointSet for transformation */ const double *grad; /* Pointer to gradient matrix of linear fit */ const double *zero; /* Pointer to zero point array of fit */ double **ptr_in; /* Pointer to input PointSet coordinates */ double **ptr_out; /* Pointer to output PointSet coordinates */ double *accum; /* Pointer to array of accumulated sums */ double x1; /* Interim x coordinate value */ double xx1; /* Initial x coordinate value */ double y1; /* Interim y coordinate value */ double yy1; /* Initial y coordinate value */ int *dim; /* Pointer to array of output pixel indices */ int *offset; /* Pointer to array of output pixel offsets */ int *stride; /* Pointer to array of output grid strides */ int coord_in; /* Loop counter for input dimensions */ int coord_out; /* Loop counter for output dimensions */ int done; /* All pixel indices done? */ int i1; /* Interim offset into "accum" array */ int i2; /* Final offset into "accum" array */ int idim; /* Loop counter for dimensions */ int ix; /* Loop counter for output x coordinate */ int iy; /* Loop counter for output y coordinate */ int npoint; /* Number of output points (pixels) */ int off1; /* Interim pixel offset into output array */ int off2; /* Interim pixel offset into output array */ int off; /* Final pixel offset into output array */ int point; /* Counter for output points (pixels ) */ int s; /* Temporary variable for strides */ /* Check the global error status. */ if ( !astOK ) return; /* Further initialisation. */ pset_in = NULL; ptr_in = NULL; ptr_out = NULL; pset_out = NULL; /* Calculate the number of input points, as given by the product of the input grid dimensions. */ for ( npoint = 1, coord_in = 0; coord_in < ndim_in; coord_in++ ) { npoint *= ubnd[ coord_in ] - lbnd[ coord_in ] + 1; } /* Allocate workspace. */ offset = astMalloc( sizeof( int ) * (size_t) npoint ); stride = astMalloc( sizeof( int ) * (size_t) ndim_in ); if ( astOK ) { /* Calculate the stride for each input grid dimension. */ off = 0; s = 1; for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { stride[ coord_in ] = s; s *= ubnd_in[ coord_in ] - lbnd_in[ coord_in ] + 1; } /* A linear fit to the Mapping is available. */ /* ========================================= */ if ( linear_fit ) { /* If a linear fit to the Mapping has been provided, then obtain pointers to the array of gradients and zero-points comprising the fit. */ grad = linear_fit + ndim_out; zero = linear_fit; /* Create a PointSet to hold the output grid coordinates and obtain an array of pointers to its coordinate data. */ pset_out = astPointSet( npoint, ndim_out, "", status ); ptr_out = astGetPoints( pset_out ); if ( astOK ) { /* Initialise the count of input points. */ point = 0; /* Handle the 1-dimensional case optimally. */ /* ---------------------------------------- */ if ( ( ndim_in == 1 ) && ( ndim_out == 1 ) ) { /* Loop through the pixels of the input grid and transform their x coordinates into the output grid's coordinate system using the linear fit supplied. Store the results in the PointSet created above. */ off = lbnd[ 0 ] - lbnd_in[ 0 ]; xx1 = zero[ 0 ] + grad[ 0 ] * (double) lbnd[ 0 ]; for ( ix = lbnd[ 0 ]; ix <= ubnd[ 0 ]; ix++ ) { ptr_out[ 0 ][ point ] = xx1; xx1 += grad[ 0 ]; offset[ point++ ] = off++; } /* Handle the 2-dimensional case optimally. */ /* ---------------------------------------- */ } else if ( ( ndim_in == 2 ) && ( ndim_out == 2 ) ) { /* Loop through the range of y coordinates in the input grid and calculate interim values of the output coordinates using the linear fit supplied. */ x1 = zero[ 0 ] + grad[ 1 ] * (double) ( lbnd[ 1 ] - 1 ); y1 = zero[ 1 ] + grad[ 3 ] * (double) ( lbnd[ 1 ] - 1 ); off1 = stride[ 1 ] * ( lbnd[ 1 ] - lbnd_in[ 1 ] - 1 ) - lbnd_in[ 0 ]; for ( iy = lbnd[ 1 ]; iy <= ubnd[ 1 ]; iy++ ) { x1 += grad[ 1 ]; y1 += grad[ 3 ]; /* Also calculate an interim pixel offset into the input array. */ off1 += stride[ 1 ]; /* Now loop through the range of input x coordinates and calculate the final values of the input coordinates, storing the results in the PointSet created above. */ xx1 = x1 + grad[ 0 ] * (double) lbnd[ 0 ]; yy1 = y1 + grad[ 2 ] * (double) lbnd[ 0 ]; off = off1 + lbnd[ 0 ]; for ( ix = lbnd[ 0 ]; ix <= ubnd[ 0 ]; ix++ ) { ptr_out[ 0 ][ point ] = xx1; xx1 += grad[ 0 ]; ptr_out[ 1 ][ point ] = yy1; yy1 += grad[ 2 ]; /* Also calculate final pixel offsets into the input array. */ offset[ point++ ] = off++; } } /* Handle other numbers of dimensions. */ /* ----------------------------------- */ } else { /* Allocate workspace. */ accum = astMalloc( sizeof( double ) * (size_t) ( ndim_in * ndim_out ) ); dim = astMalloc( sizeof( int ) * (size_t) ndim_in ); if ( astOK ) { /* Initialise an array of pixel indices for the input grid which refer to the first pixel which we will rebin. Also calculate the offset of this pixel within the input array. */ off = 0; for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { dim[ coord_in ] = lbnd[ coord_in ]; off += stride[ coord_in ] * ( dim[ coord_in ] - lbnd_in[ coord_in ] ); } /* To calculate each output grid coordinate we must perform a matrix multiply on the input grid coordinates (using the gradient matrix) and then add the zero points. However, since we will usually only be altering one input coordinate at a time (the least significant), we can avoid the full matrix multiply by accumulating partial sums for the most significant input coordinates and only altering those sums which need to change each time. The zero points never change, so we first fill the "most significant" end of the "accum" array with these. */ for ( coord_out = 0; coord_out < ndim_out; coord_out++ ) { accum[ ( coord_out + 1 ) * ndim_in - 1 ] = zero[ coord_out ]; } coord_in = ndim_in - 1; /* Now loop to process each input pixel. */ for ( done = 0; !done; point++ ) { /* To generate the output coordinate that corresponds to the current input pixel, we work down from the most significant dimension whose index has changed since the previous pixel we considered (given by "coord_in"). For each affected dimension, we accumulate in "accum" the matrix sum (including the zero point) for that dimension and all higher input dimensions. We must accumulate a separate set of sums for each output coordinate we wish to produce. (Note that for the first pixel we process, all dimensions are considered "changed", so we start by initialising the whole "accum" array.) */ for ( coord_out = 0; coord_out < ndim_out; coord_out++ ) { i1 = coord_out * ndim_in; for ( idim = coord_in; idim >= 1; idim-- ) { i2 = i1 + idim; accum[ i2 - 1 ] = accum[ i2 ] + dim[ idim ] * grad[ i2 ]; } /* The output coordinate for each dimension is given by the accumulated sum for input dimension zero (giving the sum over all input dimensions). We do not store this in the "accum" array, but assign the result directly to the coordinate array of the PointSet created earlier. */ ptr_out[ coord_out ][ point ] = accum[ i1 ] + dim[ 0 ] * grad[ i1 ]; } /* Store the offset of the current pixel in the input array. */ offset[ point ] = off; /* Now update the array of pixel indices to refer to the next input pixel. */ coord_in = 0; do { /* The least significant index which currently has less than its maximum value is incremented by one. The offset into the input array is updated accordingly. */ if ( dim[ coord_in ] < ubnd[ coord_in ] ) { dim[ coord_in ]++; off += stride[ coord_in ]; break; /* Any less significant indices which have reached their maximum value are returned to their minimum value and the input pixel offset is decremented appropriately. */ } else { dim[ coord_in ] = lbnd[ coord_in ]; off -= stride[ coord_in ] * ( ubnd[ coord_in ] - lbnd[ coord_in ] ); /* All the output pixels have been processed once the most significant pixel index has been returned to its minimum value. */ done = ( ++coord_in == ndim_in ); } } while ( !done ); } } /* Free the workspace. */ accum = astFree( accum ); dim = astFree( dim ); } } /* No linear fit to the Mapping is available. */ /* ========================================== */ } else { /* Create a PointSet to hold the coordinates of the input pixels and obtain a pointer to its coordinate data. */ pset_in = astPointSet( npoint, ndim_in, "", status ); ptr_in = astGetPoints( pset_in ); if ( astOK ) { /* Initialise the count of input points. */ point = 0; /* Handle the 1-dimensional case optimally. */ /* ---------------------------------------- */ if ( ndim_in == 1 && ndim_out == 1 ) { /* Loop through the required range of input x coordinates, assigning the coordinate values to the PointSet created above. Also store a pixel offset into the input array. */ for ( ix = lbnd[ 0 ]; ix <= ubnd[ 0 ]; ix++ ) { ptr_in[ 0 ][ point ] = (double) ix; offset[ point++ ] = ix - lbnd_in[ 0 ]; } /* Handle the 2-dimensional case optimally. */ /* ---------------------------------------- */ } else if ( ndim_in == 2 && ndim_out == 2 ) { /* Loop through the required range of input y coordinates, calculating an interim pixel offset into the input array. */ off1 = stride[ 1 ] * ( lbnd[ 1 ] - lbnd_in[ 1 ] - 1 ) - lbnd_in[ 0 ]; for ( iy = lbnd[ 1 ]; iy <= ubnd[ 1 ]; iy++ ) { off1 += stride[ 1 ]; /* Loop through the required range of input x coordinates, assigning the coordinate values to the PointSet created above. Also store a final pixel offset into the input array. */ off2 = off1 + lbnd[ 0 ]; for ( ix = lbnd[ 0 ]; ix <= ubnd[ 0 ]; ix++ ) { ptr_in[ 0 ][ point ] = (double) ix; ptr_in[ 1 ][ point ] = (double) iy; offset[ point++ ] = off2++; } } /* Handle other numbers of dimensions. */ /* ----------------------------------- */ } else { /* Allocate workspace. */ dim = astMalloc( sizeof( int ) * (size_t) ndim_in ); if ( astOK ) { /* Initialise an array of pixel indices for the input grid which refer to the first pixel to be rebinned. Also calculate the offset of this pixel within the input array. */ off = 0; for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { dim[ coord_in ] = lbnd[ coord_in ]; off += stride[ coord_in ] * ( dim[ coord_in ] - lbnd_in[ coord_in ] ); } /* Loop to generate the coordinates of each input pixel. */ for ( done = 0; !done; point++ ) { /* Copy each pixel's coordinates into the PointSet created above. */ for ( coord_in = 0; coord_in < ndim_in; coord_in++ ) { ptr_in[ coord_in ][ point ] = (double) dim[ coord_in ]; } /* Store the offset of the pixel in the input array. */ offset[ point ] = off; /* Now update the array of pixel indices to refer to the next input pixel. */ coord_in = 0; do { /* The least significant index which currently has less than its maximum value is incremented by one. The offset into the input array is updated accordingly. */ if ( dim[ coord_in ] < ubnd[ coord_in ] ) { dim[ coord_in ]++; off += stride[ coord_in ]; break; /* Any less significant indices which have reached their maximum value are returned to their minimum value and the input pixel offset is decremented appropriately. */ } else { dim[ coord_in ] = lbnd[ coord_in ]; off -= stride[ coord_in ] * ( ubnd[ coord_in ] - lbnd[ coord_in ] ); /* All the input pixels have been processed once the most significant pixel index has been returned to its minimum value. */ done = ( ++coord_in == ndim_in ); } } while ( !done ); } } /* Free the workspace. */ dim = astFree( dim ); } /* When all the input pixel coordinates have been generated, use the Mapping's forward transformation to generate the output coordinates from them. Obtain an array of pointers to the resulting coordinate data. */ pset_out = astTransform( this, pset_in, 1, NULL ); ptr_out = astGetPoints( pset_out ); } /* Annul the PointSet containing the input coordinates. */ pset_in = astAnnul( pset_in ); } } /* Copy the output coordinates into the correct positions within the supplied "out" array. */ /* ================================================================= */ if( astOK ) { for ( coord_out = 0; coord_out < ndim_out; coord_out++ ) { for ( point = 0; point < npoint; point++ ) { out[ coord_out ][ offset[ point ] ] = ptr_out[ coord_out ][ point ]; } } } /* Annul the PointSet used to hold output coordinates. */ pset_out = astAnnul( pset_out ); /* Free the workspace. */ offset = astFree( offset ); stride = astFree( stride ); } static void TranGridWithBlocking( AstMapping *this, const double *linear_fit, int ndim_in, const int *lbnd_in, const int *ubnd_in, const int *lbnd, const int *ubnd, int ndim_out, double *out[], int *status ){ /* * Name: * TranGridWithBlocking * Purpose: * Transforms positions in a section of a grid in a memory-efficient way. * Type: * Private function. * Synopsis: * #include "mapping.h" * void TranGridWithBlocking( AstMapping *this, const double *linear_fit, * int ndim_in, const int *lbnd_in, * const int *ubnd_in, const int *lbnd, * const int *ubnd, int ndim_out, * double *out[], int *status ) * Class Membership: * Mapping member function. * Description: * This function transforms positions within a specified section of a * rectangular grid (with any number of dimensions) using the forward * transformation of the supplied Mapping. * * This function is very similar to TranGridSection, except that in * order to limit memory usage and to ensure locality of reference, * it divides the input grid up into "blocks" which have a limited * extent along each input dimension. Each block, which will not * contain more than a pre-determined maximum number of pixels, is * then passed to TranGridSection for transformation. * Parameters: * this * Pointer to a Mapping, whose forward transformation may be * used to transform the coordinates of pixels in the input * grid into associated positions in the output grid. * * The number of input coordintes for the Mapping (Nin * attribute) should match the value of "ndim_in" (below), and * the number of output coordinates (Nout attribute) should * match the value of "ndim_out". * linear_fit * Pointer to an optional array of double which contains the * coefficients of a linear fit which approximates the above * Mapping's forward coordinate transformation. If this is * supplied, it will be used in preference to the above Mapping * when transforming coordinates. This may be used to enhance * performance in cases where evaluation of the Mapping's * forward transformation is expensive. If no linear fit is * available, a NULL pointer should be supplied. * * The way in which the fit coefficients are stored in this * array and the number of array elements are as defined by the * astLinearApprox function. * ndim_in * The number of dimensions in the input grid. This should be at * least one. * lbnd_in * Pointer to an array of integers, with "ndim_in" elements. * This should give the coordinates of the centre of the first * pixel in the input grid along each dimension. * ubnd_in * Pointer to an array of integers, with "ndim_in" elements. * This should give the coordinates of the centre of the last * pixel in the input grid along each dimension. * * Note that "lbnd_in" and "ubnd_in" together define the shape * and size of the whole input grid, its extent along a * particular (i'th) dimension being (ubnd_in[i] - lbnd_in[i] + * 1). They also define the input grid's coordinate system, with * each pixel being of unit extent along each dimension with * integral coordinate values at its centre. * lbnd * Pointer to an array of integers, with "ndim_in" elements. * This should give the coordinates of the first pixel in the * section of the input data grid which is to be transformed. * ubnd * Pointer to an array of integers, with "ndim_in" elements. * This should give the coordinates of the last pixel in the * section of the input data grid which is to be transformed. * * Note that "lbnd" and "ubnd" define the shape and position of the * section of the input grid which is to be transformed. * ndim_out * The number of dimensions in the output grid. This should be * at least one. * out * Pointer to an array with "ndim_out" elements. Element [i] of * this array is a pointer to an array in which to store the * transformed values for output axis "i". The points are ordered * such that the first axis of the input grid changes most rapidly. * For example, if the input grid is 2-dimensional and extends from * (2,-1) to (3,1), the output points will be stored in the order * (2,-1), (3, -1), (2,0), (3,0), (2,1), (3,1). * status * Pointer to the inherited status variable. */ /* Local Constants: */ const int mxpix = 2 * 1024; /* Maximum number of pixels in a block (this relatively small number seems to give best performance) */ /* Local Variables: */ int *dim_block; /* Pointer to array of block dimensions */ int *lbnd_block; /* Pointer to block lower bound array */ int *ubnd_block; /* Pointer to block upper bound array */ int dim; /* Dimension size */ int done; /* All blocks rebinned? */ int hilim; /* Upper limit on maximum block dimension */ int idim; /* Loop counter for dimensions */ int lolim; /* Lower limit on maximum block dimension */ int mxdim_block; /* Maximum block dimension */ int npix; /* Number of pixels in block */ /* Check the global error status. */ if ( !astOK ) return; /* Allocate workspace. */ lbnd_block = astMalloc( sizeof( int ) * (size_t) ndim_in ); ubnd_block = astMalloc( sizeof( int ) * (size_t) ndim_in ); dim_block = astMalloc( sizeof( int ) * (size_t) ndim_in ); if ( astOK ) { /* Find the optimum block size. */ /* ---------------------------- */ /* We first need to find the maximum extent which a block of input pixels may have in each dimension. We determine this by taking the input grid extent in each dimension and then limiting the maximum dimension size until the resulting number of pixels is sufficiently small. This approach allows the block shape to approximate (or match) the input grid shape when appropriate. */ /* First loop to calculate the total number of input pixels and the maximum input dimension size. */ npix = 1; mxdim_block = 0; for ( idim = 0; idim < ndim_in; idim++ ) { dim = ubnd[ idim ] - lbnd[ idim ] + 1; npix *= dim; if ( mxdim_block < dim ) mxdim_block = dim; } /* If the number of input pixels is too large for a single block, we perform iterations to determine the optimum upper limit on a block's dimension size. Initialise the limits on this result. */ if ( npix > mxpix ) { lolim = 1; hilim = mxdim_block; /* Loop to perform a binary chop, searching for the best result until the lower and upper limits on the result converge to adjacent values. */ while ( ( hilim - lolim ) > 1 ) { /* Form a new estimate from the mid-point of the previous limits. */ mxdim_block = ( hilim + lolim ) / 2; /* See how many pixels a block contains if its maximum dimension is limited to this new value. */ for ( npix = 1, idim = 0; idim < ndim_in; idim++ ) { dim = ubnd[ idim ] - lbnd[ idim ] + 1; npix *= ( dim < mxdim_block ) ? dim : mxdim_block; } /* Update the appropriate limit, according to whether the number of pixels is too large or too small. */ *( ( npix <= mxpix ) ? &lolim : &hilim ) = mxdim_block; } /* When iterations have converged, obtain the maximum limit on the dimension size of a block which results in no more than the maximum allowed number of pixels per block. However, ensure that all block dimensions are at least 2. */ mxdim_block = lolim; } if ( mxdim_block < 2 ) mxdim_block = 2; /* Calculate the block dimensions by applying this limit to the output grid dimensions. */ for ( idim = 0; idim < ndim_in; idim++ ) { dim = ubnd[ idim ] - lbnd[ idim ] + 1; dim_block[ idim ] = ( dim < mxdim_block ) ? dim : mxdim_block; /* Also initialise the lower and upper bounds of the first block of output grid pixels to be rebinned, ensuring that this does not extend outside the grid itself. */ lbnd_block[ idim ] = lbnd[ idim ]; ubnd_block[ idim ] = MinI( lbnd[ idim ] + dim_block[ idim ] - 1, ubnd[ idim ], status ); } /* Transform each block of input grid positions. */ /* --------------------------------------------- */ /* Loop to generate the extent of each block of input grid positions and to transform them. */ done = 0; while ( !done && astOK ) { /* Rebin the current block, accumulating the sum of bad pixels produced. */ TranGridSection( this, linear_fit, ndim_in, lbnd_in, ubnd_in, lbnd_block, ubnd_block, ndim_out, out, status ); /* Update the block extent to identify the next block of input pixels. */ idim = 0; do { /* We find the least significant dimension where the upper bound of the block has not yet reached the upper bound of the region of the input grid which we are rebinning. The block's position is then incremented by one block extent along this dimension, checking that the resulting extent does not go outside the region being rebinned. */ if ( ubnd_block[ idim ] < ubnd[ idim ] ) { lbnd_block[ idim ] = MinI( lbnd_block[ idim ] + dim_block[ idim ], ubnd[ idim ], status ); ubnd_block[ idim ] = MinI( lbnd_block[ idim ] + dim_block[ idim ] - 1, ubnd[ idim ], status ); break; /* If any less significant dimensions are found where the upper bound of the block has reached its maximum value, we reset the block to its lowest position. */ } else { lbnd_block[ idim ] = lbnd[ idim ]; ubnd_block[ idim ] = MinI( lbnd[ idim ] + dim_block[ idim ] - 1, ubnd[ idim ], status ); /* All the blocks have been processed once the position along the most significant dimension has been reset. */ done = ( ++idim == ndim_in ); } } while ( !done ); } } /* Free the workspace. */ lbnd_block = astFree( lbnd_block ); ubnd_block = astFree( ubnd_block ); dim_block = astFree( dim_block ); } static void TranN( AstMapping *this, int npoint, int ncoord_in, int indim, const double *in, int forward, int ncoord_out, int outdim, double *out, int *status ) { /* *++ * Name: c astTranN f AST_TRANN * Purpose: * Transform N-dimensional coordinates. * Type: * Public virtual function. * Synopsis: c #include "mapping.h" c void astTranN( AstMapping *this, int npoint, c int ncoord_in, int indim, const double *in, c int forward, c int ncoord_out, int outdim, double *out ) f CALL AST_TRANN( THIS, NPOINT, f NCOORD_IN, INDIM, IN, f FORWARD, NCOORD_OUT, OUTDIM, OUT, STATUS ) * Class Membership: * Mapping method. * Description: c This function applies a Mapping to transform the coordinates of f This routine applies a Mapping to transform the coordinates of * a set of points in an arbitrary number of dimensions. It is the * appropriate routine to use if the coordinates are not purely 1- * or 2-dimensional and are stored in a single array (which they * need not fill completely). c c If the coordinates are not stored in a single array, then the c astTranP function might be more suitable. * Parameters: c this f THIS = INTEGER (Given) * Pointer to the Mapping to be applied. c npoint f NPOINT = INTEGER (Given) * The number of points to be transformed. c ncoord_in f NCOORD_IN = INTEGER (Given) * The number of coordinates being supplied for each input point * (i.e. the number of dimensions of the space in which the * input points reside). c indim f INDIM = INTEGER (Given) c The number of elements along the second dimension of the "in" f The number of elements along the first dimension of the IN * array (which contains the input coordinates). This value is * required so that the coordinate values can be correctly * located if they do not entirely fill this array. The value c given should not be less than "npoint". f given should not be less than NPOINT. c in f IN( INDIM, NCOORD_IN ) = DOUBLE PRECISION (Given) c The address of the first element in a 2-dimensional array of c shape "[ncoord_in][indim]", c containing the coordinates of the input (untransformed) c points. These should be stored such that the value of c coordinate number "coord" for input point number "point" is c found in element "in[coord][point]". f An array containing the coordinates of the input f (untransformed) points. These should be stored such that the f value of coordinate number COORD for input point number POINT f is found in element IN(POINT,COORD). c forward f FORWARD = LOGICAL (Given) c A non-zero value indicates that the Mapping's forward c coordinate transformation is to be applied, while a zero c value indicates that the inverse transformation should be c used. f A .TRUE. value indicates that the Mapping's forward f coordinate transformation is to be applied, while a .FALSE. f value indicates that the inverse transformation should be f used. c ncoord_out f NCOORD_OUT = INTEGER (Given) * The number of coordinates being generated by the Mapping for * each output point (i.e. the number of dimensions of the * space in which the output points reside). This need not be c the same as "ncoord_in". f the same as NCOORD_IN. c outdim f OUTDIM = INTEGER (Given) c The number of elements along the second dimension of the "out" f The number of elements along the first dimension of the OUT * array (which will contain the output coordinates). This value * is required so that the coordinate values can be correctly * located if they will not entirely fill this array. The value c given should not be less than "npoint". f given should not be less than NPOINT. c out f OUT( OUTDIM, NCOORD_OUT ) = DOUBLE PRECISION (Returned) c The address of the first element in a 2-dimensional array of c shape "[ncoord_out][outdim]", into c which the coordinates of the output (transformed) points will c be written. These will be stored such that the value of c coordinate number "coord" for output point number "point" c will be found in element "out[coord][point]". f An array into which the coordinates of the output f (transformed) points will be written. These will be stored f such that the value of coordinate number COORD for output f point number POINT will be found in element OUT(POINT,COORD). f STATUS = INTEGER (Given and Returned) f The global status. * Notes: c - If the forward coordinate transformation is being applied, the c Mapping supplied must have the value of "ncoord_in" for its Nin c attribute and the value of "ncoord_out" for its Nout attribute. If c the inverse transformation is being applied, these values should c be reversed. f - If the forward coordinate transformation is being applied, the f Mapping supplied must have the value of NCOORD_IN for its Nin f attribute and the value of NCOORD_OUT for its Nout attribute. If f the inverse transformation is being applied, these values should f be reversed. *-- */ /* Local Variables: */ AstPointSet *in_points; /* Pointer to input PointSet */ AstPointSet *out_points; /* Pointer to output PointSet */ const double **in_ptr; /* Pointer to array of input data pointers */ double **out_ptr; /* Pointer to array of output data pointers */ int coord; /* Loop counter for coordinates */ /* Check the global error status. */ if ( !astOK ) return; /* Validate the mapping and numbers of points/coordinates. */ ValidateMapping( this, forward, npoint, ncoord_in, ncoord_out, "astTranN", status ); /* Also validate the input array dimension argument. */ if ( astOK && ( indim < npoint ) ) { astError( AST__DIMIN, "astTranN(%s): The input array dimension value " "(%d) is invalid.", status, astGetClass( this ), indim ); astError( AST__DIMIN, "This should not be less than the number of " "points being transformed (%d).", status, npoint ); } /* Similarly, validate the output array dimension argument. */ if ( astOK && ( outdim < npoint ) ) { astError( AST__DIMIN, "astTranN(%s): The output array dimension value " "(%d) is invalid.", status, astGetClass( this ), outdim ); astError( AST__DIMIN, "This should not be less than the number of " "points being transformed (%d).", status, npoint ); } /* Allocate memory to hold the arrays of input and output data pointers. */ if ( astOK ) { in_ptr = (const double **) astMalloc( sizeof( const double * ) * (size_t) ncoord_in ); out_ptr = astMalloc( sizeof( double * ) * (size_t) ncoord_out ); #ifdef DEBUG { int i, ns; ns = ncoord_out*outdim; for( i = 0; i < ns; i++ ) out[ i ] = 0.0; } #endif /* Initialise the input data pointers to locate the coordinate data in the "in" array. */ if ( astOK ) { for ( coord = 0; coord < ncoord_in; coord++ ) { in_ptr[ coord ] = in + coord * indim; } /* Similarly initialise the output data pointers to point into the "out" array. */ for ( coord = 0; coord < ncoord_out; coord++ ) { out_ptr[ coord ] = out + coord * outdim; } /* Create PointSets to describe the input and output points. */ in_points = astPointSet( npoint, ncoord_in, "", status ); out_points = astPointSet( npoint, ncoord_out, "", status ); /* Associate the data pointers with the PointSets (note we must explicitly remove the "const" qualifier from the input data here, although they will not be modified). */ astSetPoints( in_points, (double **) in_ptr ); astSetPoints( out_points, out_ptr ); /* Apply the required transformation to the coordinates. */ (void) astTransform( this, in_points, forward, out_points ); /* If the Mapping's Report attribute is set, report the effect the Mapping has had on the coordinates. */ if ( astGetReport( this ) ) astReportPoints( this, forward, in_points, out_points ); /* Delete the two PointSets. */ in_points = astDelete( in_points ); out_points = astDelete( out_points ); } /* Free the memory used for the data pointers. */ in_ptr = (const double **) astFree( (void *) in_ptr ); out_ptr = astFree( out_ptr ); } } static void TranP( AstMapping *this, int npoint, int ncoord_in, const double *ptr_in[], int forward, int ncoord_out, double *ptr_out[], int *status ) { /* c++ * Name: * astTranP * Purpose: * Transform N-dimensional coordinates held in separate arrays. * Type: * Public virtual function. * Synopsis: * #include "mapping.h" * void astTranP( AstMapping *this, int npoint, * int ncoord_in, const double *ptr_in[], * int forward, int ncoord_out, double *ptr_out[] ) * Class Membership: * Mapping method. * Description: * This function applies a Mapping to transform the coordinates of * a set of points in an arbitrary number of dimensions. It is the * appropriate routine to use if the coordinates are not purely 1- * or 2-dimensional and are stored in separate arrays, since each * coordinate array is located by supplying a separate pointer to * it. * * If the coordinates are stored in a single (2-dimensional) array, * then the astTranN function might be more suitable. * Parameters: * this * Pointer to the Mapping to be applied. * npoint * The number of points to be transformed. * ncoord_in * The number of coordinates being supplied for each input point * (i.e. the number of dimensions of the space in which the * input points reside). * ptr_in * An array of pointers to double, with "ncoord_in" * elements. Element "ptr_in[coord]" should point at the first * element of an array of double (with "npoint" elements) which * contain the values of coordinate number "coord" for each * input (untransformed) point. The value of coordinate number * "coord" for input point number "point" is therefore given by * "ptr_in[coord][point]" (assuming both indices are * zero-based). * forward * A non-zero value indicates that the Mapping's forward * coordinate transformation is to be applied, while a zero * value indicates that the inverse transformation should be * used. * ncoord_out * The number of coordinates being generated by the Mapping for * each output point (i.e. the number of dimensions of the space * in which the output points reside). This need not be the same * as "ncoord_in". * ptr_out * An array of pointers to double, with "ncoord_out" * elements. Element "ptr_out[coord]" should point at the first * element of an array of double (with "npoint" elements) into * which the values of coordinate number "coord" for each output * (transformed) point will be written. The value of coordinate * number "coord" for output point number "point" will therefore * be found in "ptr_out[coord][point]". * Notes: * - If the forward coordinate transformation is being applied, the * Mapping supplied must have the value of "ncoord_in" for its Nin * attribute and the value of "ncoord_out" for its Nout * attribute. If the inverse transformation is being applied, these * values should be reversed. * - This routine is not available in the Fortran 77 interface to * the AST library. c-- */ /* Local Variables: */ AstPointSet *in_points; /* Pointer to input PointSet */ AstPointSet *out_points; /* Pointer to output PointSet */ /* Check the global error status. */ if ( !astOK ) return; /* Validate the Mapping and number of points/coordinates. */ ValidateMapping( this, forward, npoint, ncoord_in, ncoord_out, "astTranP", status ); /* Create PointSets to describe the input and output points. */ if ( astOK ) { in_points = astPointSet( npoint, ncoord_in, "", status ); out_points = astPointSet( npoint, ncoord_out, "", status ); /* Associate the data pointers with the PointSets (note we must explicitly remove the "const" qualifier from the input data here, although they will not be modified). */ astSetPoints( in_points, (double **) ptr_in ); astSetPoints( out_points, ptr_out ); /* Apply the required transformation to the coordinates. */ (void) astTransform( this, in_points, forward, out_points ); /* If the Mapping's Report attribute is set, report the effect the Mapping has had on the coordinates. */ if ( astGetReport( this ) ) astReportPoints( this, forward, in_points, out_points ); /* Delete the two PointSets. */ in_points = astDelete( in_points ); out_points = astDelete( out_points ); } } static AstPointSet *Transform( AstMapping *this, AstPointSet *in, int forward, AstPointSet *out, int *status ) { /* *+ * Name: * astTransform * Purpose: * Transform a set of points. * Type: * Protected virtual function. * Synopsis: * #include "mapping.h" * AstPointSet *astTransform( AstMapping *this, AstPointSet *in, * int forward, AstPointSet *out ) * Class Membership: * Mapping method. * Description: * This function takes a Mapping and a set of points encapsulated * in a PointSet, and applies either the forward or inverse * coordinate transformation (if defined by the Mapping) to the * points. * Parameters: * this * Pointer to the Mapping. The nature of the coordinate * transformation will depend on the class of Mapping * supplied. Note that there is no constructor for the Mapping * class itself, so this object should be from a derived class. * in * Pointer to the PointSet holding the input coordinate data. * forward * A non-zero value indicates that the forward coordinate * transformation should be applied, while a zero value requests * the inverse transformation. * out * Pointer to a PointSet which will hold the transformed * (output) coordinate values. A NULL value may also be given, * in which case a new PointSet will be created by this * function. * Returned Value: * Pointer to the output (possibly new) PointSet. * Notes: * - An error will result if the Mapping supplied does not define * the requested coordinate transformation (either forward or * inverse). * - The number of coordinate values per point in the input * PointSet must match the number of input coordinates for the * Mapping being applied (or number of output coordinates if the * inverse transformation is requested). * - If an output PointSet is supplied, it must have space for * sufficient number of points and coordinate values per point to * accommodate the result (e.g. the number of Mapping output * coordinates, or number of input coordinates if the inverse * transformation is requested). Any excess space will be ignored. * - 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: */ AstPointSet *result; /* Pointer to output PointSet */ int def; /* Coordinate transformation defined? */ int ncoord_in; /* Number of input PointSet coordinates */ int ncoord_out; /* Number of coordinates in output PointSet */ int nin; /* Number of input Mapping coordinates */ int nout; /* Number of output Mapping coordinates */ int npoint; /* Number of points to transform */ int npoint_out; /* Number of points in output PointSet */ /* Check the global error status. */ if ( !astOK ) return NULL; /* Initialise. */ result = NULL; /* Determine if a coordinate transformation is defined for the requested direction. */ def = forward ? astGetTranForward( this ) : astGetTranInverse( this ); /* Report an error if the transformation is not defined. */ if ( astOK && !def ) { astError( AST__TRNND, "astTransform(%s): %s coordinate transformation " "is not defined by the %s supplied.", status, astGetClass( this ), forward ? "A forward" : "An inverse", astGetClass( this ) ); } /* Obtain the effective number of input and output coordinate values for the transformation to be performed, taking account of the transformation direction required. Note we use Mapping methods to obtain these values, as this will take account of whether the Mapping has been inverted. */ nin = forward ? astGetNin( this ) : astGetNout( this ); nout = forward ? astGetNout( this ) : astGetNin( this ); /* Obtain the number of input points to transform and the number of coordinate values per input point. */ npoint = astGetNpoint( in ); ncoord_in = astGetNcoord( in ); /* If OK, check that the number of input coordinates matches the number required by the mapping. Report an error if these numbers do not match. */ if ( astOK && ( ncoord_in != nin ) ) { astError( AST__NCPIN, "astTransform(%s): Bad number of coordinate " "values (%d) in input %s.", status, astGetClass( this ), ncoord_in, astGetClass( in ) ); astError( AST__NCPIN, "The %s given requires %d coordinate value(s) for " "each input point.", status, astGetClass( this ), nin ); } /* If still OK, and a non-NULL pointer has been given for the output PointSet, then obtain the number of points and number of coordinates per point for this PointSet. */ if ( astOK && out ) { npoint_out = astGetNpoint( out ); ncoord_out = astGetNcoord( out ); /* Check that the dimensions of this PointSet are adequate to accommodate the output coordinate values and report an error if they are not. */ if ( astOK ) { if ( npoint_out < npoint ) { astError( AST__NOPTS, "astTransform(%s): Too few points (%d) in " "output %s.", status, astGetClass( this ), npoint_out, astGetClass( out ) ); astError( AST__NOPTS, "The %s needs space to hold %d transformed " "point(s).", status, astGetClass( this ), npoint ); } else if ( ncoord_out < nout ) { astError( AST__NOCTS, "astTransform(%s): Too few coordinate " "values per point (%d) in output %s.", status, astGetClass( this ), ncoord_out, astGetClass( out ) ); astError( AST__NOCTS, "The %s supplied needs space to store %d " "coordinate value(s) per transformed point.", status, astGetClass( this ), nout ); } } } /* If all the validation stages are passed successfully, and a NULL output pointer was given, then create a new PointSet to encapsulate the output coordinate data. */ if ( astOK ) { if ( !out ) { result = astPointSet( npoint, nout, "", status ); /* Otherwise, use the PointSet supplied. */ } else { result = out; } } /* Return a pointer to the output PointSet. Note that we do not actually transform (or even copy) the coordinates. This is left for derived classes to implement. */ return result; } /* *++ * Name: c astUinterp f AST_UINTERP * Purpose: * Perform sub-pixel interpolation on a grid of data. * Type: * Fictitious function. * Synopsis: c #include "mapping.h" c void astUinterp( int ndim_in, const int lbnd_in[], const int ubnd_in[], c const in[], const in_var[], c int npoint, const int offset[], c const double *const coords[], const double params[], c int flags, badval, c out[], out_var[], int *nbad ) f CALL AST_UINTERP( NDIM_IN, LBND_IN, UBND_IN, IN, IN_VAR, f NPOINT, OFFSET, COORDS, PARAMS, FLAGS, BADVAL, f OUT, OUT_VAR, NBAD, STATUS ) * Class Membership: * Mapping member function. * Description: c This is a fictitious function which does not actually c exist. Instead, this description constitutes a template so that c you may implement a function with this interface for yourself c (and give it any name you wish). A pointer to such a function c may be passed via the "finterp" parameter of the astResample c functions (q.v.) in order to perform sub-pixel interpolation c during resampling of gridded data (you must also set the c "interp" parameter of astResample to the value c AST__UINTERP). This allows you to use your own interpolation c algorithm in addition to those which are pre-defined. f This is a fictitious routine which does not actually f exist. Instead, this description constitutes a template so that f you may implement a routine with this interface for yourself f (and give it any name you wish). Such a routine f may be passed via the FINTERP argument of the AST_RESAMPLE f functions (q.v.) in order to perform sub-pixel interpolation f during resampling of gridded data (you must also set the f INTERP argument of AST_RESAMPLE to the value f AST__UINTERP). This allows you to use your own interpolation f algorithm in addition to those which are pre-defined. * c The function interpolates an input grid of data (and, f The routine interpolates an input grid of data (and, * optionally, processes associated statistical variance estimates) * at a specified set of points. * Parameters: c ndim_in f NDIM_IN = INTEGER (Given) * The number of dimensions in the input grid. This will be at * least one. c lbnd_in f LBND_IN( NDIM_IN ) = INTEGER (Given) c Pointer to an array of integers, with "ndim_in" elements, f An array * containing the coordinates of the centre of the first pixel * in the input grid along each dimension. c ubnd_in f UBND_IN( NDIM_IN ) = INTEGER (Given) c Pointer to an array of integers, with "ndim_in" elements, f An array * containing the coordinates of the centre of the last pixel in * the input grid along each dimension. * c Note that "lbnd_in" and "ubnd_in" together define the shape, f Note that LBND_IN and UBND_IN together define the shape, * size and coordinate system of the input grid in the same c way as they do in astResample. f way as they do in AST_RESAMPLE. c in f IN( * ) = (Given) c Pointer to an array, with one element for each pixel in the f An array, with one element for each pixel in the * input grid, containing the input data. This will be the same c array as was passed to astResample via the "in" parameter. f array as was passed to AST_RESAMPLE via the IN argument. * The numerical type of this array should match that of the * data being processed. c in_var f IN_VAR( * ) = (Given) c Pointer to an optional second array with the same size and c type as the "in" array. If given, this will contain the set c of variance values associated with the input data and will be c the same array as was passed to astResample via the c "in_var" parameter. f An optional second array with the same size and type as the f IN array. This will only be given if the AST__USEVAR flag is f set via the FLAGS argument (below). If given, it will contain f the set of variance values associated with the input data and f will be the same array as was passed to AST_RESAMPLE via f the IN_VAR argument. * c If no variance values are being processed, this will be a c NULL pointer. f If the AST__USEVAR flag is not set, then no variance values f are being processed. In this case, this array of variance f values may be a dummy (e.g. one-element) array and should not f be used. c npoint f NPOINT = INTEGER (Given) * The number of points at which the input grid is to be * interpolated. This will be at least one. c offset f OFFSET( NPOINT ) = INTEGER (Given) c Pointer to an array of integers with "npoint" elements. For c each interpolation point, this will contain the zero-based c index in the "out" (and "out_var") array(s) at which the c interpolated value (and its variance, if required) should be c stored. For example, the interpolated value for point number c "point" should be stored in "out[offset[point]]" (assuming c the index "point" is zero-based). f For each interpolation point, this array will contain the f offset from the start of the OUT (and OUT_VAR) array(s) at f which the interpolated value (and its variance, if required) f should be stored. For example, the interpolated value for f point number POINT should be stored in OUT(1+OFFSET(POINT)). c coords f COORDS( NPOINT, NDIM_IN ) = DOUBLE PRECISION (Given) c An array of pointers to double, with "ndim_in" c elements. Element "coords[coord]" will point at the first c element of an array of double (with "npoint" elements) which c contains the values of coordinate number "coord" for each c interpolation point. The value of coordinate number "coord" c for interpolation point number "point" is therefore given by c "coords[coord][point]" (assuming both indices are c zero-based). f A 2-dimensional array containing the coordinates of the f points at which interpolation should be performed. These will f be stored so that coordinate number COORD for interpolation f point number POINT is found in element COORDS(POINT,COORD). * * If any interpolation point has any of its coordinates equal c to the value AST__BAD (as defined in the "ast.h" header f to the value AST__BAD (as defined in the AST_PAR include * file), then the corresponding output data (and variance) c should either be set to the value given by "badval", f should either be set to the value given by BADVAL, * or left unchanged, depending on whether the AST__NOBAD flag is c specified by "flags". f specified by FLAGS. c params f PARAMS( * ) = DOUBLE PRECISION (Given) c This will be a pointer to the same array as was given via the c "params" parameter of astResample. You may use this to f This will be the same array as was given via the f PARAMS argument of AST_RESAMPLE. You may use this to * pass any additional parameter values required by your * interpolation algorithm. c flags f FLAGS = INTEGER (Given) c This will be the same value as was given via the "flags" c parameter of astResample. You may test this value to f This will be the same value as was given via the FLAGS f argument of AST_RESAMPLE. You may test this value to * provide additional control over the operation of your * resampling algorithm. Note that the special flag values * AST__URESAMP1, 2, 3 & 4 are reserved for you to use for your * own purposes and will not clash with other pre-defined flag c values (see astResample). f values (see AST_RESAMPLE). c badval f BADVAL = (Given) c This will be the same value as was given via the "badval" c parameter of astResample, and will have the same numerical c type as the data being processed (i.e. as elements of the c "in" array). It should be used to test for bad pixels in the c input grid (but only if the AST__USEBAD flag is set via the c "flags" parameter) and (unless the AST__NOBAD flag is set in c "flags") for identifying bad output values in c the "out" (and "out_var") array(s). f This will be the same value as was given for the BADVAL f argument of AST_RESAMPLE, and will have the same numerical f type as the data being processed (i.e. as elements of the IN f array). It should be used to test for bad pixels in the f input grid (but only if the AST__USEBAD flag is set via the f FLAGS argument) and (unless the AST__NOBAD flag is set in f FLAGS) for identifying bad output values in the OUT (and f OUT_VAR) array(s). c out f OUT( * ) = (Returned) c Pointer to an array with the same numerical type as the "in" f An array with the same numerical type as the IN * array, into which the interpolated data values should be * returned. Note that details of the storage order and number * of dimensions of this array are not required, since the c "offset" array contains all necessary information about where f OFFSET array contains all necessary information about where * each returned value should be stored. * c In general, not all elements of this array (or the "out_var" f In general, not all elements of this array (or the OUT_VAR * array below) may be used in any particular invocation of the c function. Those which are not used should be returned f routine. Those which are not used should be returned * unchanged. c out_var f OUT_VAR( * ) = (Returned) c Pointer to an optional array with the same type and size as c the "out" array, into which variance estimates for the c resampled values should be returned. This array will only be c given if the "in_var" array has also been given. f An optional array with the same type and size as the OUT f array, into which variance estimates for the resampled values f should be returned. This array will only be given if the f AST__USEVAR flag is set via the FLAGS argument. * c If given, it is addressed in exactly the same way (via the c "offset" array) as the "out" array. The values returned c should be estimates of the statistical variance of the c corresponding values in the "out" array, on the assumption c that all errors in input data values are statistically c independent and that their variance estimates may simply be c summed (with appropriate weighting factors). f If given, it is addressed in exactly the same way (via the f OFFSET array) as the OUT array. The values returned should be f estimates of the statistical variance of the corresponding f values in the OUT array, on the assumption that all errors in f input data values are statistically independent and that f their variance estimates may simply be summed (with f appropriate weighting factors). * c If no output variance estimates are required, a NULL pointer c will be given. f If the AST__USEVAR flag is not set, then variance values are f not being processed. In this case, this array may be a dummy f (e.g. one-element) array and should not be used. c nbad f NBAD = INTEGER (Returned) c Pointer to an int in which to return the number of interpolation c points at f This should return the number of interpolation points at * which no valid interpolated value could be obtained. The maximum c value that should be returned is "npoint", and the minimum is f value that should be returned is NPOINT, and the minimum is * zero (indicating that all output values were successfully * obtained). f STATUS = INTEGER (Given and Returned) f The global status. * Notes: * - The data type indicates the numerical type of the data c being processed, as for astResample. f being processed, as for AST_RESAMPLE. c - This function will typically be invoked more than once for each c invocation of astResample. f - This routine will typically be invoked more than once for each f invocation of AST_RESAMPLE. c - If an error occurs within this function, it should use c astSetStatus to set the AST error status to an error value. c This will cause an immediate return from astResample. The error c value AST__UINER is available for this purpose, but other values may c also be used (e.g. if you wish to distinguish different types of c error). f - If an error occurs within this routine, it should set the f STATUS argument to an error value before returning. This will f cause an immediate return from AST_RESAMPLE. The error value f AST__UINER is available for this purpose, but other values may also f be used (e.g. if you wish to distinguish different types of error). f The AST__UINER error value is defined in the AST_ERR include file. *-- */ /* Note the above is just a description to act as a template. The function does not actually exist. */ /* *++ * Name: c astUkern1 f AST_UKERN1 * Purpose: * 1-dimensional sub-pixel interpolation kernel. * Type: * Fictitious function. * Synopsis: c #include "mapping.h" c void astUkern1( double offset, const double params[], int flags, c double *value ) f CALL AST_UKERN1( OFFSET, PARAMS, FLAGS, VALUE, STATUS ) * Class Membership: * Mapping member function. * Description: c This is a fictitious function which does not actually c exist. Instead, this description constitutes a template so that c you may implement a function with this interface for yourself c (and give it any name you wish). A pointer to such a function c may be passed via the "finterp" parameter of the astResample c functions (q.v.) in order to supply a 1-dimensional c interpolation kernel to the algorithm which performs sub-pixel c interpolation during resampling of gridded data (you must also c set the "interp" parameter of astResample to the value c AST__UKERN1). This allows you to use your own interpolation c kernel in addition to those which are pre-defined. f This is a fictitious routine which does not actually f exist. Instead, this description constitutes a template so that f you may implement a routine with this interface for yourself f (and give it any name you wish). Such a routine f may be passed via the FINTERP argument of the AST_RESAMPLE f functions (q.v.) in order to supply a 1-dimensional f interpolation kernel to the algorithm which performs sub-pixel f interpolation during resampling of gridded data (you must also f set the INTERP argument of AST_RESAMPLE to the value f AST__UKERN1). This allows you to use your own interpolation f kernel in addition to those which are pre-defined. * c The function calculates the value of a 1-dimensional sub-pixel f The routine calculates the value of a 1-dimensional sub-pixel * interpolation kernel. This determines how the weight given to * neighbouring pixels in calculating an interpolated value depends * on the pixel's offset from the interpolation point. In more than * one dimension, the weight assigned to a pixel is formed by * evaluating this 1-dimensional kernel using the offset along each * dimension in turn. The product of the returned values is then * used as the pixel weight. * Parameters: c offset f OFFSET = DOUBLE PRECISION (Given) * This will be the offset of the pixel from the interpolation * point, measured in pixels. This value may be positive or * negative, but for most practical interpolation schemes its * sign should be ignored. c params f PARAMS( * ) = DOUBLE PRECISION (Given) c This will be a pointer to the same array as was given via the c "params" parameter of astResample. You may use this to f This will be the same array as was given via the f PARAMS argument of AST_RESAMPLE. You may use this to * pass any additional parameter values required by your kernel, c but note that "params[0]" will already have been used to specify f but note that PARAMS(1) will already have been used to specify * the number of neighbouring pixels which contribute to the * interpolated value. c flags f FLAGS = INTEGER (Given) c This will be the same value as was given via the "flags" c parameter of astResample. You may test this value to f This will be the same value as was given via the FLAGS f argument of AST_RESAMPLE. You may test this value to * provide additional control over the operation of your c function. Note that the special flag values AST__URESAMP1, 2, f routine. Note that the special flag values AST__URESAMP1, 2, * 3 & 4 are reserved for you to use for your own purposes and * will not clash with other pre-defined flag c values (see astResample). f values (see AST_RESAMPLE). c value f VALUE = DOUBLE PRECISION (Returned) c Pointer to a double to receive the calculated kernel value, f The calculated kernel value, * which may be positive or negative. f STATUS = INTEGER (Given and Returned) f The global status. * Notes: * - Not all functions make good interpolation kernels. In general, * acceptable kernels tend to be symmetrical about zero, to have a * positive peak (usually unity) at zero, and to evaluate to zero * whenever the pixel offset has any other integral value (this * ensures that the interpolated values pass through the original * data). An interpolation kernel may or may not have regions with * negative values. You should consult a good book on image * processing for more details. c - If an error occurs within this function, it should use c astSetStatus to set the AST error status to an error value. c This will cause an immediate return from astResample. The error c value AST__UK1ER is available for this purpose, but other values may c also be used (e.g. if you wish to distinguish different types of c error). f - If an error occurs within this routine, it should set the f STATUS argument to an error value before returning. This will f cause an immediate return from AST_RESAMPLE. The error value f AST__UK1ER is available for this purpose, but other values may also f be used (e.g. if you wish to distinguish different types of error). f The AST__UK1ER error value is defined in the AST_ERR include file. *-- */ /* Note the above is just a description to act as a template. The function does not actually exist. */ static double UphillSimplex( const MapData *mapdata, double acc, int maxcall, const double dx[], double xmax[], double *err, int *ncall, int *status ) { /* * Name: * UphillSimplex * Purpose: * Find a function maximum using a modification of the simplex method. * Type: * Private function. * Synopsis: * #include "mapping.h" * double UphillSimplex( const MapData *mapdata, double acc, int maxcall, * const double dx[], double xmax[], double *err, * int *ncall, int *status ); * Class Membership: * Mapping member function. * Description: * This function applies a modification of the simplex method to * find a local maximum in the value returned by a Mapping * function. The modification used allows the method to cope with * coordinate constraints and (equivalently) regions where the * function returns "bad" values. The method is robust and not * susceptible to overflow, so is suitable for applying to Mapping * functions of unknown form. * Parameters: * mapdata * Pointer to a MapData structure which describes the Mapping * function, its coordinate constraints, etc. * acc * The accuracy required in the value of the maximum. * maxcall * The maximum number of Mapping function evaluations to use. * dx * Pointer to an array of double containing an offset along each * input coordinate for the Mapping function supplied. These * offsets will be used to construct the initial simplex * (i.e. they are the initial "step lengths" for each * coordinate) and may be positive or negative. * xmax * Pointer to an array of double which contains the coordinates * of an initial estimate of the location of the maximum. On * exit, this will be updated to contain the best estimate of * the location of the maximum as generated by this function. * err * Pointer to a double in which to return an estimate of the * error in the value of the maximum found. For normal * convergence, this should be no larger than "acc". However, if * the maximum number of Mapping function evaluations is * reached, the returned value may be larger than this, although * it should still be valid. In such cases, re-starting the * algorithm at the new location returned in "xmax" may be * advisable. * ncall * Pointer to an int in which the number of Mapping function * evaluations will be returned. * status * Pointer to the inherited status variable. * Returned Value: * An estimate of the Mapping function value at the local maximum. * Notes: * - The function may return before the requested accuracy has been * met and before all Mapping function evaluations have been * made. This signifies that an excessive number of function values * have been needed outside the coordinate constraints. This is * only likely if the function is unable to make progress near such * a constraint, in which case the algorithm should probably be * re-started. * - A value of AST__BAD will be returned if no maximum could be * found. This means that all the Mapping function evaluations * performed returned a value of AST__BAD. * - A value of AST__BAD will also be returned and no useful * information about a solution will be produced if this routine is * invoked with the global error status set, or if it should fail * for any reason. */ /* Local Constants: */ const double factor = 3.0; /* Simplex contraction/expansion factor */ /* Local Variables: */ double *f; /* Pointer to array of function values */ double *x; /* Pointer to array of vertex coordinates */ double *xnew; /* Pointer to workspace array */ double fnew; /* New function value */ double fsave; /* Saved function value */ double offset; /* Coordinate difference between vertices */ double range; /* Range of simplex values */ double result; /* Value to return */ double tmp; /* Temporary store for coordinate */ int coord; /* Loop counter for coordinates */ int hi; /* Index of best vertex */ int lo; /* Index of worst vertex */ int ncalla; /* Number of function calls attempted */ int ncoord; /* Number of function dimensions */ int nextlo; /* Index of second worst vertex */ int nvertex; /* Number of simplex vertices */ int vertex; /* Loop counter for vertices */ /* Initialise. */ result = AST__BAD; /* Check the global error status. */ if ( !astOK ) return result; /* Further initialisation. */ *err = DBL_MAX; *ncall = 0; /* Obtain the number of input coordinates for the Mapping function and calculate the number of simplex vertices. */ ncoord = mapdata->nin; nvertex = ncoord + 1; /* Allocate workspace. */ f = astMalloc( sizeof( double ) * (size_t) nvertex ); x = astMalloc( sizeof( double ) * (size_t) ( ncoord * nvertex ) ); xnew = astMalloc( sizeof( double ) * (size_t) ncoord ); if ( astOK ) { /* Loop to set up an initial simplex. */ for ( vertex = 0; vertex < nvertex; vertex++ ) { for ( coord = 0; coord < ncoord; coord++ ) { tmp = xmax[ coord ]; /* Displace each point (except the first) the required amount along one of the axes to generate the coordinates of the simplex vertices. */ if ( coord == ( vertex - 1 ) ) tmp += dx[ coord ]; x[ vertex * ncoord + coord ] = tmp; } /* Evaluate the Mapping function at each vertex. */ f[ vertex ] = MapFunction( mapdata, &x[ vertex * ncoord ], ncall, status ); if ( f[ vertex ] == AST__BAD ) f[ vertex ] = -DBL_MAX; } /* Initialise the number of times we attempt to call the Mapping function (not necessarily the same as the number of times it was actually called, which is stored in *ncall). */ ncalla = nvertex; /* Loop until convergence is reached or an error occurs. */ while( astOK ) { /* Initialise the index of the lowest vertex of the simplex, the next lowest vertex and the highest vertex. */ lo = ( f[ 0 ] < f[ 1 ] ) ? 0 : 1; nextlo = 1 - lo; hi = 0; /* Loop to inspect each vertex and update these values. Ensure that in the case of equal vertices, the first one is taken to be the highest. This makes the maximisation stable (so that if no better maximum can be found, the original position is returned rather than a nearby position that yields the same function value). */ for ( vertex = 0; vertex < nvertex; vertex++ ) { if ( f[ vertex ] <= f[ lo ] ) { nextlo = lo; lo = vertex; } else if ( ( f[ vertex ] <= f[ nextlo ] ) && ( vertex != lo ) ) { nextlo = vertex; } if ( f[ vertex ] > f[ hi ] ) hi = vertex; } /* Estimate the error on the result as the difference between the highest and lowest simplex vertices. */ if ( ( f[ hi ] == -DBL_MAX ) || ( f[ lo ] == -DBL_MAX ) ) { range = DBL_MAX; } else { range = f[ hi ] - f[ lo ]; } /* Test for convergence. Ideally, the accuracy criterion should have been met. However, also quit if the maximum number of Mapping function evaluations has been reached, or the number of points at which function values have been requested reaches three times this limit (this latter number will typically be larger because points lying outside the coordinate constraints do not result in the Mapping function being evaluated). */ if ( range <= fabs( acc ) || ( *ncall >= maxcall ) || ( ncalla >= ( 3 * maxcall ) ) ) { /* If quitting, return the coordinates and function value at the best simplex vertex, and the error estimate. */ for ( coord = 0; coord < ncoord; coord++ ) { xmax[ coord ] = x[ hi * ncoord + coord ]; } result = ( f[ hi ] == -DBL_MAX ) ? AST__BAD : f[ hi ]; *err = range; break; } /* If performing another iteration, first try reflecting the worst vertex through the opposite face of the simplex. Check for errors. */ fnew = NewVertex( mapdata, lo, -1.0, x, f, ncall, xnew, status ); ncalla++; if ( astOK ) { /* If this results in a point lying in a forbiddden region (either outside the coordinate constraints or where the Mapping function yields bad coordinate values), then we must make a departure from the standard simplex algorithm. This is because the inability to make forward progress in this case can cause the simplex to repeatedly contract about each face (except one) in turn. This mechanism normally results in lateral contraction as the simplex attempts to squeeze through a narrow gap which is impeding progress. However, in this case there is no gap to get through, so the lateral contraction can eventually make the simplex become degenerate (due to rounding). This prevents it from expanding laterally again and exploring the region adjacent to the constraint boundary once it has become small enough. */ if ( fnew == AST__BAD ) { /* To overcome this, we instead contract the worst simplex vertex towards the best vertex (this has the cumulative effect of contracting the simplex without changing its shape). First find the offset in each coordinate between these two vertices. */ for ( coord = 0; coord < ncoord; coord++ ) { offset = x[ lo * ncoord + coord ] - x[ hi * ncoord + coord ]; /* Scale the offset to obtain the new coordinate. */ x[ lo * ncoord + coord ] = x[ hi * ncoord + coord ] + offset / factor; /* If the distance between the two vertices has not decreased, we are in a region where rounding errors prevent them approaching each other any more closely, so simply set them equal. */ if ( fabs( x[ lo * ncoord + coord ] - x[ hi * ncoord + coord ] ) >= fabs( offset ) ) { x[ lo * ncoord + coord ] = x[ hi * ncoord + coord ]; } } /* Evaluate the Mapping function at the new vertex. */ f[ lo ] = MapFunction( mapdata, &x[ lo * ncoord ], ncall, status ); if ( f[ lo ] == AST__BAD ) f[ lo ] = -DBL_MAX; ncalla++; /* We now return to the standard simplex algorithm. If the new vertex is a new maximum, then see if more of the same is even better by trying to expand the best vertex away from the opposite face. */ } else if ( fnew >= f[ hi ] ) { fnew = NewVertex( mapdata, lo, factor, x, f, ncall, xnew, status ); ncalla++; /* Otherwise, if the new vertex was no improvement on the second worst, then try contracting the worst vertex towards the opposite face. */ } else if ( fnew <= f[ nextlo ] ) { fsave = f[ lo ]; fnew = NewVertex( mapdata, lo, 1.0 / factor, x, f, ncall, xnew, status ); ncalla++; /* If this didn't result in any improvement, then contract the entire simplex towards the best vertex. Use the same approach as earlier to protect against rounding so that all the simplex vertices will eventually coalesce if this process is repeated enough times. */ if ( astOK && ( fnew <= fsave ) ) { for ( vertex = 0; vertex < nvertex; vertex++ ) { if ( vertex != hi ) { for ( coord = 0; coord < ncoord; coord++ ) { offset = x[ vertex * ncoord + coord ] - x[ hi * ncoord + coord ]; x[ vertex * ncoord + coord ] = x[ hi * ncoord + coord ] + offset / factor; if ( fabs( x[ vertex * ncoord + coord ] - x[ hi * ncoord + coord ] ) >= fabs( offset ) ) { x[ vertex * ncoord + coord ] = x[ hi * ncoord + coord ]; } } /* Evaluate the Mapping function at each new vertex. */ f[ vertex ] = MapFunction( mapdata, &x[ vertex * ncoord ], ncall, status ); if ( f[ vertex ] == AST__BAD ) f[ vertex ] = -DBL_MAX; ncalla++; } } } } } } } /* Free workspace. */ f = astFree( f ); x = astFree( x ); xnew = astFree( xnew ); /* If an error occurred, clear the returned result. */ if ( !astOK ) result = AST__BAD; /* Return the result. */ return result; } static void ValidateMapping( AstMapping *this, int forward, int npoint, int ncoord_in, int ncoord_out, const char *method, int *status ) { /* * Name: * ValidateMapping * Purpose: * Validate a Mapping for use to transform coordinates. * Type: * Private function. * Synopsis: * #include "mapping.h" * void ValidateMapping( AstMapping *this, int forward, * int npoint, int ncoord_in, int ncoord_out, * const char *method, int *status ) * Class Membership: * Mapping member function. * Description: * This function checks that a Mapping is suitable for transforming * a set of points. It also checks that the number of points and * the number of coordinate values per point is valid. If an error * is detected, the global error status is set and an error report * made. Otherwise, the function returns without further action. * Parameters: * this * Pointer to the Mapping. * forward * A non-zero value indicates that the forward coordinate * transformation is to be checked, while a zero value requests * the inverse transformation. * npoint * The number of points being transformed. * ncoord_in * The number of coordinates associated with each input point. * ncoord_out * The number of coordinates associated with each output point. * method * Pointer to a null terminated character string containing the * name of the method which invoked this function to validate a * Mapping. This is used solely for constructing error messages. * status * Pointer to the inherited status variable. */ /* Local Variables: */ int nin; /* Mapping Nin attribute value */ int nout; /* Mapping Nout attribute value */ /* Check the global error status. */ if ( !astOK ) return; /* Report an error if the requested transformation is not defined. */ if ( !( forward ? astGetTranForward( this ) : astGetTranInverse( this ) ) && astOK ) { astError( AST__TRNND, "%s(%s): %s coordinate transformation " "is not defined by the %s supplied.", status, method, astGetClass( this ), ( forward ? "A forward" : "An inverse" ), astGetClass( this ) ); } /* Obtain the effective values of the Nin and Nout attributes for the Mapping. */ nin = forward ? astGetNin( this ) : astGetNout( this ); nout = forward ? astGetNout( this ) : astGetNin( this ); /* If OK, check that the number of input coordinates matches the number required by the Mapping. Report an error if these numbers do not match. */ if ( astOK && ( ncoord_in != nin ) ) { astError( AST__NCPIN, "%s(%s): Bad number of input coordinate values " "(%d).", status, method, astGetClass( this ), ncoord_in ); astError( AST__NCPIN, "The %s given requires %d coordinate value%s for " "each input point.", status, astGetClass( this ), nin, ( nin == 1 ) ? "" : "s" ); } /* If OK, also check that the number of output coordinates matches the number required by the Mapping. Report an error if these numbers do not match. */ if ( astOK && ( ncoord_out != nout ) ) { astError( AST__NCPIN, "%s(%s): Bad number of output coordinate values " "(%d).", status, method, astGetClass( this ), ncoord_out ); astError( AST__NCPIN, "The %s given generates %s%d coordinate value%s " "for each output point.", status, astGetClass( this ), ( nout < ncoord_out ) ? "only " : "", nout, ( nout == 1 ) ? "" : "s" ); } /* Check that the number of points being transformed is not negative and report an error if necessary. */ if ( astOK && ( npoint < 0 ) ) { astError( AST__NPTIN, "%s(%s): Number of points to be transformed (%d) " "is invalid.", status, method, astGetClass( this ), npoint ); } } /* Functions which access class attributes. */ /* ---------------------------------------- */ /* Implement member functions to access the attributes associated with this class using the macros defined for this purpose in the "object.h" file. */ /* *att++ * Name: * Invert * Purpose: * Mapping inversion flag. * Type: * Public attribute. * Synopsis: * Integer (boolean). * Description: * This attribute controls which one of a Mapping's two possible * coordinate transformations is considered the "forward" * transformation (the other being the "inverse" * transformation). If the attribute value is zero (the default), * the Mapping's behaviour will be the same as when it was first * created. However, if it is non-zero, its two transformations * will be inter-changed, so that the Mapping displays the inverse * of its original behaviour. * * Inverting the boolean sense of the Invert attribute will cause * the values of a Mapping's Nin and Nout attributes to be * interchanged. The values of its TranForward and TranInverse * attributes will also be interchanged. This operation may be c performed with the astInvert function. f performed with the AST_INVERT routine. * Applicability: * Mapping * All Mappings have this attribute. * UnitMap * The value of the Invert attribute has no effect on the * behaviour of a UnitMap. * FrameSet * Inverting the boolean sense of the Invert attribute for a * FrameSet will cause its base and current Frames (and its Base * and Current attributes) to be interchanged. This, in turn, * may affect other properties and attributes of the FrameSet * (such as Nin, Nout, Naxes, TranForward, TranInverse, * etc.). The Invert attribute of a FrameSet is not itself * affected by selecting a new base or current Frame. *att-- */ /* This ia a boolean value (0 or 1) with a value of CHAR_MAX when undefined but yielding a default of zero. */ astMAKE_CLEAR(Mapping,Invert,invert,CHAR_MAX) astMAKE_GET(Mapping,Invert,int,0,( ( this->invert == CHAR_MAX ) ? 0 : this->invert )) astMAKE_SET(Mapping,Invert,int,invert,( (this->flags&=~AST__ISSIMPLE_FLAG),(value!=0) )) astMAKE_TEST(Mapping,Invert,( this->invert != CHAR_MAX )) /* *att++ * Name: * IsLinear * Purpose: * Is the Mapping linear? * Type: * Public attribute. * Synopsis: * Integer (boolean), read-only. * Description: * This attribute indicates whether a Mapping is an instance of a * class that always represents a linear transformation. Note, some * Mapping classes can represent linear or non-linear transformations * (the MathMap class for instance). Such classes have a zero value for * the IsLinear attribute. Specific instances of such classes can be * tested for linearity using the * astLinearApprox function. * AST_LINEARAPPROX routine. * Applicability: * Mapping * All Mappings have this attribute. * CmpMap * The IsLinear value for a CmpMap is determined by the classes * of the encapsulated Mappings. For instance, a CmpMap that combines * a ZoomMap and a ShiftMap will have a non-zero value for its IsLinear * attribute, but a CmpMap that contains a MathMap will have a * value of zero for its IsLinear attribute. * Frame * The IsLinear value for a Frame is 1 (since a Frame is equivalent * to a UnitMap). * FrameSet * The IsLinear value for a FrameSet is obtained from the Mapping * from the base Frame to the current Frame. *att-- */ /* *att++ * Name: * IsSimple * Purpose: * Has the Mapping been simplified? * Type: * Public attribute. * Synopsis: * Integer (boolean), read-only. * Description: * This attribute indicates whether a Mapping has been simplified * by the c astSimplify f AST_SIMPLIFY * method. If the IsSimple value is non-zero, then the Mapping has * been simplified and so there is nothing to be gained by simplifying * it again. Indeed, the c astSimplify f AST_SIMPLIFY * method will immediately return the Mapping unchanged if the IsSimple * attribute indicates that the Mapping has already been simplified. * Applicability: * Mapping * All Mappings have this attribute. * Frame * All classes of Frame return zero for the IsSimple attribute. * This is because changes can be made to a Frame which affect the * Mapping represented by the Frame, and so there can be no * guarantee that the Mapping may not need re-simplifying. Most * non-Frame Mappings, on the other hand, are immutable and so when * they are simplified it is certain that they weill remain in a * simple state. *att-- */ astMAKE_GET(Mapping,IsSimple,int,0,((this->flags)&AST__ISSIMPLE_FLAG)) /* *att++ * Name: * Nin * Purpose: * Number of input coordinates for a Mapping. * Type: * Public attribute. * Synopsis: * Integer, read-only. * Description: * This attribute gives the number of coordinate values required to * specify an input point for a Mapping (i.e. the number of * dimensions of the space in which the Mapping's input points * reside). * Applicability: * Mapping * All Mappings have this attribute. * CmpMap * If a CmpMap's component Mappings are joined in series, then * its Nin attribute is equal to the Nin attribute of the first * component (or to the Nout attribute of the second component * if the the CmpMap's Invert attribute is non-zero). * * If a CmpMap's component Mappings are joined in parallel, then * its Nin attribute is given by the sum of the Nin attributes * of each component (or to the sum of their Nout attributes if * the CmpMap's Invert attribute is non-zero). * Frame * The Nin attribute for a Frame is always equal to the number * of Frame axes (Naxes attribute). * FrameSet * The Nin attribute of a FrameSet is equal to the number of * axes (Naxes attribute) of its base Frame (as specified by the * FrameSet's Base attribute). The Nin attribute value may * therefore change if a new base Frame is selected. *att-- */ /* *att++ * Name: * Nout * Purpose: * Number of output coordinates for a Mapping. * Type: * Public attribute. * Synopsis: * Integer, read-only. * Description: * This attribute gives the number of coordinate values generated * by a Mapping to specify each output point (i.e. the number of * dimensions of the space in which the Mapping's output points * reside). * Applicability: * Mapping * All Mappings have this attribute. * CmpMap * If a CmpMap's component Mappings are joined in series, then * its Nout attribute is equal to the Nout attribute of the * second component (or to the Nin attribute of the first * component if the the CmpMap's Invert attribute is non-zero). * * If a CmpMap's component Mappings are joined in parallel, then * its Nout attribute is given by the sum of the Nout attributes * of each component (or to the sum of their Nin attributes if * the CmpMap's Invert attribute is non-zero). * Frame * The Nout attribute for a Frame is always equal to the number * of Frame axes (Naxes attribute). * FrameSet * The Nout attribute of a FrameSet is equal to the number of * FrameSet axes (Naxes attribute) which, in turn, is equal to * the Naxes attribute of the FrameSet's current Frame (as * specified by the Current attribute). The Nout attribute value * may therefore change if a new current Frame is selected. *att-- */ /* *att++ * Name: * Report * Purpose: * Report transformed coordinates? * Type: * Public attribute. * Synopsis: * Integer (boolean). * Description: * This attribute controls whether coordinate values are reported * whenever a Mapping is used to transform a set of points. If its * value is zero (the default), no report is made. However, if it * is non-zero, the coordinates of each point are reported (both * before and after transformation) by writing them to standard * output. * * This attribute is provided as an aid to debugging, and to avoid * having to report values explicitly in simple programs. * Applicability: * Mapping * All Mappings have this attribute. * CmpMap * When applied to a compound Mapping (CmpMap), only the Report * attribute of the CmpMap, and not those of its component * Mappings, is used. Coordinate information is never reported * for the component Mappings individually, only for the * complete CmpMap. * Frame * When applied to any Frame, the formatting capabilities of the c Frame (as provided by the astFormat function) will be used to f Frame (as provided by the AST_FORMAT function) will be used to * format the reported coordinates. * FrameSet * When applied to any FrameSet, the formatting capabilities of * the base and current Frames will be used (as above) to * individually format the input and output coordinates, as * appropriate. The Report attribute of a FrameSet is not itself * affected by selecting a new base or current Frame, but the * resulting formatting capabilities may be. * Notes: * - Unlike most other attributes, the value of the Report * attribute is not transferred when a Mapping is copied. Instead, * its value is undefined (and therefore defaults to zero) in any * copy. Similarly, it becomes undefined in any external c representation of a Mapping produced by the astWrite function. f representation of a Mapping produced by the AST_WRITE routine. *att-- */ /* This ia a boolean value (0 or 1) with a value of CHAR_MAX when undefined but yielding a default of zero. */ astMAKE_CLEAR(Mapping,Report,report,CHAR_MAX) astMAKE_GET(Mapping,Report,int,0,( ( this->report == CHAR_MAX ) ? 0 : this->report )) astMAKE_SET(Mapping,Report,int,report,( value != 0 )) astMAKE_TEST(Mapping,Report,( this->report != CHAR_MAX )) /* *att++ * Name: * TranForward * Purpose: * Forward transformation defined? * Type: * Public attribute. * Synopsis: * Integer (boolean), read-only. * Description: * This attribute indicates whether a Mapping is able to transform * coordinates in the "forward" direction (i.e. converting input * coordinates into output coordinates). If this attribute is * non-zero, the forward transformation is available. Otherwise, it * is not. * Applicability: * Mapping * All Mappings have this attribute. * CmpMap * The TranForward attribute value for a CmpMap is given by the * boolean AND of the value for each component Mapping. * FrameSet * The TranForward attribute of a FrameSet applies to the * transformation which converts between the FrameSet's base * Frame and its current Frame (as specified by the Base and * Current attributes). This value is given by the boolean AND * of the TranForward values which apply to each of the * individual sub-Mappings required to perform this conversion. * The TranForward attribute value for a FrameSet may therefore * change if a new Base or Current Frame is selected. * Notes: * - An error will result if a Mapping with a TranForward value of * zero is used to transform coordinates in the forward direction. *att-- */ /* *att++ * Name: * TranInverse * Purpose: * Inverse transformation defined? * Type: * Public attribute. * Synopsis: * Integer (boolean), readonly. * Description: * This attribute indicates whether a Mapping is able to transform * coordinates in the "inverse" direction (i.e. converting output * coordinates back into input coordinates). If this attribute is * non-zero, the inverse transformation is available. Otherwise, it * is not. * Applicability: * Mapping * All Mappings have this attribute. * CmpMap * The TranInverse attribute value for a CmpMap is given by the * boolean AND of the value for each component Mapping. * FrameSet * The TranInverse attribute of a FrameSet applies to the * transformation which converts between the FrameSet's current * Frame and its base Frame (as specified by the Current and * Base attributes). This value is given by the boolean AND of * the TranInverse values which apply to each of the individual * sub-Mappings required to perform this conversion. * The TranInverse attribute value for a FrameSet may therefore * change if a new Base or Current Frame is selected. * Notes: * - An error will result if a Mapping with a TranInverse value of * zero is used to transform coordinates in the inverse direction. *att-- */ /* Copy constructor. */ /* ----------------- */ static void Copy( const AstObject *objin, AstObject *objout, int *status ) { /* * Name: * Copy * Purpose: * Copy constructor for Mapping objects. * Type: * Private function. * Synopsis: * void Copy( const AstObject *objin, AstObject *objout, int *status ) * Description: * This function implements the copy constructor for Mapping objects. * Parameters: * objin * Pointer to the Mapping to be copied. * objout * Pointer to the Mapping being constructed. * status * Pointer to the inherited status variable. * Notes: * - This constructor exists simply to ensure that the "Report" * attribute is cleared in any copy made of a Mapping. */ /* Local Variables: */ AstMapping *out; /* Pointer to output Mapping */ /* Check the global error status. */ if ( !astOK ) return; /* Obtain a pointer to the output Mapping. */ out = (AstMapping *) objout; /* Clear the output Report attribute. */ out->report = CHAR_MAX; } /* Destructor. */ /* ----------- */ static void Delete( AstObject *obj, int *status ) { /* * Name: * Delete * Purpose: * Destructor for Mapping objects. * Type: * Private function. * Synopsis: * void Delete( AstObject *obj, int *status ) * Description: * This function implements the destructor for Mapping objects. * Parameters: * obj * Pointer to the Mapping to be deleted. * status * Pointer to the inherited status variable. * Notes: * - This destructor does nothing and exists only to maintain a * one-to-one correspondence between destructors and copy * constructors. */ /* Return without action. */ } /* Dump function. */ /* -------------- */ static void Dump( AstObject *this_object, AstChannel *channel, int *status ) { /* * Name: * Dump * Purpose: * Dump function for Mapping 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 Mapping class to an output Channel. * Parameters: * this * Pointer to the Mapping 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: */ AstMapping *this; /* Pointer to the Mapping structure */ int invert; /* Mapping inverted? */ int ival; /* Integer value */ int set; /* Attribute value set? */ /* Check the global error status. */ if ( !astOK ) return; /* Obtain a pointer to the Mapping structure. */ this = (AstMapping *) this_object; /* Write out values representing the instance variables for the Mapping 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. */ /* Determine if the Mapping is inverted. The output values (e.g. number of input and output coordinates) will refer to the Mapping ***before*** this inversion flag is applied, but we need it when using (e.g.) the astGetNin/astGetNout methods to determine which one will return the required value. */ invert = astGetInvert( this ); /* (NB. there is a subtle point here that dictates the extent to which this inversion flag can be used... All use of methods (such as astGetInvert, which might be over-ridden by derived classes) must be restricted to determining the values of "unset" output quantities only (below). This is because when re-loading the Mapping, the derived classes will not have been loaded at the point when these values are re-read - hence any value whose interpretation depends on these methods cannot be reliably recovered.) */ /* Nin. */ /* ---- */ /* Use the instance variable directly to avoid the effect of the Invert attribute on the private member function. Treat zero as the default. */ set = ( this->nin != 0 ); ival = set ? this->nin : ( !invert ? astGetNin( this ) : astGetNout( this ) ); astWriteInt( channel, "Nin", set, 0, ival, "Number of input coordinates" ); /* Nout. */ /* ----- */ /* Use the instance variable directly. Treat zero as the default. */ set = ( this->nout != this->nin ); ival = set ? this->nout : ( !invert ? astGetNout( this ) : astGetNin( this ) ); astWriteInt( channel, "Nout", set, 0, ival, "Number of output coordinates" ); /* IsSimple. */ /* --------- */ ival = astGetIsSimple( this ); astWriteInt( channel, "IsSimp", ival, 0, ival, ival ? "Mapping has been simplified" : "Mapping has not been simplified" ); /* Invert. */ /* ------- */ set = TestInvert( this, status ); ival = set ? GetInvert( this, status ) : astGetInvert( this ); astWriteInt( channel, "Invert", set, 0, ival, ival ? "Mapping inverted" : "Mapping not inverted" ); /* TranForward. */ /* ------------ */ /* Use the instance variable directly. Treat 1 as the default. */ set = ( this->tran_forward == 0 ); ival = set ? this->tran_forward : ( !invert ? astGetTranForward( this ) : astGetTranInverse( this ) ); astWriteInt( channel, "Fwd", set, 0, ival, ival ? "Forward transformation defined" : "Forward transformation not defined" ); /* TranInverse. */ /* ------------ */ /* Use the instance variable directly. Treat 1 as the default. */ set = ( this->tran_inverse == 0 ); ival = set ? this->tran_inverse : ( !invert ? astGetTranInverse( this ) : astGetTranForward( this ) ); astWriteInt( channel, "Inv", set, 0, ival, ival ? "Inverse transformation defined" : "Inverse transformation not defined" ); /* Report. */ /* ------- */ set = TestReport( this, status ); ival = set ? GetReport( this, status ) : astGetReport( this ); astWriteInt( channel, "Report", set, 0, ival, ival ? "Report coordinate transformations" : "Don't report coordinate transformations" ); } /* Standard class functions. */ /* ========================= */ /* Implement the astIsAMapping and astCheckMapping functions using the macros defined for this purpose in the "object.h" header file. */ astMAKE_ISA(Mapping,Object) astMAKE_CHECK(Mapping) AstMapping *astInitMapping_( void *mem, size_t size, int init, AstMappingVtab *vtab, const char *name, int nin, int nout, int tran_forward, int tran_inverse, int *status ) { /* *+ * Name: * astInitMapping * Purpose: * Initialise a Mapping. * Type: * Protected function. * Synopsis: * #include "mapping.h" * AstMapping *astInitMapping( void *mem, size_t size, int init, * AstMappingVtab *vtab, const char *name, * int nin, int nout, * int tran_forward, int tran_inverse ) * Class Membership: * Mapping initialiser. * Description: * This function is provided for use by class implementations to initialise * a new Mapping object. It allocates memory (if necessary) to accommodate * the Mapping plus any additional data associated with the derived class. * It then initialises a Mapping 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 Mapping at the start of the memory passed via the * "vtab" parameter. * Parameters: * mem * A pointer to the memory in which the Mapping is to be initialised. * This must be of sufficient size to accommodate the Mapping data * (sizeof(Mapping)) 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 Mapping (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 Mapping * structure, so a valid value must be supplied even if not required for * allocating memory. * init * A logical flag indicating if the Mapping'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 Mapping. * 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). * nin * The number of coordinate values per input point. * nout * The number of coordinate vales per output point. * tran_forward * A non-zero value indicates that the Mapping will be able to * transform coordinates in the forward direction. A zero value * indicates that it will not. * tran_inverse * A non-zero value indicates that the Mapping will be able to * transform coordinates in the inverse direction. A zero value * indicates that it will not. * Returned Value: * A pointer to the new Mapping. * Notes: * - The Mappings produced by this function implement all the basic methods * defined by the Mapping class. However, their astTransform method does not * actually perform any coordinate transformation (although it performs all * necessary argument validation and creates an output PointSet if * necessary, leaving its coordinate values undefined). * - This means that Mappings produced by this function are of limited use * on their own, but may easily be extended by a derived class simply by * over-riding the astTransform method to add the necessary coordinate * arithmetic. * - 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: */ AstMapping *new; /* Pointer to new Mapping */ /* Check the global status. */ if ( !astOK ) return NULL; /* If necessary, initialise the virtual function table. */ if ( init ) astInitMappingVtab( vtab, name ); /* Initialise. */ new = NULL; /* Check the initialisation values for validity, reporting an error if necessary. */ if ( nin < 0 ) { astError( AST__BADNI, "astInitMapping(%s): Bad number of input " "coordinates (%d).", status, name, nin ); astError( AST__BADNI, "This number should be zero or more." , status); } else if ( nout < 0 ) { astError( AST__BADNO, "astInitMapping(%s): Bad number of output " "coordinates (%d).", status, name, nout ); astError( AST__BADNI, "This number should be zero or more." , status); } /* Initialise an Object structure (the parent class) as the first component within the Mapping structure, allocating memory if necessary. */ new = (AstMapping *) astInitObject( mem, size, 0, (AstObjectVtab *) vtab, name ); if ( astOK ) { /* Initialise the Mapping data. */ /* ---------------------------- */ /* Store the numbers of input and output coordinates. */ new->nin = nin; new->nout = nout; /* Store the flags indicating which coordinate transformations are defined (constrain these values to 0 or 1). */ new->tran_forward = ( tran_forward != 0 ); new->tran_inverse = ( tran_inverse != 0 ); /* Initialise other attributes to their undefined values. */ new->invert = CHAR_MAX; new->report = CHAR_MAX; new->flags = 0; /* If an error occurred, clean up by deleting the new object. */ if ( !astOK ) new = astDelete( new ); } /* Return a pointer to the new object. */ return new; } AstMapping *astLoadMapping_( void *mem, size_t size, AstMappingVtab *vtab, const char *name, AstChannel *channel, int *status ) { /* *+ * Name: * astLoadMapping * Purpose: * Load a Mapping. * Type: * Protected function. * Synopsis: * #include "mapping.h" * AstMapping *astLoadMapping( void *mem, size_t size, * AstMappingVtab *vtab, const char *name, * AstChannel *channel ) * Class Membership: * Mapping loader. * Description: * This function is provided to load a new Mapping 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 * Mapping 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 Mapping at the start of the memory * passed via the "vtab" parameter. * Parameters: * mem * A pointer to the memory into which the Mapping is to be * loaded. This must be of sufficient size to accommodate the * Mapping data (sizeof(Mapping)) 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 Mapping (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 Mapping 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(AstMapping) is used instead. * vtab * Pointer to the start of the virtual function table to be * associated with the new Mapping. If this is NULL, a pointer * to the (static) virtual function table for the Mapping 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 "Mapping" is used instead. * Returned Value: * A pointer to the new Mapping. * Notes: * - A null pointer will be returned if this function is invoked * with the global error status set, or if it should fail for any * reason. *- */ /* Local Variables: */ astDECLARE_GLOBALS /* Pointer to thread-specific global data */ AstMapping *new; /* Pointer to the new Mapping */ /* Initialise. */ new = NULL; /* Check the global error status. */ if ( !astOK ) return new; /* Get a pointer to the thread specific global data structure. */ astGET_GLOBALS(channel); /* If a NULL virtual function table has been supplied, then this is the first loader to be invoked for this Mapping. In this case the Mapping belongs to this class, so supply appropriate values to be passed to the parent class loader (and its parent, etc.). */ if ( !vtab ) { size = sizeof( AstMapping ); vtab = &class_vtab; name = "Mapping"; /* If required, initialise the virtual function table for this class. */ if ( !class_init ) { astInitMappingVtab( 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 Mapping. */ new = astLoadObject( mem, size, (AstObjectVtab *) 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, "Mapping" ); /* 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. */ /* Initialise bitwise flags to zero. */ new->flags = 0; /* Nin. */ /* ---- */ new->nin = astReadInt( channel, "nin", 0 ); if ( new->nin < 0 ) new->nin = 0; /* Nout. */ /* ----- */ new->nout = astReadInt( channel, "nout", new->nin ); if ( new->nout < 0 ) new->nout = 0; /* Invert. */ /* ------- */ new->invert = astReadInt( channel, "invert", CHAR_MAX ); if ( TestInvert( new, status ) ) SetInvert( new, new->invert, status ); /* IsSimple. */ /* --------- */ if( astReadInt( channel, "issimp", 0 ) ) new->flags |= AST__ISSIMPLE_FLAG; /* TranForward. */ /* ------------ */ new->tran_forward = ( astReadInt( channel, "fwd", 1 ) != 0 ); /* TranInverse. */ /* ------------ */ new->tran_inverse = ( astReadInt( channel, "inv", 1 ) != 0 ); /* Report. */ /* ------- */ new->report = astReadInt( channel, "report", CHAR_MAX ); if ( TestReport( new, status ) ) SetReport( new, new->report, status ); /* If an error occurred, clean up by deleting the new Mapping. */ if ( !astOK ) new = astDelete( new ); } /* Return the new Mapping 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. */ void astDecompose_( AstMapping *this, AstMapping **map1, AstMapping **map2, int *series, int *invert1, int *invert2, int *status ) { if ( !astOK ) return; (**astMEMBER(this,Mapping,Decompose))( this, map1, map2, series, invert1, invert2, status ); } int astGetNin_( AstMapping *this, int *status ) { if ( !astOK ) return 0; return (**astMEMBER(this,Mapping,GetNin))( this, status ); } int astGetNout_( AstMapping *this, int *status ) { if ( !astOK ) return 0; return (**astMEMBER(this,Mapping,GetNout))( this, status ); } int astGetIsLinear_( AstMapping *this, int *status ) { if ( !astOK ) return 0; return (**astMEMBER(this,Mapping,GetIsLinear))( this, status ); } int astGetTranForward_( AstMapping *this, int *status ) { if ( !astOK ) return 0; return (**astMEMBER(this,Mapping,GetTranForward))( this, status ); } int astGetTranInverse_( AstMapping *this, int *status ) { if ( !astOK ) return 0; return (**astMEMBER(this,Mapping,GetTranInverse))( this, status ); } void astInvert_( AstMapping *this, int *status ) { if ( !astOK ) return; (**astMEMBER(this,Mapping,Invert))( this, status ); } void astMapBox_( AstMapping *this, const double lbnd_in[], const double ubnd_in[], int forward, int coord_out, double *lbnd_out, double *ubnd_out, double xl[], double xu[], int *status ) { if ( !astOK ) return; (**astMEMBER(this,Mapping,MapBox))( this, lbnd_in, ubnd_in, forward, coord_out, lbnd_out, ubnd_out, xl, xu, status ); } int astMapList_( AstMapping *this, int series, int invert, int *nmap, AstMapping ***map_list, int **invert_list, int *status ) { if ( !astOK ) return 0; return (**astMEMBER(this,Mapping,MapList))( this, series, invert, nmap, map_list, invert_list, status ); } int *astMapSplit_( AstMapping *this, int nin, const int *in, AstMapping **map, int *status ){ int *result = NULL; AstMapping *tmap; if( map ) *map = NULL; if ( !astOK ) return NULL; result = (**astMEMBER(this,Mapping,MapSplit))( this, nin, in, &tmap, status ); if( tmap ) { *map = astCopy( tmap ); tmap = astAnnul( tmap ); } return result; } int astMapMerge_( AstMapping *this, int where, int series, int *nmap, AstMapping ***map_list, int **invert_list, int *status ) { if ( !astOK || astDoNotSimplify( this ) ) return -1; return (**astMEMBER(this,Mapping,MapMerge))( this, where, series, nmap, map_list, invert_list, status ); } int astDoNotSimplify_( AstMapping *this, int *status ) { if ( !astOK ) return 0; return (**astMEMBER(this,Mapping,DoNotSimplify))( this, status ); } void astReportPoints_( AstMapping *this, int forward, AstPointSet *in_points, AstPointSet *out_points, int *status ) { if ( !astOK ) return; (**astMEMBER(this,Mapping,ReportPoints))( this, forward, in_points, out_points, status ); } #define MAKE_RESAMPLE_(X,Xtype) \ int astResample##X##_( AstMapping *this, int ndim_in, const int *lbnd_in, \ const int *ubnd_in, const Xtype *in, \ const Xtype *in_var, int interp, \ void (* finterp)( void ), const double *params, \ int flags, double tol, int maxpix, Xtype badval, \ int ndim_out, \ const int *lbnd_out, const int *ubnd_out, \ const int *lbnd, const int *ubnd, Xtype *out, \ Xtype *out_var, int *status ) { \ if ( !astOK ) return 0; \ return (**astMEMBER(this,Mapping,Resample##X))( this, ndim_in, lbnd_in, \ ubnd_in, in, in_var, \ interp, finterp, params, \ flags, tol, maxpix, \ badval, ndim_out, \ lbnd_out, ubnd_out, \ lbnd, ubnd, \ out, out_var, status ); \ } #if HAVE_LONG_DOUBLE /* Not normally implemented */ MAKE_RESAMPLE_(LD,long double) #endif MAKE_RESAMPLE_(D,double) MAKE_RESAMPLE_(F,float) MAKE_RESAMPLE_(L,long int) MAKE_RESAMPLE_(UL,unsigned long int) MAKE_RESAMPLE_(I,int) MAKE_RESAMPLE_(UI,unsigned int) MAKE_RESAMPLE_(K,INT_BIG) MAKE_RESAMPLE_(UK,UINT_BIG) MAKE_RESAMPLE_(S,short int) MAKE_RESAMPLE_(US,unsigned short int) MAKE_RESAMPLE_(B,signed char) MAKE_RESAMPLE_(UB,unsigned char) #undef MAKE_RESAMPLE_ #define MAKE_REBIN_(X,Xtype) \ void astRebin##X##_( AstMapping *this, double wlim, int ndim_in, const int *lbnd_in, \ const int *ubnd_in, const Xtype *in, \ const Xtype *in_var, int interp, \ const double *params, \ int flags, double tol, int maxpix, Xtype badval, \ int ndim_out, \ const int *lbnd_out, const int *ubnd_out, \ const int *lbnd, const int *ubnd, Xtype *out, \ Xtype *out_var, int *status ) { \ if ( !astOK ) return; \ (**astMEMBER(this,Mapping,Rebin##X))( this, wlim, ndim_in, lbnd_in, \ ubnd_in, in, in_var, \ interp, params, \ flags, tol, maxpix, \ badval, ndim_out, \ lbnd_out, ubnd_out, \ lbnd, ubnd, \ out, out_var, status ); \ } #if HAVE_LONG_DOUBLE /* Not normally implemented */ MAKE_REBIN_(LD,long double) #endif MAKE_REBIN_(D,double) MAKE_REBIN_(F,float) MAKE_REBIN_(I,int) MAKE_REBIN_(B,signed char) MAKE_REBIN_(UB,unsigned char) #undef MAKE_REBIN_ #define MAKE_REBINSEQ_(X,Xtype) \ void astRebinSeq##X##_( AstMapping *this, double wlim, int ndim_in, const int *lbnd_in, \ const int *ubnd_in, const Xtype *in, \ const Xtype *in_var, int interp, \ const double *params, \ int flags, double tol, int maxpix, Xtype badval, \ int ndim_out, \ const int *lbnd_out, const int *ubnd_out, \ const int *lbnd, const int *ubnd, Xtype *out, \ Xtype *out_var, double *weights, int64_t *nused, \ int *status ) { \ if ( !astOK ) return; \ (**astMEMBER(this,Mapping,RebinSeq##X))( this, wlim, ndim_in, lbnd_in, \ ubnd_in, in, in_var, \ interp, params, \ flags, tol, maxpix, \ badval, ndim_out, \ lbnd_out, ubnd_out, \ lbnd, ubnd, out, out_var, \ weights, nused, status ); \ } #if HAVE_LONG_DOUBLE /* Not normally implemented */ MAKE_REBINSEQ_(LD,long double) #endif MAKE_REBINSEQ_(D,double) MAKE_REBINSEQ_(F,float) MAKE_REBINSEQ_(I,int) MAKE_REBINSEQ_(B,signed char) MAKE_REBINSEQ_(UB,unsigned char) #undef MAKE_REBINSEQ_ double astRate_( AstMapping *this, double *at, int ax1, int ax2, int *status ){ astDECLARE_GLOBALS if ( !astOK ) return AST__BAD; astGET_GLOBALS(this); if( ax1 < 0 || ax1 >= astGetNout( this ) ) { astError( AST__AXIIN, "astRate(%s): Invalid output index (%d) " "specified - should be in the range 1 to %d.", status, astGetClass( this ), ax1 + 1, astGetNout( this ) ); } else if( ax2 < 0 || ax2 >= astGetNin( this ) ) { astError( AST__AXIIN, "astRate(%s): Invalid input index (%d) " "specified - should be in the range 1 to %d.", status, astGetClass( this ), ax2 + 1, astGetNin( this ) ); } if( rate_disabled ) { return ( at[ ax2 ] != AST__BAD ) ? 1.0 : AST__BAD; } else { return (**astMEMBER(this,Mapping,Rate))( this, at, ax1, ax2, status ); } } AstMapping *astRemoveRegions_( AstMapping *this, int *status ) { if ( !astOK ) return NULL; return (**astMEMBER(this,Mapping,RemoveRegions))( this, status ); } AstMapping *astSimplify_( AstMapping *this, int *status ) { AstMapping *result; AstErrorContext error_context; if ( !astOK ) return NULL; /* If this Mapping has already been simplified, or if it cannot be simplified (e.g. because it is a Frame) we just returned a clone of the upplied pointer. */ if( !astGetIsSimple( this ) && !astDoNotSimplify( this ) ) { /* Start a new error reporting context. This is done so that errors caused by the siplification process attempting to do inappropriate things with the supplied mapping can be caught. */ astErrorBegin( &error_context ); /* Do the simplification. */ result = (**astMEMBER(this,Mapping,Simplify))( this, status ); /* If a result was returned, indicate it has been simplified and so does not need to be simplified again. */ if( result ) { result->flags |= AST__ISSIMPLE_FLAG; /* If the simplification process failed due to the supplied Mappings being inappropriate (e.g. because it attempted to ue an undefined transformation), clear the error status and return a clone of the supplied Mapping. */ } else if( astStatus == AST__NODEF || astStatus == AST__TRNND ){ astClearStatus; result = astClone( this ); } /* End the error reporting context. */ astErrorEnd( &error_context ); /* If the Mapping has already been simplified just return a clone. */ } else { result = astClone( this ); } return result; } AstPointSet *astTransform_( AstMapping *this, AstPointSet *in, int forward, AstPointSet *out, int *status ) { AstPointSet *result; if ( !astOK ) return NULL; result = (**astMEMBER(this,Mapping,Transform))( this, in, forward, out, status ); (void) astReplaceNaN( result ); return result; } void astTran1_( AstMapping *this, int npoint, const double xin[], int forward, double xout[], int *status ) { if ( !astOK ) return; (**astMEMBER(this,Mapping,Tran1))( this, npoint, xin, forward, xout, status ); } void astTran2_( AstMapping *this, int npoint, const double xin[], const double yin[], int forward, double xout[], double yout[], int *status ) { if ( !astOK ) return; (**astMEMBER(this,Mapping,Tran2))( this, npoint, xin, yin, forward, xout, yout, status ); } void astTranGrid_( AstMapping *this, int ncoord_in, const int lbnd[], const int ubnd[], double tol, int maxpix, int forward, int ncoord_out, int outdim, double *out, int *status ) { if ( !astOK ) return; (**astMEMBER(this,Mapping,TranGrid))( this, ncoord_in, lbnd, ubnd, tol, maxpix, forward, ncoord_out, outdim, out, status ); } void astTranN_( AstMapping *this, int npoint, int ncoord_in, int indim, const double *in, int forward, int ncoord_out, int outdim, double *out, int *status ) { if ( !astOK ) return; (**astMEMBER(this,Mapping,TranN))( this, npoint, ncoord_in, indim, in, forward, ncoord_out, outdim, out, status ); } void astTranP_( AstMapping *this, int npoint, int ncoord_in, const double *ptr_in[], int forward, int ncoord_out, double *ptr_out[], int *status ) { if ( !astOK ) return; (**astMEMBER(this,Mapping,TranP))( this, npoint, ncoord_in, ptr_in, forward, ncoord_out, ptr_out, status ); } int astLinearApprox_( AstMapping *this, const double *lbnd, const double *ubnd, double tol, double *fit, int *status ){ if ( !astOK ) return 0; return (**astMEMBER(this,Mapping,LinearApprox))( this, lbnd, ubnd, tol, fit, status ); } int astQuadApprox_( AstMapping *this, const double lbnd[2], const double ubnd[2], int nx, int ny, double *fit, double *rms, int *status ){ if ( !astOK ) return 0; return (**astMEMBER(this,Mapping,QuadApprox))( this, lbnd, ubnd, nx, ny, fit, rms, status ); } /* Public 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. */ void DecomposeId_( AstMapping *, AstMapping **, AstMapping **, int *, int *, int *, int * ); void MapBoxId_( AstMapping *, const double [], const double [], int, int, double *, double *, double [], double [], int * ); double astRateId_( AstMapping *, double *, int, int, int * ); void astMapSplitId_( AstMapping *, int, const int *, int *, AstMapping **, int * ); /* Special interface function implementations. */ /* ------------------------------------------- */ void astDecomposeId_( AstMapping *this, AstMapping **map1, AstMapping **map2, int *series, int *invert1, int *invert2, int *status ) { /* *++ * Name: c astDecompose f AST_DECOMPOSE * Purpose: * Decompose a Mapping into two component Mappings. * Type: * Public virtual function. * Synopsis: c #include "mapping.h" c void astDecompose( AstMapping *this, AstMapping **map1, c AstMapping **map2, int *series, int *invert1, c int *invert2 ) f CALL AST_DECOMPOSE( THIS, MAP1, MAP2, SERIES, INVERT1, INVERT2, STATUS ) * Class Membership: * Mapping method. * Description: c This function returns pointers to two Mappings which, when applied f This routine 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 or CmpFrames. * Parameters: c this f THIS = INTEGER (Given) * Pointer to the Mapping. c map1 f MAP1 = INTEGER (Returned) c Address of a location to receive a pointer to first component f A pointer to first component * Mapping. c map2 f MAP2 = INTEGER (Returned) c Address of a location to receive a pointer to second component f A pointer to second component * Mapping. c series f SERIES = LOGICAL (Returned) c Address of a location to receive a value indicating if the c component Mappings are applied in series or parallel. A non-zero c value means that the supplied Mapping is equivalent to applying map1 c followed by map2 in series. A zero value means that the supplied c Mapping is equivalent to applying map1 to the lower numbered axes c and map2 to the higher numbered axes, in parallel. f Indicates if the f component Mappings are applied in series or parallel. A .TRUE. f value means that the supplied Mapping is equivalent to applying MAP1 f followed by MAP2 in series. A zero value means that the supplied f Mapping is equivalent to applying MAP1 to the lower numbered axes f and MAP2 to the higher numbered axes, in parallel. c invert1 f INVERT1 = INTEGER (Returned) c The value of the Invert attribute to be used with map1. f The value of the Invert attribute to be used with MAP1. c invert2 f INVERT2 = INTEGER (Returned) c The value of the Invert attribute to be used with map2. f The value of the Invert attribute to be used with MAP2. * Applicability: * CmpMap c If the supplied Mapping is a CmpMap, then map1 and map2 will be f If the supplied Mapping is a CmpMap, then MAP1 and MAP2 will be * returned holding pointers to the component Mappings used to * create the CmpMap, either in series or parallel. Note, changing * the Invert attribute of either of the component Mappings using * the returned pointers will have no effect on the supplied CmpMap. * This is because the CmpMap remembers and uses the original settings * of the Invert attributes (that is, the values of the Invert * attributes when the CmpMap was first created). These are the c Invert values which are returned in invert1 and invert2. f Invert values which are returned in INVERT1 and INVERT2. * TranMap c If the supplied Mapping is a TranMap, then map1 and map2 will be f If the supplied Mapping is a TranMap, then MAP1 and MAP2 will be * returned holding pointers to the forward and inverse Mappings * represented by the TranMap (zero will be returned for c series). f SERIES). * Note, changing the Invert attribute of * either of the component Mappings using the returned pointers will * have no effect on the supplied TranMap. This is because the TranMap * remembers and uses the original settings of the Invert attributes * (that is, the values of the Invert attributes when the TranMap was * first created). These are the c Invert values which are returned in invert1 and invert2. f Invert values which are returned in INVERT1 and INVERT2. * Mapping c For any class of Mapping other than a CmpMap, map1 will be c returned holding a clone of the supplied Mapping pointer, and map2 c will be returned holding a NULL pointer. Invert1 will be returned c holding the current value of the Invert attribute for the supplied c Mapping, and invert2 will be returned holding zero. f For any class of Mapping other than a CmpMap, MAP1 will be f returned holding a clone of the supplied Mapping pointer, and MAP2 f will be returned holding AST__NULL. INVERT1 will be returned f holding the current value of the Invert attribute for the supplied f Mapping, and INVERT2 will be returned holding zero. * CmpFrame c If the supplied Mapping is a CmpFrame, then map1 and map2 will be f If the supplied Mapping is a CmpFrame, then MAP1 and MAP2 will be * returned holding pointers to the component Frames used to * create the CmpFrame. The component Frames are considered to be in * applied in parallel. * Frame c For any class of Frame other than a CmpFrame, map1 will be c returned holding a clone of the supplied Frame pointer, and map2 c will be returned holding a NULL pointer. f For any class of Frame other than a CmpFrame, MAP1 will be f returned holding a clone of the supplied Frame pointer, and MAP2 f will be returned holding AST__NULL. * Notes: * - The returned Invert values should be used in preference to the * current values of the Invert attribute in map1 and map2. This is * because the attributes may have changed value since the Mappings * were combined. * - Any changes made to the component Mappings using the returned * pointers will be reflected in the supplied Mapping. *-- * Implementation Notes: * This function implements the public interface for the * astDecompose method. It is identical to astDecompose_ except for * the following: * * - ID values are returned via the "map1" and "map2" parameters * instead of true C pointers. This is required because this * conversion cannot be performed by the macro that invokes the * function. */ /* Check the global error status. */ if ( !astOK ) return; /* Invoke the normal astDecompose_ function to decompose the Mapping. */ astDecompose( this, map1, map2, series, invert1, invert2 ); /* If required, return ID values for the component Mappings. */ if ( map1 ) *map1 = astMakeId( *map1 ); if ( map2 ) *map2 = astMakeId( *map2 ); } void astMapBoxId_( AstMapping *this, const double lbnd_in[], const double ubnd_in[], int forward, int coord_out, double *lbnd_out, double *ubnd_out, double xl[], double xu[], int *status ) { /* *++ * Name: c astMapBox f AST_MAPBOX * Purpose: * Find a bounding box for a Mapping. * Type: * Public virtual function. * Synopsis: c #include "mapping.h" c void astMapBox( AstMapping *this, c const double lbnd_in[], const double ubnd_in[], c int forward, int coord_out, c double *lbnd_out, double *ubnd_out, c double xl[], double xu[] ); f CALL AST_MAPBOX( THIS, LBND_IN, UBND_IN, FORWARD, COORD_OUT, f LBND_OUT, UBND_OUT, XL, XU, STATUS ) * Class Membership: * Mapping method. * Description: c This function allows you to find the "bounding box" which just c encloses another box after it has been transformed by a Mapping c (using either its forward or inverse transformation). A typical c use might be to calculate the size of an image after being c transformed by a Mapping. f This routine allows you to find the "bounding box" which just f encloses another box after it has been transformed by a Mapping f (using either its forward or inverse transformation). A typical f use might be to calculate the size of an image after being f transformed by a Mapping. * c The function works on one dimension at a time. When supplied c with the lower and upper bounds of a rectangular region (box) of c input coordinate space, it finds the lowest and highest values c taken by a nominated output coordinate within that c region. Optionally, it also returns the input coordinates where c these bounding values are attained. It should be used repeatedly c to obtain the extent of the bounding box in more than one c dimension. f The routine works on one dimension at a time. When supplied with f the lower and upper bounds of a rectangular region (box) of f input coordinate space, it finds the lowest and highest values f taken by a nominated output coordinate within that region. It f also returns the input coordinates where these bounding values f are attained. It should be used repeatedly to obtain the extent f of the bounding box in more than one dimension. * Parameters: c this f THIS = INTEGER (Given) * Pointer to the Mapping. c lbnd_in f LBND_IN( * ) = DOUBLE PRECISION (Given) c Pointer to an array of double, with one element for each c Mapping input coordinate. This should contain the lower bound c of the input box in each input dimension. f An array with one element for each Mapping input f coordinate. This should contain the lower bound of the input f box in each input dimension. c ubnd_in f UBND_IN( * ) = DOUBLE PRECISION (Given) c Pointer to an array of double, with one element for each c Mapping input coordinate. This should contain the upper bound c of the input box in each input dimension. f An array with one element for each Mapping input f coordinate. This should contain the upper bound of the input f box in each input dimension. * * Note that it is permissible for the upper bound to be less * than the corresponding lower bound, as the values will simply * be swapped before use. c forward f FORWARD = LOGICAL (Given) c If this value is non-zero, then the Mapping's forward c transformation will be used to transform the input c box. Otherwise, its inverse transformation will be used. f If this value is .TRUE., then the Mapping's forward f transformation will be used to transform the input f box. Otherwise, its inverse transformation will be used. * c (If the inverse transformation is selected, then references c to "input" and "output" coordinates in this description c should be transposed. For example, the size of the "lbnd_in" c and "ubnd_in" arrays should match the number of output c coordinates, as given by the Mapping's Nout c attribute. Similarly, the "coord_out" parameter, below, c should nominate one of the Mapping's input coordinates.) f (If the inverse transformation is selected, then references f to "input" and "output" coordinates in this description f should be transposed. For example, the size of the LBND_IN f and UBND_IN arrays should match the number of output f coordinates, as given by the Mapping's Nout attribute. f Similarly, the COORD_OUT argument, below, should nominate one f of the Mapping's input coordinates.) c coord_out f COORD_OUT = INTEGER (Given) * The index of the output coordinate for which the lower and * upper bounds are required. This value should be at least one, * and no larger than the number of Mapping output coordinates. c lbnd_out f LBND_OUT = DOUBLE PRECISION (Returned) c Pointer to a double in which to return the lowest value taken c by the nominated output coordinate within the specified c region of input coordinate space. f The lowest value taken by the nominated output coordinate f within the specified region of input coordinate space. c ubnd_out f UBND_OUT = DOUBLE PRECISION (Returned) c Pointer to a double in which to return the highest value c taken by the nominated output coordinate within the specified c region of input coordinate space. f The highest value taken by the nominated output coordinate f within the specified region of input coordinate space. c xl f XL( * ) = DOUBLE PRECISION (Returned) c An optional pointer to an array of double, with one element c for each Mapping input coordinate. If given, this array will c be filled with the coordinates of an input point (although c not necessarily a unique one) for which the nominated output c coordinate attains the lower bound value returned in c "*lbnd_out". c c If these coordinates are not required, a NULL pointer may be c supplied. f An array with one element for each Mapping input f coordinate. This will return the coordinates of an input f point (although not necessarily a unique one) for which the f nominated output coordinate attains the lower bound value f returned in LBND_OUT. c xu f XU( * ) = DOUBLE PRECISION (Returned) c An optional pointer to an array of double, with one element c for each Mapping input coordinate. If given, this array will c be filled with the coordinates of an input point (although c not necessarily a unique one) for which the nominated output c coordinate attains the upper bound value returned in c "*ubnd_out". c c If these coordinates are not required, a NULL pointer may be c supplied. f An array with one element for each Mapping input f coordinate. This will return the coordinates of an input f point (although not necessarily a unique one) for which the f nominated output coordinate attains the upper bound value f returned in UBND_OUT. f STATUS = INTEGER (Given and Returned) f The global status. * Notes: * - Any input points which are transformed by the Mapping to give * output coordinates containing the value AST__BAD are regarded as * invalid and are ignored. They will make no contribution to * determining the output bounds, even although the nominated * output coordinate might still have a valid value at such points. c - An error will occur if the required output bounds cannot be c found. Typically, this might happen if all the input points c which the function considers turn out to be invalid (see c above). The number of points considered before generating such c an error is quite large, so this is unlikely to occur by c accident unless valid points are restricted to a very small c subset of the input coordinate space. f - An error will occur if the required output bounds cannot be f found. Typically, this might happen if all the input points f which the routine considers turn out to be invalid (see f above). The number of points considered before generating such f an error is quite large, so this is unlikely to occur by f accident unless valid points are restricted to a very small f subset of the input coordinate space. c - The values returned via "lbnd_out", "ubnd_out", "xl" and "xu" c will be set to the value AST__BAD if this function should fail c for any reason. Their initial values on entry will not be c altered if the function is invoked with the AST error status c set. f - The values returned via LBND_OUT, UBND_OUT, XL and XU will be f set to the value AST__BAD if this routine should fail for any f reason. Their initial values on entry will not be altered if the f routine is invoked with STATUS set to an error value. *-- * Implementation Notes: * This function implements the public interface for the astMapBox * method. It is identical to astMapBox_ except that the nominated * output coordinate given in "coord_out" is decremented by one * before use. This is to allow the public interface to use * one-based coordinate numbering (internally, zero-based * coordinate numbering is used). */ /* Check the global error status. */ if ( !astOK ) return; /* Invoke the protected version of this function with the "coord_out" value decremented. */ astMapBox( this, lbnd_in, ubnd_in, forward, coord_out - 1, lbnd_out, ubnd_out, xl, xu ); } double astRateId_( AstMapping *this, double *at, int ax1, int ax2, int *status ){ /* *++ * Name: c astRate f AST_RATE * Purpose: * Calculate the rate of change of a Mapping output. * Type: * Public virtual function. * Synopsis: c #include "mapping.h" c double astRate( AstMapping *this, double *at, int ax1, int ax2 ) f RESULT = AST_RATE( THIS, AT, AX1, AX2, STATUS ) * Class Membership: * Mapping method. * Description: c This function f This routine * evaluates the rate of change of a specified output of the supplied * Mapping with respect to a specified input, at a specified input * position. * * The result is estimated by interpolating the function using a * fourth order polynomial in the neighbourhood of the specified * position. The size of the neighbourhood used is chosen to minimise * the RMS residual per unit length between the interpolating * polynomial and the supplied Mapping function. This method produces * good accuracy but can involve evaluating the Mapping 100 or more * times. * Parameters: c this f THIS = INTEGER (Given) * Pointer to the Mapping to be applied. c at f AT( * ) = DOUBLE PRECISION (Given) c The address of an f An * array holding the axis values at the position at which the rate * of change is to be evaluated. The number of elements in this * array should equal the number of inputs to the Mapping. c ax1 f AX1 = INTEGER (Given) * The index of the Mapping output for which the rate of change is to * be found (output numbering starts at 1 for the first output). c ax2 f AX2 = INTEGER (Given) * The index of the Mapping input which is to be varied in order to * find the rate of change (input numbering starts at 1 for the first * input). f STATUS = INTEGER (Given and Returned) f The global status. * Returned Value: c astRate() f AST_RATE = DOUBLE PRECISION c The rate of change of Mapping output "ax1" with respect to input c "ax2", evaluated at "at", or AST__BAD if the value cannot be c calculated. f The rate of change of Mapping output AX1 with respect to input f AX2, evaluated at AT, or AST__BAD if the value cannot be f calculated. * Notes: * - A value of AST__BAD 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 public interface for the astRate * method. It is identical to astRate_ except that the nominated * coordinates given in "ax1" and "ax2" are decremented by one * before use. This is to allow the public interface to use * one-based coordinate numbering (internally, zero-based * coordinate numbering is used). */ /* Check the global error status. */ if ( !astOK ) return AST__BAD; /* Invoke the protected version of this function with the axis indices decremented. */ return astRate( this, at, ax1 - 1, ax2 - 1 ); } void astMapSplitId_( AstMapping *this, int nin, const int *in, int *out, AstMapping **map, int *status ){ /* *++ * Name: c astMapSplit f AST_MAPSPLIT * Purpose: * Split a Mapping up into parallel component Mappings. * Type: * Public virtual function. * Synopsis: c #include "mapping.h" c void astMapSplit( AstMapping *this, int nin, const int *in, int *out, c AstMapping **map ) f CALL AST_MAPSPLIT( THIS, NIN, IN, OUT, MAP, STATUS ) * Class Membership: * Mapping method. * Description: c This function f This routine * creates a new Mapping which connects specified inputs within a * supplied Mapping to the corresponding outputs of the supplied Mapping. * This is only possible if the specified inputs correspond to some * subset of the Mapping outputs. That is, there must exist a subset of * the Mapping outputs for which each output depends only on the selected * Mapping inputs, and not on any of the inputs which have not been * selected. Also, any output which is not in this subset must not depend * on any of the selected inputs. If these conditions are not met by the * supplied Mapping, then c a NULL f an AST__NULL * Mapping pointer is returned. * Parameters: c this f THIS = INTEGER (Given) * Pointer to the Mapping to be split. c nin f NIN = INTEGER (Given) c The number of inputs to pick from "this". f The number of inputs to pick from THIS. c in f IN( NIN ) = INTEGER (Given) c Pointer to an f An * array holding the indices within the supplied Mapping of the inputs * which are to be picked from the Mapping. c This array should have "nin" elements. * If "Nin" is the number of inputs of the supplied Mapping, then each * element should have a value in the range 1 to Nin. c out f OUT( * ) = INTEGER (Returned) c Pointer to an f An * array in which to return the indices of the outputs of the supplied * Mapping which are fed by the picked inputs. A value of one is * used to refer to the first Mapping output. The supplied array should * have a length at least equal to the number of outputs in the * supplied Mapping. The number of values stored in the array on * exit will equal the number of outputs in the returned Mapping. * The i'th element in the returned array holds the index within * the supplied Mapping which corresponds to the i'th output of * the returned Mapping. c map f MAP = INTEGER (Returned) c Address of a location at which to return a pointer to the f The * returned Mapping. This Mapping will have c "nin" inputs (the number of outputs may be different to "nin"). NULL f NIN inputs (the number of outputs may be different to NIN). AST__NULL * is returned if the supplied Mapping has no subset of outputs which * depend only on the selected inputs. The returned Mapping is a * deep copy of the required parts of the supplied Mapping. * Notes: * - If this c function f routine * is invoked with the global error status set, or if it should fail for * any reason, then c a NULL value f AST__NULL * will be returned for c the "map" pointer. f MAP. *-- * Implementation Notes: * - This function implements the astMapSplit method available via the * public interface to the Mapping class and uses 1-based axis indices. * The protected interface method is provided by the astMapSplit function * and uses zero-based axis indices. Also, an ID value is returned for * "map" rather than a pointer. */ /* Local Variables: */ int *in_zero; /* Pointer to array of zero-based input indices */ int *result; /* Pointer to array of zero-based output indices*/ int i; /* Axis index */ int nout; /* No of outputs */ /* Initialise */ *map = NULL; /* Check the global error status. */ if ( !astOK ) return; /* Decrement the axis indices by 1. */ in_zero = astMalloc( sizeof( int )*(size_t) nin ); if( in_zero ) { for( i = 0; i < nin; i++ ) in_zero[ i ] = in[ i ] - 1; /* Invoked the protected astMapSplit functon. */ result = astMapSplit( this, nin, in_zero, map ); /* If succesful, copy the output axes to the supplied array. */ if( result ) { nout = astGetNout( *map ); for( i = 0; i < nout; i++ ) out[ i ] = result[ i ] + 1; /* Free resurces. */ result = astFree( result ); } in_zero = astFree( in_zero ); } /* Free the returned Mapping if an error has occurred. */ if( !astOK ) *map = astAnnul( *map ); /* Return an ID value for the Mapping. */ *map = astMakeId( *map ); }