Skip to content

Commit 448082f

Browse files
authored
Merge pull request #82 from ArneSpang/main
Added inPolygon with tests
2 parents 4d794a7 + 35237d2 commit 448082f

File tree

2 files changed

+115
-0
lines changed

2 files changed

+115
-0
lines changed

src/utils.jl

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export RotateTranslateScale
77
export LithostaticPressure!
88
export FlattenCrossSection
99
export AddField, RemoveField
10+
export inPolyPoint, inPolyPointF, inPolygon!
1011

1112
using NearestNeighbors
1213

@@ -1640,4 +1641,106 @@ function LithostaticPressure!(Plithos::Array{T,N}, Density::Array{T,N}, dz::Numb
16401641
Plithos[:] = reverse!(cumsum(reverse!(Plithos),dims=N))
16411642

16421643
return nothing
1644+
end
1645+
1646+
"""
1647+
inPolygon!(INSIDE::Matrix, PolyX::Vector, PolyY::Vector, X::Matrix, Y::Matrix; fast=false)
1648+
1649+
Checks if points given by matrices `X` and `Y` are in or on (both cases return true) a polygon given by `PolyX` and `PolyY`. Boolean `fast` will trigger faster version that may miss points that are exactly on the edge of the polygon. Speedup is a factor of 3.
1650+
1651+
"""
1652+
function inPolygon!(INSIDE::Matrix{Bool}, PolyX::Vector{T}, PolyY::Vector{T}, X::Matrix{T}, Y::Matrix{T}; fast=false) where T <: Real
1653+
iSteps = collect(eachindex(PolyX))
1654+
jSteps = [length(PolyX); collect(1:length(PolyX)-1)]
1655+
1656+
if fast
1657+
for j = 1 : size(X, 2)
1658+
for i = 1 : size(X, 1)
1659+
INSIDE[i,j] = inPolyPointF(PolyX, PolyY, X[i,j], Y[i,j], iSteps, jSteps)
1660+
end
1661+
end
1662+
else
1663+
for j = 1 : size(X, 2)
1664+
for i = 1 : size(X, 1)
1665+
INSIDE[i,j] = (inPolyPoint(PolyX, PolyY, X[i,j], Y[i,j], iSteps, jSteps) || inPolyPoint(PolyY, PolyX, Y[i,j], X[i,j], iSteps, jSteps))
1666+
end
1667+
end
1668+
end
1669+
end
1670+
1671+
"""
1672+
inPolygon!(inside::Vector, PolyX::Vector, PolyY::Vector, x::Vector, y::Vector; fast=false)
1673+
1674+
Same as above but `inside`, `X` and `Y` and are vectors.
1675+
1676+
"""
1677+
function inPolygon!(inside::Vector{Bool}, PolyX::Vector{T}, PolyY::Vector{T}, x::Vector{T}, y::Vector{T}; fast=false) where T <: Real
1678+
iSteps = collect(eachindex(PolyX))
1679+
jSteps = [length(PolyX); collect(1:length(PolyX)-1)]
1680+
1681+
if fast
1682+
for i = eachindex(x)
1683+
inside[i] = inPolyPointF(PolyX, PolyY, x[i], y[i], iSteps, jSteps)
1684+
end
1685+
else
1686+
for i = eachindex(x)
1687+
inside[i] = (inPolyPoint(PolyX, PolyY, x[i], y[i], iSteps, jSteps) || inPolyPoint(PolyY, PolyX, y[i], x[i], iSteps, jSteps))
1688+
end
1689+
end
1690+
end
1691+
1692+
"""
1693+
inPolyPoint(PolyX::Vector, PolyY::Vector, x::Number, y::Number, iSteps::Vector, jSteps::)
1694+
1695+
Checks if a point given by x and y is in or on (both cases return true) a polygon given by PolyX and PolyY, iSteps and jSteps provide the connectivity between the polygon edges. This function should be used through inPolygon!().
1696+
1697+
"""
1698+
function inPolyPoint(PolyX::Vector{T}, PolyY::Vector{T}, x::T, y::T, iSteps::Vector{Int64}, jSteps::Vector{Int64}) where T <: Real
1699+
inside1, inside2, inside3, inside4 = false, false, false, false
1700+
for (i,j) in zip(iSteps, jSteps)
1701+
xi = PolyX[i]
1702+
yi = PolyY[i]
1703+
xj = PolyX[j]
1704+
yj = PolyY[j]
1705+
1706+
con1 = ((yi > y) != (yj > y))
1707+
con2 = ((yi >= y) != (yj >= y))
1708+
if con1 && (x > (xj - xi) * (y - yi) / (yj - yi + eps()) + xi)
1709+
inside1 = !inside1
1710+
end
1711+
1712+
if con1 && (x >= (xj - xi) * (y - yi) / (yj - yi + eps()) + xi)
1713+
inside2 = !inside2
1714+
end
1715+
1716+
if con2 && (x > (xj - xi) * (y - yi) / (yj - yi + eps()) + xi)
1717+
inside3 = !inside3
1718+
end
1719+
1720+
if con2 && (x >= (xj - xi) * (y - yi) / (yj - yi + eps()) + xi)
1721+
inside4 = !inside4
1722+
end
1723+
end
1724+
return ((inside1 || inside2) || (inside3 || inside4))
1725+
end
1726+
1727+
"""
1728+
inPolyPointF(PolyX::Vector, PolyY::Vector, x::Number, y::Number, iSteps::Vector, jSteps::)
1729+
1730+
Faster version of inPolyPoint() but will miss some points that are on the edge of the polygon.
1731+
1732+
"""
1733+
function inPolyPointF(PolyX::Vector{T}, PolyY::Vector{T}, x::T, y::T, iSteps::Vector{Int64}, jSteps::Vector{Int64}) where T <: Real
1734+
inside = false
1735+
for (i,j) in zip(iSteps, jSteps)
1736+
xi = PolyX[i]
1737+
yi = PolyY[i]
1738+
xj = PolyX[j]
1739+
yj = PolyY[j]
1740+
1741+
if ((yi > y) != (yj > y)) && (x > (xj - xi) * (y - yi) / (yj - yi + eps()) + xi)
1742+
inside = !inside
1743+
end
1744+
end
1745+
return inside
16431746
end

test/test_utils.jl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,3 +239,15 @@ cross_tmp = CrossSection(Data_EQ,Lon_level=16.4,section_width=10km)
239239
cross_tmp = CrossSection(Data_EQ,Start=(15.0,35.0),End=(17.0,37.0),section_width=10km)
240240
@test cross_tmp.fields.lon_proj[20] ==15.314329874961091
241241
@test cross_tmp.fields.lat_proj[20] == 35.323420618580585
242+
243+
# test inPolygon
244+
PolyX = [-2.,-1,0,1,2,1,3,3,8,3,3,1,2,1,0,-1,-2,-1,-3,-3,-8,-3,-3,-1,-2]
245+
PolyY = [3.,3,8.01,3,3,1,2,1,0,-1,-2,-1,-3,-3,-8,-3,-3,-1,-2,-1,0,1,2,1,3]
246+
xvec = collect(-9:0.5:9); yvec = collect(-9:0.5:9); zvec = collect(1.:1.);
247+
X,Y,Z = meshgrid(xvec, yvec, zvec)
248+
X, Y = X[:,:,1], Y[:,:,1]
249+
yN = zeros(Bool, size(X))
250+
inPolygon!(yN, PolyX, PolyY, X, Y, fast=true)
251+
@test sum(yN) == 194
252+
inPolygon!(yN, PolyX, PolyY, X, Y)
253+
@test sum(yN) == 217

0 commit comments

Comments
 (0)