Sutherland-Hodgman Convex-Convex Clipping
export ConvexConvexSutherlandHodgman
"""
ConvexConvexSutherlandHodgman{M <: Manifold} <: GeometryOpsCore.Algorithm{M}
Sutherland-Hodgman polygon clipping algorithm optimized for convex-convex intersection.
Both input polygons MUST be convex. If either polygon is non-convex, results are undefined.
This is simpler and faster than Foster-Hormann for small convex polygons, with O(n*m)
complexity where n and m are vertex counts.Example
```julia
import GeometryOps as GO, GeoInterface as GI
square1 = GI.Polygon([[(0.0, 0.0), (2.0, 0.0), (2.0, 2.0), (0.0, 2.0), (0.0, 0.0)]])
square2 = GI.Polygon([[(1.0, 1.0), (3.0, 1.0), (3.0, 3.0), (1.0, 3.0), (1.0, 1.0)]])
result = GO.intersection(GO.ConvexConvexSutherlandHodgman(), square1, square2)
```
"""
struct ConvexConvexSutherlandHodgman{M <: Manifold} <: GeometryOpsCore.Algorithm{M}
manifold::M
endDefault constructor uses Planar
ConvexConvexSutherlandHodgman() = ConvexConvexSutherlandHodgman(Planar())Main entry point - algorithm dispatch
function intersection(
alg::ConvexConvexSutherlandHodgman,
geom_a,
geom_b,
::Type{T}=Float64;
kwargs...
) where {T<:AbstractFloat}
return _intersection_sutherland_hodgman(
alg, T,
GI.trait(geom_a), geom_a,
GI.trait(geom_b), geom_b
)
endPolygon-Polygon intersection using Sutherland-Hodgman
function _intersection_sutherland_hodgman(
alg::ConvexConvexSutherlandHodgman{Planar},
::Type{T},
::GI.PolygonTrait, poly_a,
::GI.PolygonTrait, poly_b
) where {T}Get exterior rings (convex polygons have no holes)
ring_a = GI.getexterior(poly_a)
ring_b = GI.getexterior(poly_b)Start with vertices of poly_a as the output list (excluding closing point)
output = Tuple{T,T}[]
for point in GI.getpoint(ring_a)
pt = _tuple_point(point, T)Skip the closing point (same as first)
if !isempty(output) && pt == output[1]
continue
end
push!(output, pt)
endClip against each edge of poly_b
for (edge_start, edge_end) in eachedge(ring_b, T)
isempty(output) && break
output = _sh_clip_to_edge(output, edge_start, edge_end, T)
endHandle empty result (no intersection) - return degenerate polygon with zero area
if isempty(output)
zero_pt = (zero(T), zero(T))
return GI.Polygon([[zero_pt, zero_pt, zero_pt]])
endClose the ring
push!(output, output[1])Return polygon
return GI.Polygon([output])
endClip polygon against a single edge using Sutherland-Hodgman rules
function _sh_clip_to_edge(polygon_points::Vector{Tuple{T,T}}, edge_start, edge_end, ::Type{T}) where T
output = Tuple{T,T}[]
n = length(polygon_points)
n == 0 && return output
for i in 1:n
current = polygon_points[i]
next_pt = polygon_points[mod1(i + 1, n)]Determine if points are inside (left of or on the edge) orient > 0 means left (inside for CCW polygon), == 0 means on edge, < 0 means right (outside)
current_inside = Predicates.orient(edge_start, edge_end, current; exact=False()) >= 0
next_inside = Predicates.orient(edge_start, edge_end, next_pt; exact=False()) >= 0
if current_inside
push!(output, current)
if !next_insideExiting: add intersection point
intr_pt = _sh_line_intersection(current, next_pt, edge_start, edge_end, T)
push!(output, intr_pt)
end
elseif next_insideEntering: add intersection point
intr_pt = _sh_line_intersection(current, next_pt, edge_start, edge_end, T)
push!(output, intr_pt)
endBoth outside: add nothing
end
return output
endCompute intersection point of line segment (p1, p2) with line through (p3, p4)
function _sh_line_intersection(p1::Tuple{T,T}, p2::Tuple{T,T}, p3::Tuple{T,T}, p4::Tuple{T,T}, ::Type{T}) where T
x1, y1 = p1
x2, y2 = p2
x3, y3 = p3
x4, y4 = p4
denom = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4)Lines are parallel - shouldn't happen in valid Sutherland-Hodgman usage
if abs(denom) < eps(T)
return p1 # Fallback
end
t = ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / denom
x = x1 + t * (x2 - x1)
y = y1 + t * (y2 - y1)
return (T(x), T(y))
endFallback for unsupported geometry combinations
function _intersection_sutherland_hodgman(
alg::ConvexConvexSutherlandHodgman,
::Type{T},
trait_a, geom_a,
trait_b, geom_b
) where {T}
throw(ArgumentError(
"ConvexConvexSutherlandHodgman only supports Polygon-Polygon intersection, " *
"got $(typeof(trait_a)) and $(typeof(trait_b))"
))
endThis page was generated using Literate.jl.