diff options
author | William Joye <wjoye@cfa.harvard.edu> | 2019-05-10 18:45:23 (GMT) |
---|---|---|
committer | William Joye <wjoye@cfa.harvard.edu> | 2019-05-10 18:45:23 (GMT) |
commit | 0b1addf5643c828955a6add53f2a4f7a7a813e41 (patch) | |
tree | 94fe32a76399be787746c3c403cca2076a253e29 /ast/mapping.c | |
parent | 75fab9d80911f74aaab738fa9ab8a4f9b0f57a6b (diff) | |
download | blt-0b1addf5643c828955a6add53f2a4f7a7a813e41.zip blt-0b1addf5643c828955a6add53f2a4f7a7a813e41.tar.gz blt-0b1addf5643c828955a6add53f2a4f7a7a813e41.tar.bz2 |
upgrade ast 8.7.1
Diffstat (limited to 'ast/mapping.c')
-rw-r--r-- | ast/mapping.c | 24759 |
1 files changed, 24759 insertions, 0 deletions
diff --git a/ast/mapping.c b/ast/mapping.c new file mode 100644 index 0000000..6bcf058 --- /dev/null +++ b/ast/mapping.c @@ -0,0 +1,24759 @@ +/* +*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<X>: Rebin a region of a data grid +c - astRebinSeq<X>: Rebin a region of a sequence of data grids +c - astResample<X>: 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<X>: Rebin a region of a data grid +f - AST_REBINSEQ<X>: Rebin a region of a sequence of data grids +f - AST_REMOVEREGIONS: Remove any Regions from a Mapping +f - AST_RESAMPLE<X>: 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 +* <http://www.gnu.org/licenses/>. + +* 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<X>. +* 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<X>. +* 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<X>. +* 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_<x>D rebinning macros, correct the test for the +* central point being outside the bounds of the output image. +* 13-AUG-2010 (DSB): +* In astRebinSeq<X>, 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<X> 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<X> 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<X>. +* 30-AUG_2012 (DSB): +* Added AST__CONSERVEFLUX flag for astRebinSeq<X>. +* 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<X> 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<X> 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<X> 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 <config.h> +#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 <float.h> +#include <limits.h> +#include <math.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +/* 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<X> + +* 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<X>( double factor, int npoint, const int *offset, +* <Xtype> badval, <Xtype> *out, +* <Xtype> *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 <X> 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<X> + +* Purpose: +* Resample a data grid, using a 1-d interpolation kernel. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* 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 ) + +* 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 <X> 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 + <X>, HI_<X> gives the least positive floating point value which + just overflows that data type towards plus infinity, while LO_<X> + gives the least negative floating point value which just overflows + that data type towards minus infinity. Thus, a floating point value + must satisfy LO<flt_value<HI if overflow is not to occur when it is + converted to that data type. + + The data type of each limit should be that of the smallest + precision floating point type which will accommodate the full range + of values that the target type may take. */ + +/* If <X> 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<X> + +* Purpose: +* Resample a data grid, using the linear interpolation scheme. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* 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 ) + +* 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 <X> 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<X> + +* Purpose: +* Resample a data grid, using the nearest-pixel interpolation scheme. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* 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 ) + +* 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 <X> 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<X> + +* Purpose: +* Resample a data grid, using multidimensional block averaging. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* 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 ) + +* 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 <X> 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 + <X>, HI_<X> gives the least positive floating point value which + just overflows that data type towards plus infinity, while LO_<X> + gives the least negative floating point value which just overflows + that data type towards minus infinity. Thus, a floating point value + must satisfy LO<flt_value<HI if overflow is not to occur when it is + converted to that data type. + + The data type of each limit should be that of the smallest + precision floating point type which will accommodate the full range + of values that the target type may take. */ + +/* If <X> 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<X> +f AST_REBIN<X> + +* Purpose: +* Rebin a region of a data grid. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "mapping.h" +c void astRebin<X>( AstMapping *this, double wlim, int ndim_in, +c const int lbnd_in[], const int ubnd_in[], +c const <Xtype> in[], const <Xtype> in_var[], +c int spread, const double params[], int flags, +c double tol, int maxpix, +c <Xtype> badval, int ndim_out, +c const int lbnd_out[], const int ubnd_out[], +c const int lbnd[], const int ubnd[], +c <Xtype> out[], <Xtype> out_var[] ); +f CALL AST_REBIN<X>( 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<X> functions +f AST_REBINSEQ<X> 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 <X> in +c the generic function name astRebin<X> by an appropriate 1- or +f the generic function name AST_REBIN<X> 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( * ) = <Xtype> (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( * ) = <Xtype> (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 = <Xtype> (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( * ) = <Xtype> (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( * ) = <Xtype> (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 <X> in the generic function name astRebin<X> with a +f replace <X> in the generic function name AST_REBIN<X> with a +* 1- or 2-character data type code, so as to match the numerical +* type <Xtype> of the data you are processing, as follows: +c - D: double +c - F: float +c - 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<X>, the astRebin<X> +f AST_RESAMPLE<X>, the AST_REBIN<X> +* 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<X> +f AST_RESAMPLE<X> +* 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<X> +f AST_RESAMPLE<X> +* 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<X> +f AST_RESAMPLE<X> +* 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<X> but not with AST_RESAMPLE<X>: +c astRebin<X> but not with astResample<X>: +* +* - 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<X> +f AST_REBINSEQ<X> + +* Purpose: +* Rebin a region of a sequence of data grids. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "mapping.h" +c void astRebinSeq<X>( AstMapping *this, double wlim, int ndim_in, +c const int lbnd_in[], const int ubnd_in[], +c const <Xtype> in[], const <Xtype> in_var[], +c int spread, const double params[], int flags, +c double tol, int maxpix, <Xtype> badval, +c int ndim_out, const int lbnd_out[], +c const int ubnd_out[], const int lbnd[], +c const int ubnd[], <Xtype> out[], <Xtype> out_var[], +c double weights[], int64_t *nused ); +f CALL AST_REBINSEQ<X>( 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<X> +f routines is identical to AST_REBIN<X> +* 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<X>). +f AST_RESAMPLE<X>). +* 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( * ) = <Xtype> (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( * ) = <Xtype> (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<X> functions. +f AST_REBIN<X> 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<X> functions. +f AST_REBIN<X> 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 = <Xtype> (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( * ) = <Xtype> (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( * ) = <Xtype> (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 <X> in the generic function name astRebinSeq<X> with a +f replace <X> in the generic function name AST_REBINSEQ<X> with a +* 1- or 2-character data type code, so as to match the numerical +* type <Xtype> of the data you are processing, as follows: +c - D: double +c - F: float +c - 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<X>, the astRebinSeq<X> +f AST_RESAMPLE<X>, the AST_REBINSEQ<X> +* 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<X> functions). +f AST_REBIN<X> 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<F> behaves similarly to AST_RESAMPLE<X>). This +f (i.e. AST_REBINSEQ<F> behaves similarly to AST_RESAMPLE<X>). 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<X> +f AST_REBINSEQ<X> +* 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 + "<bad>" to indicate missing values. */ + printf( "(" ); + for ( coord = 0; coord < ncoord_in; coord++ ) { + if ( ptr_in[ coord ][ point ] == AST__BAD ) { + printf( "%s<bad>", 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<bad>", coord ? ", " : "" ); + } else { + printf( "%s%.*g", coord ? ", " : "", + AST__DBL_DIG, ptr_out[ coord ][ point ] ); + } + } + printf( ")\n" ); + } +} + +/* +*++ +* Name: +c astResample<X> +f AST_RESAMPLE<X> + +* Purpose: +* Resample a region of a data grid. + +* Type: +* Public virtual function. + +* Synopsis: +c #include "mapping.h" +c int astResample<X>( AstMapping *this, int ndim_in, +c const int lbnd_in[], const int ubnd_in[], +c const <Xtype> in[], const <Xtype> in_var[], +c int interp, void (* finterp)( void ), +c const double params[], int flags, +c double tol, int maxpix, +c <Xtype> badval, int ndim_out, +c const int lbnd_out[], const int ubnd_out[], +c const int lbnd[], const int ubnd[], +c <Xtype> out[], <Xtype> out_var[] ); +f RESULT = AST_RESAMPLE<X>( 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 <X> in +c the generic function name astResample<X> by an appropriate 1- or +f the generic function name AST_RESAMPLE<X> 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<X> or astRebinSeq<X> family of functions +f AST_REBIN<X> or AST_REBINSEQ<X> 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( * ) = <Xtype> (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( * ) = <Xtype> (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<X>). 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 = <Xtype> (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<X> +f circumstances under which this may occur). The AST_RESAMPLE<X> +* 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( * ) = <Xtype> (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( * ) = <Xtype> (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<X>() +f AST_RESAMPLE<X> = 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 <X> in the generic function name astResample<X> with a +f replace <X> in the generic function name AST_RESAMPLE<X> with a +* 1- or 2-character data type code, so as to match the numerical +* type <Xtype> of the data you are processing, as follows: +c - D: double +c - F: float +c - L: long int (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<X> 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<X>, 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<X> 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<X>, 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<X> + +* Purpose: +* Rebin a data grid, using a 1-d interpolation kernel. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* 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 ) + +* 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 <X> 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<X> + +* Purpose: +* Rebin a data grid, using the linear spreading scheme. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* 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 ) + +* 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 <X> 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<X> + +* Purpose: +* Rebin a data grid, using the nearest-pixel spreading scheme. + +* Type: +* Private function. + +* Synopsis: +* #include "mapping.h" +* 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 ) + +* 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 <X> 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 <Xtype> in[], const <Xtype> in_var[], +c int npoint, const int offset[], +c const double *const coords[], const double params[], +c int flags, <Xtype> badval, +c <Xtype> out[], <Xtype> 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<X> +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<X> 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<X> +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<X> 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<X>. +f way as they do in AST_RESAMPLE<X>. +c in +f IN( * ) = <Xtype> (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<X> via the "in" parameter. +f array as was passed to AST_RESAMPLE<X> via the IN argument. +* The numerical type of this array should match that of the +* data being processed. +c in_var +f IN_VAR( * ) = <Xtype> (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<X> 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<X> 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<X>. You may use this to +f This will be the same array as was given via the +f PARAMS argument of AST_RESAMPLE<X>. 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<X>. You may test this value to +f This will be the same value as was given via the FLAGS +f argument of AST_RESAMPLE<X>. 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<X>). +f values (see AST_RESAMPLE<X>). +c badval +f BADVAL = <Xtype> (Given) +c This will be the same value as was given via the "badval" +c parameter of astResample<X>, 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<X>, 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( * ) = <Xtype> (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( * ) = <Xtype> (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 <Xtype> indicates the numerical type of the data +c being processed, as for astResample<X>. +f being processed, as for AST_RESAMPLE<X>. +c - This function will typically be invoked more than once for each +c invocation of astResample<X>. +f - This routine will typically be invoked more than once for each +f invocation of AST_RESAMPLE<X>. +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<X>. 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<X>. 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<X> +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<X> 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<X> +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<X> 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<X>. You may use this to +f This will be the same array as was given via the +f PARAMS argument of AST_RESAMPLE<X>. 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<X>. You may test this value to +f This will be the same value as was given via the FLAGS +f argument of AST_RESAMPLE<X>. 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<X>). +f values (see AST_RESAMPLE<X>). +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<X>. 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<X>. 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 ); +} + + + + + + + + |