Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clipping degeneracies #58

Merged
merged 46 commits into from
Mar 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
aba090f
Fix up intersection point base calculation
skygering Oct 2, 2023
ab16785
Update intersects and add line tests
skygering Oct 4, 2023
7e75e86
Add more tests and debug intersects
skygering Oct 4, 2023
a7a7367
Add comments to point_in_poly
skygering Oct 4, 2023
b99e37d
Remove CairoMakie
skygering Oct 4, 2023
319bd88
Update equals and overlaps
skygering Oct 18, 2023
0b9799d
Remove use of findfirst for 1.6 compat
skygering Oct 18, 2023
90fff1c
Updated geom, multi-geom equality
skygering Oct 20, 2023
ab211f3
Merge branch 'asinghvi17:main' into sg/update_intersects
skygering Oct 20, 2023
6184971
Merge pull request #1 from Caltech-OCTO/sg/update_intersects
skygering Oct 20, 2023
a224c87
Merge branch 'main' of github.com:Caltech-OCTO/GeometryOps.jl
LanaLubecke Oct 27, 2023
98d060f
Merge branch 'main' of github.com:Caltech-OCTO/GeometryOps.jl
LanaLubecke Dec 4, 2023
6cf1ac9
Merge branch 'main' of github.com:Caltech-OCTO/GeometryOps.jl
LanaLubecke Feb 8, 2024
0e881ae
Update frac calculations
skygering Feb 9, 2024
175d64e
Fix non collinear intersection points
skygering Feb 10, 2024
62df817
Fix frac labeling
skygering Feb 10, 2024
871a007
Remove idx field
skygering Feb 10, 2024
5a6f551
Update intersection phase
skygering Feb 10, 2024
51a17ab
Add comments
skygering Feb 10, 2024
ae12cde
Merge branch 'main' of github.com:Caltech-OCTO/GeometryOps.jl
LanaLubecke Feb 12, 2024
b464aa7
add helper functions for labelling degeneracies
LanaLubecke Feb 12, 2024
668e346
build_ab_list
LanaLubecke Feb 13, 2024
bcb2f0c
Update crossings and entry exit status
skygering Feb 18, 2024
f9c00f1
Merge branch 'main' of github.com:Caltech-OCTO/GeometryOps.jl
LanaLubecke Feb 19, 2024
bd88321
Merge branch 'main' into sg_ll/clipping_degeneracies
LanaLubecke Feb 19, 2024
4b1dcee
Merge branch 'sg_ll/clipping_degeneracies' of github.com:Caltech-OCTO…
LanaLubecke Feb 19, 2024
0d8fbea
adds tracining for simple intersections
LanaLubecke Feb 20, 2024
8d55a9a
Remove unneeded list
skygering Feb 20, 2024
e7093b5
Fix entry exit tagging bug
skygering Feb 20, 2024
0b98906
Update clipping edge cases
skygering Feb 22, 2024
852d20c
Add entry/exit flagging for cutting
skygering Feb 22, 2024
541fb18
Reorganize tests
skygering Feb 23, 2024
901b2fe
Fix test file
skygering Feb 23, 2024
5fb9527
Still debugging difference overflow
skygering Feb 24, 2024
db62f59
Add comments for fix
skygering Feb 25, 2024
6fe3b0e
small adjustments in processing holes for union
LanaLubecke Feb 26, 2024
04abf20
Merge branch 'sg_ll/clipping_degeneracies' of github.com:Caltech-OCTO…
skygering Feb 26, 2024
0a8b7e0
Fix add holes logic
skygering Feb 29, 2024
5bb2424
Consolidate clipping tests
skygering Feb 29, 2024
70e7371
Turn all tests back on
skygering Feb 29, 2024
90623cc
Fix merge remenants
skygering Feb 29, 2024
05c235b
Fix inconsistent commenting
skygering Feb 29, 2024
cff1ecf
add more test cases
LanaLubecke Feb 29, 2024
f8ed3c5
Merge branch 'sg_ll/clipping_degeneracies' of github.com:Caltech-OCTO…
LanaLubecke Feb 29, 2024
9763a82
Remove try file
skygering Feb 29, 2024
77ce84e
Remove box
skygering Mar 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
419 changes: 352 additions & 67 deletions src/methods/clipping/clipping_processor.jl

Large diffs are not rendered by default.

8 changes: 5 additions & 3 deletions src/methods/clipping/cut.jl
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ Return given geom cut by given line as a list of geometries of the same type as
geom. Return the original geometry as only list element if none are found. Line must cut
fully through given geometry or the original geometry will be returned.

Note: This currently doesn't work for degenerate cases there line crosses through vertices.

## Example

```jldoctest
Expand Down Expand Up @@ -70,7 +72,7 @@ function _cut(::Type{T}, ::GI.PolygonTrait, poly, ::GI.LineTrait, line) where T
return [tuples(poly)]
end
# Cut polygon by line
cut_coords = _cut(T, ext_poly, poly_list, intr_list, n_intr_pts)
cut_coords = _cut(T, ext_poly, line, poly_list, intr_list, n_intr_pts)
# Close coords and create polygons
for c in cut_coords
push!(c, c[1])
Expand All @@ -94,10 +96,10 @@ end
of cut geometry in Vector{Vector{Tuple}} format.

Note: degenerate cases where intersection points are vertices do not work right now. =#
function _cut(::Type{T}, geom, geom_list, intr_list, n_intr_pts) where T
function _cut(::Type{T}, geom, line, geom_list, intr_list, n_intr_pts) where T
# Sort and catagorize the intersection points
sort!(intr_list, by = x -> geom_list[x].fracs[2])
_flag_ent_exit!(geom, geom_list)
_flag_ent_exit!(GI.LineTrait(), line, geom_list)
# Add first point to output list
return_coords = [[geom_list[1].point]]
cross_backs = [(T(Inf),T(Inf))]
Expand Down
21 changes: 13 additions & 8 deletions src/methods/clipping/difference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,23 @@ function _difference(
::GI.PolygonTrait, poly_b,
) where T
# Get the exterior of the polygons
ext_poly_a = GI.getexterior(poly_a)
ext_poly_b = GI.getexterior(poly_b)
ext_a = GI.getexterior(poly_a)
ext_b = GI.getexterior(poly_b)
# Find the difference of the exterior of the polygons
a_list, b_list, a_idx_list = _build_ab_list(T, ext_poly_a, ext_poly_b)
a_list, b_list, a_idx_list = _build_ab_list(T, ext_a, ext_b)
polys = _trace_polynodes(T, a_list, b_list, a_idx_list, (x, y) -> (x ⊻ y) ? 1 : (-1))
# if no crossing points, determine if either poly is inside of the other
if isempty(polys)
if _point_filled_curve_orientation(b_list[1].point, ext_poly_a) == point_in
poly_a_b_hole = GI.Polygon([ext_poly_a, ext_poly_b])
a_in_b, b_in_a = _find_non_cross_orientation(a_list, b_list, ext_a, ext_b)
# add case for if they polygons are the same (all intersection points!)
# add a find_first check to find first non-inter poly!
if b_in_a && !a_in_b # b in a and can't be the same polygon
share_edge_warn(a_list, "Edge case: polygons share edge but one is hole of the other.") # will get taken care of with "glued edges"
poly_a_b_hole = GI.Polygon([tuples(ext_a), tuples(ext_b)])
push!(polys, poly_a_b_hole)
elseif _point_filled_curve_orientation(a_list[1].point, ext_poly_b) != point_in
# Two polygons don't intersect and are not contained in one another
push!(polys, GI.Polygon([ext_poly_a]))
elseif !b_in_a && !a_in_b # polygons don't intersect
push!(polys, tuples(poly_a))
return polys
end
end

Expand Down
34 changes: 20 additions & 14 deletions src/methods/clipping/intersection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,17 @@ function _intersection(
::GI.PolygonTrait, poly_b,
) where {T}
# First we get the exteriors of 'poly_a' and 'poly_b'
ext_poly_a = GI.getexterior(poly_a)
ext_poly_b = GI.getexterior(poly_b)
ext_a = GI.getexterior(poly_a)
ext_b = GI.getexterior(poly_b)
# Then we find the intersection of the exteriors
a_list, b_list, a_idx_list = _build_ab_list(T, ext_poly_a, ext_poly_b)
a_list, b_list, a_idx_list = _build_ab_list(T, ext_a, ext_b)
polys = _trace_polynodes(T, a_list, b_list, a_idx_list, (x, y) -> x ? 1 : (-1))

if isempty(polys)
if _point_filled_curve_orientation(a_list[1].point, ext_poly_b) == point_in
push!(polys, GI.Polygon([ext_poly_a]))
elseif _point_filled_curve_orientation(b_list[1].point, ext_poly_a) == point_in
push!(polys, GI.Polygon([ext_poly_b]))
if isempty(polys) # no crossing points, determine if either poly is inside the other
a_in_b, b_in_a = _find_non_cross_orientation(a_list, b_list, ext_a, ext_b)
if a_in_b
push!(polys, GI.Polygon([tuples(ext_a)]))
elseif b_in_a
push!(polys, GI.Polygon([tuples(ext_b)]))
end
end
# If the original polygons had holes, take that into account.
Expand Down Expand Up @@ -161,14 +161,20 @@ function _intersection_point(::Type{T}, (a1, a2)::Tuple, (b1, b2)::Tuple) where
sx, sy = GI.x(b2) - qx, GI.y(b2) - qy
# Intersection will be where p + tr = q + us where 0 < t, u < 1 and
r_cross_s = rx * sy - ry * sx
if r_cross_s != 0
Δqp_x = qx - px
Δqp_y = qy - py
Δqp_x = qx - px
Δqp_y = qy - py
point, fracs = if r_cross_s != 0 # if lines aren't parallel
t = (Δqp_x * sy - Δqp_y * sx) / r_cross_s
u = (Δqp_x * ry - Δqp_y * rx) / r_cross_s
x = px + t * rx
y = py + t * ry
return (T(x), T(y)), (T(t), T(u))
(T(x), T(y)), (T(t), T(u))
elseif sx * Δqp_y == sy * Δqp_x # if parallel lines are collinear
t = (Δqp_x * rx + Δqp_y * ry) / (rx^2 + ry^2)
u = -(Δqp_x * sx + Δqp_y * sy) / (sx^2 + sy^2)
nothing, (T(t), T(u))
else
nothing, nothing
end
return nothing, nothing
return point, fracs
end
51 changes: 22 additions & 29 deletions src/methods/clipping/union.jl
Original file line number Diff line number Diff line change
Expand Up @@ -41,42 +41,35 @@ function _union(
::GI.PolygonTrait, poly_b,
) where T
# First, I get the exteriors of the two polygons
ext_poly_a = GI.getexterior(poly_a)
ext_poly_b = GI.getexterior(poly_b)
ext_a = GI.getexterior(poly_a)
ext_b = GI.getexterior(poly_b)
# Then, I get the union of the exteriors
a_list, b_list, a_idx_list = _build_ab_list(T, ext_poly_a, ext_poly_b)
a_list, b_list, a_idx_list = _build_ab_list(T, ext_a, ext_b)
polys = _trace_polynodes(T, a_list, b_list, a_idx_list, (x, y) -> x ? (-1) : 1)
n_pieces = length(polys)
# Check if one polygon totally within other and if so, return the larger polygon.
if isempty(polys)
if _point_filled_curve_orientation(a_list[1].point, ext_poly_b) == point_in
push!(polys, GI.Polygon([ext_poly_b]))
elseif _point_filled_curve_orientation(b_list[1].point, ext_poly_a) == point_in
push!(polys, GI.Polygon([ext_poly_a]))
if n_pieces == 0 # no crossing points, determine if either poly is inside the other
a_in_b, b_in_a = _find_non_cross_orientation(a_list, b_list, ext_a, ext_b)
if a_in_b
push!(polys, GI.Polygon([tuples(ext_b)]))
elseif b_in_a
push!(polys, GI.Polygon([tuples(ext_a)]))
else
push!(polys, poly_a)
push!(polys, poly_b)
share_edge_warn(a_list, "Edge case: polygons share edge but can't be combined.") # will get taken care of with "glued edges"
push!(polys, tuples(poly_a))
push!(polys, tuples(poly_b))
return polys
end
else
sort!(polys, by = area, rev = true)
polys = [GI.Polygon([GI.getexterior(p) for p in polys])]
elseif n_pieces > 1 # extra polygons are holes (n_pieces == 1 is the desired state)
sort!(polys, by = area, rev = true) # sort so first element is the exterior
end

n_b_holes = GI.nhole(poly_b)
if GI.nhole(poly_a) != 0 || n_b_holes != 0
new_poly = [GI.getexterior(polys[1]); collect(GI.gethole(polys[1]))]
current_poly = GI.Polygon([ext_poly_b])
for (i, hole) in enumerate(Iterators.flatten((GI.gethole(poly_a), GI.gethole(poly_b))))
# Use ext_poly_b to not overcount overlapping holes in poly_a and in poly_b
new_hole = difference(GI.Polygon([hole]), current_poly, T; target = GI.PolygonTrait)
for h in new_hole
push!(new_poly, GI.getexterior(h))
end
if i == n_b_holes
current_poly = poly_a
end
end
polys[1] = GI.Polygon(new_poly)
# the first element is the exterior, the rest are holes
new_holes = @views (GI.getexterior(p) for p in polys[2:end])
polys = n_pieces > 1 ? polys[1:1] : polys
# Add holes back in for there are any
if GI.nhole(poly_a) != 0 || GI.nhole(poly_b) != 0 || n_pieces > 1
hole_iterator = Iterators.flatten((GI.gethole(poly_a), GI.gethole(poly_b), new_holes))
_add_holes_to_polys!(T, polys, hole_iterator)
end
return polys
end
Expand Down
13 changes: 8 additions & 5 deletions src/methods/geom_relations/geom_geom_processors.jl
Original file line number Diff line number Diff line change
Expand Up @@ -224,12 +224,12 @@ end
#= Find where line and curve segments intersect by fraction of length. α is the fraction of
the line (ls to le) and β is the traction of the curve (cs to ce). All inputs are tuples. =#
function _find_intersect_fracs(ls, le, cs, ce)
_, fracs = _intersection_point(
point, fracs = _intersection_point(
Float64,
(ls, le),
(cs, ce)
)
(α, β) = if !isnothing(fracs)
(α, β) = if !isnothing(point)
fracs
else # line and curve segments are parallel
if equals(ls, cs)
Expand Down Expand Up @@ -507,8 +507,8 @@ function _point_filled_curve_orientation(
n -= equals(GI.getpoint(curve, 1), GI.getpoint(curve, n)) ? 1 : 0
k = 0 # counter for ray crossings
p_start = GI.getpoint(curve, n)
@inbounds for i in 1:n
p_end = GI.getpoint(curve, i)
for (i, p_end) in enumerate(GI.getpoint(curve))
i > n && break
v1 = GI.y(p_start) - y
v2 = GI.y(p_end) - y
if !((v1 < 0 && v2 < 0) || (v1 > 0 && v2 > 0)) # if not cases 11 or 26
Expand Down Expand Up @@ -671,7 +671,10 @@ function _line_filled_curve_interactions(
curve
)
npoints = length(ipoints) # since hinge, at least one
sort!(ipoints, by = p -> _euclid_distance(Float64, p, l_start))
dist_from_lstart = let l_start = l_start
x -> _euclid_distance(Float64, x, l_start)
end
sort!(ipoints, by = dist_from_lstart)
p_start = _tuple_point(l_start)
for i in 1:(npoints + 1)
p_end = i ≤ npoints ?
Expand Down
41 changes: 0 additions & 41 deletions test/methods/clipping/difference.jl

This file was deleted.

Loading
Loading