Here is the patch. Please let me know if you have any questions.

Thanks.

- Tim



On Mon, Jun 18, 2012 at 7:16 PM, Tim Haines <[email protected]>wrote:

> Hi, Chris and David.
>
>
> >> Ok, although it is usually better to write some perl
> >> code to call pp_def() to generated both routines
> >> from the same code---avoids the case where one
> >> routine is modified but not the other.
>
> Agreed. Updated (see below).
>
>
> >> The proposed route is usually good to allow for things to
> >> be fully tested and incrementally developed.  In the case
> >> where one has a "developer on fire" (TM) then a lot of the
> >> stages can be skipped.  :-)
>
> Aah, I see now. I tried to code it so that it would be as immediately
> beneficial as possible while not breaking the current codebase at the
> expense of increasing the Image2D::poly* namespace. I definitely understand
> slow integration steps to avoid breaking things on a project this big with
> such a diverse user base. :)
>
>
> >> Since the PP and non-PP implementations do the
> >> same thing---just better, the pnpoly routine should
> >> be migrated to use pnpoly_pp under the hood.
>
> That will be a bit of a complication. Currently, the PDL::pnpoly interface
> is (in PP jargon) 'x(m,n); y(m,n); px(k); py(k); int [o] msk(m,n)' and the
> interface for pnpoly_pp is 'a(m,n); ps(k,l); int [o] msk(m,n)'. Certainly,
> the inputs to PDL::pnpoly could be manipulated and sent off to pnpoly_pp,
> but the expensive xvals/yvals calls still exist in userland. This is why I
> was thinking that PDL::pnpoly could be put on the path to deprecation (not
> for a long while, though!) and pnpoly_pp could be exported now to allow
> people to move over to the PP version. However, I shall leave that
> executive decision in your hands.
>
>
> >> Once we have some more user experience
> >> with the new routine, we could prepare to make polyfill and
> >> polyfillv call the pnpoly routines under the hood with a flag
> >> to control the option.
>
> Agreed. Having two methods of solving the points-in-a-plane problem isn't
> terribly beneficial in the long run.
>
>
> >> Our PDL standard test module is Test::More but not all
> >> existing tests have been migrated to use that.  All new
> >> tests should start with that.
>
> The Image2D suite is still using just Test, so I went with that.
>
> @David,
>
>
> >> I wonder why you don't use loop %{ %} blocks?
>
> Those are very reasonable nits to pick. I wasn't using them originally
> because I couldn't figure out how to get at a single element of a 2D
> piddle, but I figured that out today and added them in. (See below)
>
>
> >> However, I quite like your use of the #define statements and may add a
> note about that in PDL::Book::PP. :-)
>
> Thanks! I got the idea from how polyfill was setup and I needed to be able
> to keep the vertx/verty in the algorithm straight since there are so many
> different invocations of them.
>
>
> >> Do you have a fork with this work on the web anywhere? I know, you're
> almost done, but if you want, you can easily fork the PDL project at
> github: https://github.com/PDLPorters/pdl.
>
> I just did a 'git clone git://pdl.git.sourceforge.net/gitroot/pdl/pdlPDL'. I 
> usually submit my patches through the mailing list and someone else
> will commit them for me. Would I be able to do commits with my own fork?
>
>
>
> Here is what I decided on for dynamically creating the pnpoly_pp and
> pnpolyfill_pp routines.
>
> my %pnpolyFields = (
>     'pnpoly_pp' => {'pars' => 'a(m,n); ps(k,l); int [o] msk(m,n)',
> 'special' => '$msk() = c;'},
>     'pnpolyfill_pp' => {'pars' => '[o,nc] a(m,n); ps(k,l); int col()',
> 'special' => 'if(c) { $a() = $col(); }'}
> );
>
> for my $name (keys %pnpolyFields) {
>     pp_def($name,
>         HandleBad => 0,
>         Pars => $pnpolyFields{$name}->{'pars'},
>         Code => '
>             int i, j, c, nvert;
>             nvert = $SIZE(l);
>
>             #define VERTX(q) $ps(k=>0,l=>q)
>             #define VERTY(q) $ps(k=>1,l=>q)
>
>             threadloop %{
>                 loop(n) %{
>                     loop(m) %{
>
>                         c = 0;
>                         for(i=0,j=nvert-1;i<nvert;j=i++) {
>                             if( ((VERTY(i)>n) != (VERTY(j)>n)) &&
>                                 (m < (VERTX(j)-VERTX(i)) * (n-VERTY(i)) /
> (VERTY(j)-VERTY(i)) + VERTX(i)) )
>                                 c = !c;
>                         }
>                        ' . $pnpolyFields{$name}->{'special'} .'
>                     %}
>                 %}
>             %}
>
>             #undef VERTX
>             #undef VERTY
>      '
>     );
> }
>
> Everything is building/testing fine on my Perl 5.14.2 and PDL 2.4.10. I
> will give the documentation a thorough inspection, then get a patch out.
>
> Many thanks.
>
> - Tim
diff --git a/Lib/Image2D/image2d.pd b/Lib/Image2D/image2d.pd
index 3f6318e..e92401b 100644
--- a/Lib/Image2D/image2d.pd
+++ b/Lib/Image2D/image2d.pd
@@ -1180,6 +1180,43 @@ void polyfill(PDL_Long *image, int wx, int wy, float 
*ps, int n,
 
 ');
 
+pp_def('polyfill',
+       HandleBad => 0, # a marker
+       Pars => 'int [o,nc] im(m,n); float ps(two=2,np); int col()',
+       Code => 'int ierr = 0, nerr;
+                threadloop %{
+                  polyfill($P(im), $SIZE(m), $SIZE(n), $P(ps), $SIZE(np), 
$col(), &nerr);
+                  ierr = ierr < nerr ? nerr : ierr;
+                %}
+                if (ierr) warn("errors during polygonfilling");
+                ',
+       Doc => << 'EOD',
+
+=for ref
+
+fill the area of the given polygon with the given colour.
+
+This function works inplace, i.e. modifies C<im>.
+
+=for usage
+
+  polyfill($im,$ps,$colour);
+  
+The method of determining which points lie inside of the polygon used here
+is not as strict as the method used in L<pnpoly|pnpoly>. Often, it includes 
vertices
+and edge points. See also L<pnpolyfill|pnpolyfill>.
+  
+=for example
+
+  # Make a convex 3x3 square of 1s in an image
+  $ps = pdl([3,3],[3,6],[6,6],[6,3]);
+  polyfill($im,$ps,1);
+
+=cut
+
+EOD
+);
+
 pp_add_exported('', 'pnpoly');
 pp_addpm(<<'EOPM');
 
@@ -1193,7 +1230,7 @@ pp_addpm(<<'EOPM');
 
   $mask = pnpoly($x, $y, $px, $py);
 
-For a closed polygon determined by the sequence of points in {$py,$py}
+For a closed polygon determined by the sequence of points in {$px,$py}
 the output of pnpoly is a mask corresponding to whether or not each
 coordinate (x,y) in the set of test points, {$x,$y}, is in the interior
 of the polygon.  This is the 'points in a polygon' algorithm from
@@ -1244,28 +1281,77 @@ sub PDL::pnpoly {
 
 EOPM
 
-pp_def('polyfill',
-       HandleBad => 0, # a marker
-       Pars => 'int [o,nc] im(m,n); float ps(two=2,np); int col()',
-       Code => 'int ierr = 0, nerr;
+my %pnpolyFields = (
+       'pnpoly_pp' => {'pars' => 'a(m,n); ps(k,l); int [o] msk(m,n)', 
'special' => '$msk() = c;'},
+       'pnpolyfill_pp' => {'pars' => '[o,nc] a(m,n); ps(k,l); int col()', 
'special' => 'if(c) { $a() = $col(); }'}
+);
+
+for my $name (keys %pnpolyFields) {
+       pp_def($name,
+               HandleBad => 0,
+               Pars => $pnpolyFields{$name}->{'pars'},
+               Code => '
+               int i, j, c, nvert;
+               nvert = $SIZE(l);
+       
+               #define VERTX(q) $ps(k=>0,l=>q)
+               #define VERTY(q) $ps(k=>1,l=>q)
+       
                 threadloop %{
-                  polyfill($P(im), $SIZE(m), $SIZE(n), $P(ps), $SIZE(np), 
$col(), &nerr);
-                  ierr = ierr < nerr ? nerr : ierr;
+                   loop(n) %{
+                       loop(m) %{
+                           c = 0;
+                           for(i=0,j=nvert-1;i<nvert;j=i++) {
+                               if( ((VERTY(i)>n) != (VERTY(j)>n)) &&
+                                   (m < (VERTX(j)-VERTX(i)) * (n-VERTY(i)) / 
(VERTY(j)-VERTY(i)) + VERTX(i)) )
+                                   c = !c;
+                           }
+                          ' . $pnpolyFields{$name}->{'special'} .'
                 %}
-                if (ierr) warn("errors during polygonfilling");
-                ',
-       Doc => << 'EOD',
+                               %}
+                       %}
+       
+               #undef VERTX
+               #undef VERTY
+        '
+       );
+}
+
+pp_add_exported('', 'pnpolyfill');
+pp_addpm(<<'EOPM');
+=head2 pnpolyfill
 
 =for ref
 
-fill the area inside the given polygon with a given colour
+fill the area of the given polygon with the given colour.
 
 This function works inplace, i.e. modifies C<im>.
 
+=for usage
+
+  pnpolyfill($im,$ps,$colour);
+  
+This uses the L<pnpoly|pnpoly> method of determining points interior to the 
set $ps.
+
+=for example
+
+  # Make a convex 3x3 square of 1s in an image
+  $ps = pdl([3,3],[3,6],[6,6],[6,3]);
+  pnpolyfill($im,$ps,1);
+
 =cut
 
-EOD
-);
+sub PDL::pnpolyfill {
+       my ($im, $ps, $col) = @_;
+       barf("ps must contain pairwise points.\n") unless $ps->getdim(0) == 2;
+       barf("color not specified.\n") unless $col;
+       pnpolyfill_pp($im,$ps,$col);
+       return $im;
+}
+
+*pnpolyfill = \&PDL::pnpolyfill;
+
+EOPM
 
 pp_add_exported('', 'polyfillv');
 pp_addpm(<<'EOPM');
@@ -1274,12 +1360,20 @@ pp_addpm(<<'EOPM');
 
 =for ref
 
-return the (dataflown) area of an image within a polygon
+return the (dataflown) area of an image described by a polygon
+
+=for usage
+
+  polyfillv($im,$ps);
+  
+The method of determining which points lie inside of the polygon used here
+is not as strict as the method used in L<pnpoly|pnpoly>. Often, it includes 
vertices
+and edge points. See also L<pnpolyfillv|pnpolyfillv>.
 
 =for example
 
   # increment intensity in area bounded by $poly
-  $im->polyfillv($pol)++; # legal in perl >= 5.6
+  $im->polyfillv($poly)++; # legal in perl >= 5.6
   # compute average intensity within area bounded by $poly
   $av = $im->polyfillv($poly)->avg;
 
@@ -1295,6 +1389,41 @@ sub PDL::polyfillv :lvalue {
 
 EOPM
 
+pp_add_exported('','pnpolyfillv');
+pp_addpm(<<'EOPM');
+
+=head2 pnpolyfillv
+
+=for ref
+
+return the (dataflown) area of an image described by a polygon
+
+=for usage
+
+  pnpolyfillv($im,$ps);
+  
+This uses the L<pnpoly|pnpoly> method of determining points interior to the 
set $ps.
+
+=for example
+
+  # increment intensity in area bounded by $poly
+  $im->pnpolyfillv($poly)++; # legal in perl >= 5.6
+  # compute average intensity within area bounded by $poly
+  $av = $im->pnpolyfillv($poly)->avg;
+
+=cut
+
+sub PDL::pnpolyfillv :lvalue {
+       my ($im, $ps) = @_;
+       barf("ps must contain pairwise points.\n") unless $ps->getdim(0) == 2;
+       return $im->where(pnpoly_pp($im, $ps));
+}
+
+*pnpolyfillv = \&PDL::pnpolyfillv;
+=cut
+
+EOPM
+
 pp_addhdr('#include "rotate.c"'."\n\n");
 pp_add_exported('','rotnewsz');
 pp_addxs('
diff --git a/t/image2d.t b/t/image2d.t
index 5b45265..a18e213 100644
--- a/t/image2d.t
+++ b/t/image2d.t
@@ -3,7 +3,7 @@
 
 use Test;
 BEGIN {
-    plan tests => 22;
+    plan tests => 25;
 }
 
 use PDL;
@@ -147,9 +147,26 @@ ok($@ eq '');
 my $px = pdl(0,3,1);
 my $py = pdl(0,1,4);
 my $im = zeros(5,5);
+my $im2 = zeroes(5,5);
 my $x = $im->xvals;
 my $y = $im->yvals;
+my $ps = $px->cat($py)->xchg(0,1);
 my $im_mask = pnpoly($x,$y,$px,$py);
 ok(sum($im_mask) == 5);
 my $inpixels = pdl q[ 1 1 ; 1 2 ; 1 3 ; 2 1 ; 2 2 ];
 ok(sum($inpixels - qsortvec(scalar whichND($im_mask))) == 0);
+
+# Make sure the PDL pnpoly and the PP pnpoly give the same result
+ok(all($im_mask == pnpolyfill($im,$ps,1)));
+
+# Trivial test to make sure the pnpolyfills are working
+$im .= 0;
+pnpolyfillv($im2,$ps) .= 22;
+ok(all(pnpolyfill($im,$ps,22) == $im2));
+
+# Trivial test to make sure the polyfills are working
+$im .= 0;
+$im2 .= 0;
+polyfillv($im2,$ps) .= 25;
+polyfill($im,$ps,25);
+ok(all($im == $im2));
_______________________________________________
Perldl mailing list
[email protected]
http://mailman.jach.hawaii.edu/mailman/listinfo/perldl

Reply via email to