Skip to content

Instantly share code, notes, and snippets.

@mauro3
Created October 11, 2016 19:28
Show Gist options
  • Save mauro3/757223ce0003ebfdd69ed881f3529784 to your computer and use it in GitHub Desktop.
Save mauro3/757223ce0003ebfdd69ed881f3529784 to your computer and use it in GitHub Desktop.
inpoly using winding number
"""
Determines the winding number of a point and a polygon, i.e. how many
times a polygon winds around the point.
It follows Dan Sunday: http://geomalgorithms.com/a03-_inclusion.html.
"""
function windnr(p, poly::Matrix)
@assert length(p)==2
@assert poly[:,1]==poly[:,end]
# Loop over edges
px = p[1]
py = p[2]
wn = 0 # winding number, inside if >0
len = length(poly)-2
@inbounds for k=1:2:len #size(poly,2)-1 # @fastmath makes it worse
# ex1,ey1,ex2,ey2 = poly[k:k+3] # is slow...
ex1,ey1,ex2,ey2 = poly[k], poly[k+1], poly[k+2], poly[k+3]
# Reject edge if p totally above or below p:
if (ey1>py && ey2>py) || (ey1<py && ey2<py)
continue
end
# Check edges intersecting a horizontal ray:
orient = leftorright(px,py,ex1,ey1,ex2,ey2)
if up(ey1,ey2)
if orient==-1; wn-=1 end # p strictly left of e
elseif down(ey1,ey2)
if orient==1; wn+=1 end # p strictly right of e
end
end
return wn
end
up(ey1,ey2) = ey1<ey2
down(ey1,ey2) = ey1>ey2
function leftorright(px,py,ex1,ey1,ex2,ey2)
# returns:
# -1 if on left of line
# 0 if on line
# 1 if on right of line
vx,vy = ex2-ex1, ey2-ey1
px,py = px-ex1, py-ey1
sign(px*vy-py*vx)
end
"""
Determines if a point is inside a polygon.
- p -- point (x,y) or [x,y]
- poly -- polygon vertices [x1 x2 ... xn x1
y1 y2 ... yn y1]
(a closed poly)
Returns true if point has an odd winding number. This should label
points as exterior which are inside outcrops. See test for a test.
"""
inpoly(p, poly::Matrix) = isodd(windnr(p,poly))
########
using Base.Test
## inpoly
@test leftorright(0.5,0.5, 1,0,1,1)==-1
@test leftorright(1.5,.5, 1,0,1,1)==1
@test leftorright(1,0.5, 1,0,1,1)==0
poly = Float64[0 0
0 1
1 1
1 0
0 0]'
p1 = [0.5, 0.5]
p2 = [0.5, 0.99]
p22 = [0.5, 1] # on edge
p23 = [0.5, 0] # on edge
p24 = [0, 0] # on corner
p25 = [0, .4] # on edge
p3 = [0.5, 1.1]
@test inpoly(p1, poly)
@test inpoly(p2, poly)
@test inpoly(p22, poly)
@test inpoly(p23, poly)
@test inpoly(p24, poly)
@test inpoly(p25, poly)
@test !inpoly(p3, poly)
# clockwise poly
poly = Float64[0 0
1 0
1 1
0 1
0 0]'
@test inpoly(p1, poly)
@test inpoly(p2, poly)
@test inpoly(p22, poly)
@test inpoly(p23, poly)
@test inpoly(p24, poly)
@test inpoly(p25, poly)
@test !inpoly(p3, poly)
# cross-over poly
poly = Float64[0 0
1 0
0 1
1 1
0 0]'
if VERSION>=v"0.5-"
eval(:(@test_broken inpoly(p1, poly) )) # should be true
end
@test inpoly(p2, poly)
@test inpoly(p22, poly)
@test inpoly(p23, poly)
@test inpoly(p24, poly)
@test !inpoly(p25, poly) # different
@test !inpoly(p3, poly)
# with interior region
poly = Float64[0 0
# interior
0.1 0.1
0.1 0.6
0.6 0.6
0.6 0.1
0.1 0.1
# exterior
0 0
0 1
1 1
1 0
0 0]'
# inside interior poly: i.e. labeled as outside
@test !inpoly([0.3,0.3], poly)
@test !inpoly([0.3,0.5], poly)
poly = Float64[0 0
# interior
0.1 0.1
0.1 0.6
0.6 0.6
# in-interior
0.4 0.4
0.4 0.2
0.2 0.2
0.2 0.4
0.4 0.4
# interior
0.6 0.6
0.6 0.1
0.1 0.1
# exterior
0 0
0 1
1 1
1 0
0 0]'
# inside in-interior poly
@test inpoly([0.3,0.3], poly)
@test !inpoly([0.3,0.5], poly)
poly = Float64[0 0
# interior
0.1 0.1
0.1 0.6
0.6 0.6
# in-interior
0.4 0.4
0.2 0.4
0.2 0.2
0.4 0.2
0.4 0.4
# interior
0.6 0.6
0.6 0.1
0.1 0.1
# exterior
0 0
0 1
1 1
1 0
0 0]'
# inside in-interior poly
@test inpoly([0.3,0.3], poly)
@test !inpoly([0.3,0.5], poly)
poly = Float64[0 0
# interior #1
0.1 0.1
0.1 0.6
0.4 0.6
0.4 0.6
0.4 0.1
0.1 0.1
0 0
# interior #2
0.6 0.4
0.6 0.6
0.8 0.6
0.8 0.4
0.6 0.4
0 0
# exterior
0 1
1 1
1 0
0 0]'
@test !inpoly([0.2,0.4], poly)
@test !inpoly([0.3,0.15], poly)
@test inpoly([0.5,0.4], poly)
@test inpoly([0.5,0.2], poly)
@test !inpoly([0.7,0.5], poly)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment