Reputation: 1299
I try to rewrite this piece of Javascript code, coming from http://jsfromhell.com/math/is-point-in-poly, in coffeescript. It detects if a point is in a polygon, and works perfectly fine, but I am not sure what an elegant coffeescript translation would be?
function isPointInPoly(poly, pt){
for(var c = false, i = -1, l = poly.length, j = l - 1; ++i < l; j = i)
((poly[i].y <= pt.y && pt.y < poly[j].y) || (poly[j].y <= pt.y && pt.y < poly[i].y))
&& (pt.x < (poly[j].x - poly[i].x) * (pt.y - poly[i].y) / (poly[j].y - poly[i].y) + poly[i].x)
&& (c = !c);
return c;
}
Upvotes: 0
Views: 525
Reputation: 28880
Just as a point of comparison (pun intended), here's a version of this algorithm I wrote several years ago as part of PolyGonzo (a fast polygon drawing library I used in Google's election maps). This is adapted from the original, which handled multipolygons as commonly used in geographic work instead of just single polygons.
I wrote this code for speed - it should be quite a bit faster than the code in the question for old browsers, and somewhat faster in new browsers. (I haven't benchmarked it in a while, but I suspect the difference is less now that browsers have gotten better at optimizing JavaScript.)
For example, it avoids all of the repeated array and property dereferencing used in the jsfromhell example. I think it's a bit easier to read too. Of course the algorithm itself is still a bit complicated!
function pointInPoly( poly, point ) {
var inside = false,
x = point.x, y = point.y,
n = poly.length,
vertex = poly[ n - 1 ],
x1 = vertex.x, y1 = vertex.y;
for( var i = 0; i < n; ++i ) {
vertex = poly[i];
var x2 = vertex.x, y2 = vertex.y;
if( ( y1 < y ) != ( y2 < y ) )
if( x1 + ( y - y1 ) / ( y2 - y1 ) * ( x2 - x1 ) < x )
inside = ! inside;
x1 = x2, y1 = y2;
}
return inside;
}
I don't really like the translation that js2coffee.org produces for that code. In particular, the nested if
statements in the loop turn into a long one-liner:
inside = not inside if x1 + (y - y1) / (y2 - y1) * (x2 - x1) < x unless (y1 < y) is (y2 < y)
But it was easy to use that as a starting point and turn it into a nicer CoffeeScript version:
pointInPoly = ( poly, point ) ->
inside = false
x = point.x
y = point.y
vertex = poly[ poly.length - 1 ]
x1 = vertex.x
y1 = vertex.y
for vertex in poly
x2 = vertex.x
y2 = vertex.y
if ( y1 < y ) != ( y2 < y )
if x1 + ( y - y1 ) / ( y2 - y1 ) * ( x2 - x1 ) < x
inside = not inside
x1 = x2
y1 = y2
inside
This is a few more lines of code than the other version, but again it's optimized more for speed than brevity.
Upvotes: 4
Reputation: 665555
I'd go with
isPointInPoly = (poly, pt) ->
c = false
j = poly.length - 1
for b, i in poly
a = poly[j]
if ((a.y <= pt.y && pt.y < b.y) || (b.y <= pt.y && pt.y < a.y)) && (pt.x < (b.x - a.x) * (pt.y - a.y) / (b.y - a.y) + a.x)
c = not c
j = i
c
Upvotes: 1