简体   繁体   中英

how to rewrite a more complex for loop from javascript to coffeescript?

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;
}

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.

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

( demo compilation )

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM