File Coverage

lib/PDL/Transform.pd
Criterion Covered Total %
statement 382 391 97.7
branch 400 2034 19.6
condition n/a
subroutine n/a
pod n/a
total 782 2425 32.2


line stmt bran cond sub pod time code
1             pp_addhdr("#include \n") unless $^O =~ /MSWin32/i;
2             pp_addpm({At=>'Top'},<<'+======EOD======');
3              
4             =head1 NAME
5              
6             PDL::Transform - Coordinate transforms, image warping, and N-D functions
7              
8             =head1 SYNOPSIS
9              
10             use PDL::Transform;
11              
12             my $t = PDL::Transform::->new()
13              
14             $out = $t->apply($in) # Apply transform to some N-vectors (Transform method)
15             $out = $in->apply($t) # Apply transform to some N-vectors (PDL method)
16              
17             $im1 = $t->map($im); # Transform image coordinates (Transform method)
18             $im1 = $im->map($t); # Transform image coordinates (PDL method)
19              
20             $t2 = $t->compose($t1); # compose two transforms
21             $t2 = $t x $t1; # compose two transforms (by analogy to matrix mult.)
22              
23             $t3 = $t2->inverse(); # invert a transform
24             $t3 = !$t2; # invert a transform (by analogy to logical "not")
25              
26             =head1 DESCRIPTION
27              
28             PDL::Transform is a convenient way to represent coordinate
29             transformations and resample images. It embodies functions mapping
30             R^N -> R^M, both with and without inverses. Provision exists for
31             parametrizing functions, and for composing them. You can use this
32             part of the Transform object to keep track of arbitrary functions
33             mapping R^N -> R^M with or without inverses.
34              
35             The simplest way to use a Transform object is to transform vector
36             data between coordinate systems. The L method
37             accepts a PDL whose 0th dimension is coordinate index (all other
38             dimensions are broadcasted over) and transforms the vectors into the new
39             coordinate system.
40              
41             Transform also includes image resampling, via the L method.
42             You define a coordinate transform using a Transform object, then use
43             it to remap an image PDL. The output is a remapped, resampled image.
44              
45             You can define and compose several transformations, then apply them
46             all at once to an image. The image is interpolated only once, when
47             all the composed transformations are applied.
48              
49             In keeping with standard practice, but somewhat counterintuitively,
50             the L engine uses the inverse transform to map coordinates
51             FROM the destination dataspace (or image plane) TO the source dataspace;
52             hence PDL::Transform keeps track of both the forward and inverse transform.
53              
54             For terseness and convenience, most of the constructors are exported
55             into the current package with the name C<< t_ >>, so the following
56             (for example) are synonyms:
57              
58             $t = PDL::Transform::Radial->new; # Long way
59              
60             $t = t_radial(); # Short way
61              
62             Several math operators are overloaded, so that you can compose and
63             invert functions with expression syntax instead of method syntax (see below).
64              
65             =head1 EXAMPLE
66              
67             Coordinate transformations and mappings are a little counterintuitive
68             at first. Here are some examples of transforms in action:
69              
70             use PDL::Transform;
71             use PDL::Graphics::Simple;
72             $x = rfits('m51.fits'); # Substitute path if necessary!
73             $ts = t_linear(Scale=>3); # Scaling transform
74              
75             $w = pgswin(xs);
76             $w->plot(with=>'image', $x);
77              
78             ## Grow m51 by a factor of 3; origin is at lower left.
79             $y = $ts->map($x,{pix=>1}); # pix option uses direct pixel coord system
80             $w->plot(with=>'image', $y);
81              
82             ## Shrink m51 by a factor of 3; origin still at lower left.
83             $c = $ts->unmap($x, {pix=>1});
84             $w->plot(with=>'image', $c);
85              
86             ## Grow m51 by a factor of 3; origin is at scientific origin.
87             $d = $ts->map($x,$x->hdr); # FITS hdr template prevents autoscaling
88             $w->plot(with=>'image', $d);
89              
90             ## Shrink m51 by a factor of 3; origin is still at sci. origin.
91             $e = $ts->unmap($x,$x->hdr);
92             $w->plot(with=>'image', $e);
93              
94             ## A no-op: shrink m51 by a factor of 3, then autoscale back to size
95             $f = $ts->map($x); # No template causes autoscaling of output
96              
97             =head1 OPERATOR OVERLOADS
98              
99             =over 3
100              
101             =item '!'
102              
103             The bang is a unary inversion operator. It binds exactly as
104             tightly as the normal bang operator.
105              
106             =item 'x'
107              
108             By analogy to matrix multiplication, 'x' is the compose operator, so these
109             two expressions are equivalent:
110              
111             $f->inverse()->compose($g)->compose($f) # long way
112             !$f x $g x $f # short way
113              
114             Both of those expressions are equivalent to the mathematical expression
115             f^-1 o g o f, or f^-1(g(f(x))).
116              
117             =item '**'
118              
119             By analogy to numeric powers, you can apply an operator a positive
120             integer number of times with the ** operator:
121              
122             $f->compose($f)->compose($f) # long way
123             $f**3 # short way
124              
125             =back
126              
127             =head1 INTERNALS
128              
129             Transforms are perl hashes. Here's a list of the meaning of each key:
130              
131             =over 3
132              
133             =item func
134              
135             Ref to a subroutine that evaluates the transformed coordinates. It's
136             called with the input coordinate, and the "params" hash. This
137             springboarding is done via explicit ref rather than by subclassing,
138             for convenience both in coding new transforms (just add the
139             appropriate sub to the module) and in adding custom transforms at
140             run-time. Note that, if possible, new Cs should support
141             L operation to save memory when the data are flagged
142             inplace. But C should always return its result even when
143             flagged to compute in-place.
144              
145             C should treat the 0th dimension of its input as a dimensional
146             index (running 0..N-1 for R^N operation) and broadcast over all other input
147             dimensions.
148              
149             =item inv
150              
151             Ref to an inverse method that reverses the transformation. It must
152             accept the same "params" hash that the forward method accepts. This
153             key can be left undefined in cases where there is no inverse.
154              
155             =item idim, odim
156              
157             Number of useful dimensions for indexing on the input and output sides
158             (ie the order of the 0th dimension of the coordinates to be fed in or
159             that come out). If this is set to 0, then as many are allocated as needed.
160              
161             =item name
162              
163             A shorthand name for the transformation (convenient for debugging).
164             You should plan on using UNIVERAL::isa to identify classes of
165             transformation, e.g. all linear transformations should be subclasses
166             of PDL::Transform::Linear. That makes it easier to add smarts to,
167             e.g., the compose() method.
168              
169             =item itype
170              
171             An array containing the name of the quantity that is expected from the
172             input ndarray for the transform, for each dimension. This field is advisory,
173             and can be left blank if there's no obvious quantity associated with
174             the transform. This is analogous to the CTYPEn field used in FITS headers.
175              
176             =item oname
177              
178             Same as itype, but reporting what quantity is delivered for each
179             dimension.
180              
181             =item iunit
182              
183             The units expected on input, if a specific unit (e.g. degrees) is expected.
184             This field is advisory, and can be left blank if there's no obvious
185             unit associated with the transform.
186              
187             =item ounit
188              
189             Same as iunit, but reporting what quantity is delivered for each dimension.
190              
191             =item params
192              
193             Hash ref containing relevant parameters or anything else the func needs to
194             work right.
195              
196             =item is_inverse
197              
198             Bit indicating whether the transform has been inverted. That is useful
199             for some stringifications (see the PDL::Transform::Linear
200             stringifier), and may be useful for other things.
201              
202             =back
203              
204             Transforms should be inplace-aware where possible, to prevent excessive
205             memory usage.
206              
207             If you define a new type of transform, consider generating a new stringify
208             method for it. Just define the sub "stringify" in the subclass package.
209             It should call SUPER::stringify to generate the first line (though
210             the PDL::Transform::Composition bends this rule by tweaking the
211             top-level line), then output (indented) additional lines as necessary to
212             fully describe the transformation.
213              
214             =head1 NOTES
215              
216             Transforms have a mechanism for labeling the units and type of each
217             coordinate, but it is just advisory. A routine to identify and, if
218             necessary, modify units by scaling would be a good idea. Currently,
219             it just assumes that the coordinates are correct for (e.g.) FITS
220             scientific-to-pixel transformations.
221              
222             Composition works OK but should probably be done in a more
223             sophisticated way so that, for example, linear transformations are
224             combined at the matrix level instead of just strung together
225             pixel-to-pixel.
226              
227             =head1 MODULE INTERFACE
228              
229             There are both operators and constructors. The constructors are all
230             exported, all begin with "t_", and all return objects that are subclasses
231             of PDL::Transform.
232              
233             The L, L, L,
234             and L methods are also exported to the C package: they
235             are both Transform methods and PDL methods.
236              
237             =cut
238              
239             use strict;
240             use warnings;
241              
242             +======EOD======
243              
244              
245             pp_addpm({At=>'Bot'},<<'+======EOD======');
246              
247             =head1 AUTHOR
248              
249             Copyright 2002, 2003 Craig DeForest. There is no warranty. You are allowed
250             to redistribute this software under certain conditions. For details,
251             see the file COPYING in the PDL distribution. If this file is
252             separated from the PDL distribution, the copyright notice should be
253             included in the file.
254              
255             =cut
256              
257             package PDL::Transform;
258             use Carp;
259             use overload '""' => \&_strval;
260             use overload 'x' => \&_compose_op;
261             use overload '**' => \&_pow_op;
262             use overload '!' => \&t_inverse;
263              
264             use PDL::LiteF;
265             use PDL::MatrixOps;
266              
267             our $PI = 3.1415926535897932384626;
268             our $DEG2RAD = $PI / 180;
269             our $RAD2DEG = 180/$PI;
270             our $E = exp(1);
271              
272              
273             #### little helper kludge parses a list of synonyms
274             sub _opt {
275             my($hash) = shift;
276             my($synonyms) = shift;
277             my($alt) = shift; # default is undef -- ok.
278             local($_);
279             foreach $_(@$synonyms){
280             return (UNIVERSAL::isa($alt,'PDL')) ? PDL->pdl($hash->{$_}) : $hash->{$_}
281             if defined($hash->{$_}) ;
282             }
283             return $alt;
284             }
285              
286             ######################################################################
287             #
288             # Stringification hack. _strval just does a method search on stringify
289             # for the object itself. This gets around the fact that stringification
290             # overload is a subroutine call, not a method search.
291             #
292              
293             sub _strval {
294             my($me) = shift;
295             $me->stringify();
296             }
297              
298             ######################################################################
299             #
300             # PDL::Transform overall stringifier. Subclassed stringifiers should
301             # call this routine first then append auxiliary information.
302             #
303             sub stringify {
304             my($me) = shift;
305             my($mestr) = (ref $me);
306             $mestr =~ s/PDL::Transform:://;
307             my $out = $mestr . " (" . $me->{name} . "): ";
308             $out .= "fwd ". ((defined ($me->{func})) ? ( (ref($me->{func}) eq 'CODE') ? "ok" : "non-CODE(!!)" ): "missing")."; ";
309             $out .= "inv ". ((defined ($me->{inv})) ? ( (ref($me->{inv}) eq 'CODE') ? "ok" : "non-CODE(!!)" ):"missing").".\n";
310             }
311              
312             +======EOD======
313              
314             pp_add_exported('apply');
315             pp_addpm(<<'+======EOD_apply======');
316              
317             =head2 apply
318              
319             =for sig
320              
321             Signature: (data(); PDL::Transform t)
322              
323             =for usage
324              
325             $out = $data->apply($t);
326             $out = $t->apply($data);
327              
328             =for ref
329              
330             Apply a transformation to some input coordinates.
331              
332             In the example, C<$t> is a PDL::Transform and C<$data> is a PDL to
333             be interpreted as a collection of N-vectors (with index in the 0th
334             dimension). The output is a similar but transformed PDL.
335              
336             For convenience, this is both a PDL method and a Transform method.
337              
338             =cut
339              
340             use Carp;
341              
342             *PDL::apply = \&apply;
343             sub apply {
344             my ($me, $from) = UNIVERSAL::isa($_[0],'PDL') ? @_[1,0] : @_;
345             croak "apply requires both a PDL and a PDL::Transform.\n"
346             unless UNIVERSAL::isa($me,'PDL::Transform') && UNIVERSAL::isa($from,'PDL');
347             croak "Applying a PDL::Transform with no func! Oops.\n"
348             unless(defined($me->{func}) and ref($me->{func}) eq 'CODE');
349             my $result = $me->{func}->($from,$me->{params});
350             $result->is_inplace(0); # clear inplace flag, just in case.
351             if (defined $from->gethdr && $from->hdr->{NAXIS}) {
352             my $nd = $me->{odim} || $me->{idim} || 2;
353             for my $d (1..$nd) {
354             $result->hdr->{"CTYPE$d"} = ( (!defined($me->{otype}) ? "" :
355             ref($me->{otype}) ne 'ARRAY' ? $me->{otype} :
356             $me->{otype}[$d-1])
357             || $from->hdr->{"CTYPE$d"}
358             || "");
359             $result->hdr->{"CUNIT$d"} = ( (!defined($me->{ounit}) ? "" :
360             ref($me->{ounit}) ne 'ARRAY' ? $me->{ounit} :
361             $me->{ounit}[$d-1])
362             || $from->hdr->{"CUNIT$d"}
363             || $from->hdr->{"CTYPE$d"}
364             || "");
365             }
366             }
367             return $result;
368             }
369              
370             +======EOD_apply======
371              
372             pp_add_exported('invert');
373             pp_addpm(<<'+======EOD_invert======');
374              
375             =head2 invert
376              
377             =for sig
378              
379             Signature: (data(); PDL::Transform t)
380              
381             =for usage
382              
383             $out = $t->invert($data);
384             $out = $data->invert($t);
385              
386             =for ref
387              
388             Apply an inverse transformation to some input coordinates.
389              
390             In the example, C<$t> is a PDL::Transform and C<$data> is an ndarray to
391             be interpreted as a collection of N-vectors (with index in the 0th
392             dimension). The output is a similar ndarray.
393              
394             For convenience this is both a PDL method and a PDL::Transform method.
395              
396             =cut
397              
398             *PDL::invert = \&invert;
399             sub invert {
400             my ($me, $from) = UNIVERSAL::isa($_[0],'PDL') ? @_[1,0] : @_;
401             croak "invert requires a PDL and a PDL::Transform (did you want 'inverse' instead?)\n"
402             unless UNIVERSAL::isa($me,'PDL::Transform') && UNIVERSAL::isa($from,'PDL');
403             croak "Inverting a PDL::Transform with no inverse! Oops.\n"
404             unless(defined($me->{inv}) and ref($me->{inv}) eq 'CODE');
405             my $result = $me->{inv}->($from, $me->{params});
406             $result->is_inplace(0); # make sure inplace flag is clear.
407             return $result;
408             }
409              
410             +======EOD_invert======
411              
412             pp_add_exported('map');
413             pp_def('map',
414             Pars=>'k0()', # Dummy to set type (should match the type of "in").
415             OtherPars=>'pdl *in; pdl *out; pdl *map; SV *boundary; SV *method;
416             long big; double blur; double sv_min; char flux; SV *bv',
417             CHeader => pp_line_numbers(__LINE__, <<'+==EOD_map_auxiliary=='),
418             /*
419             * Singular-value decomposition code is borrowed from
420             * MatrixOps -- cut-and-pasted here because of linker trouble.
421             * It's used by the auxiliary matrix manipulation code, below.
422             */
423 922         7 static inline void pdl_xform_svd(PDL_Double *W, PDL_Double *Z, PDL_Indx nRow, PDL_Indx nCol)
424             {
425             int i, j, k, EstColRank, RotCount, SweepCount, slimit;
426             PDL_Double eps, e2, tol, vt, p, x0, y0, q, r, c0, s0, d1, d2;
427 922         2 eps = 1e-6;
428 922         30 slimit = nCol/4;
429 922 100       3 if (slimit < 6.0)
430 922         1 slimit = 6;
431 922         47 SweepCount = 0;
432 922         4 e2 = 10.0*nRow*eps*eps;
433 922         2 tol = eps*.1;
434 922         10454 EstColRank = nCol;
435 2764 100       9 for (i=0; i
436 5527 100       1 for (j=0; j
437 3685         77 W[nCol*(nRow+i)+j] = 0.0;
438             }
439 1843         4 W[nCol*(nRow+i)+i] = 1.0; /* rjrw 7/7/99: moved this line out of j loop */
440             }
441 922         2 RotCount = EstColRank*(EstColRank-1)/2;
442 2343 100       8 while (RotCount != 0 && SweepCount <= slimit)
    50          
443             {
444 1422         65 RotCount = EstColRank*(EstColRank-1)/2;
445 1422         1 SweepCount++;
446 2843 100       2 for (j=0; j
447             {
448 2843 100       41 for (k=j+1; k
449             {
450 1422         2 p = q = r = 0.0;
451 4264 100       2 for (i=0; i
452             {
453 2843         32 x0 = W[nCol*i+j]; y0 = W[nCol*i+k];
454 2843         7 p += x0*y0; q += x0*x0; r += y0*y0;
455             }
456 1422         3 Z[j] = q; Z[k] = r;
457 1422 100       34 if (q >= r)
458             {
459 922 50       2 if (q<=e2*Z[0] || fabs(p)<=tol*q) RotCount--;
    100          
460             else
461             {
462 1         5 p /= q; r = 1 - r/q; vt = sqrt(4*p*p+r*r);
463 1         3 c0 = sqrt(fabs(.5*(1+r/vt))); s0 = p/(vt*c0);
464 1 50       3 for (i=0; i
465             {
466 1         6 d1 = W[nCol*i+j]; d2 = W[nCol*i+k];
467 607         882 W[nCol*i+j] = d1*c0+d2*s0; W[nCol*i+k] = -d1*s0+d2*c0;
468             }
469             }
470             }
471             else
472             {
473 1107         894 p /= r; q = q/r-1; vt = sqrt(4*p*p+q*q);
474 1107         721 s0 = sqrt(fabs(.5*(1-q/vt)));
475 1107 100       646 if (p<0) s0 = -s0;
476 1107         985 c0 = p/(vt*s0);
477 4097 100       3691 for (i=0; i
478             {
479 2436         1114 d1 = W[nCol*i+j]; d2 = W[nCol*i+k];
480 2000         0 W[nCol*i+j] = d1*c0+d2*s0; W[nCol*i+k] = -d1*s0+d2*c0;
481             }
482             }
483             }
484             }
485 1421 100       0 while (EstColRank>=3 && Z[(EstColRank-1)]<=Z[0]*tol+tol*tol)
    100          
486 0         0 EstColRank--;
487             }
488 921         0 }
489              
490             /*
491             * PDL_xform_aux:
492             * This handles the matrix manipulation part of the Jacobian filtered
493             * mapping code. It's separate from the main code because it's
494             * independent of the data type of the original arrays.
495             *
496             *Given a pre-allocated workspace and
497             * an integer set of coordinates, generate the discrete Jacobian
498             * from the map, pad the singular values, and return the inverse
499             * Jacobian, the largest singular value of the Jacobian itself, and
500             * the determinant of the original Jacobian. Boundary values use the
501             * asymmetric discrete Jacobian; others use the symmetric discrete Jacobian.
502             *
503             * The map and workspace must be of type PDL_D. If the dimensionality is
504             * d, then the workspace must have at least 3*n^2+n elements. The
505             * inverse of the padded Jacobian is returned in the first n^2 elements.
506             * The determinant of the original Jacobian gets stuffed into the n^2
507             * element of the workspace. The largest padded singular value is returned.
508             *
509             */
510              
511 921         0 static inline PDL_Double PDL_xform_aux ( pdl *map, PDL_Indx *coords, PDL_Double *tmp, PDL_Double sv_min) {
512             PDL_Indx ndims;
513             PDL_Indx i, j, k;
514             PDL_Indx offset;
515             PDL_Double det;
516             PDL_Double *jptr;
517             PDL_Double *svptr;
518             PDL_Double *aptr,*bptr;
519 921         0 PDL_Double max_sv = 0.0;
520              
521 921         0 ndims = map->ndims-1;
522              
523             /****** Accumulate the Jacobian */
524             /* Accumulate the offset into the map array */
525 2763 100       0 for( i=offset=0; i
526 1866         695 offset += coords[i] * map->dimincs[i+1];
527              
528 945         179 jptr = tmp + ndims*ndims;
529              
530 2787 100       154 for( i=0; i
531 1866         77 char bot = (coords[i] <=0);
532 1866         68 char top = (coords[i] >= map->dims[i+1]-1);
533 1866 100       99 char symmetric = !(bot || top);
    100          
534             PDL_Double *ohi,*olo;
535 1842         0 PDL_Indx diff = map->dimincs[i+1];
536 1842 100       0 ohi = ((PDL_Double *)map->data) + ( offset + ( top ? 0 : diff ));
537 1842 100       0 olo = ((PDL_Double *)map->data) + ( offset - ( bot ? 0 : diff ));
538              
539 5526 100       0 for( j=0; j
540 3708         157 PDL_Double jel = *ohi - *olo;
541              
542 3714         210 ohi += map->dimincs[0];
543 3714         201 olo += map->dimincs[0];
544              
545 3714 100       187 if(symmetric)
546 2890         108 jel /= 2;
547 3714         98 *(jptr++) = jel;
548             }
549             }
550              
551              
552             /****** Singular-value decompose the Jacobian
553             * The svd routine produces the squares of the singular values,
554             * and requires normalization for one of the rotation matrices.
555             */
556              
557 951         55 jptr = tmp + ndims*ndims;
558 931         2138 svptr = tmp + 3*ndims*ndims;
559 931         26 pdl_xform_svd(jptr,svptr,ndims,ndims);
560              
561 931         79 aptr = svptr;
562 2773 100       36 for (i=0;i
563 1852         32 *aptr = sqrt(*aptr);
564              
565              
566             /* fix-up matrices here */
567 931         37 bptr = jptr;
568 2792 100       995 for(i=0; i
569 1871         252 aptr = svptr;
570 5555 100       203 for(j=0;j
571 3713         243 *(bptr++) /= *(aptr++);
572             }
573              
574             /****** Store the determinant, and pad the singular values as necessary.*/
575 950         197 aptr = svptr;
576 950         88 det = 1.0;
577 2792 100       121 for(i=0;i
578 1871         62 det *= *aptr;
579 1871 100       156 if(*aptr < sv_min)
580 1005         27 *aptr = sv_min;
581 1847 100       32 if(*aptr > max_sv )
582 925         14 max_sv = *aptr;
583 1843         11 aptr++;
584             }
585              
586             /****** Generate the inverse matrix */
587             /* Multiply B-transpose times 1/S times A-transpose. */
588             /* since S is diagonal we just divide by the appropriate element. */
589             /* */
590 922         277 aptr = tmp + ndims*ndims;
591 944         73 bptr = aptr + ndims*ndims;
592 921         0 jptr= tmp;
593              
594 2792 100       1826 for(i=0;i
595 5526 100       0 for(j=0;j
596 3684         0 *jptr = 0;
597              
598 11081 100       124 for(k=0;k
599 7397         107 *jptr += aptr[j*ndims + k] * bptr[k*ndims + i] / *svptr;
600              
601 3713         98 jptr++;
602             }
603 1871         154 svptr++;
604             }
605              
606 950         114 *jptr = det;
607 950         63 return max_sv;
608             }
609             +==EOD_map_auxiliary==
610             Code => pp_line_numbers(__LINE__, <<'+==EOD_map_c_code=='),
611              
612             /*
613             * Pixel interpolation & averaging code
614             *
615             * Calls a common coordinate-transformation block (see following hdr)
616             * that isn't dependent on the type of the input variable.
617             *
618             * The inputs are SVs to avoid hassling with broadcastloops; broadcasting
619             * is handled internally. To simplify the broadcasting business, any
620             * broadcast dimensions should all be collapsed to a single one by the
621             * perl front-end.
622             *
623             */
624              
625 41         60 pdl *in = $COMP(in);
626 41         51 pdl *out = $COMP(out);
627 41         94 pdl *map = $COMP(map);
628              
629 41         64 PDL_Indx ndims = map->ndims -1; /* Number of dimensions we're working in */
630 41         135 PDL_Double tmp[3*ndims*ndims+ndims]; /* Workspace for prefrobnication */
631 41         246 PDL_Indx ovec[ndims]; /* output pixel loop vector */
632 41         279 PDL_Indx ivec[ndims]; /* input pixel loop vector */
633 21         22 PDL_Indx ibvec[ndims]; /* input pixel base offset vector */
634 21         108 PDL_Double dvec[ndims]; /* Residual vector for linearization */
635 21         15 PDL_Double tvec[ndims]; /* Temporary floating-point vector */
636 21         39 PDL_Double acc[in->dims[ndims]]; /* Broadcasted accumulator */
637 21         31 PDL_Double wgt[in->dims[ndims]]; /* Broadcasted weight accumulator */
638 21         0 PDL_Double wgt2[in->dims[ndims]]; /* Broadcasted weight accumulator for badval finding */
639 21         30 char bounds[ndims]; /* Boundary condition packed string */
640 21         25 PDL_Indx index_stash[ndims]; /* Stash to store the opening index of dim sample scans */
641             char method; /* Method identifier (gets one of 'h','g') */
642             PDL_Long big; /* Max size of input footprint for each pix */
643             PDL_Double blur; /* Scaling of filter */
644             PDL_Double sv_min; /* minimum singular value */
645             char flux; /* Flag to indicate flux conservation */
646             PDL_Indx i;
647 12         0 $GENERIC() badval = SvNV($COMP(bv));
648             #define HANNING_LOOKUP_SIZE 2500
649             static PDL_Double hanning_lookup[HANNING_LOOKUP_SIZE + 2];
650             static int needs_hanning_calc = 1;
651 12         0 PDL_Double zeta = 0;
652             PDL_Double hanning_offset;
653              
654             #define GAUSSIAN_LOOKUP_SIZE 4000
655             #define GAUSSIAN_MAXVAL 6.25 /* 2.5 HWHMs (square it) */
656             static PDL_Double gaussian_lookup[GAUSSIAN_LOOKUP_SIZE + 2];
657             static int needs_gaussian_calc = 1;
658              
659 12 0       0 PDL_RETERROR(PDL_err, PDL->make_physical(in));
    0          
    50          
    100          
    100          
    100          
    0          
    0          
    0          
    50          
    50          
    50          
660 12 50       0 PDL_RETERROR(PDL_err, PDL->make_physical(out));
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    100          
    100          
    100          
661 12 50       0 PDL_RETERROR(PDL_err, PDL->make_physical(map));
    100          
    100          
    50          
    100          
    0          
    50          
    50          
    50          
    50          
    50          
    100          
662              
663             /***
664             * Fill in the boundary condition array
665             */
666             {
667             char *bstr;
668             STRLEN blen;
669 12         0 bstr = SvPV($COMP(boundary),blen);
670              
671 12         0 if(blen == 0) {
672             /* If no boundary is specified then every dim gets truncated */
673             PDL_Indx i;
674 9 50       116 for (i=0;i
    50          
    0          
    50          
    50          
    100          
    100          
    50          
    100          
    50          
    50          
    50          
675 5         23 bounds[i] = 1;
676             } else {
677             PDL_Indx i;
678 36 50       0 for(i=0;i
    50          
    0          
    0          
    100          
    0          
    0          
    0          
    0          
    0          
    100          
    0          
679 29 0       11 switch(bstr[i < blen ? i : blen-1 ]) {
    0          
    0          
    0          
    100          
    100          
    50          
    50          
    100          
    100          
    100          
    100          
680 9         50 case '0': case 'f': case 'F': /* forbid */
681 9         74 bounds[i] = 0;
682 9         73 break;
683 31         23 case '1': case 't': case 'T': /* truncate */
684 31         47 bounds[i] = 1;
685 31         28 break;
686 0         0 case '2': case 'e': case 'E': /* extend */
687 0         0 bounds[i] = 2;
688 0         0 break;
689 1         0 case '3': case 'p': case 'P': /* periodic */
690 1         0 bounds[i] = 3;
691 1         0 break;
692 1         0 case '4': case 'm': case 'M': /* mirror */
693 10         66 bounds[i] = 4;
694 10         61 break;
695 9         15 default:
696 9         44 $CROAK("Error in map: Unknown boundary condition '%c'",bstr[i]);
697             break;
698             }
699             }
700             }
701             }
702              
703             /***
704             * Parse out the 'method', 'big', 'blur', and 'sv_min' arguments
705             */
706 21         46 big = labs($COMP(big));
707 21 100       479 if(big <= 0)
    50          
    50          
    100          
    100          
    50          
    50          
    100          
    100          
    50          
    50          
    50          
708 9         110 $CROAK("map: 'big' parameter must be >0");
709              
710 21         38 blur = fabs($COMP(blur));
711 21 50       27 if(blur < 0)
    100          
    50          
    100          
    100          
    100          
    50          
    100          
    50          
    50          
    100          
    0          
712 9         31 $CROAK("map: 'blur' parameter must be >= 0");
713              
714 21         135 sv_min = fabs($COMP(sv_min));
715 21 0       74 if(sv_min < 0)
    0          
    0          
    0          
    50          
    0          
    0          
    0          
    50          
    0          
    50          
    50          
716 9         31 $CROAK("map: 'sv_min' parameter must be >= 0");
717              
718 12         0 flux = $COMP(flux) != 0;
719              
720             {
721             char *mstr;
722             STRLEN mlen;
723 12         0 mstr = SvPV($COMP(method),mlen);
724              
725 12         0 if(mlen==0)
726 9         23 method = 'h';
727 21         24 switch(*mstr) {
728 15 100       21 case 'h': // h - lookup-table hanning window
    50          
    50          
    50          
    100          
    50          
    0          
    0          
    50          
    50          
    100          
    100          
729             if( needs_hanning_calc ) {
730 5010 50       26 int i;
    0          
    0          
    0          
    100          
    0          
    0          
    0          
    0          
    0          
    100          
    0          
731 5016         216 for(i=0;i
732             hanning_lookup[i] = 0.5 + 0.5 * cos(3.1415926536 / HANNING_LOOKUP_SIZE * i);
733 18         180 }
734 18         164 hanning_lookup[HANNING_LOOKUP_SIZE] = 0;
735 18         167 hanning_lookup[HANNING_LOOKUP_SIZE+1] = 0;
736             needs_hanning_calc = 0;
737 23         248 }
738 24         399 zeta = HANNING_LOOKUP_SIZE / blur; /* fall-through deliberate */
739 16 0       130 case 'H': // H - rigorous hanning window
    0          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    50          
    0          
740 16         69 method = *mstr;
741             hanning_offset = (blur >= 1) ? 0 : 0.5 * (1.0 - blur);
742 11         424 break;
743 11         394  
744             case 'g': case 'j': // Gaussian and/or Jacobian, using lookup table
745 11 0       356 method = 'g';
    0          
    0          
    0          
    100          
    0          
    0          
    0          
    0          
    0          
    50          
    0          
746             zeta = GAUSSIAN_LOOKUP_SIZE / GAUSSIAN_MAXVAL;
747 8010 0       180  
    0          
    0          
    0          
    100          
    0          
    0          
    0          
    0          
    0          
    100          
    0          
748 8001         4 if( needs_gaussian_calc ) {
749             int i;
750 3         6 for(i=0;i
751 3         6 gaussian_lookup[i] = exp( - i * 1.386294 / zeta );
752 3         6 }
753             gaussian_lookup[GAUSSIAN_LOOKUP_SIZE] = 0;
754 3         0 gaussian_lookup[GAUSSIAN_LOOKUP_SIZE+1] = 0;
755             needs_gaussian_calc = 0;
756 2         4 }
757 1         2 break;
758 1         3  
759             case 'G': case 'J': // Jacobian -- same thing, with direct calculation
760             method = 'G'; break;
761             default:
762             $CROAK("Bug in map: unknown method '%c'",*mstr);
763             break;
764             }
765             }
766              
767              
768              
769             /* End of initialization */
770             /*************************************************************/
771 37 0       2 /* Start of Real Work */
    0          
    0          
    0          
    100          
    0          
    0          
    0          
    0          
    0          
    100          
    0          
772 25         3  
773             /* Initialize coordinate vector and map offset
774 14         4 */
775 14         19 for(i=0;i
776 26 0       138 ovec[i] = 0;
    0          
    0          
    0          
    100          
    0          
    0          
    0          
    0          
    0          
    100          
    0          
777 38 0       134  
    0          
    0          
    0          
    100          
    0          
    0          
    0          
    0          
    0          
    100          
    0          
778 26         148 PDL_Double *map_ptr = (PDL_Double *)(map->data);
779             PDL_Indx npdls = 1, incs[npdls*ndims], offs[npdls];
780             for (i=0; i < npdls; i++) offs[i] = 0;
781             for (i=0; i < ndims; i++) {
782 911         344 incs[i*npdls + 0] = map->dimincs[i+1];
783             }
784              
785 923         208 /* Main pixel loop (iterates over pixels in the output plane) */
786             do {
787             PDL_Indx psize; // Size of the region to accumulate over for this pixel
788             PDL_Indx i_off; // Offset into the data array of the source PDL
789             PDL_Indx j; // Generic loop index
790             char t_vio; // counter used for truncation boundary violations
791             char carry; // flag used for multidimensional loop iteration
792              
793             /* Prefrobnicate the transformation matrix */
794             psize = (PDL_Long)(blur * PDL_xform_aux(map, ovec, tmp, sv_min) + 0.5)+1; /* assignment */
795              
796             #ifdef DEBUG_MAP
797             {
798             PDL_Indx k; PDL_Indx foo = 0;
799             printf("ovec: [");
800             for(k=0;k
801             foo += ovec[k] * map->dimincs[k+1];
802             printf(" %2td ",(PDL_Indx)(ovec[k]));
803             }
804 925         31 printf("]; psize is %ld; big is %d; blur is %8.2f; map is [",psize,big, blur);
805             for(k=0;k
806             printf("%8.2f",(double)((($GENERIC() *)(map->data))[foo + k*map->dimincs[0]]));
807             }
808             printf("]\n");
809 925         1208 }
810 2764 0       2 #endif
    0          
    0          
    0          
    100          
    0          
    0          
    0          
    0          
    0          
    100          
    0          
811 1843         371  
812 1843         8 /* Don't bother accumulating output if psize is too large */
813             if(psize <= big) {
814             /* Use the prefrobnicated matrix to generate a local linearization.
815             * dvec gets the delta; ibvec gets the base.
816             */
817 2792 0       474 {
    0          
    0          
    0          
    100          
    0          
    0          
    0          
    0          
    0          
    100          
    0          
818 1871         76 PDL_Double *mp = map_ptr + offs[0];
819             for (i=0;i
820             dvec[i] = *mp - ( ibvec[i] = (PDL_Long)(*mp + 0.5)); /* assignment */
821             mp += map->dimincs[0];
822 931         55 }
823 1871 0       340 }
    0          
    0          
    0          
    100          
    0          
    0          
    0          
    0          
    0          
    100          
    0          
824 950         107  
825             /* Initialize input delta vector */
826             for(i=0;i
827             ivec[i] = -psize;
828 950         140  
829 1871 0       113 /* Initialize accumulators */
    0          
    0          
    0          
    100          
    0          
    0          
    0          
    0          
    0          
    100          
    0          
830 950         81 {
831             PDL_Double *ac = acc;
832             for(i=0; i < in->dims[ndims]; i++)
833 950         152 *(ac++) = 0.0;
834 1871 0       76  
    0          
    0          
    0          
    100          
    0          
    0          
    0          
    0          
    0          
    100          
    0          
835 938         159 }
836             {
837             PDL_Double *wg = wgt;
838             for(i=0;i < in->dims[ndims]; i++)
839             *(wg++) = 0.0;
840             }
841             {
842             PDL_Double *wg = wgt2;
843             for(i=0;i < in->dims[ndims]; i++)
844             *(wg++) = 0.0;
845             }
846              
847              
848             /*
849 938         183 * Calculate the original offset into the data array, to enable
850 938         1067 * delta calculations in the pixel loop
851 2775 0       79 *
    0          
    0          
    0          
    100          
    0          
    0          
    0          
    0          
    0          
    100          
    0          
852 1854         45 * i runs over dims; j holds the working integer index in the
853 1854 0       72 * current dim.
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    100          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    100          
    100          
    0          
    0          
854 780         44 *
855 12         42 * This code matches the incrementation code at the bottom of the accumulation loop
856 12         114 */
857            
858 668         0 t_vio = 0; /* truncation-boundary violation count - don't bother if it is nonzero */
859 668         0 i_off = 0;
860             for(i=0;i
861 668         0 j = ivec[i] + ibvec[i];
862 668 0       0 if(j<0 || j >= in->dims[i]) {
    0          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    100          
    0          
863 608         0 switch(bounds[i]) {
864 60         0 case 0: /* no breakage allowed */
865 680         51 $CROAK("index out-of-bounds in map");
866 62         44 break;
867 62         33 case 1: /* truncation */
868 62 0       31 t_vio++;
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    100          
    0          
869 32         58 /* fall through */
870 62         44 case 2: /* extension -- crop */
871 62         53 if(j<0)
872 62         89 j=0;
873 62         32 else //if(j>=in->dims[i])
874 62 0       57 j = in->dims[i] - 1;
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
    0          
875 12         42 break;
876 62         32 case 3: /* periodic -- mod it */
877 62 0       32 j %= in->dims[i];
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
    0          
878 62         182 if(j<0)
879 62         22 j += in->dims[i];
880             break;
881 62         78 case 4: /* mirror -- reflect off the edges */
882 12         577 j += in->dims[i];
883 0         0 j %= (in->dims[i]*2);
884             if(j<0)
885             j += in->dims[i]*2;
886             j -= in->dims[i];
887 1842         0 if(j<0) {
888             j *= -1;
889             j -= 1;
890             }
891             break;
892             default:
893             $CROAK("Unknown boundary condition %td in map -- bug alert!", (PDL_Indx)bounds[i]);
894             break;
895 2763 0       0 }
    0          
    0          
    0          
    100          
    0          
    0          
    0          
    0          
    0          
    100          
    0          
896 1842         0 }
897             i_off += in->dimincs[i] * j;
898             }
899              
900             /* Initialize index stashes for later reference as we scan the footprint */
901             /* It's a pain in the ass to deal with boundaries, and doubly so at the */
902             /* end of a dimensional scan. So we stash the index location at the */
903             /* start of each dimensional scan here. When we finish incrementing */
904             /* through a particular dim, we pull its value back out of the stash. */
905             for(i=0;i
906             index_stash[i] = i_off;
907             }
908              
909             /* The input accumulation loop is the hotspot for the entire operation. */
910             /* We loop over pixels in the region of interest (+/- psize in each dimension) */
911             /* in the input array, use the linearized transform to bring each pixel center */
912             /* forward to the output plane, and calculate a weighting based on the chosen */
913             /* filter function. 'h' is a fast Hanning window rolloff using a lookup */
914             /* table that is initialized the first time through the code. 'H' is the */
915             /* same process, but explicitly calculated for each interation (~2x slower). */
916             /* 'g' uses a radial Gaussian filter. Rather than calculate the array offset */
917             /* into the input array fresh from the current input array vector each time, */
918 24201 0       0 /* we walk through the array using dimincs and the old offset. This saves */
    0          
    0          
    0          
    100          
    0          
    0          
    0          
    0          
    0          
    100          
    0          
919             /* about half of the time spent on index calculation. */
920 16432         0  
921 16432         0 do { /* Input accumulation loop */
922 16432         0 PDL_Double *cp;
923 49314 0       4823 PDL_Double alpha; // weighting coefficient for the current point
    0          
    0          
    0          
    100          
    0          
    0          
    0          
    0          
    0          
    100          
    0          
924 32882         75  
925             /* Calculate the weight of the current input point. Don't bother if we're
926 16450         517 * violating any truncation boundaries (in that case our value is zero, but
927             * for the interpolation we also set the weight to zero).
928 8191         0 */
929             if( !t_vio ) {
930              
931             PDL_Double *ap = tvec;
932             PDL_Double *bp = dvec;
933             PDL_Indx *ip = ivec;
934             for(i=0; i
935 8221         53 *(ap++) = *(ip++) - *(bp++);
936 8221         73  
937 19219 0       100 switch(method) {
    0          
    0          
    0          
    100          
    0          
    0          
    0          
    0          
    0          
    100          
    0          
938             PDL_Double dd; // tmp space for calculations in filters
939              
940             case 'h':
941 11028         72 /* This is the Hanning window rolloff. It is a product of a simple */
942 11028         104 /* cos^2(theta) rolloff in each dimension. Using a lookup table */
943             /* is about 2x faster than using cos(theta) directly in each */
944 33024 0       49 /* weighting calculation, so we do. Using 2500 entries and linear */
    0          
    0          
    0          
    100          
    0          
    0          
    0          
    0          
    0          
    100          
    0          
945 22026         53 /* interpolation is accurate to about 10^-7, and should preserve */
946             /* the contents of cache pretty well. */
947             alpha = 1;
948             cp = tmp;
949             for (i=0; i
950             PDL_Indx lodex;
951             PDL_Indx hidex;
952             PDL_Double beta;
953 11059         149 dd = 0;
954 11059 0       136 ap = tvec;
    0          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    100          
    0          
955 9581 0       233 /* Get the matrix-multiply element for this dimension */
    0          
    0          
    0          
    100          
    0          
    0          
    0          
    0          
    0          
    100          
    0          
956 7234         26 for (j=0;j
957 7224         0 dd += *(ap++) * *(cp++);
958              
959 2296         0 /* Do linear interpolation from the table */
960 2296         0 /* The table captures a hanning window centered 0.5 pixel from center. */
961 2296 0       0 /* We scale the filter by the blur parameter -- but if blur is less */
    0          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    50          
    0          
962 10         20 /* than unity, we shrink the hanning blur window while keeping the 0.5 */
963 2306         27 /* value on the pixel edge at 0.5. For blur greater than unity, we */
964 2316         34 /* scale simply. */
965             beta = fabs(dd) - hanning_offset;
966             if (beta > 0) {
967             if (beta >= blur) {
968 8211         60 alpha = 0;
969             i = ndims;
970 1901         87 } else {
971             beta *= zeta;
972             lodex = beta;
973 1901         126 beta -= lodex; //lodex now has the integer part, beta has the fractional part
974 1880         123 if (lodex > HANNING_LOOKUP_SIZE)
975 4525 0       70 lodex = HANNING_LOOKUP_SIZE;
    0          
    0          
    0          
    100          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
976 2675         63 hidex = lodex+1;
977 2675         54 //do a linear interpolation between the two nearest values
978 7965 0       63 alpha *= hanning_lookup[hidex]*beta + hanning_lookup[lodex]*(1-beta);
    0          
    0          
    0          
    100          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
979 5320         88 } /* end of interpolation branch */
980 2675         254 } /* end of beta > 0 branch */
981 2661 0       35 } /* end of dimension loop */
    0          
    0          
    0          
    100          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
982 1533         61 break;
983 1533         29  
984             case 'H':
985 1144         48 /* This is the Hanning window rolloff with explicit calculation, preserved */
986             /* in case someone actually wants the slower longer method. */
987 1890         199 alpha = 1;
988             cp = tmp;
989 4581         124 for (i=0; i
990             dd = 0;
991             ap = tvec;
992 4557         46 for (j=0;j
993 4557         30 dd += *(ap++) * *(cp++);
994 13451 0       147 dd = (fabs(dd) - hanning_offset) / blur;
    0          
    0          
    0          
    100          
    0          
    0          
    0          
    0          
    0          
    100          
    0          
995 8890         15 if ( dd > 1 ) {
996 8890         31 alpha = 0;
997 26650 0       16 i = ndims;
    0          
    0          
    0          
    100          
    0          
    0          
    0          
    0          
    0          
    100          
    0          
998 17770         28 } else
999 8910         134 alpha *= (0.5 + 0.5 * cos( dd * 3.1415926536 ));
1000 8910         56 }
1001 8910 0       65 break;
    0          
    0          
    0          
    100          
    0          
    0          
    0          
    0          
    0          
    100          
    0          
1002 942         54  
1003 962         148 case 'g':
1004             /* This is the Gaussian rolloff. It does lookup into a precalculated exponential. */
1005             {
1006 4541 0       0 PDL_Double sum = 0;
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    100          
    50          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    100          
    50          
    50          
    0          
    0          
    0          
1007 932         0 cp = tmp;
1008             for(i=0; i
1009             dd = 0;
1010 3609         0 ap = tvec;
1011             for(j=0;j
1012 3638         76 dd += *(ap++) * *(cp++);
1013 3638         108 dd /= blur;
1014 3609         0 sum += dd * dd;
1015             if(sum > GAUSSIAN_MAXVAL) {
1016             i = ndims; /* exit early if we're too far out */
1017             alpha = 0;
1018 4541         0 }
1019             }
1020 1850         0 if( sum > GAUSSIAN_MAXVAL || !isfinite(sum) || isnan(sum) ) {
1021             alpha = 0;
1022             } else {
1023             PDL_Indx lodex,hidex;
1024 1850         0 PDL_Double beta = fabs(zeta * sum);
1025 1850         0  
1026 5198 0       0 lodex = beta;
    0          
    0          
    0          
    100          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
1027 3348         0 beta -= lodex; // lodex now has the integer part, beta has the fractional part
1028 3348         0 hidex = lodex+1;
1029 10044 0       0 //do a linear interpolation between the two nearest values
    0          
    0          
    0          
    100          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
1030 6732         76 alpha = gaussian_lookup[hidex]*beta + gaussian_lookup[lodex]*(1 - beta);
1031 3380         139  
1032 3448         165 }
1033 3448 0       647 }
    0          
    0          
    0          
    100          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
1034 992         335 break;
1035              
1036             case 'G':
1037 1850 0       0 /* This is the Gaussian rolloff with explicit calculation, preserved */
    0          
    0          
    0          
    100          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
1038 332         0 /* in case someone actually wants the slower longer method. */
1039             {
1040 1518         0 PDL_Double sum = 0;
1041             cp = tmp;
1042 1850         0 for(i=0; i
1043 0         0 dd = 0;
1044 0         0 ap = tvec;
1045             for(j=0;j
1046             dd += *(ap++) * *(cp++);
1047             dd /= blur;
1048             sum += dd * dd;
1049             if(sum > 4) /* 2 pixels -- four half-widths */
1050 16432         0 i = ndims; /* exit early if this pixel is too far outside the footprint of the ideal point */
1051 16432         0 }
1052 32864 0       0  
    0          
    0          
    0          
    100          
    0          
    0          
    0          
    0          
    0          
    100          
    0          
1053 16432 0       0 if(sum > GAUSSIAN_MAXVAL)
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    100          
    50          
    0          
    0          
1054 16432         0 alpha = 0;
1055 16432         0 else
1056 16432         0 alpha = exp(-sum * 1.386294); /* Gaussian, rt(2)-pix HWHM */
1057             }
1058 16432         0 break;
1059             default:
1060             $CROAK("This can't happen: method='%c'",method);
1061             }
1062              
1063             { /* convenience block -- accumulate the current point into the weighted sum. */
1064             /* This is more than simple assignment because we have our own explicit poor */
1065 24201         0 /* man's broadcastloop here, so we accumulate each broadcasted element separately. */
1066 53105 0       0 $GENERIC() *dat = (($GENERIC() *)(in->data)) + i_off;
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    100          
    100          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    100          
    100          
    0          
    0          
1067             PDL_Indx max = out->dims[ndims];
1068 28904         0 for( i=0; i < max; i++ ) {
1069 28904         0 if( (badval==0) || (*dat != badval) ) {
1070             acc[i] += *dat * alpha;
1071             dat += in->dimincs[ndims];
1072 28904 0       0 wgt[i] += alpha;
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    100          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    100          
    100          
    0          
    0          
1073             }
1074 22001         0 wgt2[i] += alpha; // Accumulate what weight we would have with no bad values
1075             }
1076             }
1077 6903         0 } /* end of t_vio check (i.e. of input accumulation) */
1078 4923         0  
1079              
1080 4923 0       0 /* Advance input accumulation loop. */
    0          
    0          
    0          
    100          
    0          
    0          
    0          
    0          
    0          
    100          
    0          
1081 1042         0 /* We both increment the total vector and also advance the index. */
1082 3881 0       0 carry = 1;
    0          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    100          
    0          
1083 950         0 for (i=0; i
1084 4923         0 /* Advance the current element of the offset vector */
1085 0         0 ivec[i]++;
1086 0         0 j = ivec[i] + ibvec[i];
1087 330         0  
1088 330 0       0 /* Advance the offset into the data array */
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    100          
    0          
1089 100         0 if( j > 0 && j <= in->dims[i]-1 ) {
1090             /* Normal case -- just advance the input vector */
1091 230         0 i_off += in->dimincs[i];
1092             } else {
1093 330         0 /* Busted a boundary - either before or after. */
1094 1650         0 switch(bounds[i]){
1095 1650         0 case 0: /* no breakage allowed -- treat as truncation for interpolation */
1096 1650         0 case 1: /* truncation -- if we crossed the boundary mark ourselves out-of-bounds */
1097 1650         0 if( j == 0 )
1098 1650 0       0 t_vio--;
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    100          
    100          
    0          
    0          
1099 1150 0       0 else if( j == in->dims[i] )
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    100          
    0          
1100 1000         0 t_vio++;
1101             break;
1102 150         0 case 2: /* extension -- do nothing (so the same input point is re-used) */
1103             break;
1104 1650         0 case 3: /* periodic -- advance and mod into the allowed range */
1105             if((j % in->dims[i]) == 0) {
1106             i_off -= in->dimincs[i] * (in->dims[i]-1);
1107             } else {
1108             i_off += in->dimincs[i];
1109 28904 0       0 }
    0          
    0          
    0          
    100          
    0          
    0          
    0          
    0          
    0          
    100          
    0          
1110             break;
1111             case 4: /* mirror -- advance or retreat depending on phase */
1112 27062 0       0 j += in->dims[i];
    0          
    0          
    0          
    100          
    0          
    0          
    0          
    0          
    0          
    100          
    0          
1113 3782         0 j %= (in->dims[i]*2);
1114             j -= in->dims[i];
1115 23280         0 if( j!=0 && j!= -in->dims[i] ) {
1116             if(j<0)
1117             i_off -= in->dimincs[i];
1118 5624         0 else
1119 5624 0       0 i_off += in->dimincs[i];
    0          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    100          
    0          
1120 5024         0 }
1121 5024 0       0 break;
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
    100          
    0          
    0          
1122 1130         0 }
1123 5024         0 }
1124 5024         0  
1125 5024 0       0 /* Now check for carry */
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    100          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    100          
    100          
    0          
    0          
1126 1222         0 if (ivec[i] <= psize) {
1127 5024         0 /* Normal case -- copy the current offset to the faster-running dim stashes */
1128             PDL_Indx k;
1129 600         0 for(k=0;k
1130             index_stash[k] = i_off;
1131             }
1132             carry = 0;
1133 24201 0       0  
    0          
    0          
    0          
    100          
    0          
    0          
    0          
    0          
    0          
    100          
    0          
1134             } else { /* End of this scan -- recover the last position, and mark carry */
1135             i_off = index_stash[i];
1136 921         0 if(bounds[i]==1) {
1137 921         0 j = ivec[i] + ibvec[i];
1138 921         0 if( j < 0 || j >= in->dims[i] )
1139 921         0 t_vio--;
1140             ivec[i] = -psize;
1141             j = ivec[i] + ibvec[i];
1142 2763 0       0 if( j < 0 || j >= in->dims[i] )
    0          
    0          
    0          
    100          
    0          
    0          
    0          
    0          
    0          
    100          
    0          
1143 1842         0 t_vio++;
1144             carry = 1;
1145 921 0       0 } else {
    0          
    0          
    0          
    50          
    0          
    0          
    0          
    0          
    0          
    50          
    0          
1146             ivec[i] = -psize;
1147 1842 0       0 }
    0          
    0          
    0          
    100          
    0          
    0          
    0          
    0          
    0          
    100          
    0          
1148 921 0       0 }
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    50          
    50          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    100          
    50          
    0          
    0          
1149 921         0 } /* End of counter-advance loop */
1150 921         0 } while (carry==0); /* end of total data accumulation loop (termination condition has carry on last dim) */
1151              
1152             {
1153             PDL_Double *ac = acc;
1154 13         264082 PDL_Double *wg = wgt;
1155 36 0       79 PDL_Double *wg2 = wgt2;
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
1156 36 0       77 $GENERIC() *dat = out->data;
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
1157 36         220  
1158 36         97 /* Calculate output vector offset */
1159             for(i=0;i
1160 12         69 dat += out->dimincs[i] * ovec[i];
1161 36         148  
1162             if (!flux) {
1163 36         165 /* Flux flag is NOT set -- normal case. Copy the weighted accumulated data. */
1164             for (i=0; i < out->dims[ndims]; i++) {
1165             *dat = (*wg && (*wg2 / *wg) < 1.5) ? *ac / *wg : badval;
1166             ac++; wg++; wg2++;
1167             dat += out->dimincs[ndims];
1168             }
1169             } else {
1170             /* Flux flag is set - scale by the (unpadded) determinant of the Jacobian */
1171 36         173 PDL_Double det = tmp[ndims*ndims];
1172 36 0       229 for(i=0; i < out->dims[ndims]; i++) {
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
1173 36         168 if(*wg && (*wg2 / *wg) < 1.5 ) {
1174 36 0       185 *dat = *(ac++) / *(wg++) * det;
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
1175 36         168 wg2++;
1176 36         164 } else {
1177             *dat = badval;
1178             ac++; wg++; wg2++;
1179             }
1180             dat += out->dimincs[ndims];
1181 957 0       129 } /* end of for loop */
    0          
    0          
    0          
    100          
    0          
    0          
    0          
    0          
    0          
    100          
    0          
1182             } /* end of flux flag set conditional */
1183 48         110 } /* end of convenience block */
1184            
1185             /* End of code for normal pixels */
1186             } else {
1187             /* The pixel was ludicrously huge -- just set this pixel to nan */
1188             $GENERIC() *dat = out->data;
1189             for(i=0;i
1190             dat += out->dimincs[i] * ovec[i];
1191             for(i=0;idims[ndims];i++) {
1192             *dat = badval; /* Should handle bad values too -- not yet */
1193             dat += out->dimincs[ndims];
1194             }
1195             }
1196              
1197             /* Increment the pixel counter */
1198             if (!pdl_broadcast_nd_step(npdls, offs, 0, ndims, incs, out->dims, ovec)) break;
1199             } while (1);
1200             PDL->changed(out, PDL_PARENTDATACHANGED, 0);
1201             +==EOD_map_c_code==
1202              
1203             Doc=><<'+==EOD_map_doc==',
1204              
1205             =head2 match
1206              
1207             =for usage
1208              
1209             $y = $x->match($c); # Match $c's header and size
1210             $y = $x->match([100,200]); # Rescale to 100x200 pixels
1211             $y = $x->match([100,200],{rect=>1}); # Rescale and remove rotation/skew.
1212              
1213             =for ref
1214              
1215             Resample a scientific image to the same coordinate system as another.
1216              
1217             The example above is syntactic sugar for
1218              
1219             $y = $x->map(t_identity, $c, ...);
1220              
1221             it resamples the input PDL with the identity transformation in
1222             scientific coordinates, and matches the pixel coordinate system to
1223             $c's FITS header.
1224              
1225             There is one difference between match and map: match makes the
1226             C option to C default to 0, not 1. This only affects
1227             matching where autoscaling is required (i.e. the array ref example
1228             above). By default, that example simply scales $x to the new size and
1229             maintains any rotation or skew in its scientific-to-pixel coordinate
1230             transform.
1231              
1232             =head2 map
1233              
1234             =for usage
1235              
1236             $y = $x->map($xform,[