Created
October 11, 2016 19:28
-
-
Save mauro3/757223ce0003ebfdd69ed881f3529784 to your computer and use it in GitHub Desktop.
inpoly using winding number
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
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