Skip to content

Commit ae6c160

Browse files
asinghvi17rafaqz
andauthored
Algorithm + manifold and accelerator passthrough in clipping functions (#273)
Co-authored-by: Rafael Schouten <[email protected]>
1 parent fb0bad6 commit ae6c160

File tree

7 files changed

+334
-111
lines changed

7 files changed

+334
-111
lines changed

src/methods/clipping/clipping_processor.jl

Lines changed: 128 additions & 31 deletions
Large diffs are not rendered by default.

src/methods/clipping/cut.jl

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -58,29 +58,31 @@ GI.coordinates.(cut_polys)
5858
[[[5.0, 0.0], [10.0, 0.0], [10.0, 10.0], [5.0, 10.0], [5.0, 0.0]]]
5959
```
6060
"""
61-
cut(geom, line, ::Type{T} = Float64) where {T <: AbstractFloat} =
62-
_cut(T, GI.trait(geom), geom, GI.trait(line), line; exact = True())
61+
cut(geom, line, ::Type{T} = Float64) where {T <: AbstractFloat} = cut(FosterHormannClipping(), geom, line, T)
62+
cut(m::Manifold, geom, line, ::Type{T} = Float64) where {T <: AbstractFloat} = cut(FosterHormannClipping(m), geom, line, T)
63+
cut(alg::FosterHormannClipping{M, A}, geom, line, ::Type{T} = Float64) where {T <: AbstractFloat, M, A} =
64+
_cut(alg, T, GI.trait(geom), geom, GI.trait(line), line; exact = True())
6365

6466
#= Cut a given polygon by given line. Add polygon holes back into resulting pieces if there
6567
are any holes. =#
66-
function _cut(::Type{T}, ::GI.PolygonTrait, poly, ::GI.LineTrait, line; exact) where T
68+
function _cut(alg::FosterHormannClipping{M, A}, ::Type{T}, ::GI.PolygonTrait, poly, ::GI.LineTrait, line; exact) where {T, M, A}
6769
ext_poly = GI.getexterior(poly)
68-
poly_list, intr_list = _build_a_list(T, ext_poly, line; exact)
70+
poly_list, intr_list = _build_a_list(alg, T, ext_poly, line; exact)
6971
n_intr_pts = length(intr_list)
7072
# If an impossible number of intersection points, return original polygon
7173
if n_intr_pts < 2 || isodd(n_intr_pts)
7274
return [tuples(poly)]
7375
end
7476
# Cut polygon by line
75-
cut_coords = _cut(T, ext_poly, line, poly_list, intr_list, n_intr_pts; exact)
77+
cut_coords = _cut(alg, T, ext_poly, line, poly_list, intr_list, n_intr_pts; exact)
7678
# Close coords and create polygons
7779
for c in cut_coords
7880
push!(c, c[1])
7981
end
8082
cut_polys = [GI.Polygon([c]) for c in cut_coords]
8183
# Add original polygon holes back in
8284
remove_idx = falses(length(cut_polys))
83-
_add_holes_to_polys!(T, cut_polys, GI.gethole(poly), remove_idx; exact)
85+
_add_holes_to_polys!(alg, T, cut_polys, GI.gethole(poly), remove_idx; exact)
8486
return cut_polys
8587
end
8688

@@ -97,10 +99,10 @@ end
9799
of cut geometry in Vector{Vector{Tuple}} format.
98100
99101
Note: degenerate cases where intersection points are vertices do not work right now. =#
100-
function _cut(::Type{T}, geom, line, geom_list, intr_list, n_intr_pts; exact) where T
102+
function _cut(alg::FosterHormannClipping{M, A}, ::Type{T}, geom, line, geom_list, intr_list, n_intr_pts; exact) where {T, M, A}
101103
# Sort and categorize the intersection points
102104
sort!(intr_list, by = x -> geom_list[x].fracs[2])
103-
_flag_ent_exit!(GI.LineTrait(), line, geom_list; exact)
105+
_flag_ent_exit!(alg, GI.LineTrait(), line, geom_list; exact)
104106
# Add first point to output list
105107
return_coords = [[geom_list[1].point]]
106108
cross_backs = [(T(Inf),T(Inf))]

src/methods/clipping/difference.jl

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -33,19 +33,23 @@ GI.coordinates.(diff_poly)
3333
```
3434
"""
3535
function difference(
36-
geom_a, geom_b, ::Type{T} = Float64; target=nothing, kwargs...,
36+
alg::FosterHormannClipping, geom_a, geom_b, ::Type{T} = Float64; target=nothing, kwargs...,
3737
) where {T<:AbstractFloat}
3838
return _difference(
39-
TraitTarget(target), T, GI.trait(geom_a), geom_a, GI.trait(geom_b), geom_b;
39+
alg, TraitTarget(target), T, GI.trait(geom_a), geom_a, GI.trait(geom_b), geom_b;
4040
exact = True(), kwargs...,
4141
)
4242
end
43+
# fallback definitions
44+
difference(geom_a, geom_b, ::Type{T} = Float64; target=nothing, kwargs...) where T = difference(FosterHormannClipping(Planar()), geom_a, geom_b, T; target, kwargs...)
45+
# if manifold but no algorithm - assume FosterHormannClipping with provided manifold.
46+
difference(m::Manifold, geom_a, geom_b, ::Type{T} = Float64; target=nothing, kwargs...) where T = difference(FosterHormannClipping(m), geom_a, geom_b, T; target, kwargs...)
4347

4448
#= The 'difference' function returns the difference of two polygons as a list of polygons.
4549
The algorithm to determine the difference was adapted from "Efficient clipping of efficient
4650
polygons," by Greiner and Hormann (1998). DOI: https://doi.org/10.1145/274363.274364 =#
4751
function _difference(
48-
::TraitTarget{GI.PolygonTrait}, ::Type{T},
52+
alg::FosterHormannClipping, target::TraitTarget{GI.PolygonTrait}, ::Type{T},
4953
::GI.PolygonTrait, poly_a,
5054
::GI.PolygonTrait, poly_b;
5155
exact, kwargs...
@@ -54,11 +58,11 @@ function _difference(
5458
ext_a = GI.getexterior(poly_a)
5559
ext_b = GI.getexterior(poly_b)
5660
# Find the difference of the exterior of the polygons
57-
a_list, b_list, a_idx_list = _build_ab_list(T, ext_a, ext_b, _diff_delay_cross_f, _diff_delay_bounce_f; exact)
58-
polys = _trace_polynodes(T, a_list, b_list, a_idx_list, _diff_step, poly_a, poly_b)
61+
a_list, b_list, a_idx_list = _build_ab_list(alg, T, ext_a, ext_b, _diff_delay_cross_f, _diff_delay_bounce_f; exact)
62+
polys = _trace_polynodes(alg, T, a_list, b_list, a_idx_list, _diff_step, poly_a, poly_b)
5963
# if no crossing points, determine if either poly is inside of the other
6064
if isempty(polys)
61-
a_in_b, b_in_a = _find_non_cross_orientation(a_list, b_list, ext_a, ext_b; exact)
65+
a_in_b, b_in_a = _find_non_cross_orientation(alg.manifold, a_list, b_list, ext_a, ext_b; exact)
6266
# add case for if they polygons are the same (all intersection points!)
6367
# add a find_first check to find first non-inter poly!
6468
if b_in_a && !a_in_b # b in a and can't be the same polygon
@@ -72,19 +76,19 @@ function _difference(
7276
remove_idx = falses(length(polys))
7377
# If the original polygons had holes, take that into account.
7478
if GI.nhole(poly_a) != 0
75-
_add_holes_to_polys!(T, polys, GI.gethole(poly_a), remove_idx; exact)
79+
_add_holes_to_polys!(alg, T, polys, GI.gethole(poly_a), remove_idx; exact)
7680
end
7781
if GI.nhole(poly_b) != 0
7882
for hole in GI.gethole(poly_b)
7983
hole_poly = GI.Polygon(StaticArrays.SVector(hole))
80-
new_polys = intersection(hole_poly, poly_a, T; target = GI.PolygonTrait)
84+
new_polys = intersection(alg, hole_poly, poly_a, T; target = GI.PolygonTrait)
8185
if length(new_polys) > 0
8286
append!(polys, new_polys)
8387
end
8488
end
8589
end
8690
# Remove unneeded collinear points on same edge
87-
_remove_collinear_points!(polys, remove_idx, poly_a, poly_b)
91+
_remove_collinear_points!(alg, polys, remove_idx, poly_a, poly_b)
8892
return polys
8993
end
9094

@@ -110,15 +114,15 @@ _diff_step(x, y) = (x ⊻ y) ? 1 : (-1)
110114
#= Polygon with multipolygon difference - note that all intersection regions between
111115
`poly_a` and any of the sub-polygons of `multipoly_b` are removed from `poly_a`. =#
112116
function _difference(
113-
target::TraitTarget{GI.PolygonTrait}, ::Type{T},
117+
alg::FosterHormannClipping, target::TraitTarget{GI.PolygonTrait}, ::Type{T},
114118
::GI.PolygonTrait, poly_a,
115119
::GI.MultiPolygonTrait, multipoly_b;
116120
kwargs...,
117121
) where T
118122
polys = [tuples(poly_a, T)]
119123
for poly_b in GI.getpolygon(multipoly_b)
120124
isempty(polys) && break
121-
polys = mapreduce(p -> difference(p, poly_b; target), append!, polys)
125+
polys = mapreduce(p -> difference(alg, p, poly_b; target), append!, polys)
122126
end
123127
return polys
124128
end
@@ -128,7 +132,7 @@ sub-polygons of `multipoly_a` and `poly_b` will be removed from the correspondin
128132
sub-polygon. Unless specified with `fix_multipoly = nothing`, `multipolygon_a` will be
129133
validated using the given (default is `UnionIntersectingPolygons()`) correction. =#
130134
function _difference(
131-
target::TraitTarget{GI.PolygonTrait}, ::Type{T},
135+
alg::FosterHormannClipping, target::TraitTarget{GI.PolygonTrait}, ::Type{T},
132136
::GI.MultiPolygonTrait, multipoly_a,
133137
::GI.PolygonTrait, poly_b;
134138
fix_multipoly = UnionIntersectingPolygons(), kwargs...,
@@ -139,7 +143,7 @@ function _difference(
139143
polys = Vector{_get_poly_type(T)}()
140144
sizehint!(polys, GI.npolygon(multipoly_a))
141145
for poly_a in GI.getpolygon(multipoly_a)
142-
append!(polys, difference(poly_a, poly_b; target))
146+
append!(polys, difference(alg, poly_a, poly_b; target))
143147
end
144148
return polys
145149
end
@@ -150,7 +154,7 @@ corresponding sub-polygon of `multipoly_a`. Unless specified with `fix_multipoly
150154
`multipolygon_a` will be validated using the given (default is `UnionIntersectingPolygons()`)
151155
correction. =#
152156
function _difference(
153-
target::TraitTarget{GI.PolygonTrait}, ::Type{T},
157+
alg::FosterHormannClipping, target::TraitTarget{GI.PolygonTrait}, ::Type{T},
154158
::GI.MultiPolygonTrait, multipoly_a,
155159
::GI.MultiPolygonTrait, multipoly_b;
156160
fix_multipoly = UnionIntersectingPolygons(), kwargs...,
@@ -165,26 +169,39 @@ function _difference(
165169
pieces of `multipolygon_a`` are removed, continue to take difference with new shape
166170
`polys` =#
167171
polys = if i == 1
168-
difference(multipoly_a, poly_b; target, fix_multipoly)
172+
difference(alg, multipoly_a, poly_b; target, fix_multipoly)
169173
else
170-
difference(GI.MultiPolygon(polys), poly_b; target, fix_multipoly)
174+
difference(alg, GI.MultiPolygon(polys), poly_b; target, fix_multipoly)
171175
end
172176
#= One multipoly_a has been completely covered (and thus removed) there is no need to
173177
continue taking the difference =#
174178
isempty(polys) && break
175179
end
176180
return polys
177181
end
178-
182+
function _difference(
183+
alg::FosterHormannClipping, ::TraitTarget{GI.MultiPolygonTrait}, ::Type{T},
184+
trait_a::Union{GI.PolygonTrait, GI.MultiPolygonTrait}, polylike_a,
185+
trait_b::Union{GI.PolygonTrait, GI.MultiPolygonTrait}, polylike_b;
186+
fix_multipoly = UnionIntersectingPolygons(), kwargs...
187+
) where T
188+
polys = _difference(alg, TraitTarget(GI.PolygonTrait()), T, trait_a, polylike_a, trait_b, polylike_b; kwargs...)
189+
if isnothing(fix_multipoly)
190+
return GI.MultiPolygon(polys)
191+
else
192+
return fix_multipoly(GI.MultiPolygon(polys))
193+
end
194+
end
179195
# Many type and target combos aren't implemented
180196
function _difference(
181-
::TraitTarget{Target}, ::Type{T},
197+
alg::GeometryOpsCore.Algorithm, target::TraitTarget{Target}, ::Type{T},
182198
trait_a::GI.AbstractTrait, geom_a,
183199
trait_b::GI.AbstractTrait, geom_b,
200+
kw...
184201
) where {Target, T}
185202
@assert(
186203
false,
187-
"Difference between $trait_a and $trait_b with target $Target isn't implemented yet.",
204+
"Difference between $trait_a and $trait_b with target $Target and algorithm $alg isn't implemented yet.",
188205
)
189206
return nothing
190207
end

0 commit comments

Comments
 (0)