00001 //# Slicer.h: specify which elements to extract from an n-dimensional array 00002 //# Copyright (C) 1994,1995,1997,1999 00003 //# Associated Universities, Inc. Washington DC, USA. 00004 //# 00005 //# This library is free software; you can redistribute it and/or modify it 00006 //# under the terms of the GNU Library General Public License as published by 00007 //# the Free Software Foundation; either version 2 of the License, or (at your 00008 //# option) any later version. 00009 //# 00010 //# This library is distributed in the hope that it will be useful, but WITHOUT 00011 //# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 00012 //# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public 00013 //# License for more details. 00014 //# 00015 //# You should have received a copy of the GNU Library General Public License 00016 //# along with this library; if not, write to the Free Software Foundation, 00017 //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. 00018 //# 00019 //# Correspondence concerning AIPS++ should be addressed as follows: 00020 //# Internet email: aips2-request@nrao.edu. 00021 //# Postal address: AIPS++ Project Office 00022 //# National Radio Astronomy Observatory 00023 //# 520 Edgemont Road 00024 //# Charlottesville, VA 22903-2475 USA 00025 //# 00026 //# $Id$ 00027 00028 #ifndef CASA_SLICER_H 00029 #define CASA_SLICER_H 00030 00031 00032 //# Includes 00033 #include <casacore/casa/aips.h> 00034 #include <casacore/casa/Arrays/IPosition.h> 00035 00036 namespace casacore { //# NAMESPACE CASACORE - BEGIN 00037 00038 //# Forward Declarations 00039 class Slice; 00040 00041 00042 // <summary> 00043 // Specify which elements to extract from an n-dimensional array 00044 // </summary> 00045 00046 // <reviewed reviewer="Paul Shannon" date="1994/07/07" tests="tSlicer"> 00047 // The review and modification of this class were undertaken, in part, 00048 // with the aim of making this class header an example -- this is what 00049 // the Casacore project thinks a class header should look like. 00050 // </reviewed> 00051 00052 // <prerequisite> 00053 // You should have at least a preliminary understanding of these classes: 00054 // <li> <linkto class=IPosition>IPosition</linkto> 00055 // <li> <linkto class=Array>Array</linkto> 00056 // <li> <linkto class=Slice>Slice</linkto> 00057 // </prerequisite> 00058 00059 // <etymology> 00060 // The class name "Slicer" may be thought of as a short form 00061 // of "n-Dimensional Slice Specifier." Some confusion is possible 00062 // between class "Slice" and this class. 00063 // </etymology> 00064 // 00065 // <synopsis> 00066 // If you need to extract or operate upon a portion of an array, 00067 // the Slicer class is the best way to specify the subarray you are 00068 // interested in. 00069 // 00070 // Slicer has many constructors. Of these, some require that the 00071 // programmer supply a full specification of the array elements he 00072 // wants to extract; other constructors make do with partial information. 00073 // In the latter case, the constructor will assume sensible default values or, 00074 // when directed, infer missing information from the array that's getting 00075 // sliced (hereafter, the "source" array). 00076 // 00077 // <h4> Constructing With Full Information </h4> 00078 // 00079 // To fully specify a subarray, you must supply three pieces of information 00080 // for each axis of the subarray: 00081 // 00082 // <ol> 00083 // <li> where to start 00084 // <li> how many elements to extract 00085 // <li> what stride (or "increment" or "interval") to use: a stride of 00086 // "n" means pick extract only every "nth" element along an axis 00087 // </ol> 00088 // 00089 // The most basic constructor for Slicer illustrates this. To create 00090 // an Slicer for getting selected elements from a 3D array: 00091 // 00092 // <srcblock> 00093 // IPosition start (3,0,0,0), length (3,10,10,10), stride (3,3,3,3); 00094 // Slicer slicer (start, length, stride); 00095 // // assume proper declarations, and meaningful values in the source array 00096 // subArray = sourceArray (slicer); 00097 // </srcblock> 00098 // It gets elements 0,3,6,9,12,15,18,21,24,27 for each dimension. 00099 // 00100 // <note role=caution> If you wish to extract elements from the array 00101 // at intervals, these intervals must be regular. The interval is one 00102 // constant integer for each dimension of the array: it cannot be a function. 00103 // </note> 00104 // 00105 // <note role=caution> "length", the second parameter to the Slicer 00106 // constructor above, may actually be used in two ways. In normal 00107 // (and default) use, it specifies how many elements to select from the 00108 // source. In the alternative use, it specifies the index of the last element 00109 // to extract from the source array. This ambiguity (does "end" mean 00110 // "length" or does it mean "last index"?) is handled by a default 00111 // fourth parameter to the constructor. This code fragment will 00112 // extract the same subarray as the example above: 00113 // <srcblock> 00114 // IPosition start (3,0,0,0), end (3,27,27,27), stride (3,3,3,3); 00115 // Slicer slicer (start, end, stride, Slicer::endIsLast); 00116 // subArray = sourceArray (slicer); 00117 // </srcblock> 00118 // Note that in this example end(3,28,29,28) gives the same result. 00119 // (We use "end" as the name of the formal parameter because it supports 00120 // both meanings -- "last index" or "length." You may wish to use a 00121 // clarifying name for the actual parameter in your code, as we have 00122 // above when we used "length".) 00123 // </note> 00124 // Similar to Python it is possible to address the start and/or end value 00125 // from the end by giving a negative value (-1 means the last value). 00126 // However, a length and stride cannot be negative. 00127 // Unlike Python the end value is inclusive (as discussed above). 00128 // For example, 00129 // <srcblock> 00130 // Slicer slicer (IPosition(1,-4), IPosition(1,-2), Slicer::endIsLast) 00131 // Slicer slicer (IPosition(1,6), IPosition(1,8), Slicer::endIsLast) 00132 // </srcblock> 00133 // Both Slicers give the same result when used on a Vector with length 10. 00134 // 00135 // <h4> Constructing with Partial Information </h4> 00136 // 00137 // Some of the constructors don't require complete information: Slicer 00138 // either calculates sensible default values or deduces them from the 00139 // source array. If you do not specify a "stride" argument, for example, 00140 // a value of 1 will be used for all dimensions. If you specify a "start" 00141 // but nothing else, a stride of 1, and (perhaps against expectation) 00142 // a length of 1 will be used. 00143 // 00144 // Note that using a negative start or end is also partial information. 00145 // The actual array shape is needed to derive the exact start or end value. 00146 // 00147 // To instruct the Slicer to get otherwise unspecified information 00148 // from the source array, you can create an IPosition like "end" 00149 // as shown here: 00150 // 00151 // <srcblock> 00152 // IPosition start (3,0,0,0), stride (3,3,3,3); 00153 // IPosition end (3,Slicer::MimicSource, Slicer::MimicSource, 00154 // Slicer::MimicSource); 00155 // Slicer smartSlicer (start, end, stride); 00156 // // assume proper declarations... 00157 // subArray = sourceArray (smartSlicer) 00158 // </srcblock> 00159 // 00160 // If you are a library programmer, and write a class that can be sliced 00161 // by the Slicer class, you need to understand the mechanism for 00162 // completing the information which the application programmer, in using 00163 // your class, specified incompletely. (If you are an application 00164 // programmer, who wants to slice a library class, this explanation will 00165 // be only of academic interest.) 00166 // 00167 // When the source array (the library class you provide) gets the Slicer -- 00168 // which typically comes when the source array is asked to return a 00169 // reference to a subarray -- the source does a callback to the Slicer 00170 // object. The source array passes its own shape as one of the arguments 00171 // to the Slicer callback and asks the Slicer to fill in the missing 00172 // values from that shape. 00173 // 00174 // In use, and with an imagined class "MyVector", code would look 00175 // like this: 00176 // <srcblock> 00177 // // first, a fragment from the application program: 00178 // IPosition start (1,10), end (1, Slicer::MimicSource); 00179 // Slicer slicer (start, end); 00180 // MyVector <Int> v0 (100); 00181 // MyVector <Int> v1 = v0 (slicer); 00182 // //.... 00183 // // second, a fragment from a constructor of the library class "MyVector": 00184 // // the MyVector class will construct v1 as a reference to 00185 // // selected elements of v0, using (among other things) a 00186 // // callback to the slicer it was passed (above, in the 00187 // // construction of v1. 00188 // // 00189 // IPosition start, end, stride; 00190 // fullSliceInformation = 00191 // slicer.inferShapeFromSource (MyVector::shape(), start, end, stride); 00192 // // now the MyVector instance knows everything it needs to 00193 // // construct the instance. 00194 // </srcblock> 00195 // Please note that v1 will have a length of 90, and refer to elements 00196 // 10-99 of v0. 00197 // 00198 // <note role=warning> An exception will be thrown if the positions 00199 // defined in the Slicer exceed the source array's shape. 00200 // </note> 00201 // </synopsis> 00202 // 00203 // <example> 00204 // Given a large image, 4k on a side, extract (by sampling) an image 00205 // 1k on a side, but covering the same region as the original. 00206 // 00207 // <srcblock> 00208 // Image <Float> image ("N5364.fits"); // a 4-d VLA map, 4096 x 4096 x 3 x 1 00209 // IPosition start (4,0,0,0,0), stride (4,4,4,1,1); 00210 // IPosition end (4, Slicer::MimicSource, Slicer::MimicSource, 00211 // Slicer::MimicSource, Slicer::MimicSource); 00212 // Slicer smartSlicer (start, end, stride); 00213 // // assume proper declarations... 00214 // Image <Float> subImage = image (smartSlicer); 00215 // </srcblock> 00216 // 00217 // </example> 00218 00219 // <motivation> 00220 // Slicer is particularly convenient for designers of other library 00221 // classes: Array and Image, for example. (In fact, this convenience 00222 // was the original motivation for the class.) The benefit 00223 // is this: the application programmer, who needs a slice of an Array, 00224 // may provide slicing specifications in many different ways, but the 00225 // Array class author needs to provide only one member function to 00226 // return the slice. The Slicer class, in effect, and with its 00227 // many constructors, provides a way to funnel all of the variety 00228 // into a single member function call to the array or image class. 00229 // 00230 // For example, imagine a 100 x 100 x 100 array from which you want to 00231 // extract various subarrays. Here are some of the ways you might 00232 // specify the the subarray in the -absence- of Slicer. 00233 // 00234 // <srcblock> 00235 // // preliminaries: create a cube and assign values to all elements -- 00236 // // this will be "source" array 00237 // Cube <Int> bigCube (IPosition (3, 100, 100, 100)); 00238 // assignValues (bigCube); 00239 // // declare a smaller cube, the destination array. 00240 // Cube <Int> smallCube (IPosition (3, 10, 10, 10)); 00241 // 00242 // // example 1: use Slice objects to extract a subcube -- the first 00243 // // ten elements along each axis 00244 // Slice xIndices (0,10,1), yIndices (0,10,1), zIndices (0,10,1); 00245 // smallCube = bigCube (xIndices, yIndices, zIndices); 00246 // 00247 // // example 2: get the same subcube using three IPosition arguments 00248 // IPosition start (3,0,0,0), end (3,10,10,10), stride (3,1,1,1); 00249 // smallCube = bigCube (start, end, stride); 00250 // 00251 // // example 3: use 2 IPositions, letting the 3rd (stride) default to 00252 // // IPosition (3,1,1,1) 00253 // smallCube = bigCube (start, end); 00254 // </srcblock> 00255 // 00256 // So the Cube class (together with its base class) must define three separate 00257 // member functions for the essentially identical operation of 00258 // extracting a subcube. The same replication is also required of 00259 // Image, Array, and the other Array subclasses (Matrix and Vector). 00260 // 00261 // The Slicer class collapses all of this into a single member 00262 // function per class: 00263 // 00264 // <srcblock> 00265 // Slicer slicer = (call the constructor that best suits your problem) 00266 // smallCube = bigCube (slicer); 00267 // </srcblock> 00268 // 00269 // Since there are many constructors available for Slicer, you 00270 // can still specify the subarray that you may want in a number of 00271 // different ways, by constructing the Slicer in the way most natural 00272 // to your circumstances. You then pass the Slicer to the array, and 00273 // you will get back the slice you want. 00274 // 00275 // This class also offers the application programmer considerable 00276 // flexibility by allowing the shape of the source array to determine 00277 // some of the slice specification. This benefit is explained and 00278 // demonstrated above. 00279 // </motivation> 00280 00281 // <todo asof="1994/07/01"> 00282 // <li> This class, and the TableArray, Array and Image classes, 00283 // could allow for the extraction of a subarray with fewer axes than the 00284 // source array. At present, for example, you cannot, directly slice 00285 // a matrix from a cube. 00286 // </todo> 00287 00288 00289 class Slicer 00290 { 00291 public: 00292 00293 // Define the "MimicSource" value which defines the open start or end. 00294 // This value should be different from MIN_INT in IPosition.h. 00295 // It should also not be the lowest possible value, since that 00296 // will probably be used as an undefined value. 00297 // It must be a negative number. 00298 enum {MimicSource= -2147483646}; 00299 00300 // Define the possible interpretations of the end-value. 00301 enum LengthOrLast { 00302 // The end-values given in the constructor define the lengths. 00303 endIsLength, 00304 // The end-values given in the constructor define the trc. 00305 endIsLast 00306 }; 00307 00308 // Construct a 1-dimensional Slicer. 00309 // Start and end are inferred from the source; stride=1. 00310 // "endIsLength" and "endIsLast" are identical here, so there's 00311 // no need to discriminate between them by using a default parameter. 00312 Slicer(); 00313 00314 // The member function <src>inferShapeFromSource</src> 00315 // (invoked as a callback by the 00316 // source array) will use the shape of the source array for the 00317 // unspecified values: IPosition elements with the value 00318 // Slicer::MimicSource 00319 // <thrown> 00320 // <li> ArraySlicerError 00321 // </thrown> 00322 // Create a Slicer with a given start, end (or length), and stride. 00323 // An exception will be thrown if a negative length or non-positive 00324 // stride is given or if the IPositions start, end, and stride 00325 // do not have the same dimensionality. 00326 // If length or stride is not given, they default to 1. 00327 // <br> It is possible to leave values in start and end undefined 00328 // by giving the value <src>MimicSource</src>. They can be filled 00329 // in later with the actual array shape using function 00330 // <src>inferShapeFromSource</src>. 00331 // <group> 00332 Slicer (const IPosition& start, const IPosition& end, 00333 const IPosition& stride, 00334 LengthOrLast endInterpretation = endIsLength); 00335 Slicer (const IPosition& start, const IPosition& end, 00336 LengthOrLast endInterpretation = endIsLength); 00337 explicit Slicer (const IPosition& start); 00338 // </group> 00339 00340 // Create a Slicer object from Slice objects. 00341 // In a Slice object one defines the start, length, and stride for 00342 // one axis. 00343 // The default Slice constructor (called with no arguments) creates 00344 // a Slice with start and length equal to zero, and an undefined stride. 00345 // <group> 00346 // Create a Slicer for a 1-dimensional array. 00347 Slicer (const Slice& x, LengthOrLast endInterpretation = endIsLength); 00348 00349 // Create a Slicer for a 2-dim array. 00350 Slicer (const Slice& x, const Slice& y, 00351 LengthOrLast endInterpretation = endIsLength); 00352 00353 // Create a Slicer for a 3-dim array. 00354 Slicer (const Slice& x, const Slice& y, const Slice& z, 00355 LengthOrLast endInterpretation = endIsLength); 00356 // </group> 00357 00358 // Copy constructor (copy semantics). 00359 Slicer (const Slicer&); 00360 00361 // Assignment (copy semantics). 00362 Slicer& operator= (const Slicer&); 00363 00364 // Equality 00365 Bool operator==(const Slicer&) const; 00366 00367 // Return the number of dimensions of the Slicer. 00368 uInt ndim() const; 00369 00370 // This function checks all of the start, length (or end), 00371 // and stride IPositions, and fills in missing values by 00372 // getting the corresponding values from the shape of the 00373 // source array. 00374 // These will first be resized, if necessary. 00375 // If, for a given axis, (end < start) , it means that a 00376 // length of zero was specified. 00377 // An exception is thrown if the 00378 // start, end, or length exceeds the array shape or if the 00379 // dimensionality of the array and Slicer do not conform. 00380 // <thrown> 00381 // <li> ArraySlicerError 00382 // </thrown> 00383 IPosition inferShapeFromSource 00384 (const IPosition& shape, IPosition& startResult, 00385 IPosition& endResult, IPosition& strideResult) const; 00386 00387 // Report the defined starting position. 00388 const IPosition& start() const; 00389 00390 // Report the defined ending position. 00391 const IPosition& end() const; 00392 00393 // Report the defined stride. 00394 const IPosition& stride() const; 00395 00396 // Report the length of the resulting axes. 00397 const IPosition& length() const; 00398 00399 // Are all values fixed (i.e., no MimicSource given)? 00400 Bool isFixed() const; 00401 00402 // Set the start and end positions. No explicit checking is done that 00403 // the input parameters make sense, so you must be certain if you 00404 // call these. These are useful if you have a loop with many iterations 00405 // and you do not wish the overhead of creating a new Slicer object 00406 // for each iteration if the only thing you are doing is adjusting 00407 // the start and end positions. Other than for performance reasons, 00408 // these methods should not be called and you should prefer the 00409 // error checking provided by constructing a new Slicer object. 00410 // Note that the length is not updated, so in principle care should 00411 // be taken that the length does not change. 00412 // <group> 00413 void setStart (const IPosition& start) 00414 { start_p = start; } 00415 void setEnd (const IPosition& end) 00416 { end_p = end; } 00417 // </group> 00418 00419 00420 private: 00421 LengthOrLast asEnd_p; 00422 IPosition start_p; 00423 IPosition end_p; 00424 IPosition stride_p; 00425 IPosition len_p; // Length of input 00426 Bool fixed_p; // no MimicSource used 00427 00428 // Define a private constructor taking an ssize_t. 00429 // This is to prevent the user from the unexpected and meaningless 00430 // Slicer that would result when the ssize_t argument is promoted to 00431 // an IPosition. 00432 Slicer (ssize_t); 00433 00434 // Check the given start, end/length and stride. 00435 // Fill in the length or end. 00436 // It also calls <src>fillFixed</src> to fill the fixed flag. 00437 void fillEndLen(); 00438 00439 // Fill in start, len and stride from a Slice. 00440 void fillSlice (const Slice&, ssize_t& start, ssize_t& length, 00441 ssize_t& stride); 00442 00443 // Fill the fixed flag. 00444 void fillFixed(); 00445 }; 00446 00447 00448 // <summary>IO functions for Slicer's</summary> 00449 // <group name="Slicer IO"> 00450 // Print the contents of the specified Slicer to the specified stream. 00451 std::ostream& operator << (std::ostream& stream, const Slicer& slicer); 00452 // </group> 00453 00454 00455 00456 inline uInt Slicer::ndim() const 00457 { return start_p.nelements(); } 00458 00459 inline const IPosition& Slicer::start() const 00460 { return start_p; } 00461 00462 inline const IPosition& Slicer::end() const 00463 { return end_p; } 00464 00465 inline const IPosition& Slicer::stride() const 00466 { return stride_p; } 00467 00468 inline const IPosition& Slicer::length() const 00469 { return len_p; } 00470 00471 inline Bool Slicer::isFixed() const 00472 { return fixed_p; } 00473 00474 00475 00476 } //# NAMESPACE CASACORE - END 00477 00478 #endif 00479