Skip to content

Commit d7aea25

Browse files
committed
👢 Remove LibGEOS dependency
1 parent 03d8761 commit d7aea25

3 files changed

Lines changed: 92 additions & 27 deletions

File tree

Project.toml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,11 @@ version = "0.1.0"
55

66
[deps]
77
CondaPkg = "992eb4ea-22a4-4c89-a5bb-47a3300528ab"
8-
LibGEOS = "a90b1aa1-3769-5649-ba7e-abc5a9d163eb"
98
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
109
PythonCall = "6099a3de-0909-46bc-b1f4-468b9a2dfc0d"
1110

1211
[compat]
1312
CondaPkg = "0.2"
14-
LibGEOS = "0.9"
1513
PythonCall = "0.9"
1614
julia = "1.11"
1715

src/FastPointQuery.jl

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module FastPointQuery
22

3-
using CondaPkg, LibGEOS, Printf, PythonCall
3+
using CondaPkg, Printf, PythonCall
44

55
const np = PythonCall.pynew()
66
const shapely = PythonCall.pynew()
@@ -19,11 +19,13 @@ function __init__()
1919
end
2020
end
2121

22+
include(joinpath(@__DIR__, "STLInfo.jl"))
2223
include(joinpath(@__DIR__, "polygon.jl"))
2324

25+
2426
# export structs
25-
export QueryPolygon
27+
export QueryPolygon, STLInfo
2628
# export functions
27-
export get_polygon, pip_query
29+
export get_polygon, pip_query, loadmesh
2830

2931
end

src/polygon.jl

Lines changed: 87 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -21,37 +21,31 @@ struct QueryPolygon
2121
end
2222

2323
"""
24-
get_polygon(pts; ratio=0.1)
24+
get_polygon(points::AbstractArray; ratio::Bool=0.1)
2525
2626
Description:
2727
---
28-
Get the polygon from the given points. `pts` is a 2xN array, and `ratio` is the
28+
Get the polygon from the given points. `points` is a 2xN array, and `ratio` is the
2929
parameter for the concave hull. The default value is 0.1.
30+
31+
Example:
32+
---
33+
```julia
34+
polygon_xy = [0 0; 1 0; 1 1; 0 1]' # 2xN array
35+
polygon = get_polygon(polygon_xy, ratio=1) # polygon.py_xy to visualize
36+
```
3037
"""
3138
function get_polygon(points::AbstractArray; ratio::Real=0.1)
3239
# inputs checking
3340
n, m = size(points)
3441
n == 2 || error("points must be a 2xN array")
3542
m >= 3 || error("at least 3 points are required")
3643
ratio > 0 || error("ratio must be positive")
37-
allow_holes = 0
38-
pts = Array{Float64}(points)
39-
40-
# convert to GEOS datatype
41-
gpts = LibGEOS.MultiPoint([pts[:, i] for i in axes(pts, 2)])
42-
ctx = LibGEOS.get_context(gpts)
43-
ptr = LibGEOS.GEOSConcaveHullByLength_r(ctx, gpts, ratio, allow_holes)
44-
conc = LibGEOS.geomFromGEOS(ptr)
45-
conc == C_NULL && error("LibGEOS: Error in GEOSConvexHull")
46-
47-
# convert back to julia datatype
48-
ob_outer = LibGEOS.exteriorRing(conc)
49-
cs_outer = LibGEOS.getCoordSeq(ob_outer)
50-
xs_outer = LibGEOS.getX(cs_outer)
51-
ys_outer = LibGEOS.getY(cs_outer)
52-
xy = vcat(permutedims(xs_outer), permutedims(ys_outer))
53-
54-
return QueryPolygon(xy, shapely.Polygon(xy'))
44+
# use shapely to get the polygon
45+
py_points = shapely.MultiPoint(np.array(points'))
46+
poly = shapely.concave_hull(py_points, ratio=ratio, allow_holes=false)
47+
xy = pyconvert(Array, np.array(poly.exterior.xy)) # get the exterior points of the polygon
48+
return QueryPolygon(xy, poly)
5549
end
5650

5751
"""
@@ -68,8 +62,8 @@ Example:
6862
```julia
6963
polypon_xy = [0 0; 1 0; 1 1; 0 1]' # 2xN array
7064
polygon = get_polygon(polygon_xy, ratio=1) # polygon.py_xy to visualize
71-
particle_in_polygon(polygon, 0, 0) # check points (0, 0) and return false
72-
particle_in_polygon(polygon, 0, 0, edge=true) # check points (0, 0) and return true
65+
pip_query(polygon, 0, 0) # check points (0, 0) and return false
66+
pip_query(polygon, 0, 0, edge=true) # check points (0, 0) and return true
7367
```
7468
"""
7569
function pip_query(
@@ -92,10 +86,12 @@ array, where each column represents a point (x, y). If `edge` is set to true, it
9286
the points are on the edge of the polygon as well.
9387
9488
Example:
89+
---
9590
```julia
9691
polygon_xy = [0 0; 1 0; 1 1; 0 1]' # 2xN array
9792
polygon = get_polygon(polygon_xy, ratio=1) # polygon.py_xy to visualize
9893
pip_query(polygon, [0 0; 0.5 0.5; 1 1], edge=true) # returns [true, true, true]
94+
```
9995
"""
10096
function pip_query(
10197
polygon::QueryPolygon,
@@ -129,10 +125,12 @@ where each column represents a point (x, y). If `edge` is set to true, it checks
129125
points are on the edge of the mesh as well.
130126
131127
Example:
128+
---
132129
```julia
133130
stl_file = "path/to/your/file.stl"
134131
points = [0 0; 0.5 0.5; 1 1]'
135132
pip_query(stl_file, points, edge=true)
133+
```
136134
"""
137135
function pip_query(
138136
stl_file::String,
@@ -164,3 +162,70 @@ function pip_query(
164162
tmp = py_pip(stl_file, py_points, trimesh, shapely)
165163
return pyconvert(Vector{Bool}, tmp)
166164
end
165+
166+
"""
167+
pip_query(mesh::Py, points::AbstractMatrix; edge::Bool=false)
168+
169+
Description:
170+
---
171+
Determine whether a set of points is inside a 3D mesh defined by a Python object. The input
172+
`mesh` should be a Python object representing the mesh (`trimesh`), and `points` should be
173+
a 2xN array where each column represents a point (x, y). If `edge` is set to true, it checks
174+
if the points are on the edge of the mesh as well.
175+
176+
Example:
177+
---
178+
```julia
179+
mesh = trimesh.load("path/to/your/file.stl", force="mesh")
180+
points = [0 0; 0.5 0.5; 1 1]'
181+
pip_query(mesh, points, edge=true)
182+
```
183+
"""
184+
function pip_query(
185+
mesh ::Py,
186+
points ::AbstractMatrix;
187+
edge ::Bool=false
188+
)
189+
size(points, 1) == 2 || error("points must be a 2xN array")
190+
py_points = shapely.points(points')
191+
if edge
192+
@pyexec """
193+
def py_pip(mesh, py_points, trimesh, shapely):
194+
tris2d = mesh.triangles[:, :, :2]
195+
region = shapely.unary_union([shapely.Polygon(t) for t in tris2d])
196+
mask = shapely.covers(region, py_points)
197+
return mask
198+
""" => py_pip
199+
else
200+
@pyexec """
201+
def py_pip(mesh, py_points, trimesh, shapely):
202+
tris2d = mesh.triangles[:, :, :2]
203+
region = shapely.unary_union([shapely.Polygon(t) for t in tris2d])
204+
mask = shapely.within(py_points, region)
205+
return mask
206+
""" => py_pip
207+
end
208+
tmp = py_pip(mesh, py_points, trimesh, shapely)
209+
return pyconvert(Vector{Bool}, tmp)
210+
end
211+
212+
"""
213+
pip_query(stl_model::STLInfo, points::AbstractMatrix; edge::Bool=false)
214+
215+
Description:
216+
---
217+
Determine whether a set of points is inside a 3D mesh defined by a 'STLInfo'. The input
218+
`stl_model` should be initiated by function `loadmesh(stl_file)`, and `points` should be
219+
a 2xN array where each column represents a point (x, y). If `edge` is set to true, it checks
220+
if the points are on the edge of the mesh as well.
221+
222+
Example:
223+
---
224+
```julia
225+
stl_model = loadmesh("path/to/your/file.stl")
226+
points = [0 0; 0.5 0.5; 1 1]'
227+
pip_query(stl_model, points, edge=true)
228+
```
229+
"""
230+
pip_query(stl_model::STLInfo, points::AbstractMatrix; edge::Bool=false) = pip_query(
231+
stl_model.mesh, points; edge=edge)

0 commit comments

Comments
 (0)