From c5a3b904065140219a1d9437b79b14fd45e2eda0 Mon Sep 17 00:00:00 2001 From: souma4 Date: Tue, 11 Feb 2025 10:17:12 -0700 Subject: [PATCH 01/60] add test segment. proper test later --- test/utils.jl | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/utils.jl b/test/utils.jl index 825897847..7f38a57de 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -54,4 +54,14 @@ c = (T(30), T(60)) p = latlon(c) |> Proj(Cartesian) @inferred Meshes.withcrs(p, c, LatLon) + + # test bentley-ottmann algorithm + S = [ + Segment(cart(0, 0), cart(1, 1)), + Segment(cart(1, 0), cart(0, 1)), + Segment(cart(0, 0), cart(0, 1)), + Segment(cart(0, 0), cart(1, 0)), + Segment(cart(0, 1), cart(1, 1)), + Segment(cart(1, 0), cart(1, 1)) + ] end From e0048106f49352a19fae4f25a9de85b44b4f3775 Mon Sep 17 00:00:00 2001 From: souma4 Date: Tue, 11 Feb 2025 10:18:12 -0700 Subject: [PATCH 02/60] Clean single commit for BentleyOttman algorithm. Essentially, it outputs a dictionary of vertex keys with segment values --- src/utils/sweepline.jl | 236 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 236 insertions(+) create mode 100644 src/utils/sweepline.jl diff --git a/src/utils/sweepline.jl b/src/utils/sweepline.jl new file mode 100644 index 000000000..27425e241 --- /dev/null +++ b/src/utils/sweepline.jl @@ -0,0 +1,236 @@ +# Implementation of Bentley-Ottmann algorith +# https://en.wikipedia.org/wiki/Bentley%E2%80%93Ottmann_algorithm + +using BinaryTrees + +""" + bentleyottmann(segments) + +Compute pairwise intersections between n `segments` +in O(nโ‹…log(n)) time using Bentley-Ottmann sweep line +algorithm. +""" +function bentleyottmann(segments) + # adjust vertices of segments + segs = map(segments) do s + a, b = extrema(s) + a > b ? reverse(s) : s + end + + # retrieve relevant info + s = first(segs) + p = minimum(s) + P = typeof(p) + S = Tuple{P,P} + + # initialization + ๐’ฌ = BinaryTrees.AVLTree{P}() + ๐’ฏ = BinaryTrees.AVLTree{S}() + โ„’ = Dict{P,Vector{S}}() + ๐’ฐ = Dict{P,Vector{S}}() + ๐’ž = Dict{P,Vector{S}}() + for s in segs + a, b = extrema(s) + BinaryTrees.insert!(๐’ฌ, a) + BinaryTrees.insert!(๐’ฌ, b) + haskey(โ„’, a) ? push!(โ„’[a], (a, b)) : (โ„’[a] = [(a, b)]) + haskey(๐’ฐ, b) ? push!(๐’ฐ[b], (a, b)) : (๐’ฐ[b] = [(a, b)]) + haskey(โ„’, b) || (โ„’[b] = S[]) + haskey(๐’ฐ, a) || (๐’ฐ[a] = S[]) + haskey(๐’ž, a) || (๐’ž[a] = S[]) + haskey(๐’ž, b) || (๐’ž[b] = S[]) + end + + # sweep line + I = Dict{P,Vector{S}}() + while !isnothing(BinaryTrees.root(๐’ฌ)) + p = _key(_leftmost(BinaryTrees.root(๐’ฌ))) + BinaryTrees.delete!(๐’ฌ, p) + handle!(I, p, ๐’ฌ, ๐’ฏ, โ„’, ๐’ฐ, ๐’ž) + end + I +end + +function handle!(I, p, ๐’ฌ, ๐’ฏ, โ„’, ๐’ฐ, ๐’ž) + start_segments = get(โ„’, p, S[]) + end_segments = get(๐’ฐ, p, S[]) + intersection_segments = get(๐’ž, p, S[]) + _process_start_segments!(start_segments, ๐’ฌ, ๐’ฏ, ๐’ž) + _process_end_segments!(end_segments, ๐’ฌ, ๐’ฏ, ๐’ž) + _process_intersection_segments!(intersection_segments, ๐’ฌ, ๐’ฏ, ๐’ž) + + if length(start_segments โˆช end_segments โˆช intersection_segments) > 1 + I[p] = start_segments โˆช end_segments โˆช intersection_segments + end +end + +function _process_start_segments!(start_segments, ๐’ฌ, ๐’ฏ, ๐’ž) + [BinaryTrees.insert!(๐’ฏ, s) for s in start_segments] + for s in start_segments + above, below = find_above_below(BinaryTrees.root(๐’ฏ), BinaryTrees.search(๐’ฏ, s)) + s = Segment(s) + if above !== nothing && below !== nothing + new_geom, new_type = _newevent(Segment(_key(above)), Segment(_key(below))) + if new_type == IntersectionType(0) + BinaryTrees.insert!(๐’ฌ, new_geom) + haskey(๐’ž, new_geom) ? push!(๐’ž[new_geom], _key(above), _key(below)) : (๐’ž[new_geom] = [_key(above), _key(below)]) + end + end + if below !== nothing + new_geom, new_type = _newevent(Segment(_key(below)), s) + if new_type == IntersectionType(0) + BinaryTrees.insert!(๐’ฌ, new_geom) + haskey(๐’ž, new_geom) ? push!(๐’ž[new_geom], _key(below), _segdata(s)) : (๐’ž[new_geom] = [_key(below), _segdata(s)]) + end + end + if above !== nothing + new_geom, new_type = _newevent(s, Segment(_key(above))) + if new_type == IntersectionType(0) + BinaryTrees.insert!(๐’ฌ, new_geom) + haskey(๐’ž, new_geom) ? push!(๐’ž[new_geom], _segdata(s), _key(above)) : (๐’ž[new_geom] = [_segdata(s), _key(above)]) + end + end + end +end + +function _process_end_segments!(end_segments, ๐’ฌ, ๐’ฏ, ๐’ž) + for s in end_segments + above, below = find_above_below(BinaryTrees.root(๐’ฏ), BinaryTrees.search(๐’ฏ, s)) + BinaryTrees.delete!(๐’ฏ, s) + s = Segment(s) + if above !== nothing && below !== nothing + new_geom, new_type = _newevent(Segment(_key(above)), Segment(_key(below))) + if new_type == IntersectionType(0) + BinaryTrees.insert!(๐’ฌ, new_geom) + haskey(๐’ž, new_geom) ? push!(๐’ž[new_geom], _key(above), _key(below)) : (๐’ž[new_geom] = [_key(above), _key(below)]) + end + end + end +end + +function _process_intersection_segments!(intersection_segments, ๐’ฌ, ๐’ฏ, ๐’ž) + for s in intersection_segments + _, below = find_above_below(BinaryTrees.root(๐’ฏ), BinaryTrees.search(๐’ฏ, s)) + if below !== nothing + # Swap positions of s and t in ๐’ฏ + BinaryTrees.delete!(๐’ฏ, s) + BinaryTrees.delete!(๐’ฏ, _key(below)) + BinaryTrees.insert!(๐’ฏ, _key(below)) + BinaryTrees.insert!(๐’ฏ, s) + + # Find segments r and u + _, r = find_above_below(BinaryTrees.root(๐’ฏ), BinaryTrees.search(๐’ฏ, _key(below))) + u, _ = find_above_below(BinaryTrees.root(๐’ฏ), BinaryTrees.search(๐’ฏ, s)) + + # Remove crossing points rs and tu from event queue + if r !== nothing + new_geom, new_type = _newevent(Segment(_key(r)), Segment(s)) + if new_type == IntersectionType(0) + BinaryTrees.delete!(๐’ฌ, new_geom) + end + end + if u !== nothing + new_geom, new_type = _newevent(Segment(_key(u)), Segment(_key(below))) + if new_type == IntersectionType(0) + BinaryTrees.delete!(๐’ฌ, new_geom) + end + end + + # Add crossing points rt and su to event queue + if r !== nothing + new_geom, new_type = _newevent(Segment(_key(r)), Segment(_key(below))) + if new_type == IntersectionType(0) + BinaryTrees.insert!(๐’ฌ, new_geom) + haskey(๐’ž, new_geom) ? push!(๐’ž[new_geom], _key(r), _key(below)) : (๐’ž[new_geom] = [_key(r), _key(below)]) + end + end + if u !== nothing + new_geom, new_type = _newevent(Segment(_key(u)), Segment(s)) + if new_type == IntersectionType(0) + BinaryTrees.insert!(๐’ฌ, new_geom) + haskey(๐’ž, new_geom) ? push!(๐’ž[new_geom], _key(u), s) : (๐’ž[new_geom] = [_key(u), s]) + end + end + end + end +end + +_segdata(seg::Segment) = seg.vertices.data +_key(node::BinaryTrees.AVLNode) = node.key +_key(node::Nothing) = nothing +_leftmost(node::BinaryTrees.AVLNode) = node.left === nothing ? node : _leftmost(node.left) +_geom(intersect::Intersection) = intersect.geom +_type(intersect::Intersection) = intersect.type +function _newevent(sโ‚::Segment, sโ‚‚::Segment) + new_event = intersection(sโ‚, sโ‚‚) + if new_event !== nothing + _geom(new_event), _type(new_event) + else + nothing, nothing + end +end + +# Helper: return the leftmost node (minimum) in a subtree. +function _bst_minimum(node::BinaryTrees.AVLNode) + while node.left !== nothing + node = node.left + end + return node +end + +# Helper: return the rightmost node (maximum) in a subtree. +function _bst_maximum(node::BinaryTrees.AVLNode) + while node.right !== nothing + node = node.right + end + return node +end + +""" + find_above_below(root, x) + +Find the node above and below `x` in the binary search tree rooted at `root`. +Returns a tuple `(above, below)` where `above` is the node with the smallest key +greater than `x.key` and `below` is the node with the largest key smaller than `x.key`. +If `x` is not found, returns the best candidates for `above` and `below`. +""" +function find_above_below(root::BinaryTrees.AVLNode, x::BinaryTrees.AVLNode) + above = nothing + below = nothing + current = root + # Traverse from the root to the target node, updating candidates. + while current !== nothing && current.key != x.key + if x.key < current.key + # current is a potential above (successor) + above = current + current = current.left + else # x.key > current.key + # current is a potential below (predecessor) + below = current + current = current.right + end + end + + # If the node wasn't found, return the best candidate values + if current === nothing + return (above, below) + end + + # Found the node with key equal to x.key. + # Now, if there is a left subtree, the true below (predecessor) is the maximum in that subtree. + if current.left !== nothing + below = _bst_maximum(current.left) + end + # Similarly, if there is a right subtree, the true above (successor) is the minimum in that subtree. + if current.right !== nothing + above = _bst_minimum(current.right) + end + + (above, below) +end +find_above_below(root::BinaryTrees.AVLNode, x::Nothing) = (nothing, nothing) +function Segment(nodeโ‚::BinaryTrees.BinaryNode, nodeโ‚‚::BinaryTrees.BinaryNode) + nodeโ‚ = _key(nodeโ‚) + nodeโ‚‚ = _key(nodeโ‚‚) + Segment((nodeโ‚, nodeโ‚‚)) +end From 491a13024e1a4ff0be856968f1805888c16a6f28 Mon Sep 17 00:00:00 2001 From: souma4 Date: Tue, 11 Feb 2025 10:54:11 -0700 Subject: [PATCH 03/60] test for valid answer --- test/utils.jl | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/utils.jl b/test/utils.jl index 7f38a57de..4cc4e48f2 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -64,4 +64,16 @@ Segment(cart(0, 1), cart(1, 1)), Segment(cart(1, 0), cart(1, 1)) ] + I = BentleyOttmann(S) + S_check = Dict( + cart(1, 1) => [(cart(0, 0), cart(1, 1)), (cart(0, 1), cart(1, 1)), (cart(1, 0), cart(1, 1))], + cart(0, 1) => [(cart(0, 1), cart(1, 0)), (cart(0, 1), cart(1, 1)), (cart(0, 0), cart(0, 1))], + cart(0.5, 0.5) => [(cart(0, 0), cart(1, 1)), (cart(0, 1), cart(1, 0))], + cart(1, 0) => [(cart(1, 0), cart(1, 1)), (cart(0, 1), cart(1, 0)), (cart(0, 0), cart(1, 0))], + cart(0, 0) => [(cart(0, 0), cart(1, 1)), (cart(0, 0), cart(0, 1)), (cart(0, 0), cart(1, 0))] + ) + @test typeof(I) == typeof(S_check) + @test length(I) == length(S_check) + @test keys(I) == keys(S_check) + @test all(values(I) .== values(S_check)) end From 840c1a58c39896cdde76045b3d4971b4127dd521 Mon Sep 17 00:00:00 2001 From: souma4 Date: Tue, 11 Feb 2025 11:20:18 -0700 Subject: [PATCH 04/60] moved sweepline algorithm to Intersections since it is dependent on intersections and ultimately calculates all intersections --- src/intersections.jl | 1 + src/{utils => intersections}/sweepline.jl | 0 test/intersections.jl | 24 +++++++++++++++++++++++ test/utils.jl | 22 --------------------- 4 files changed, 25 insertions(+), 22 deletions(-) rename src/{utils => intersections}/sweepline.jl (100%) diff --git a/src/intersections.jl b/src/intersections.jl index f46455bcb..9e64d54c4 100644 --- a/src/intersections.jl +++ b/src/intersections.jl @@ -115,3 +115,4 @@ include("intersections/planes.jl") include("intersections/boxes.jl") include("intersections/polygons.jl") include("intersections/domains.jl") +include("intersections/sweepline.jl") diff --git a/src/utils/sweepline.jl b/src/intersections/sweepline.jl similarity index 100% rename from src/utils/sweepline.jl rename to src/intersections/sweepline.jl diff --git a/test/intersections.jl b/test/intersections.jl index edcc1c43f..dffebf2c4 100644 --- a/test/intersections.jl +++ b/test/intersections.jl @@ -1187,3 +1187,27 @@ end @test pset โˆฉ grid == grid โˆฉ pset == pset @test pset โˆฉ ball == ball โˆฉ pset == PointSet(cart(0.5, 0.5)) end + +@testitem "Pairwise Intersections" setup = [Setup] begin + # test bentley-ottmann algorithm + S = [ + Segment(cart(0, 0), cart(1, 1)), + Segment(cart(1, 0), cart(0, 1)), + Segment(cart(0, 0), cart(0, 1)), + Segment(cart(0, 0), cart(1, 0)), + Segment(cart(0, 1), cart(1, 1)), + Segment(cart(1, 0), cart(1, 1)) + ] + I = BentleyOttmann(S) + S_check = Dict( + cart(1, 1) => [(cart(0, 0), cart(1, 1)), (cart(0, 1), cart(1, 1)), (cart(1, 0), cart(1, 1))], + cart(0, 1) => [(cart(0, 1), cart(1, 0)), (cart(0, 1), cart(1, 1)), (cart(0, 0), cart(0, 1))], + cart(0.5, 0.5) => [(cart(0, 0), cart(1, 1)), (cart(0, 1), cart(1, 0))], + cart(1, 0) => [(cart(1, 0), cart(1, 1)), (cart(0, 1), cart(1, 0)), (cart(0, 0), cart(1, 0))], + cart(0, 0) => [(cart(0, 0), cart(1, 1)), (cart(0, 0), cart(0, 1)), (cart(0, 0), cart(1, 0))] + ) + @test typeof(I) == typeof(S_check) + @test length(I) == length(S_check) + @test keys(I) == keys(S_check) + @test all(values(I) .== values(S_check)) +end diff --git a/test/utils.jl b/test/utils.jl index 4cc4e48f2..825897847 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -54,26 +54,4 @@ c = (T(30), T(60)) p = latlon(c) |> Proj(Cartesian) @inferred Meshes.withcrs(p, c, LatLon) - - # test bentley-ottmann algorithm - S = [ - Segment(cart(0, 0), cart(1, 1)), - Segment(cart(1, 0), cart(0, 1)), - Segment(cart(0, 0), cart(0, 1)), - Segment(cart(0, 0), cart(1, 0)), - Segment(cart(0, 1), cart(1, 1)), - Segment(cart(1, 0), cart(1, 1)) - ] - I = BentleyOttmann(S) - S_check = Dict( - cart(1, 1) => [(cart(0, 0), cart(1, 1)), (cart(0, 1), cart(1, 1)), (cart(1, 0), cart(1, 1))], - cart(0, 1) => [(cart(0, 1), cart(1, 0)), (cart(0, 1), cart(1, 1)), (cart(0, 0), cart(0, 1))], - cart(0.5, 0.5) => [(cart(0, 0), cart(1, 1)), (cart(0, 1), cart(1, 0))], - cart(1, 0) => [(cart(1, 0), cart(1, 1)), (cart(0, 1), cart(1, 0)), (cart(0, 0), cart(1, 0))], - cart(0, 0) => [(cart(0, 0), cart(1, 1)), (cart(0, 0), cart(0, 1)), (cart(0, 0), cart(1, 0))] - ) - @test typeof(I) == typeof(S_check) - @test length(I) == length(S_check) - @test keys(I) == keys(S_check) - @test all(values(I) .== values(S_check)) end From 6f018a38caa8a0ac91a9817a5e61d7d2b62935a2 Mon Sep 17 00:00:00 2001 From: souma4 Date: Mon, 17 Feb 2025 08:51:33 -0700 Subject: [PATCH 05/60] Update Doc for bentley ottman implementation to specify output --- src/intersections/sweepline.jl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/intersections/sweepline.jl b/src/intersections/sweepline.jl index 27425e241..c11f890c3 100644 --- a/src/intersections/sweepline.jl +++ b/src/intersections/sweepline.jl @@ -9,6 +9,10 @@ using BinaryTrees Compute pairwise intersections between n `segments` in O(nโ‹…log(n)) time using Bentley-Ottmann sweep line algorithm. + +Outputs a Dictionary of {Point, Vector{Tuple{Point, Point}}} +where the key is each intersection point and the values are all +pairs of segments that intersect at that point. """ function bentleyottmann(segments) # adjust vertices of segments From eb0d56b992c615a144fed90968ff2abf52838bd6 Mon Sep 17 00:00:00 2001 From: souma4 Date: Mon, 17 Feb 2025 08:52:21 -0700 Subject: [PATCH 06/60] update project.toml to include BinaryTrees dependency --- Project.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/Project.toml b/Project.toml index 2ac8bb7b4..04dd1d00d 100644 --- a/Project.toml +++ b/Project.toml @@ -5,6 +5,7 @@ version = "0.52.2" [deps] Bessels = "0e736298-9ec6-45e8-9647-e4fc86a2fe38" +BinaryTrees = "289e92be-c05a-437b-9e67-5b0c799891f8" CircularArrays = "7a955b69-7140-5f4e-a0ed-f168c5e2e749" Colorfy = "03fe91ce-8ec6-4610-8e8d-e7491ccca690" CoordRefSystems = "b46f11dc-f210-4604-bfba-323c1ec968cb" From b869b6e1c6491eaa84381a981d415bd02ca87ea3 Mon Sep 17 00:00:00 2001 From: souma4 Date: Sun, 23 Feb 2025 22:45:26 -0700 Subject: [PATCH 07/60] major commit. Moved sweepline from intersections to utils along with tests. Edited test to be more intensive. updated entire script to meet style and Julia best practices. Removed unneded functions. Shifted BinaryTrees relevant materials to PR #12 in BinaryTrees and adjusted code as needed. Reordered point processing to reduce the likelihood of a bug when processing start and intersection segments. --- src/Meshes.jl | 1 + src/intersections.jl | 1 - src/intersections/sweepline.jl | 240 --------------------------------- src/utils.jl | 1 + src/utils/sweepline.jl | 185 +++++++++++++++++++++++++ test/intersections.jl | 24 ---- test/utils.jl | 24 ++++ 7 files changed, 211 insertions(+), 265 deletions(-) delete mode 100644 src/intersections/sweepline.jl create mode 100644 src/utils/sweepline.jl diff --git a/src/Meshes.jl b/src/Meshes.jl index e7b067e91..d1c3c6e8f 100644 --- a/src/Meshes.jl +++ b/src/Meshes.jl @@ -4,6 +4,7 @@ module Meshes +using BinaryTrees using CoordRefSystems using StaticArrays using SparseArrays diff --git a/src/intersections.jl b/src/intersections.jl index 9e64d54c4..f46455bcb 100644 --- a/src/intersections.jl +++ b/src/intersections.jl @@ -115,4 +115,3 @@ include("intersections/planes.jl") include("intersections/boxes.jl") include("intersections/polygons.jl") include("intersections/domains.jl") -include("intersections/sweepline.jl") diff --git a/src/intersections/sweepline.jl b/src/intersections/sweepline.jl deleted file mode 100644 index c11f890c3..000000000 --- a/src/intersections/sweepline.jl +++ /dev/null @@ -1,240 +0,0 @@ -# Implementation of Bentley-Ottmann algorith -# https://en.wikipedia.org/wiki/Bentley%E2%80%93Ottmann_algorithm - -using BinaryTrees - -""" - bentleyottmann(segments) - -Compute pairwise intersections between n `segments` -in O(nโ‹…log(n)) time using Bentley-Ottmann sweep line -algorithm. - -Outputs a Dictionary of {Point, Vector{Tuple{Point, Point}}} -where the key is each intersection point and the values are all -pairs of segments that intersect at that point. -""" -function bentleyottmann(segments) - # adjust vertices of segments - segs = map(segments) do s - a, b = extrema(s) - a > b ? reverse(s) : s - end - - # retrieve relevant info - s = first(segs) - p = minimum(s) - P = typeof(p) - S = Tuple{P,P} - - # initialization - ๐’ฌ = BinaryTrees.AVLTree{P}() - ๐’ฏ = BinaryTrees.AVLTree{S}() - โ„’ = Dict{P,Vector{S}}() - ๐’ฐ = Dict{P,Vector{S}}() - ๐’ž = Dict{P,Vector{S}}() - for s in segs - a, b = extrema(s) - BinaryTrees.insert!(๐’ฌ, a) - BinaryTrees.insert!(๐’ฌ, b) - haskey(โ„’, a) ? push!(โ„’[a], (a, b)) : (โ„’[a] = [(a, b)]) - haskey(๐’ฐ, b) ? push!(๐’ฐ[b], (a, b)) : (๐’ฐ[b] = [(a, b)]) - haskey(โ„’, b) || (โ„’[b] = S[]) - haskey(๐’ฐ, a) || (๐’ฐ[a] = S[]) - haskey(๐’ž, a) || (๐’ž[a] = S[]) - haskey(๐’ž, b) || (๐’ž[b] = S[]) - end - - # sweep line - I = Dict{P,Vector{S}}() - while !isnothing(BinaryTrees.root(๐’ฌ)) - p = _key(_leftmost(BinaryTrees.root(๐’ฌ))) - BinaryTrees.delete!(๐’ฌ, p) - handle!(I, p, ๐’ฌ, ๐’ฏ, โ„’, ๐’ฐ, ๐’ž) - end - I -end - -function handle!(I, p, ๐’ฌ, ๐’ฏ, โ„’, ๐’ฐ, ๐’ž) - start_segments = get(โ„’, p, S[]) - end_segments = get(๐’ฐ, p, S[]) - intersection_segments = get(๐’ž, p, S[]) - _process_start_segments!(start_segments, ๐’ฌ, ๐’ฏ, ๐’ž) - _process_end_segments!(end_segments, ๐’ฌ, ๐’ฏ, ๐’ž) - _process_intersection_segments!(intersection_segments, ๐’ฌ, ๐’ฏ, ๐’ž) - - if length(start_segments โˆช end_segments โˆช intersection_segments) > 1 - I[p] = start_segments โˆช end_segments โˆช intersection_segments - end -end - -function _process_start_segments!(start_segments, ๐’ฌ, ๐’ฏ, ๐’ž) - [BinaryTrees.insert!(๐’ฏ, s) for s in start_segments] - for s in start_segments - above, below = find_above_below(BinaryTrees.root(๐’ฏ), BinaryTrees.search(๐’ฏ, s)) - s = Segment(s) - if above !== nothing && below !== nothing - new_geom, new_type = _newevent(Segment(_key(above)), Segment(_key(below))) - if new_type == IntersectionType(0) - BinaryTrees.insert!(๐’ฌ, new_geom) - haskey(๐’ž, new_geom) ? push!(๐’ž[new_geom], _key(above), _key(below)) : (๐’ž[new_geom] = [_key(above), _key(below)]) - end - end - if below !== nothing - new_geom, new_type = _newevent(Segment(_key(below)), s) - if new_type == IntersectionType(0) - BinaryTrees.insert!(๐’ฌ, new_geom) - haskey(๐’ž, new_geom) ? push!(๐’ž[new_geom], _key(below), _segdata(s)) : (๐’ž[new_geom] = [_key(below), _segdata(s)]) - end - end - if above !== nothing - new_geom, new_type = _newevent(s, Segment(_key(above))) - if new_type == IntersectionType(0) - BinaryTrees.insert!(๐’ฌ, new_geom) - haskey(๐’ž, new_geom) ? push!(๐’ž[new_geom], _segdata(s), _key(above)) : (๐’ž[new_geom] = [_segdata(s), _key(above)]) - end - end - end -end - -function _process_end_segments!(end_segments, ๐’ฌ, ๐’ฏ, ๐’ž) - for s in end_segments - above, below = find_above_below(BinaryTrees.root(๐’ฏ), BinaryTrees.search(๐’ฏ, s)) - BinaryTrees.delete!(๐’ฏ, s) - s = Segment(s) - if above !== nothing && below !== nothing - new_geom, new_type = _newevent(Segment(_key(above)), Segment(_key(below))) - if new_type == IntersectionType(0) - BinaryTrees.insert!(๐’ฌ, new_geom) - haskey(๐’ž, new_geom) ? push!(๐’ž[new_geom], _key(above), _key(below)) : (๐’ž[new_geom] = [_key(above), _key(below)]) - end - end - end -end - -function _process_intersection_segments!(intersection_segments, ๐’ฌ, ๐’ฏ, ๐’ž) - for s in intersection_segments - _, below = find_above_below(BinaryTrees.root(๐’ฏ), BinaryTrees.search(๐’ฏ, s)) - if below !== nothing - # Swap positions of s and t in ๐’ฏ - BinaryTrees.delete!(๐’ฏ, s) - BinaryTrees.delete!(๐’ฏ, _key(below)) - BinaryTrees.insert!(๐’ฏ, _key(below)) - BinaryTrees.insert!(๐’ฏ, s) - - # Find segments r and u - _, r = find_above_below(BinaryTrees.root(๐’ฏ), BinaryTrees.search(๐’ฏ, _key(below))) - u, _ = find_above_below(BinaryTrees.root(๐’ฏ), BinaryTrees.search(๐’ฏ, s)) - - # Remove crossing points rs and tu from event queue - if r !== nothing - new_geom, new_type = _newevent(Segment(_key(r)), Segment(s)) - if new_type == IntersectionType(0) - BinaryTrees.delete!(๐’ฌ, new_geom) - end - end - if u !== nothing - new_geom, new_type = _newevent(Segment(_key(u)), Segment(_key(below))) - if new_type == IntersectionType(0) - BinaryTrees.delete!(๐’ฌ, new_geom) - end - end - - # Add crossing points rt and su to event queue - if r !== nothing - new_geom, new_type = _newevent(Segment(_key(r)), Segment(_key(below))) - if new_type == IntersectionType(0) - BinaryTrees.insert!(๐’ฌ, new_geom) - haskey(๐’ž, new_geom) ? push!(๐’ž[new_geom], _key(r), _key(below)) : (๐’ž[new_geom] = [_key(r), _key(below)]) - end - end - if u !== nothing - new_geom, new_type = _newevent(Segment(_key(u)), Segment(s)) - if new_type == IntersectionType(0) - BinaryTrees.insert!(๐’ฌ, new_geom) - haskey(๐’ž, new_geom) ? push!(๐’ž[new_geom], _key(u), s) : (๐’ž[new_geom] = [_key(u), s]) - end - end - end - end -end - -_segdata(seg::Segment) = seg.vertices.data -_key(node::BinaryTrees.AVLNode) = node.key -_key(node::Nothing) = nothing -_leftmost(node::BinaryTrees.AVLNode) = node.left === nothing ? node : _leftmost(node.left) -_geom(intersect::Intersection) = intersect.geom -_type(intersect::Intersection) = intersect.type -function _newevent(sโ‚::Segment, sโ‚‚::Segment) - new_event = intersection(sโ‚, sโ‚‚) - if new_event !== nothing - _geom(new_event), _type(new_event) - else - nothing, nothing - end -end - -# Helper: return the leftmost node (minimum) in a subtree. -function _bst_minimum(node::BinaryTrees.AVLNode) - while node.left !== nothing - node = node.left - end - return node -end - -# Helper: return the rightmost node (maximum) in a subtree. -function _bst_maximum(node::BinaryTrees.AVLNode) - while node.right !== nothing - node = node.right - end - return node -end - -""" - find_above_below(root, x) - -Find the node above and below `x` in the binary search tree rooted at `root`. -Returns a tuple `(above, below)` where `above` is the node with the smallest key -greater than `x.key` and `below` is the node with the largest key smaller than `x.key`. -If `x` is not found, returns the best candidates for `above` and `below`. -""" -function find_above_below(root::BinaryTrees.AVLNode, x::BinaryTrees.AVLNode) - above = nothing - below = nothing - current = root - # Traverse from the root to the target node, updating candidates. - while current !== nothing && current.key != x.key - if x.key < current.key - # current is a potential above (successor) - above = current - current = current.left - else # x.key > current.key - # current is a potential below (predecessor) - below = current - current = current.right - end - end - - # If the node wasn't found, return the best candidate values - if current === nothing - return (above, below) - end - - # Found the node with key equal to x.key. - # Now, if there is a left subtree, the true below (predecessor) is the maximum in that subtree. - if current.left !== nothing - below = _bst_maximum(current.left) - end - # Similarly, if there is a right subtree, the true above (successor) is the minimum in that subtree. - if current.right !== nothing - above = _bst_minimum(current.right) - end - - (above, below) -end -find_above_below(root::BinaryTrees.AVLNode, x::Nothing) = (nothing, nothing) -function Segment(nodeโ‚::BinaryTrees.BinaryNode, nodeโ‚‚::BinaryTrees.BinaryNode) - nodeโ‚ = _key(nodeโ‚) - nodeโ‚‚ = _key(nodeโ‚‚) - Segment((nodeโ‚, nodeโ‚‚)) -end diff --git a/src/utils.jl b/src/utils.jl index adc3a611f..882db44aa 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -11,3 +11,4 @@ include("utils/cmp.jl") include("utils/units.jl") include("utils/crs.jl") include("utils/misc.jl") +include("utils/sweepline.jl") diff --git a/src/utils/sweepline.jl b/src/utils/sweepline.jl new file mode 100644 index 000000000..3dd096ff5 --- /dev/null +++ b/src/utils/sweepline.jl @@ -0,0 +1,185 @@ +# Implementation of Bentley-Ottmann algorith +# https://en.wikipedia.org/wiki/Bentley%E2%80%93Ottmann_algorithm + +""" + BentleyOttmann(segments) + +Compute pairwise intersections between n `segments` +in O(nโ‹…log(n)) time using Bentley-Ottmann sweep line +algorithm. + +Outputs a Dictionary of {Point, Vector{Tuple{Point, Point}}} +where the key is each intersection point and the values are all +pairs of segments that intersect at that point. +""" +function BentleyOttmann(segments) + # adjust vertices of segments + segs = map(segments) do s + a, b = extrema(s) + a > b ? reverse(s) : s + end + + # retrieve relevant info + s = first(segs) + p = minimum(s) + P = typeof(p) + S = Tuple{P,P} + + # initialization + ๐’ฌ = BinaryTrees.AVLTree{P}() + ๐’ฏ = BinaryTrees.AVLTree{S}() + โ„’ = Dict{P,Vector{S}}() + ๐’ฐ = Dict{P,Vector{S}}() + ๐’ž = Dict{P,Vector{S}}() + lookup = Dict{S,Int}() + for (i, s) in enumerate(segs) + a, b = extrema(s) + BinaryTrees.insert!(๐’ฌ, a) + BinaryTrees.insert!(๐’ฌ, b) + haskey(โ„’, a) ? push!(โ„’[a], (a, b)) : (โ„’[a] = [(a, b)]) + haskey(๐’ฐ, b) ? push!(๐’ฐ[b], (a, b)) : (๐’ฐ[b] = [(a, b)]) + haskey(โ„’, b) || (โ„’[b] = S[]) + haskey(๐’ฐ, a) || (๐’ฐ[a] = S[]) + haskey(๐’ž, a) || (๐’ž[a] = S[]) + haskey(๐’ž, b) || (๐’ž[b] = S[]) + lookup[(a, b)] = i + end + + # sweep line + I = Dict{P,Vector{Tuple{IntersectionType,Vector{Int}}}}() + while !isnothing(BinaryTrees.root(๐’ฌ)) + p = BinaryTrees.key(BinaryTrees.minnode(๐’ฌ)) + BinaryTrees.delete!(๐’ฌ, p) + Meshes.handle!(I, lookup, p, S, ๐’ฌ, ๐’ฏ, โ„’, ๐’ฐ, ๐’ž) + end + I +end + +function handle!(I, lookup, p, S, ๐’ฌ, ๐’ฏ, โ„’, ๐’ฐ, ๐’ž) + ๐’ฎโ‚› = get(โ„’, p, S[]) + ๐’ฎโ‚‘ = get(๐’ฐ, p, S[]) + ๐’ฎแตข = get(๐’ž, p, S[]) + _processends!(๐’ฎโ‚‘, ๐’ฌ, ๐’ฏ, ๐’ž) + _processstarts!(๐’ฎโ‚›, ๐’ฌ, ๐’ฏ, ๐’ž) + __processintersects!(๐’ฎแตข, ๐’ฌ, ๐’ฏ, ๐’ž) + println("p: ", p) + println("๐’ฎโ‚‘:", ๐’ฎโ‚‘) + println("๐’ฎแตข: ", ๐’ฎแตข) + println("-----------------") + if !isempty(๐’ฎโ‚› โˆช ๐’ฎโ‚‘ โˆช ๐’ฎแตข) + corners = ๐’ฎโ‚› โˆช ๐’ฎโ‚‘ + crossings = ๐’ฎแตข + I[p] = _intersection(lookup, corners, crossings) + end +end + +function _processstarts!(๐’ฎโ‚›, ๐’ฌ, ๐’ฏ, ๐’ž) + [BinaryTrees.insert!(๐’ฏ, s) for s in ๐’ฎโ‚›] + for s in ๐’ฎโ‚› + prev, next = BinaryTrees.prevnext(๐’ฏ, s) + s = Segment(s) + if !isnothing(prev) && !isnothing(next) + new_geom, new_type = _newevent(Segment(BinaryTrees.key(next)), Segment(BinaryTrees.key(prev))) + if _checkintersection(new_type) + BinaryTrees.insert!(๐’ฌ, new_geom) + haskey(๐’ž, new_geom) ? push!(๐’ž[new_geom], BinaryTrees.key(next), BinaryTrees.key(prev)) : + (๐’ž[new_geom] = [BinaryTrees.key(next), BinaryTrees.key(prev)]) + end + end + if !isnothing(prev) + new_geom, new_type = _newevent(Segment(BinaryTrees.key(prev)), s) + if new_type == IntersectionType(0) + BinaryTrees.insert!(๐’ฌ, new_geom) + haskey(๐’ž, new_geom) ? push!(๐’ž[new_geom], BinaryTrees.key(prev), vertices(s)) : + (๐’ž[new_geom] = [BinaryTrees.key(prev), vertices(s)]) + end + end + if !isnothing(next) + new_geom, new_type = _newevent(s, Segment(BinaryTrees.key(next))) + if new_type == IntersectionType(0) + BinaryTrees.insert!(๐’ฌ, new_geom) + haskey(๐’ž, new_geom) ? push!(๐’ž[new_geom], vertices(s), BinaryTrees.key(next)) : + (๐’ž[new_geom] = [vertices(s), BinaryTrees.key(next)]) + end + end + end +end + +function _processends!(๐’ฎโ‚‘, ๐’ฌ, ๐’ฏ, ๐’ž) + for s in ๐’ฎโ‚‘ + prev, next = BinaryTrees.prevnext(๐’ฏ, s) + BinaryTrees.delete!(๐’ฏ, s) + s = Segment(s) + if !isnothing(prev) && !isnothing(next) + new_geom, new_type = _newevent(Segment(BinaryTrees.key(next)), Segment(BinaryTrees.key(prev))) + if new_type == IntersectionType(0) + BinaryTrees.insert!(๐’ฌ, new_geom) + haskey(๐’ž, new_geom) ? push!(๐’ž[new_geom], BinaryTrees.key(next), BinaryTrees.key(prev)) : + (๐’ž[new_geom] = [BinaryTrees.key(next), BinaryTrees.key(prev)]) + end + end + end +end + +function __processintersects!(๐’ฎแตข, ๐’ฌ, ๐’ฏ, ๐’ž) + for s in ๐’ฎแตข + prev, _ = BinaryTrees.prevnext(๐’ฏ, s) + if !isnothing(prev) + + # Find segments r and u + r, _ = BinaryTrees.prevnext(๐’ฏ, s) + _, u = BinaryTrees.prevnext(๐’ฏ, BinaryTrees.key(prev)) + + # Remove crossing points rs and tu from event queue + if !isnothing(r) + new_geom, new_type = _newevent(Segment(BinaryTrees.key(r)), Segment(s)) + if new_type == IntersectionType(0) + BinaryTrees.delete!(๐’ฌ, new_geom) + end + end + if !isnothing(u) + new_geom, new_type = _newevent(Segment(BinaryTrees.key(u)), Segment(BinaryTrees.key(prev))) + if new_type == IntersectionType(0) + BinaryTrees.delete!(๐’ฌ, new_geom) + end + end + + # Add crossing points rt and su to event queue + if !isnothing(r) + new_geom, new_type = _newevent(Segment(BinaryTrees.key(r)), Segment(BinaryTrees.key(prev))) + if new_type == IntersectionType(0) + BinaryTrees.insert!(๐’ฌ, new_geom) + haskey(๐’ž, new_geom) ? push!(๐’ž[new_geom], BinaryTrees.key(r), BinaryTrees.key(prev)) : + (๐’ž[new_geom] = [BinaryTrees.key(r), BinaryTrees.key(prev)]) + end + end + if !isnothing(u) + new_geom, new_type = _newevent(Segment(BinaryTrees.key(u)), Segment(s)) + if new_type == IntersectionType(0) + BinaryTrees.insert!(๐’ฌ, new_geom) + haskey(๐’ž, new_geom) ? push!(๐’ž[new_geom], BinaryTrees.key(u), s) : (๐’ž[new_geom] = [BinaryTrees.key(u), s]) + end + end + end + end +end + +function _pushintersection(lookup, corners, crossings) + return [ + (IntersectionType(4), [lookup[segment] for segment in corners]), + (IntersectionType(0), [lookup[segment] for segment in crossings]) + ] +end +_key(node::Nothing) = nothing +function _newevent(sโ‚::Segment, sโ‚‚::Segment) + new_event = intersection(sโ‚, sโ‚‚) + if !isnothing(new_event) + get(new_event), type(new_event) + else + nothing, nothing + end +end + +function _checkintersection(type) + type == IntersectionType(0) || type == IntersectionType(1) || type == IntersectionType(2) +end diff --git a/test/intersections.jl b/test/intersections.jl index dffebf2c4..edcc1c43f 100644 --- a/test/intersections.jl +++ b/test/intersections.jl @@ -1187,27 +1187,3 @@ end @test pset โˆฉ grid == grid โˆฉ pset == pset @test pset โˆฉ ball == ball โˆฉ pset == PointSet(cart(0.5, 0.5)) end - -@testitem "Pairwise Intersections" setup = [Setup] begin - # test bentley-ottmann algorithm - S = [ - Segment(cart(0, 0), cart(1, 1)), - Segment(cart(1, 0), cart(0, 1)), - Segment(cart(0, 0), cart(0, 1)), - Segment(cart(0, 0), cart(1, 0)), - Segment(cart(0, 1), cart(1, 1)), - Segment(cart(1, 0), cart(1, 1)) - ] - I = BentleyOttmann(S) - S_check = Dict( - cart(1, 1) => [(cart(0, 0), cart(1, 1)), (cart(0, 1), cart(1, 1)), (cart(1, 0), cart(1, 1))], - cart(0, 1) => [(cart(0, 1), cart(1, 0)), (cart(0, 1), cart(1, 1)), (cart(0, 0), cart(0, 1))], - cart(0.5, 0.5) => [(cart(0, 0), cart(1, 1)), (cart(0, 1), cart(1, 0))], - cart(1, 0) => [(cart(1, 0), cart(1, 1)), (cart(0, 1), cart(1, 0)), (cart(0, 0), cart(1, 0))], - cart(0, 0) => [(cart(0, 0), cart(1, 1)), (cart(0, 0), cart(0, 1)), (cart(0, 0), cart(1, 0))] - ) - @test typeof(I) == typeof(S_check) - @test length(I) == length(S_check) - @test keys(I) == keys(S_check) - @test all(values(I) .== values(S_check)) -end diff --git a/test/utils.jl b/test/utils.jl index 825897847..3efaca9fb 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -55,3 +55,27 @@ p = latlon(c) |> Proj(Cartesian) @inferred Meshes.withcrs(p, c, LatLon) end + +@testitem "Pairwise Intersections" setup = [Setup] begin + # test bentley-ottmann algorithm + S = [ + Segment(cart(0, 0), cart(1.1, 1.1)), + Segment(cart(1, 0), cart(0, 1)), + Segment(cart(0, 0), cart(0, 1)), + Segment(cart(0, 0), cart(1, 0)), + Segment(cart(0, 1), cart(1, 1)), + Segment(cart(1, 0), cart(1, 1)) + ] + I = BentleyOttmann(S) + S_check = Dict( + cart(1, 1) => [(cart(0, 0), cart(1, 1)), (cart(0, 1), cart(1, 1)), (cart(1, 0), cart(1, 1))], + cart(0, 1) => [(cart(0, 1), cart(1, 0)), (cart(0, 1), cart(1, 1)), (cart(0, 0), cart(0, 1))], + cart(0.5, 0.5) => [(cart(0, 0), cart(1, 1)), (cart(0, 1), cart(1, 0))], + cart(1, 0) => [(cart(1, 0), cart(1, 1)), (cart(0, 1), cart(1, 0)), (cart(0, 0), cart(1, 0))], + cart(0, 0) => [(cart(0, 0), cart(1, 1)), (cart(0, 0), cart(0, 1)), (cart(0, 0), cart(1, 0))] + ) + @test typeof(I) == typeof(S_check) + @test length(I) == length(S_check) + @test keys(I) == keys(S_check) + @test all(values(I) .== values(S_check)) +end From 6be63f1659227cdcee81137bd1dd87b27404055c Mon Sep 17 00:00:00 2001 From: souma4 Date: Sun, 23 Feb 2025 22:50:53 -0700 Subject: [PATCH 08/60] new_type and new_geom didn't meet style, so fixed. Removed an unneeded function. removed println calls used for debugging --- src/utils/sweepline.jl | 81 ++++++++++++++++++++---------------------- 1 file changed, 38 insertions(+), 43 deletions(-) diff --git a/src/utils/sweepline.jl b/src/utils/sweepline.jl index 3dd096ff5..a278cac5e 100644 --- a/src/utils/sweepline.jl +++ b/src/utils/sweepline.jl @@ -62,10 +62,6 @@ function handle!(I, lookup, p, S, ๐’ฌ, ๐’ฏ, โ„’, ๐’ฐ, ๐’ž) _processends!(๐’ฎโ‚‘, ๐’ฌ, ๐’ฏ, ๐’ž) _processstarts!(๐’ฎโ‚›, ๐’ฌ, ๐’ฏ, ๐’ž) __processintersects!(๐’ฎแตข, ๐’ฌ, ๐’ฏ, ๐’ž) - println("p: ", p) - println("๐’ฎโ‚‘:", ๐’ฎโ‚‘) - println("๐’ฎแตข: ", ๐’ฎแตข) - println("-----------------") if !isempty(๐’ฎโ‚› โˆช ๐’ฎโ‚‘ โˆช ๐’ฎแตข) corners = ๐’ฎโ‚› โˆช ๐’ฎโ‚‘ crossings = ๐’ฎแตข @@ -79,27 +75,27 @@ function _processstarts!(๐’ฎโ‚›, ๐’ฌ, ๐’ฏ, ๐’ž) prev, next = BinaryTrees.prevnext(๐’ฏ, s) s = Segment(s) if !isnothing(prev) && !isnothing(next) - new_geom, new_type = _newevent(Segment(BinaryTrees.key(next)), Segment(BinaryTrees.key(prev))) - if _checkintersection(new_type) - BinaryTrees.insert!(๐’ฌ, new_geom) - haskey(๐’ž, new_geom) ? push!(๐’ž[new_geom], BinaryTrees.key(next), BinaryTrees.key(prev)) : - (๐’ž[new_geom] = [BinaryTrees.key(next), BinaryTrees.key(prev)]) + newgeom, newtype = _newevent(Segment(BinaryTrees.key(next)), Segment(BinaryTrees.key(prev))) + if _checkintersection(newtype) + BinaryTrees.insert!(๐’ฌ, newgeom) + haskey(๐’ž, newgeom) ? push!(๐’ž[newgeom], BinaryTrees.key(next), BinaryTrees.key(prev)) : + (๐’ž[newgeom] = [BinaryTrees.key(next), BinaryTrees.key(prev)]) end end if !isnothing(prev) - new_geom, new_type = _newevent(Segment(BinaryTrees.key(prev)), s) - if new_type == IntersectionType(0) - BinaryTrees.insert!(๐’ฌ, new_geom) - haskey(๐’ž, new_geom) ? push!(๐’ž[new_geom], BinaryTrees.key(prev), vertices(s)) : - (๐’ž[new_geom] = [BinaryTrees.key(prev), vertices(s)]) + newgeom, newtype = _newevent(Segment(BinaryTrees.key(prev)), s) + if newtype == IntersectionType(0) + BinaryTrees.insert!(๐’ฌ, newgeom) + haskey(๐’ž, newgeom) ? push!(๐’ž[newgeom], BinaryTrees.key(prev), vertices(s)) : + (๐’ž[newgeom] = [BinaryTrees.key(prev), vertices(s)]) end end if !isnothing(next) - new_geom, new_type = _newevent(s, Segment(BinaryTrees.key(next))) - if new_type == IntersectionType(0) - BinaryTrees.insert!(๐’ฌ, new_geom) - haskey(๐’ž, new_geom) ? push!(๐’ž[new_geom], vertices(s), BinaryTrees.key(next)) : - (๐’ž[new_geom] = [vertices(s), BinaryTrees.key(next)]) + newgeom, newtype = _newevent(s, Segment(BinaryTrees.key(next))) + if newtype == IntersectionType(0) + BinaryTrees.insert!(๐’ฌ, newgeom) + haskey(๐’ž, newgeom) ? push!(๐’ž[newgeom], vertices(s), BinaryTrees.key(next)) : + (๐’ž[newgeom] = [vertices(s), BinaryTrees.key(next)]) end end end @@ -111,11 +107,11 @@ function _processends!(๐’ฎโ‚‘, ๐’ฌ, ๐’ฏ, ๐’ž) BinaryTrees.delete!(๐’ฏ, s) s = Segment(s) if !isnothing(prev) && !isnothing(next) - new_geom, new_type = _newevent(Segment(BinaryTrees.key(next)), Segment(BinaryTrees.key(prev))) - if new_type == IntersectionType(0) - BinaryTrees.insert!(๐’ฌ, new_geom) - haskey(๐’ž, new_geom) ? push!(๐’ž[new_geom], BinaryTrees.key(next), BinaryTrees.key(prev)) : - (๐’ž[new_geom] = [BinaryTrees.key(next), BinaryTrees.key(prev)]) + newgeom, newtype = _newevent(Segment(BinaryTrees.key(next)), Segment(BinaryTrees.key(prev))) + if newtype == IntersectionType(0) + BinaryTrees.insert!(๐’ฌ, newgeom) + haskey(๐’ž, newgeom) ? push!(๐’ž[newgeom], BinaryTrees.key(next), BinaryTrees.key(prev)) : + (๐’ž[newgeom] = [BinaryTrees.key(next), BinaryTrees.key(prev)]) end end end @@ -132,32 +128,32 @@ function __processintersects!(๐’ฎแตข, ๐’ฌ, ๐’ฏ, ๐’ž) # Remove crossing points rs and tu from event queue if !isnothing(r) - new_geom, new_type = _newevent(Segment(BinaryTrees.key(r)), Segment(s)) - if new_type == IntersectionType(0) - BinaryTrees.delete!(๐’ฌ, new_geom) + newgeom, newtype = _newevent(Segment(BinaryTrees.key(r)), Segment(s)) + if newtype == IntersectionType(0) + BinaryTrees.delete!(๐’ฌ, newgeom) end end if !isnothing(u) - new_geom, new_type = _newevent(Segment(BinaryTrees.key(u)), Segment(BinaryTrees.key(prev))) - if new_type == IntersectionType(0) - BinaryTrees.delete!(๐’ฌ, new_geom) + newgeom, newtype = _newevent(Segment(BinaryTrees.key(u)), Segment(BinaryTrees.key(prev))) + if newtype == IntersectionType(0) + BinaryTrees.delete!(๐’ฌ, newgeom) end end # Add crossing points rt and su to event queue if !isnothing(r) - new_geom, new_type = _newevent(Segment(BinaryTrees.key(r)), Segment(BinaryTrees.key(prev))) - if new_type == IntersectionType(0) - BinaryTrees.insert!(๐’ฌ, new_geom) - haskey(๐’ž, new_geom) ? push!(๐’ž[new_geom], BinaryTrees.key(r), BinaryTrees.key(prev)) : - (๐’ž[new_geom] = [BinaryTrees.key(r), BinaryTrees.key(prev)]) + newgeom, newtype = _newevent(Segment(BinaryTrees.key(r)), Segment(BinaryTrees.key(prev))) + if newtype == IntersectionType(0) + BinaryTrees.insert!(๐’ฌ, newgeom) + haskey(๐’ž, newgeom) ? push!(๐’ž[newgeom], BinaryTrees.key(r), BinaryTrees.key(prev)) : + (๐’ž[newgeom] = [BinaryTrees.key(r), BinaryTrees.key(prev)]) end end if !isnothing(u) - new_geom, new_type = _newevent(Segment(BinaryTrees.key(u)), Segment(s)) - if new_type == IntersectionType(0) - BinaryTrees.insert!(๐’ฌ, new_geom) - haskey(๐’ž, new_geom) ? push!(๐’ž[new_geom], BinaryTrees.key(u), s) : (๐’ž[new_geom] = [BinaryTrees.key(u), s]) + newgeom, newtype = _newevent(Segment(BinaryTrees.key(u)), Segment(s)) + if newtype == IntersectionType(0) + BinaryTrees.insert!(๐’ฌ, newgeom) + haskey(๐’ž, newgeom) ? push!(๐’ž[newgeom], BinaryTrees.key(u), s) : (๐’ž[newgeom] = [BinaryTrees.key(u), s]) end end end @@ -170,11 +166,10 @@ function _pushintersection(lookup, corners, crossings) (IntersectionType(0), [lookup[segment] for segment in crossings]) ] end -_key(node::Nothing) = nothing function _newevent(sโ‚::Segment, sโ‚‚::Segment) - new_event = intersection(sโ‚, sโ‚‚) - if !isnothing(new_event) - get(new_event), type(new_event) + newevent = intersection(sโ‚, sโ‚‚) + if !isnothing(newevent) + get(newevent), type(newevent) else nothing, nothing end From ed09d87fe89c8bcce3b1542dab4bf9d1cb4f4f43 Mon Sep 17 00:00:00 2001 From: souma4 Date: Sun, 23 Feb 2025 22:56:31 -0700 Subject: [PATCH 09/60] forgot to rename _intersection to _pushintersection --- src/utils/sweepline.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/sweepline.jl b/src/utils/sweepline.jl index a278cac5e..6776a8059 100644 --- a/src/utils/sweepline.jl +++ b/src/utils/sweepline.jl @@ -65,7 +65,7 @@ function handle!(I, lookup, p, S, ๐’ฌ, ๐’ฏ, โ„’, ๐’ฐ, ๐’ž) if !isempty(๐’ฎโ‚› โˆช ๐’ฎโ‚‘ โˆช ๐’ฎแตข) corners = ๐’ฎโ‚› โˆช ๐’ฎโ‚‘ crossings = ๐’ฎแตข - I[p] = _intersection(lookup, corners, crossings) + I[p] = _pushintersection(lookup, corners, crossings) end end From 2b83b1af7d032c5416151bb571ad167f1c347dd4 Mon Sep 17 00:00:00 2001 From: Jeffrey Chandler Date: Tue, 4 Mar 2025 10:37:25 -0700 Subject: [PATCH 10/60] small commits to improve compatibility and fix mixed up old names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Jรบlio Hoffimann --- src/utils.jl | 2 +- src/utils/sweepline.jl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/utils.jl b/src/utils.jl index 882db44aa..e6c2e1c7e 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -11,4 +11,4 @@ include("utils/cmp.jl") include("utils/units.jl") include("utils/crs.jl") include("utils/misc.jl") -include("utils/sweepline.jl") +include("utils/intersection.jl") diff --git a/src/utils/sweepline.jl b/src/utils/sweepline.jl index 6776a8059..c153c6d9b 100644 --- a/src/utils/sweepline.jl +++ b/src/utils/sweepline.jl @@ -12,7 +12,7 @@ Outputs a Dictionary of {Point, Vector{Tuple{Point, Point}}} where the key is each intersection point and the values are all pairs of segments that intersect at that point. """ -function BentleyOttmann(segments) +function bentleyottmann(segments) # adjust vertices of segments segs = map(segments) do s a, b = extrema(s) @@ -50,7 +50,7 @@ function BentleyOttmann(segments) while !isnothing(BinaryTrees.root(๐’ฌ)) p = BinaryTrees.key(BinaryTrees.minnode(๐’ฌ)) BinaryTrees.delete!(๐’ฌ, p) - Meshes.handle!(I, lookup, p, S, ๐’ฌ, ๐’ฏ, โ„’, ๐’ฐ, ๐’ž) + handle!(I, lookup, p, S, ๐’ฌ, ๐’ฏ, โ„’, ๐’ฐ, ๐’ž) end I end From 7484dc1f0c837bdb4dec67e838d2a403d31ef772 Mon Sep 17 00:00:00 2001 From: souma4 Date: Thu, 6 Mar 2025 13:18:33 -0700 Subject: [PATCH 11/60] small updates to License text, cases, variable and function names, cleaner enum handling, and shifted the final dictionary to be unique values --- src/utils/sweepline.jl | 69 ++++++++++++++++++++++-------------------- test/utils.jl | 2 +- 2 files changed, 38 insertions(+), 33 deletions(-) diff --git a/src/utils/sweepline.jl b/src/utils/sweepline.jl index 6776a8059..a33eb4598 100644 --- a/src/utils/sweepline.jl +++ b/src/utils/sweepline.jl @@ -1,8 +1,8 @@ -# Implementation of Bentley-Ottmann algorith -# https://en.wikipedia.org/wiki/Bentley%E2%80%93Ottmann_algorithm - +# ------------------------------------------------------------------ +# Licensed under the MIT License. See LICENSE in the project root. +# ------------------------------------------------------------------ """ - BentleyOttmann(segments) + bentleyottmann(segments) Compute pairwise intersections between n `segments` in O(nโ‹…log(n)) time using Bentley-Ottmann sweep line @@ -11,8 +11,13 @@ algorithm. Outputs a Dictionary of {Point, Vector{Tuple{Point, Point}}} where the key is each intersection point and the values are all pairs of segments that intersect at that point. + +## References + +* Bentley, J. L., & Ottmann, T. 1979. [Algorithms for reporting and counting geometric intersections] + (https://www.itseng.org/research/papers/topics/VLSI_Physical_Design_Automation/Physical_Verification/DRC/Geometric_Intersection_Problems/1979-Bentley.pdf) """ -function BentleyOttmann(segments) +function bentleyottmann(segments) # adjust vertices of segments segs = map(segments) do s a, b = extrema(s) @@ -56,22 +61,22 @@ function BentleyOttmann(segments) end function handle!(I, lookup, p, S, ๐’ฌ, ๐’ฏ, โ„’, ๐’ฐ, ๐’ž) - ๐’ฎโ‚› = get(โ„’, p, S[]) - ๐’ฎโ‚‘ = get(๐’ฐ, p, S[]) - ๐’ฎแตข = get(๐’ž, p, S[]) - _processends!(๐’ฎโ‚‘, ๐’ฌ, ๐’ฏ, ๐’ž) - _processstarts!(๐’ฎโ‚›, ๐’ฌ, ๐’ฏ, ๐’ž) - __processintersects!(๐’ฎแตข, ๐’ฌ, ๐’ฏ, ๐’ž) - if !isempty(๐’ฎโ‚› โˆช ๐’ฎโ‚‘ โˆช ๐’ฎแตข) - corners = ๐’ฎโ‚› โˆช ๐’ฎโ‚‘ - crossings = ๐’ฎแตข + โ„ฌ = get(โ„’, p, S[]) + โ„ฐ = get(๐’ฐ, p, S[]) + โ„ = get(๐’ž, p, S[]) + _processend!(โ„ฐ, ๐’ฌ, ๐’ฏ, ๐’ž) + _processbegin!(โ„ฌ, ๐’ฌ, ๐’ฏ, ๐’ž) + _processintersects!(โ„, ๐’ฌ, ๐’ฏ, ๐’ž) + if !isempty(โ„ฌ โˆช โ„ฐ โˆช โ„) + corners = โ„ฌ โˆช โ„ฐ + crossings = โ„ I[p] = _pushintersection(lookup, corners, crossings) end end -function _processstarts!(๐’ฎโ‚›, ๐’ฌ, ๐’ฏ, ๐’ž) - [BinaryTrees.insert!(๐’ฏ, s) for s in ๐’ฎโ‚›] - for s in ๐’ฎโ‚› +function _processbegin!(โ„ฌ, ๐’ฌ, ๐’ฏ, ๐’ž) + [BinaryTrees.insert!(๐’ฏ, s) for s in โ„ฌ] + for s in โ„ฌ prev, next = BinaryTrees.prevnext(๐’ฏ, s) s = Segment(s) if !isnothing(prev) && !isnothing(next) @@ -84,7 +89,7 @@ function _processstarts!(๐’ฎโ‚›, ๐’ฌ, ๐’ฏ, ๐’ž) end if !isnothing(prev) newgeom, newtype = _newevent(Segment(BinaryTrees.key(prev)), s) - if newtype == IntersectionType(0) + if _checkintersection(newtype) BinaryTrees.insert!(๐’ฌ, newgeom) haskey(๐’ž, newgeom) ? push!(๐’ž[newgeom], BinaryTrees.key(prev), vertices(s)) : (๐’ž[newgeom] = [BinaryTrees.key(prev), vertices(s)]) @@ -92,7 +97,7 @@ function _processstarts!(๐’ฎโ‚›, ๐’ฌ, ๐’ฏ, ๐’ž) end if !isnothing(next) newgeom, newtype = _newevent(s, Segment(BinaryTrees.key(next))) - if newtype == IntersectionType(0) + if _checkintersection(newtype) BinaryTrees.insert!(๐’ฌ, newgeom) haskey(๐’ž, newgeom) ? push!(๐’ž[newgeom], vertices(s), BinaryTrees.key(next)) : (๐’ž[newgeom] = [vertices(s), BinaryTrees.key(next)]) @@ -101,14 +106,14 @@ function _processstarts!(๐’ฎโ‚›, ๐’ฌ, ๐’ฏ, ๐’ž) end end -function _processends!(๐’ฎโ‚‘, ๐’ฌ, ๐’ฏ, ๐’ž) - for s in ๐’ฎโ‚‘ +function _processend!(โ„ฐ, ๐’ฌ, ๐’ฏ, ๐’ž) + for s in โ„ฐ prev, next = BinaryTrees.prevnext(๐’ฏ, s) BinaryTrees.delete!(๐’ฏ, s) s = Segment(s) if !isnothing(prev) && !isnothing(next) newgeom, newtype = _newevent(Segment(BinaryTrees.key(next)), Segment(BinaryTrees.key(prev))) - if newtype == IntersectionType(0) + if _checkintersection(newtype) BinaryTrees.insert!(๐’ฌ, newgeom) haskey(๐’ž, newgeom) ? push!(๐’ž[newgeom], BinaryTrees.key(next), BinaryTrees.key(prev)) : (๐’ž[newgeom] = [BinaryTrees.key(next), BinaryTrees.key(prev)]) @@ -117,8 +122,8 @@ function _processends!(๐’ฎโ‚‘, ๐’ฌ, ๐’ฏ, ๐’ž) end end -function __processintersects!(๐’ฎแตข, ๐’ฌ, ๐’ฏ, ๐’ž) - for s in ๐’ฎแตข +function _processintersects!(โ„, ๐’ฌ, ๐’ฏ, ๐’ž) + for s in โ„ prev, _ = BinaryTrees.prevnext(๐’ฏ, s) if !isnothing(prev) @@ -129,13 +134,13 @@ function __processintersects!(๐’ฎแตข, ๐’ฌ, ๐’ฏ, ๐’ž) # Remove crossing points rs and tu from event queue if !isnothing(r) newgeom, newtype = _newevent(Segment(BinaryTrees.key(r)), Segment(s)) - if newtype == IntersectionType(0) + if _checkintersection(newtype) BinaryTrees.delete!(๐’ฌ, newgeom) end end if !isnothing(u) newgeom, newtype = _newevent(Segment(BinaryTrees.key(u)), Segment(BinaryTrees.key(prev))) - if newtype == IntersectionType(0) + if _checkintersection(newtype) BinaryTrees.delete!(๐’ฌ, newgeom) end end @@ -143,7 +148,7 @@ function __processintersects!(๐’ฎแตข, ๐’ฌ, ๐’ฏ, ๐’ž) # Add crossing points rt and su to event queue if !isnothing(r) newgeom, newtype = _newevent(Segment(BinaryTrees.key(r)), Segment(BinaryTrees.key(prev))) - if newtype == IntersectionType(0) + if _checkintersection(newtype) BinaryTrees.insert!(๐’ฌ, newgeom) haskey(๐’ž, newgeom) ? push!(๐’ž[newgeom], BinaryTrees.key(r), BinaryTrees.key(prev)) : (๐’ž[newgeom] = [BinaryTrees.key(r), BinaryTrees.key(prev)]) @@ -151,7 +156,7 @@ function __processintersects!(๐’ฎแตข, ๐’ฌ, ๐’ฏ, ๐’ž) end if !isnothing(u) newgeom, newtype = _newevent(Segment(BinaryTrees.key(u)), Segment(s)) - if newtype == IntersectionType(0) + if _checkintersection(newtype) BinaryTrees.insert!(๐’ฌ, newgeom) haskey(๐’ž, newgeom) ? push!(๐’ž[newgeom], BinaryTrees.key(u), s) : (๐’ž[newgeom] = [BinaryTrees.key(u), s]) end @@ -161,9 +166,9 @@ function __processintersects!(๐’ฎแตข, ๐’ฌ, ๐’ฏ, ๐’ž) end function _pushintersection(lookup, corners, crossings) - return [ - (IntersectionType(4), [lookup[segment] for segment in corners]), - (IntersectionType(0), [lookup[segment] for segment in crossings]) + [ + (CornerCrossing, unique([lookup[segment] for segment in corners])), + (Crossing, unique([lookup[segment] for segment in crossings])) ] end function _newevent(sโ‚::Segment, sโ‚‚::Segment) @@ -176,5 +181,5 @@ function _newevent(sโ‚::Segment, sโ‚‚::Segment) end function _checkintersection(type) - type == IntersectionType(0) || type == IntersectionType(1) || type == IntersectionType(2) + type == Crossing || type == EdgeTouching end diff --git a/test/utils.jl b/test/utils.jl index 3efaca9fb..121fe5eef 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -66,7 +66,7 @@ end Segment(cart(0, 1), cart(1, 1)), Segment(cart(1, 0), cart(1, 1)) ] - I = BentleyOttmann(S) + I = bentleyottmann(S) S_check = Dict( cart(1, 1) => [(cart(0, 0), cart(1, 1)), (cart(0, 1), cart(1, 1)), (cart(1, 0), cart(1, 1))], cart(0, 1) => [(cart(0, 1), cart(1, 0)), (cart(0, 1), cart(1, 1)), (cart(0, 0), cart(0, 1))], From ec4c5930167245e4efceb61d07f84a9e08398eff Mon Sep 17 00:00:00 2001 From: souma4 Date: Fri, 7 Mar 2025 15:03:50 -0700 Subject: [PATCH 12/60] Updated algorithm to be simpler, output is points and segment indices that connect to the point --- src/utils/sweepline.jl | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/utils/sweepline.jl b/src/utils/sweepline.jl index 1593238ea..371db6f97 100644 --- a/src/utils/sweepline.jl +++ b/src/utils/sweepline.jl @@ -51,16 +51,17 @@ function bentleyottmann(segments) end # sweep line - I = Dict{P,Vector{Tuple{IntersectionType,Vector{Int}}}}() + points = Vector{P}() + segs = Vector{Vector{Int}}() while !isnothing(BinaryTrees.root(๐’ฌ)) p = BinaryTrees.key(BinaryTrees.minnode(๐’ฌ)) BinaryTrees.delete!(๐’ฌ, p) - handle!(I, lookup, p, S, ๐’ฌ, ๐’ฏ, โ„’, ๐’ฐ, ๐’ž) + handle!(points, segs, lookup, p, S, ๐’ฌ, ๐’ฏ, โ„’, ๐’ฐ, ๐’ž) end - I + points, segs end -function handle!(I, lookup, p, S, ๐’ฌ, ๐’ฏ, โ„’, ๐’ฐ, ๐’ž) +function handle!(points, segs, lookup, p, S, ๐’ฌ, ๐’ฏ, โ„’, ๐’ฐ, ๐’ž) โ„ฌ = get(โ„’, p, S[]) โ„ฐ = get(๐’ฐ, p, S[]) โ„ = get(๐’ž, p, S[]) @@ -68,9 +69,9 @@ function handle!(I, lookup, p, S, ๐’ฌ, ๐’ฏ, โ„’, ๐’ฐ, ๐’ž) _processbegin!(โ„ฌ, ๐’ฌ, ๐’ฏ, ๐’ž) _processintersects!(โ„, ๐’ฌ, ๐’ฏ, ๐’ž) if !isempty(โ„ฌ โˆช โ„ฐ โˆช โ„) - corners = โ„ฌ โˆช โ„ฐ - crossings = โ„ - I[p] = _pushintersection(lookup, corners, crossings) + segments = โ„ฌ โˆช โ„ฐ โˆช โ„ + push!(points, p) + push!(segs, _pushintersection(lookup, segments)) end end @@ -165,12 +166,8 @@ function _processintersects!(โ„, ๐’ฌ, ๐’ฏ, ๐’ž) end end -function _pushintersection(lookup, corners, crossings) - [ - (CornerCrossing, unique([lookup[segment] for segment in corners])), - (Crossing, unique([lookup[segment] for segment in crossings])) - ] -end +_pushintersection(lookup, segments) = unique([lookup[segment] for segment in segments]) + function _newevent(sโ‚::Segment, sโ‚‚::Segment) newevent = intersection(sโ‚, sโ‚‚) if !isnothing(newevent) From e18eb990632f705ab9fabe32d6263eaecc159a36 Mon Sep 17 00:00:00 2001 From: souma4 Date: Fri, 7 Mar 2025 20:32:41 -0700 Subject: [PATCH 13/60] updated tests to reflect changed output type --- test/utils.jl | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/test/utils.jl b/test/utils.jl index 121fe5eef..508380dc2 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -66,16 +66,12 @@ end Segment(cart(0, 1), cart(1, 1)), Segment(cart(1, 0), cart(1, 1)) ] - I = bentleyottmann(S) - S_check = Dict( - cart(1, 1) => [(cart(0, 0), cart(1, 1)), (cart(0, 1), cart(1, 1)), (cart(1, 0), cart(1, 1))], - cart(0, 1) => [(cart(0, 1), cart(1, 0)), (cart(0, 1), cart(1, 1)), (cart(0, 0), cart(0, 1))], - cart(0.5, 0.5) => [(cart(0, 0), cart(1, 1)), (cart(0, 1), cart(1, 0))], - cart(1, 0) => [(cart(1, 0), cart(1, 1)), (cart(0, 1), cart(1, 0)), (cart(0, 0), cart(1, 0))], - cart(0, 0) => [(cart(0, 0), cart(1, 1)), (cart(0, 0), cart(0, 1)), (cart(0, 0), cart(1, 0))] - ) - @test typeof(I) == typeof(S_check) - @test length(I) == length(S_check) - @test keys(I) == keys(S_check) - @test all(values(I) .== values(S_check)) + points, segs = bentleyottmann(S) + + @inferred bentleyottmann(S) + @test all(points .โ‰ˆ [cart(0, 0), cart(0, 1), cart(0.5, 0.5), cart(1, 0), cart(1, 1), cart(1.1, 1.1)]) + @test length(points) == 6 + @test length(segs) == 6 + @test maximum(reduce(vcat, segs)) == length(S) + @test segs == [[1, 3, 4], [2, 5, 3], [1, 2], [6, 2, 4], [5, 6, 1], [1]] end From e27d9f197453af7314f8202fd34b0153d28bc88e Mon Sep 17 00:00:00 2001 From: souma4 Date: Sun, 9 Mar 2025 10:58:06 -0600 Subject: [PATCH 14/60] add compat for BinaryTrees --- Project.toml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Project.toml b/Project.toml index 04dd1d00d..59be03768 100644 --- a/Project.toml +++ b/Project.toml @@ -23,8 +23,15 @@ TiledIteration = "06e1c1a7-607b-532d-9fad-de7d9aa2abac" TransformsBase = "28dd2a49-a57a-4bfb-84ca-1a49db9b96b8" Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" +[weakdeps] +Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" + +[extensions] +MeshesMakieExt = "Makie" + [compat] Bessels = "0.2" +BinaryTrees = "0.1.1" CircularArrays = "1.3" Colorfy = "1.0" CoordRefSystems = "0.15" @@ -43,9 +50,3 @@ TiledIteration = "0.5" TransformsBase = "1.6" Unitful = "1.17" julia = "1.9" - -[extensions] -MeshesMakieExt = "Makie" - -[weakdeps] -Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a" From 56018d3f4b7172ce8f02e7932c1e66ab9b04e2c8 Mon Sep 17 00:00:00 2001 From: souma4 Date: Sun, 9 Mar 2025 16:05:52 -0600 Subject: [PATCH 15/60] fixed names of variables and helper functions. removed output type from docstring --- src/utils/sweepline.jl | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/utils/sweepline.jl b/src/utils/sweepline.jl index 371db6f97..31928b66d 100644 --- a/src/utils/sweepline.jl +++ b/src/utils/sweepline.jl @@ -1,6 +1,7 @@ # ------------------------------------------------------------------ # Licensed under the MIT License. See LICENSE in the project root. # ------------------------------------------------------------------ + """ bentleyottmann(segments) @@ -8,10 +9,6 @@ Compute pairwise intersections between n `segments` in O(nโ‹…log(n)) time using Bentley-Ottmann sweep line algorithm. -Outputs a Dictionary of {Point, Vector{Tuple{Point, Point}}} -where the key is each intersection point and the values are all -pairs of segments that intersect at that point. - ## References * Bentley, J. L., & Ottmann, T. 1979. [Algorithms for reporting and counting geometric intersections] @@ -52,16 +49,16 @@ function bentleyottmann(segments) # sweep line points = Vector{P}() - segs = Vector{Vector{Int}}() + segmentidxs = Vector{Vector{Int}}() while !isnothing(BinaryTrees.root(๐’ฌ)) p = BinaryTrees.key(BinaryTrees.minnode(๐’ฌ)) BinaryTrees.delete!(๐’ฌ, p) - handle!(points, segs, lookup, p, S, ๐’ฌ, ๐’ฏ, โ„’, ๐’ฐ, ๐’ž) + _handle!(points, segmentidxs, lookup, p, S, ๐’ฌ, ๐’ฏ, โ„’, ๐’ฐ, ๐’ž) end - points, segs + points, segmentidxs end -function handle!(points, segs, lookup, p, S, ๐’ฌ, ๐’ฏ, โ„’, ๐’ฐ, ๐’ž) +function _handle!(points, segmentidxs, lookup, p, S, ๐’ฌ, ๐’ฏ, โ„’, ๐’ฐ, ๐’ž) โ„ฌ = get(โ„’, p, S[]) โ„ฐ = get(๐’ฐ, p, S[]) โ„ = get(๐’ž, p, S[]) @@ -71,12 +68,14 @@ function handle!(points, segs, lookup, p, S, ๐’ฌ, ๐’ฏ, โ„’, ๐’ฐ, ๐’ž) if !isempty(โ„ฌ โˆช โ„ฐ โˆช โ„) segments = โ„ฌ โˆช โ„ฐ โˆช โ„ push!(points, p) - push!(segs, _pushintersection(lookup, segments)) + push!(segmentidxs, _pushintersection(lookup, segments)) end end function _processbegin!(โ„ฌ, ๐’ฌ, ๐’ฏ, ๐’ž) - [BinaryTrees.insert!(๐’ฏ, s) for s in โ„ฌ] + for s in โ„ฌ + BinaryTrees.insert!(๐’ฏ, s) + end for s in โ„ฌ prev, next = BinaryTrees.prevnext(๐’ฏ, s) s = Segment(s) @@ -166,7 +165,7 @@ function _processintersects!(โ„, ๐’ฌ, ๐’ฏ, ๐’ž) end end -_pushintersection(lookup, segments) = unique([lookup[segment] for segment in segments]) +_pushintersection(lookup, segments) = unique(lookup[segment] for segment in segments) function _newevent(sโ‚::Segment, sโ‚‚::Segment) newevent = intersection(sโ‚, sโ‚‚) @@ -177,6 +176,4 @@ function _newevent(sโ‚::Segment, sโ‚‚::Segment) end end -function _checkintersection(type) - type == Crossing || type == EdgeTouching -end +_checkintersection(type) = type == Crossing || type == EdgeTouching From 442861b15621a9fb4e0edd99b60a044640cff19f Mon Sep 17 00:00:00 2001 From: souma4 Date: Mon, 10 Mar 2025 08:54:51 -0600 Subject: [PATCH 16/60] updated utils include path and refactored if else to new function for easier edits if needed --- src/utils.jl | 2 +- src/utils/sweepline.jl | 37 ++++++++++++++++++++----------------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/utils.jl b/src/utils.jl index e6c2e1c7e..882db44aa 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -11,4 +11,4 @@ include("utils/cmp.jl") include("utils/units.jl") include("utils/crs.jl") include("utils/misc.jl") -include("utils/intersection.jl") +include("utils/sweepline.jl") diff --git a/src/utils/sweepline.jl b/src/utils/sweepline.jl index 31928b66d..4cf179529 100644 --- a/src/utils/sweepline.jl +++ b/src/utils/sweepline.jl @@ -12,7 +12,7 @@ algorithm. ## References * Bentley, J. L., & Ottmann, T. 1979. [Algorithms for reporting and counting geometric intersections] - (https://www.itseng.org/research/papers/topics/VLSI_Physical_Design_Automation/Physical_Verification/DRC/Geometric_Intersection_Problems/1979-Bentley.pdf) + (https://ieeexplore.ieee.org/document/1675432) """ function bentleyottmann(segments) # adjust vertices of segments @@ -49,16 +49,16 @@ function bentleyottmann(segments) # sweep line points = Vector{P}() - segmentidxs = Vector{Vector{Int}}() + seginds = Vector{Vector{Int}}() while !isnothing(BinaryTrees.root(๐’ฌ)) p = BinaryTrees.key(BinaryTrees.minnode(๐’ฌ)) BinaryTrees.delete!(๐’ฌ, p) - _handle!(points, segmentidxs, lookup, p, S, ๐’ฌ, ๐’ฏ, โ„’, ๐’ฐ, ๐’ž) + _handle!(points, seginds, lookup, p, S, ๐’ฌ, ๐’ฏ, โ„’, ๐’ฐ, ๐’ž) end - points, segmentidxs + points, seginds end -function _handle!(points, segmentidxs, lookup, p, S, ๐’ฌ, ๐’ฏ, โ„’, ๐’ฐ, ๐’ž) +function _handle!(points, seginds, lookup, p, S, ๐’ฌ, ๐’ฏ, โ„’, ๐’ฐ, ๐’ž) โ„ฌ = get(โ„’, p, S[]) โ„ฐ = get(๐’ฐ, p, S[]) โ„ = get(๐’ž, p, S[]) @@ -68,7 +68,7 @@ function _handle!(points, segmentidxs, lookup, p, S, ๐’ฌ, ๐’ฏ, โ„’, ๐’ฐ, ๐’ž if !isempty(โ„ฌ โˆช โ„ฐ โˆช โ„) segments = โ„ฌ โˆช โ„ฐ โˆช โ„ push!(points, p) - push!(segmentidxs, _pushintersection(lookup, segments)) + push!(seginds, _pushintersection(lookup, segments)) end end @@ -83,24 +83,21 @@ function _processbegin!(โ„ฌ, ๐’ฌ, ๐’ฏ, ๐’ž) newgeom, newtype = _newevent(Segment(BinaryTrees.key(next)), Segment(BinaryTrees.key(prev))) if _checkintersection(newtype) BinaryTrees.insert!(๐’ฌ, newgeom) - haskey(๐’ž, newgeom) ? push!(๐’ž[newgeom], BinaryTrees.key(next), BinaryTrees.key(prev)) : - (๐’ž[newgeom] = [BinaryTrees.key(next), BinaryTrees.key(prev)]) + _newintersection!(๐’ž, newgeom, BinaryTrees.key(next), BinaryTrees.key(prev)) end end if !isnothing(prev) newgeom, newtype = _newevent(Segment(BinaryTrees.key(prev)), s) if _checkintersection(newtype) BinaryTrees.insert!(๐’ฌ, newgeom) - haskey(๐’ž, newgeom) ? push!(๐’ž[newgeom], BinaryTrees.key(prev), vertices(s)) : - (๐’ž[newgeom] = [BinaryTrees.key(prev), vertices(s)]) + _newintersection!(๐’ž, newgeom, BinaryTrees.key(prev), vertices(s)) end end if !isnothing(next) newgeom, newtype = _newevent(s, Segment(BinaryTrees.key(next))) if _checkintersection(newtype) BinaryTrees.insert!(๐’ฌ, newgeom) - haskey(๐’ž, newgeom) ? push!(๐’ž[newgeom], vertices(s), BinaryTrees.key(next)) : - (๐’ž[newgeom] = [vertices(s), BinaryTrees.key(next)]) + _newintersection!(๐’ž, newgeom, vertices(s), BinaryTrees.key(next)) end end end @@ -115,8 +112,7 @@ function _processend!(โ„ฐ, ๐’ฌ, ๐’ฏ, ๐’ž) newgeom, newtype = _newevent(Segment(BinaryTrees.key(next)), Segment(BinaryTrees.key(prev))) if _checkintersection(newtype) BinaryTrees.insert!(๐’ฌ, newgeom) - haskey(๐’ž, newgeom) ? push!(๐’ž[newgeom], BinaryTrees.key(next), BinaryTrees.key(prev)) : - (๐’ž[newgeom] = [BinaryTrees.key(next), BinaryTrees.key(prev)]) + _newintersection!(๐’ž, newgeom, BinaryTrees.key(next), BinaryTrees.key(prev)) end end end @@ -150,15 +146,14 @@ function _processintersects!(โ„, ๐’ฌ, ๐’ฏ, ๐’ž) newgeom, newtype = _newevent(Segment(BinaryTrees.key(r)), Segment(BinaryTrees.key(prev))) if _checkintersection(newtype) BinaryTrees.insert!(๐’ฌ, newgeom) - haskey(๐’ž, newgeom) ? push!(๐’ž[newgeom], BinaryTrees.key(r), BinaryTrees.key(prev)) : - (๐’ž[newgeom] = [BinaryTrees.key(r), BinaryTrees.key(prev)]) + _newintersection!(๐’ž, newgeom, BinaryTrees.key(r), BinaryTrees.key(prev)) end end if !isnothing(u) newgeom, newtype = _newevent(Segment(BinaryTrees.key(u)), Segment(s)) if _checkintersection(newtype) BinaryTrees.insert!(๐’ฌ, newgeom) - haskey(๐’ž, newgeom) ? push!(๐’ž[newgeom], BinaryTrees.key(u), s) : (๐’ž[newgeom] = [BinaryTrees.key(u), s]) + _newintersection!(๐’ž, newgeom, BinaryTrees.key(u), s) end end end @@ -177,3 +172,11 @@ function _newevent(sโ‚::Segment, sโ‚‚::Segment) end _checkintersection(type) = type == Crossing || type == EdgeTouching + +function _newintersection!(๐’ž, newgeom, segโ‚, segโ‚‚) + if haskey(๐’ž, newgeom) + push!(๐’ž[newgeom], segโ‚, segโ‚‚) + else + ๐’ž[newgeom] = [segโ‚, segโ‚‚] + end +end From 0cc5d91424ce0a0d420201ee04c83e3d4a9ab294 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Mon, 10 Mar 2025 17:24:00 -0300 Subject: [PATCH 17/60] Minor adjustments before review --- src/utils/sweepline.jl | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/utils/sweepline.jl b/src/utils/sweepline.jl index 4cf179529..c2a3219b4 100644 --- a/src/utils/sweepline.jl +++ b/src/utils/sweepline.jl @@ -5,14 +5,13 @@ """ bentleyottmann(segments) -Compute pairwise intersections between n `segments` -in O(nโ‹…log(n)) time using Bentley-Ottmann sweep line -algorithm. +Compute pairwise intersections between n `segments` in +O(nโ‹…log(n)) time using Bentley-Ottmann sweep line algorithm. ## References -* Bentley, J. L., & Ottmann, T. 1979. [Algorithms for reporting and counting geometric intersections] - (https://ieeexplore.ieee.org/document/1675432) +* Bentley, J. L., & Ottmann, T. 1979. [Algorithms for reporting + and counting geometric intersections](https://ieeexplore.ieee.org/document/1675432) """ function bentleyottmann(segments) # adjust vertices of segments @@ -65,10 +64,10 @@ function _handle!(points, seginds, lookup, p, S, ๐’ฌ, ๐’ฏ, โ„’, ๐’ฐ, ๐’ž) _processend!(โ„ฐ, ๐’ฌ, ๐’ฏ, ๐’ž) _processbegin!(โ„ฌ, ๐’ฌ, ๐’ฏ, ๐’ž) _processintersects!(โ„, ๐’ฌ, ๐’ฏ, ๐’ž) - if !isempty(โ„ฌ โˆช โ„ฐ โˆช โ„) - segments = โ„ฌ โˆช โ„ฐ โˆช โ„ + segs = โ„ฌ โˆช โ„ฐ โˆช โ„ + if !isempty(segs) push!(points, p) - push!(seginds, _pushintersection(lookup, segments)) + push!(seginds, _pushintersection(lookup, segs)) end end @@ -123,11 +122,11 @@ function _processintersects!(โ„, ๐’ฌ, ๐’ฏ, ๐’ž) prev, _ = BinaryTrees.prevnext(๐’ฏ, s) if !isnothing(prev) - # Find segments r and u + # find segments r and u r, _ = BinaryTrees.prevnext(๐’ฏ, s) _, u = BinaryTrees.prevnext(๐’ฏ, BinaryTrees.key(prev)) - # Remove crossing points rs and tu from event queue + # remove crossing points rs and tu from event queue if !isnothing(r) newgeom, newtype = _newevent(Segment(BinaryTrees.key(r)), Segment(s)) if _checkintersection(newtype) @@ -141,7 +140,7 @@ function _processintersects!(โ„, ๐’ฌ, ๐’ฏ, ๐’ž) end end - # Add crossing points rt and su to event queue + # add crossing points rt and su to event queue if !isnothing(r) newgeom, newtype = _newevent(Segment(BinaryTrees.key(r)), Segment(BinaryTrees.key(prev))) if _checkintersection(newtype) From 1c039472b816f45ab242a04c0b78112c0f6d69df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Tue, 11 Mar 2025 15:38:37 -0300 Subject: [PATCH 18/60] More fixes --- src/utils.jl | 2 +- src/utils/{sweepline.jl => intersect.jl} | 4 ++-- test/utils.jl | 16 ++++++++-------- 3 files changed, 11 insertions(+), 11 deletions(-) rename src/utils/{sweepline.jl => intersect.jl} (97%) diff --git a/src/utils.jl b/src/utils.jl index 882db44aa..1a74be7cd 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -11,4 +11,4 @@ include("utils/cmp.jl") include("utils/units.jl") include("utils/crs.jl") include("utils/misc.jl") -include("utils/sweepline.jl") +include("utils/intersect.jl") diff --git a/src/utils/sweepline.jl b/src/utils/intersect.jl similarity index 97% rename from src/utils/sweepline.jl rename to src/utils/intersect.jl index c2a3219b4..40bccd980 100644 --- a/src/utils/sweepline.jl +++ b/src/utils/intersect.jl @@ -10,8 +10,8 @@ O(nโ‹…log(n)) time using Bentley-Ottmann sweep line algorithm. ## References -* Bentley, J. L., & Ottmann, T. 1979. [Algorithms for reporting - and counting geometric intersections](https://ieeexplore.ieee.org/document/1675432) +* Bentley & Ottmann 1979. [Algorithms for reporting and counting + geometric intersections](https://ieeexplore.ieee.org/document/1675432) """ function bentleyottmann(segments) # adjust vertices of segments diff --git a/test/utils.jl b/test/utils.jl index 508380dc2..5de828703 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -56,9 +56,8 @@ @inferred Meshes.withcrs(p, c, LatLon) end -@testitem "Pairwise Intersections" setup = [Setup] begin - # test bentley-ottmann algorithm - S = [ +@testitem "Bentley-Ottmann" setup = [Setup] begin + segs = [ Segment(cart(0, 0), cart(1.1, 1.1)), Segment(cart(1, 0), cart(0, 1)), Segment(cart(0, 0), cart(0, 1)), @@ -66,12 +65,13 @@ end Segment(cart(0, 1), cart(1, 1)), Segment(cart(1, 0), cart(1, 1)) ] - points, segs = bentleyottmann(S) - @inferred bentleyottmann(S) + points, seginds = Meshes.bentleyottmann(segs) + + @inferred Meshes.bentleyottmann(segs) @test all(points .โ‰ˆ [cart(0, 0), cart(0, 1), cart(0.5, 0.5), cart(1, 0), cart(1, 1), cart(1.1, 1.1)]) @test length(points) == 6 - @test length(segs) == 6 - @test maximum(reduce(vcat, segs)) == length(S) - @test segs == [[1, 3, 4], [2, 5, 3], [1, 2], [6, 2, 4], [5, 6, 1], [1]] + @test length(seginds) == 6 + @test maximum(reduce(vcat, seginds)) == length(segs) + @test seginds == [[1, 3, 4], [2, 5, 3], [1, 2], [6, 2, 4], [5, 6, 1], [1]] end From 00e2151ea090356ddc7685019cd3de7a69ff4f75 Mon Sep 17 00:00:00 2001 From: souma4 Date: Tue, 11 Mar 2025 16:22:14 -0600 Subject: [PATCH 19/60] changed S = Tuple{P, P} to V and changed the empty vectors of S to call the Vector function of type V --- src/utils/intersect.jl | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/utils/intersect.jl b/src/utils/intersect.jl index 40bccd980..9e6fc4d04 100644 --- a/src/utils/intersect.jl +++ b/src/utils/intersect.jl @@ -24,25 +24,25 @@ function bentleyottmann(segments) s = first(segs) p = minimum(s) P = typeof(p) - S = Tuple{P,P} + V = Tuple{P,P} # initialization ๐’ฌ = BinaryTrees.AVLTree{P}() - ๐’ฏ = BinaryTrees.AVLTree{S}() - โ„’ = Dict{P,Vector{S}}() - ๐’ฐ = Dict{P,Vector{S}}() - ๐’ž = Dict{P,Vector{S}}() - lookup = Dict{S,Int}() + ๐’ฏ = BinaryTrees.AVLTree{V}() + โ„’ = Dict{P,Vector{V}}() + ๐’ฐ = Dict{P,Vector{V}}() + ๐’ž = Dict{P,Vector{V}}() + lookup = Dict{V,Int}() for (i, s) in enumerate(segs) a, b = extrema(s) BinaryTrees.insert!(๐’ฌ, a) BinaryTrees.insert!(๐’ฌ, b) haskey(โ„’, a) ? push!(โ„’[a], (a, b)) : (โ„’[a] = [(a, b)]) haskey(๐’ฐ, b) ? push!(๐’ฐ[b], (a, b)) : (๐’ฐ[b] = [(a, b)]) - haskey(โ„’, b) || (โ„’[b] = S[]) - haskey(๐’ฐ, a) || (๐’ฐ[a] = S[]) - haskey(๐’ž, a) || (๐’ž[a] = S[]) - haskey(๐’ž, b) || (๐’ž[b] = S[]) + haskey(โ„’, b) || (โ„’[b] = Vector{V}()) + haskey(๐’ฐ, a) || (๐’ฐ[a] = Vector{V}()) + haskey(๐’ž, a) || (๐’ž[a] = Vector{V}()) + haskey(๐’ž, b) || (๐’ž[b] = Vector{V}()) lookup[(a, b)] = i end @@ -52,15 +52,15 @@ function bentleyottmann(segments) while !isnothing(BinaryTrees.root(๐’ฌ)) p = BinaryTrees.key(BinaryTrees.minnode(๐’ฌ)) BinaryTrees.delete!(๐’ฌ, p) - _handle!(points, seginds, lookup, p, S, ๐’ฌ, ๐’ฏ, โ„’, ๐’ฐ, ๐’ž) + _handle!(points, seginds, lookup, p, V, ๐’ฌ, ๐’ฏ, โ„’, ๐’ฐ, ๐’ž) end points, seginds end -function _handle!(points, seginds, lookup, p, S, ๐’ฌ, ๐’ฏ, โ„’, ๐’ฐ, ๐’ž) - โ„ฌ = get(โ„’, p, S[]) - โ„ฐ = get(๐’ฐ, p, S[]) - โ„ = get(๐’ž, p, S[]) +function _handle!(points, seginds, lookup, p, V, ๐’ฌ, ๐’ฏ, โ„’, ๐’ฐ, ๐’ž) + โ„ฌ = get(โ„’, p, Vector{V}()) + โ„ฐ = get(๐’ฐ, p, Vector{V}()) + โ„ = get(๐’ž, p, Vector{V}()) _processend!(โ„ฐ, ๐’ฌ, ๐’ฏ, ๐’ž) _processbegin!(โ„ฌ, ๐’ฌ, ๐’ฏ, ๐’ž) _processintersects!(โ„, ๐’ฌ, ๐’ฏ, ๐’ž) From 83093e4070718fc33f76474eb5caf86b9932ab07 Mon Sep 17 00:00:00 2001 From: souma4 Date: Wed, 12 Mar 2025 13:53:21 -0600 Subject: [PATCH 20/60] the s iter variable was modified to segment using the same name, so my guess is it led to type instability. I removed redefinitions of s and just call Segment() as needed. Hopefully this stops s from being converted to an Svector --- src/utils/intersect.jl | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/utils/intersect.jl b/src/utils/intersect.jl index 9e6fc4d04..04eac4155 100644 --- a/src/utils/intersect.jl +++ b/src/utils/intersect.jl @@ -77,7 +77,6 @@ function _processbegin!(โ„ฌ, ๐’ฌ, ๐’ฏ, ๐’ž) end for s in โ„ฌ prev, next = BinaryTrees.prevnext(๐’ฏ, s) - s = Segment(s) if !isnothing(prev) && !isnothing(next) newgeom, newtype = _newevent(Segment(BinaryTrees.key(next)), Segment(BinaryTrees.key(prev))) if _checkintersection(newtype) @@ -86,17 +85,17 @@ function _processbegin!(โ„ฌ, ๐’ฌ, ๐’ฏ, ๐’ž) end end if !isnothing(prev) - newgeom, newtype = _newevent(Segment(BinaryTrees.key(prev)), s) + newgeom, newtype = _newevent(Segment(BinaryTrees.key(prev)), Segment(s)) if _checkintersection(newtype) BinaryTrees.insert!(๐’ฌ, newgeom) - _newintersection!(๐’ž, newgeom, BinaryTrees.key(prev), vertices(s)) + _newintersection!(๐’ž, newgeom, BinaryTrees.key(prev), s) end end if !isnothing(next) - newgeom, newtype = _newevent(s, Segment(BinaryTrees.key(next))) + newgeom, newtype = _newevent(Segment(s), Segment(BinaryTrees.key(next))) if _checkintersection(newtype) BinaryTrees.insert!(๐’ฌ, newgeom) - _newintersection!(๐’ž, newgeom, vertices(s), BinaryTrees.key(next)) + _newintersection!(๐’ž, newgeom, s, BinaryTrees.key(next)) end end end @@ -106,7 +105,6 @@ function _processend!(โ„ฐ, ๐’ฌ, ๐’ฏ, ๐’ž) for s in โ„ฐ prev, next = BinaryTrees.prevnext(๐’ฏ, s) BinaryTrees.delete!(๐’ฏ, s) - s = Segment(s) if !isnothing(prev) && !isnothing(next) newgeom, newtype = _newevent(Segment(BinaryTrees.key(next)), Segment(BinaryTrees.key(prev))) if _checkintersection(newtype) From 76d8be36fa60951429b04f3a53b29c710c8f4048 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 13 Mar 2025 14:56:03 -0300 Subject: [PATCH 21/60] Rename V -> S --- src/utils/intersect.jl | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/utils/intersect.jl b/src/utils/intersect.jl index 04eac4155..200ee69af 100644 --- a/src/utils/intersect.jl +++ b/src/utils/intersect.jl @@ -24,25 +24,25 @@ function bentleyottmann(segments) s = first(segs) p = minimum(s) P = typeof(p) - V = Tuple{P,P} + S = Tuple{P,P} # initialization ๐’ฌ = BinaryTrees.AVLTree{P}() - ๐’ฏ = BinaryTrees.AVLTree{V}() - โ„’ = Dict{P,Vector{V}}() - ๐’ฐ = Dict{P,Vector{V}}() - ๐’ž = Dict{P,Vector{V}}() - lookup = Dict{V,Int}() + ๐’ฏ = BinaryTrees.AVLTree{S}() + โ„’ = Dict{P,Vector{S}}() + ๐’ฐ = Dict{P,Vector{S}}() + ๐’ž = Dict{P,Vector{S}}() + lookup = Dict{S,Int}() for (i, s) in enumerate(segs) a, b = extrema(s) BinaryTrees.insert!(๐’ฌ, a) BinaryTrees.insert!(๐’ฌ, b) haskey(โ„’, a) ? push!(โ„’[a], (a, b)) : (โ„’[a] = [(a, b)]) haskey(๐’ฐ, b) ? push!(๐’ฐ[b], (a, b)) : (๐’ฐ[b] = [(a, b)]) - haskey(โ„’, b) || (โ„’[b] = Vector{V}()) - haskey(๐’ฐ, a) || (๐’ฐ[a] = Vector{V}()) - haskey(๐’ž, a) || (๐’ž[a] = Vector{V}()) - haskey(๐’ž, b) || (๐’ž[b] = Vector{V}()) + haskey(โ„’, b) || (โ„’[b] = Vector{S}()) + haskey(๐’ฐ, a) || (๐’ฐ[a] = Vector{S}()) + haskey(๐’ž, a) || (๐’ž[a] = Vector{S}()) + haskey(๐’ž, b) || (๐’ž[b] = Vector{S}()) lookup[(a, b)] = i end @@ -52,15 +52,15 @@ function bentleyottmann(segments) while !isnothing(BinaryTrees.root(๐’ฌ)) p = BinaryTrees.key(BinaryTrees.minnode(๐’ฌ)) BinaryTrees.delete!(๐’ฌ, p) - _handle!(points, seginds, lookup, p, V, ๐’ฌ, ๐’ฏ, โ„’, ๐’ฐ, ๐’ž) + _handle!(points, seginds, lookup, p, S, ๐’ฌ, ๐’ฏ, โ„’, ๐’ฐ, ๐’ž) end points, seginds end -function _handle!(points, seginds, lookup, p, V, ๐’ฌ, ๐’ฏ, โ„’, ๐’ฐ, ๐’ž) - โ„ฌ = get(โ„’, p, Vector{V}()) - โ„ฐ = get(๐’ฐ, p, Vector{V}()) - โ„ = get(๐’ž, p, Vector{V}()) +function _handle!(points, seginds, lookup, p, S, ๐’ฌ, ๐’ฏ, โ„’, ๐’ฐ, ๐’ž) + โ„ฌ = get(โ„’, p, Vector{S}()) + โ„ฐ = get(๐’ฐ, p, Vector{S}()) + โ„ = get(๐’ž, p, Vector{S}()) _processend!(โ„ฐ, ๐’ฌ, ๐’ฏ, ๐’ž) _processbegin!(โ„ฌ, ๐’ฌ, ๐’ฏ, ๐’ž) _processintersects!(โ„, ๐’ฌ, ๐’ฏ, ๐’ž) From 1e36abe9fe49f9b60278bd511ca5efc0f9d43f05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 13 Mar 2025 15:02:20 -0300 Subject: [PATCH 22/60] More refactoring --- src/utils/intersect.jl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/utils/intersect.jl b/src/utils/intersect.jl index 200ee69af..236799be0 100644 --- a/src/utils/intersect.jl +++ b/src/utils/intersect.jl @@ -39,10 +39,10 @@ function bentleyottmann(segments) BinaryTrees.insert!(๐’ฌ, b) haskey(โ„’, a) ? push!(โ„’[a], (a, b)) : (โ„’[a] = [(a, b)]) haskey(๐’ฐ, b) ? push!(๐’ฐ[b], (a, b)) : (๐’ฐ[b] = [(a, b)]) - haskey(โ„’, b) || (โ„’[b] = Vector{S}()) - haskey(๐’ฐ, a) || (๐’ฐ[a] = Vector{S}()) - haskey(๐’ž, a) || (๐’ž[a] = Vector{S}()) - haskey(๐’ž, b) || (๐’ž[b] = Vector{S}()) + haskey(โ„’, b) || (โ„’[b] = S[]) + haskey(๐’ฐ, a) || (๐’ฐ[a] = S[]) + haskey(๐’ž, a) || (๐’ž[a] = S[]) + haskey(๐’ž, b) || (๐’ž[b] = S[]) lookup[(a, b)] = i end @@ -58,9 +58,9 @@ function bentleyottmann(segments) end function _handle!(points, seginds, lookup, p, S, ๐’ฌ, ๐’ฏ, โ„’, ๐’ฐ, ๐’ž) - โ„ฌ = get(โ„’, p, Vector{S}()) - โ„ฐ = get(๐’ฐ, p, Vector{S}()) - โ„ = get(๐’ž, p, Vector{S}()) + โ„ฌ = get(โ„’, p, S[]) + โ„ฐ = get(๐’ฐ, p, S[]) + โ„ = get(๐’ž, p, S[]) _processend!(โ„ฐ, ๐’ฌ, ๐’ฏ, ๐’ž) _processbegin!(โ„ฌ, ๐’ฌ, ๐’ฏ, ๐’ž) _processintersects!(โ„, ๐’ฌ, ๐’ฏ, ๐’ž) From d590d36aef02edf416bf81d3e3f32253ca5464c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 14 Mar 2025 10:43:49 -0300 Subject: [PATCH 23/60] Rename \scrT to \scrR to match paper notation, and avoid confusion with \scrI --- src/utils/intersect.jl | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/utils/intersect.jl b/src/utils/intersect.jl index 236799be0..b9fdb6562 100644 --- a/src/utils/intersect.jl +++ b/src/utils/intersect.jl @@ -20,7 +20,7 @@ function bentleyottmann(segments) a > b ? reverse(s) : s end - # retrieve relevant info + # retrieve types s = first(segs) p = minimum(s) P = typeof(p) @@ -28,7 +28,7 @@ function bentleyottmann(segments) # initialization ๐’ฌ = BinaryTrees.AVLTree{P}() - ๐’ฏ = BinaryTrees.AVLTree{S}() + โ„› = BinaryTrees.AVLTree{S}() โ„’ = Dict{P,Vector{S}}() ๐’ฐ = Dict{P,Vector{S}}() ๐’ž = Dict{P,Vector{S}}() @@ -52,18 +52,18 @@ function bentleyottmann(segments) while !isnothing(BinaryTrees.root(๐’ฌ)) p = BinaryTrees.key(BinaryTrees.minnode(๐’ฌ)) BinaryTrees.delete!(๐’ฌ, p) - _handle!(points, seginds, lookup, p, S, ๐’ฌ, ๐’ฏ, โ„’, ๐’ฐ, ๐’ž) + _handle!(points, seginds, lookup, p, S, ๐’ฌ, โ„›, โ„’, ๐’ฐ, ๐’ž) end points, seginds end -function _handle!(points, seginds, lookup, p, S, ๐’ฌ, ๐’ฏ, โ„’, ๐’ฐ, ๐’ž) +function _handle!(points, seginds, lookup, p, S, ๐’ฌ, โ„›, โ„’, ๐’ฐ, ๐’ž) โ„ฌ = get(โ„’, p, S[]) โ„ฐ = get(๐’ฐ, p, S[]) โ„ = get(๐’ž, p, S[]) - _processend!(โ„ฐ, ๐’ฌ, ๐’ฏ, ๐’ž) - _processbegin!(โ„ฌ, ๐’ฌ, ๐’ฏ, ๐’ž) - _processintersects!(โ„, ๐’ฌ, ๐’ฏ, ๐’ž) + _processend!(โ„ฐ, ๐’ฌ, โ„›, ๐’ž) + _processbegin!(โ„ฌ, ๐’ฌ, โ„›, ๐’ž) + _processintersects!(โ„, ๐’ฌ, โ„›, ๐’ž) segs = โ„ฌ โˆช โ„ฐ โˆช โ„ if !isempty(segs) push!(points, p) @@ -71,12 +71,12 @@ function _handle!(points, seginds, lookup, p, S, ๐’ฌ, ๐’ฏ, โ„’, ๐’ฐ, ๐’ž) end end -function _processbegin!(โ„ฌ, ๐’ฌ, ๐’ฏ, ๐’ž) +function _processbegin!(โ„ฌ, ๐’ฌ, โ„›, ๐’ž) for s in โ„ฌ - BinaryTrees.insert!(๐’ฏ, s) + BinaryTrees.insert!(โ„›, s) end for s in โ„ฌ - prev, next = BinaryTrees.prevnext(๐’ฏ, s) + prev, next = BinaryTrees.prevnext(โ„›, s) if !isnothing(prev) && !isnothing(next) newgeom, newtype = _newevent(Segment(BinaryTrees.key(next)), Segment(BinaryTrees.key(prev))) if _checkintersection(newtype) @@ -101,10 +101,10 @@ function _processbegin!(โ„ฌ, ๐’ฌ, ๐’ฏ, ๐’ž) end end -function _processend!(โ„ฐ, ๐’ฌ, ๐’ฏ, ๐’ž) +function _processend!(โ„ฐ, ๐’ฌ, โ„›, ๐’ž) for s in โ„ฐ - prev, next = BinaryTrees.prevnext(๐’ฏ, s) - BinaryTrees.delete!(๐’ฏ, s) + prev, next = BinaryTrees.prevnext(โ„›, s) + BinaryTrees.delete!(โ„›, s) if !isnothing(prev) && !isnothing(next) newgeom, newtype = _newevent(Segment(BinaryTrees.key(next)), Segment(BinaryTrees.key(prev))) if _checkintersection(newtype) @@ -115,14 +115,14 @@ function _processend!(โ„ฐ, ๐’ฌ, ๐’ฏ, ๐’ž) end end -function _processintersects!(โ„, ๐’ฌ, ๐’ฏ, ๐’ž) +function _processintersects!(โ„, ๐’ฌ, โ„›, ๐’ž) for s in โ„ - prev, _ = BinaryTrees.prevnext(๐’ฏ, s) + prev, _ = BinaryTrees.prevnext(โ„›, s) if !isnothing(prev) # find segments r and u - r, _ = BinaryTrees.prevnext(๐’ฏ, s) - _, u = BinaryTrees.prevnext(๐’ฏ, BinaryTrees.key(prev)) + r, _ = BinaryTrees.prevnext(โ„›, s) + _, u = BinaryTrees.prevnext(โ„›, BinaryTrees.key(prev)) # remove crossing points rs and tu from event queue if !isnothing(r) From 93ce4a6d6db123ed7bcf542452e5362348c78a08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 14 Mar 2025 10:52:37 -0300 Subject: [PATCH 24/60] Refactor _newevent to improve readability --- src/utils/intersect.jl | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/utils/intersect.jl b/src/utils/intersect.jl index b9fdb6562..7ad2981a0 100644 --- a/src/utils/intersect.jl +++ b/src/utils/intersect.jl @@ -78,21 +78,21 @@ function _processbegin!(โ„ฌ, ๐’ฌ, โ„›, ๐’ž) for s in โ„ฌ prev, next = BinaryTrees.prevnext(โ„›, s) if !isnothing(prev) && !isnothing(next) - newgeom, newtype = _newevent(Segment(BinaryTrees.key(next)), Segment(BinaryTrees.key(prev))) + newgeom, newtype = _newevent(BinaryTrees.key(next), BinaryTrees.key(prev)) if _checkintersection(newtype) BinaryTrees.insert!(๐’ฌ, newgeom) _newintersection!(๐’ž, newgeom, BinaryTrees.key(next), BinaryTrees.key(prev)) end end if !isnothing(prev) - newgeom, newtype = _newevent(Segment(BinaryTrees.key(prev)), Segment(s)) + newgeom, newtype = _newevent(BinaryTrees.key(prev), s) if _checkintersection(newtype) BinaryTrees.insert!(๐’ฌ, newgeom) _newintersection!(๐’ž, newgeom, BinaryTrees.key(prev), s) end end if !isnothing(next) - newgeom, newtype = _newevent(Segment(s), Segment(BinaryTrees.key(next))) + newgeom, newtype = _newevent(s, BinaryTrees.key(next)) if _checkintersection(newtype) BinaryTrees.insert!(๐’ฌ, newgeom) _newintersection!(๐’ž, newgeom, s, BinaryTrees.key(next)) @@ -106,7 +106,7 @@ function _processend!(โ„ฐ, ๐’ฌ, โ„›, ๐’ž) prev, next = BinaryTrees.prevnext(โ„›, s) BinaryTrees.delete!(โ„›, s) if !isnothing(prev) && !isnothing(next) - newgeom, newtype = _newevent(Segment(BinaryTrees.key(next)), Segment(BinaryTrees.key(prev))) + newgeom, newtype = _newevent(BinaryTrees.key(next), BinaryTrees.key(prev)) if _checkintersection(newtype) BinaryTrees.insert!(๐’ฌ, newgeom) _newintersection!(๐’ž, newgeom, BinaryTrees.key(next), BinaryTrees.key(prev)) @@ -126,13 +126,13 @@ function _processintersects!(โ„, ๐’ฌ, โ„›, ๐’ž) # remove crossing points rs and tu from event queue if !isnothing(r) - newgeom, newtype = _newevent(Segment(BinaryTrees.key(r)), Segment(s)) + newgeom, newtype = _newevent(BinaryTrees.key(r), s) if _checkintersection(newtype) BinaryTrees.delete!(๐’ฌ, newgeom) end end if !isnothing(u) - newgeom, newtype = _newevent(Segment(BinaryTrees.key(u)), Segment(BinaryTrees.key(prev))) + newgeom, newtype = _newevent(BinaryTrees.key(u), BinaryTrees.key(prev)) if _checkintersection(newtype) BinaryTrees.delete!(๐’ฌ, newgeom) end @@ -140,14 +140,14 @@ function _processintersects!(โ„, ๐’ฌ, โ„›, ๐’ž) # add crossing points rt and su to event queue if !isnothing(r) - newgeom, newtype = _newevent(Segment(BinaryTrees.key(r)), Segment(BinaryTrees.key(prev))) + newgeom, newtype = _newevent(BinaryTrees.key(r), BinaryTrees.key(prev)) if _checkintersection(newtype) BinaryTrees.insert!(๐’ฌ, newgeom) _newintersection!(๐’ž, newgeom, BinaryTrees.key(r), BinaryTrees.key(prev)) end end if !isnothing(u) - newgeom, newtype = _newevent(Segment(BinaryTrees.key(u)), Segment(s)) + newgeom, newtype = _newevent(BinaryTrees.key(u), s) if _checkintersection(newtype) BinaryTrees.insert!(๐’ฌ, newgeom) _newintersection!(๐’ž, newgeom, BinaryTrees.key(u), s) @@ -159,8 +159,8 @@ end _pushintersection(lookup, segments) = unique(lookup[segment] for segment in segments) -function _newevent(sโ‚::Segment, sโ‚‚::Segment) - newevent = intersection(sโ‚, sโ‚‚) +function _newevent((aโ‚, bโ‚), (aโ‚‚, bโ‚‚)) + newevent = intersection(Segment(aโ‚, bโ‚), Segment(aโ‚‚, bโ‚‚)) if !isnothing(newevent) get(newevent), type(newevent) else From 9fdaf93fe4d039f758f6e359ef590e3ad62e51d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 14 Mar 2025 11:03:16 -0300 Subject: [PATCH 25/60] More refactoring to improve readability --- src/utils/intersect.jl | 69 +++++++++++++++++++----------------------- 1 file changed, 31 insertions(+), 38 deletions(-) diff --git a/src/utils/intersect.jl b/src/utils/intersect.jl index 7ad2981a0..957d9b57c 100644 --- a/src/utils/intersect.jl +++ b/src/utils/intersect.jl @@ -78,24 +78,24 @@ function _processbegin!(โ„ฌ, ๐’ฌ, โ„›, ๐’ž) for s in โ„ฌ prev, next = BinaryTrees.prevnext(โ„›, s) if !isnothing(prev) && !isnothing(next) - newgeom, newtype = _newevent(BinaryTrees.key(next), BinaryTrees.key(prev)) - if _checkintersection(newtype) - BinaryTrees.insert!(๐’ฌ, newgeom) - _newintersection!(๐’ž, newgeom, BinaryTrees.key(next), BinaryTrees.key(prev)) + event = _newevent(BinaryTrees.key(next), BinaryTrees.key(prev)) + if _checkintersection(type(event)) + BinaryTrees.insert!(๐’ฌ, get(event)) + _newintersection!(๐’ž, get(event), BinaryTrees.key(next), BinaryTrees.key(prev)) end end if !isnothing(prev) - newgeom, newtype = _newevent(BinaryTrees.key(prev), s) - if _checkintersection(newtype) - BinaryTrees.insert!(๐’ฌ, newgeom) - _newintersection!(๐’ž, newgeom, BinaryTrees.key(prev), s) + event = _newevent(BinaryTrees.key(prev), s) + if _checkintersection(type(event)) + BinaryTrees.insert!(๐’ฌ, get(event)) + _newintersection!(๐’ž, get(event), BinaryTrees.key(prev), s) end end if !isnothing(next) - newgeom, newtype = _newevent(s, BinaryTrees.key(next)) - if _checkintersection(newtype) - BinaryTrees.insert!(๐’ฌ, newgeom) - _newintersection!(๐’ž, newgeom, s, BinaryTrees.key(next)) + event = _newevent(s, BinaryTrees.key(next)) + if _checkintersection(type(event)) + BinaryTrees.insert!(๐’ฌ, get(event)) + _newintersection!(๐’ž, get(event), s, BinaryTrees.key(next)) end end end @@ -106,10 +106,10 @@ function _processend!(โ„ฐ, ๐’ฌ, โ„›, ๐’ž) prev, next = BinaryTrees.prevnext(โ„›, s) BinaryTrees.delete!(โ„›, s) if !isnothing(prev) && !isnothing(next) - newgeom, newtype = _newevent(BinaryTrees.key(next), BinaryTrees.key(prev)) - if _checkintersection(newtype) - BinaryTrees.insert!(๐’ฌ, newgeom) - _newintersection!(๐’ž, newgeom, BinaryTrees.key(next), BinaryTrees.key(prev)) + event = _newevent(BinaryTrees.key(next), BinaryTrees.key(prev)) + if _checkintersection(type(event)) + BinaryTrees.insert!(๐’ฌ, get(event)) + _newintersection!(๐’ž, get(event), BinaryTrees.key(next), BinaryTrees.key(prev)) end end end @@ -126,31 +126,31 @@ function _processintersects!(โ„, ๐’ฌ, โ„›, ๐’ž) # remove crossing points rs and tu from event queue if !isnothing(r) - newgeom, newtype = _newevent(BinaryTrees.key(r), s) - if _checkintersection(newtype) - BinaryTrees.delete!(๐’ฌ, newgeom) + event = _newevent(BinaryTrees.key(r), s) + if _checkintersection(type(event)) + BinaryTrees.delete!(๐’ฌ, get(event)) end end if !isnothing(u) - newgeom, newtype = _newevent(BinaryTrees.key(u), BinaryTrees.key(prev)) - if _checkintersection(newtype) - BinaryTrees.delete!(๐’ฌ, newgeom) + event = _newevent(BinaryTrees.key(u), BinaryTrees.key(prev)) + if _checkintersection(type(event)) + BinaryTrees.delete!(๐’ฌ, get(event)) end end # add crossing points rt and su to event queue if !isnothing(r) - newgeom, newtype = _newevent(BinaryTrees.key(r), BinaryTrees.key(prev)) - if _checkintersection(newtype) - BinaryTrees.insert!(๐’ฌ, newgeom) - _newintersection!(๐’ž, newgeom, BinaryTrees.key(r), BinaryTrees.key(prev)) + event = _newevent(BinaryTrees.key(r), BinaryTrees.key(prev)) + if _checkintersection(type(event)) + BinaryTrees.insert!(๐’ฌ, get(event)) + _newintersection!(๐’ž, get(event), BinaryTrees.key(r), BinaryTrees.key(prev)) end end if !isnothing(u) - newgeom, newtype = _newevent(BinaryTrees.key(u), s) - if _checkintersection(newtype) - BinaryTrees.insert!(๐’ฌ, newgeom) - _newintersection!(๐’ž, newgeom, BinaryTrees.key(u), s) + event = _newevent(BinaryTrees.key(u), s) + if _checkintersection(type(event)) + BinaryTrees.insert!(๐’ฌ, get(event)) + _newintersection!(๐’ž, get(event), BinaryTrees.key(u), s) end end end @@ -159,14 +159,7 @@ end _pushintersection(lookup, segments) = unique(lookup[segment] for segment in segments) -function _newevent((aโ‚, bโ‚), (aโ‚‚, bโ‚‚)) - newevent = intersection(Segment(aโ‚, bโ‚), Segment(aโ‚‚, bโ‚‚)) - if !isnothing(newevent) - get(newevent), type(newevent) - else - nothing, nothing - end -end +_newevent((aโ‚, bโ‚), (aโ‚‚, bโ‚‚)) = intersection(Segment(aโ‚, bโ‚), Segment(aโ‚‚, bโ‚‚)) _checkintersection(type) = type == Crossing || type == EdgeTouching From 353cf70dcd702d3242e960ac846b32b9295c49f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 14 Mar 2025 11:24:32 -0300 Subject: [PATCH 26/60] More refactoring to improve readability and performance --- src/utils/intersect.jl | 60 ++++++++++++++++-------------------------- 1 file changed, 22 insertions(+), 38 deletions(-) diff --git a/src/utils/intersect.jl b/src/utils/intersect.jl index 957d9b57c..6d260f571 100644 --- a/src/utils/intersect.jl +++ b/src/utils/intersect.jl @@ -78,25 +78,13 @@ function _processbegin!(โ„ฌ, ๐’ฌ, โ„›, ๐’ž) for s in โ„ฌ prev, next = BinaryTrees.prevnext(โ„›, s) if !isnothing(prev) && !isnothing(next) - event = _newevent(BinaryTrees.key(next), BinaryTrees.key(prev)) - if _checkintersection(type(event)) - BinaryTrees.insert!(๐’ฌ, get(event)) - _newintersection!(๐’ž, get(event), BinaryTrees.key(next), BinaryTrees.key(prev)) - end + _newevent!(๐’ฌ, ๐’ž, BinaryTrees.key(next), BinaryTrees.key(prev)) end if !isnothing(prev) - event = _newevent(BinaryTrees.key(prev), s) - if _checkintersection(type(event)) - BinaryTrees.insert!(๐’ฌ, get(event)) - _newintersection!(๐’ž, get(event), BinaryTrees.key(prev), s) - end + _newevent!(๐’ฌ, ๐’ž, BinaryTrees.key(prev), s) end if !isnothing(next) - event = _newevent(s, BinaryTrees.key(next)) - if _checkintersection(type(event)) - BinaryTrees.insert!(๐’ฌ, get(event)) - _newintersection!(๐’ž, get(event), s, BinaryTrees.key(next)) - end + _newevent!(๐’ฌ, ๐’ž, s, BinaryTrees.key(next)) end end end @@ -104,14 +92,10 @@ end function _processend!(โ„ฐ, ๐’ฌ, โ„›, ๐’ž) for s in โ„ฐ prev, next = BinaryTrees.prevnext(โ„›, s) - BinaryTrees.delete!(โ„›, s) if !isnothing(prev) && !isnothing(next) - event = _newevent(BinaryTrees.key(next), BinaryTrees.key(prev)) - if _checkintersection(type(event)) - BinaryTrees.insert!(๐’ฌ, get(event)) - _newintersection!(๐’ž, get(event), BinaryTrees.key(next), BinaryTrees.key(prev)) - end + _newevent!(๐’ฌ, ๐’ž, BinaryTrees.key(next), BinaryTrees.key(prev)) end + BinaryTrees.delete!(โ„›, s) end end @@ -140,18 +124,10 @@ function _processintersects!(โ„, ๐’ฌ, โ„›, ๐’ž) # add crossing points rt and su to event queue if !isnothing(r) - event = _newevent(BinaryTrees.key(r), BinaryTrees.key(prev)) - if _checkintersection(type(event)) - BinaryTrees.insert!(๐’ฌ, get(event)) - _newintersection!(๐’ž, get(event), BinaryTrees.key(r), BinaryTrees.key(prev)) - end + _newevent!(๐’ฌ, ๐’ž, BinaryTrees.key(r), BinaryTrees.key(prev)) end if !isnothing(u) - event = _newevent(BinaryTrees.key(u), s) - if _checkintersection(type(event)) - BinaryTrees.insert!(๐’ฌ, get(event)) - _newintersection!(๐’ž, get(event), BinaryTrees.key(u), s) - end + _newevent!(๐’ฌ, ๐’ž, BinaryTrees.key(u), s) end end end @@ -161,12 +137,20 @@ _pushintersection(lookup, segments) = unique(lookup[segment] for segment in segm _newevent((aโ‚, bโ‚), (aโ‚‚, bโ‚‚)) = intersection(Segment(aโ‚, bโ‚), Segment(aโ‚‚, bโ‚‚)) -_checkintersection(type) = type == Crossing || type == EdgeTouching - -function _newintersection!(๐’ž, newgeom, segโ‚, segโ‚‚) - if haskey(๐’ž, newgeom) - push!(๐’ž[newgeom], segโ‚, segโ‚‚) - else - ๐’ž[newgeom] = [segโ‚, segโ‚‚] +function _newevent!(๐’ฌ, ๐’ž, (aโ‚, bโ‚), (aโ‚‚, bโ‚‚)) + segโ‚ = Segment(aโ‚, bโ‚) + segโ‚‚ = Segment(aโ‚‚, bโ‚‚) + intersection(segโ‚, segโ‚‚) do I + if type(I) == Crossing || type(I) == EdgeTouching + p = get(I) + BinaryTrees.insert!(๐’ฌ, p) + if haskey(๐’ž, p) + push!(๐’ž[p], (aโ‚, bโ‚), (aโ‚‚, bโ‚‚)) + else + ๐’ž[p] = [(aโ‚, bโ‚), (aโ‚‚, bโ‚‚)] + end + end end end + +_checkintersection(type) = type == Crossing || type == EdgeTouching From d136b6e3a8a7dfa43a62a78ac0df8d3542422474 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 14 Mar 2025 11:30:35 -0300 Subject: [PATCH 27/60] More refactoring to improve readability --- src/utils/intersect.jl | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/utils/intersect.jl b/src/utils/intersect.jl index 6d260f571..e0c821f6c 100644 --- a/src/utils/intersect.jl +++ b/src/utils/intersect.jl @@ -103,23 +103,16 @@ function _processintersects!(โ„, ๐’ฌ, โ„›, ๐’ž) for s in โ„ prev, _ = BinaryTrees.prevnext(โ„›, s) if !isnothing(prev) - # find segments r and u r, _ = BinaryTrees.prevnext(โ„›, s) _, u = BinaryTrees.prevnext(โ„›, BinaryTrees.key(prev)) # remove crossing points rs and tu from event queue if !isnothing(r) - event = _newevent(BinaryTrees.key(r), s) - if _checkintersection(type(event)) - BinaryTrees.delete!(๐’ฌ, get(event)) - end + _rmevent!(๐’ฌ, BinaryTrees.key(r), s) end if !isnothing(u) - event = _newevent(BinaryTrees.key(u), BinaryTrees.key(prev)) - if _checkintersection(type(event)) - BinaryTrees.delete!(๐’ฌ, get(event)) - end + _rmevent!(๐’ฌ, BinaryTrees.key(u), BinaryTrees.key(prev)) end # add crossing points rt and su to event queue @@ -135,12 +128,8 @@ end _pushintersection(lookup, segments) = unique(lookup[segment] for segment in segments) -_newevent((aโ‚, bโ‚), (aโ‚‚, bโ‚‚)) = intersection(Segment(aโ‚, bโ‚), Segment(aโ‚‚, bโ‚‚)) - function _newevent!(๐’ฌ, ๐’ž, (aโ‚, bโ‚), (aโ‚‚, bโ‚‚)) - segโ‚ = Segment(aโ‚, bโ‚) - segโ‚‚ = Segment(aโ‚‚, bโ‚‚) - intersection(segโ‚, segโ‚‚) do I + intersection(Segment(aโ‚, bโ‚), Segment(aโ‚‚, bโ‚‚)) do I if type(I) == Crossing || type(I) == EdgeTouching p = get(I) BinaryTrees.insert!(๐’ฌ, p) @@ -150,7 +139,15 @@ function _newevent!(๐’ฌ, ๐’ž, (aโ‚, bโ‚), (aโ‚‚, bโ‚‚)) ๐’ž[p] = [(aโ‚, bโ‚), (aโ‚‚, bโ‚‚)] end end + nothing end end -_checkintersection(type) = type == Crossing || type == EdgeTouching +function _rmevent!(๐’ฌ, (aโ‚, bโ‚), (aโ‚‚, bโ‚‚)) + intersection(Segment(aโ‚, bโ‚), Segment(aโ‚‚, bโ‚‚)) do I + if type(I) == Crossing || type(I) == EdgeTouching + BinaryTrees.delete!(๐’ฌ, get(I)) + end + nothing + end +end From 602a5799c4613a013e26ca4542e430050d2c5119 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 14 Mar 2025 11:50:25 -0300 Subject: [PATCH 28/60] More refactoring --- src/utils/intersect.jl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/utils/intersect.jl b/src/utils/intersect.jl index e0c821f6c..65d0765e9 100644 --- a/src/utils/intersect.jl +++ b/src/utils/intersect.jl @@ -65,9 +65,10 @@ function _handle!(points, seginds, lookup, p, S, ๐’ฌ, โ„›, โ„’, ๐’ฐ, ๐’ž) _processbegin!(โ„ฌ, ๐’ฌ, โ„›, ๐’ž) _processintersects!(โ„, ๐’ฌ, โ„›, ๐’ž) segs = โ„ฌ โˆช โ„ฐ โˆช โ„ + inds = [lookup[s] for s in segs] if !isempty(segs) push!(points, p) - push!(seginds, _pushintersection(lookup, segs)) + push!(seginds, unique(inds)) end end @@ -126,8 +127,6 @@ function _processintersects!(โ„, ๐’ฌ, โ„›, ๐’ž) end end -_pushintersection(lookup, segments) = unique(lookup[segment] for segment in segments) - function _newevent!(๐’ฌ, ๐’ž, (aโ‚, bโ‚), (aโ‚‚, bโ‚‚)) intersection(Segment(aโ‚, bโ‚), Segment(aโ‚‚, bโ‚‚)) do I if type(I) == Crossing || type(I) == EdgeTouching From 36e249729a72739e66d63ce00075c9b06f1e9fd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 14 Mar 2025 12:41:48 -0300 Subject: [PATCH 29/60] More refactoring --- src/utils/intersect.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/intersect.jl b/src/utils/intersect.jl index 65d0765e9..6d6e0fb65 100644 --- a/src/utils/intersect.jl +++ b/src/utils/intersect.jl @@ -68,7 +68,7 @@ function _handle!(points, seginds, lookup, p, S, ๐’ฌ, โ„›, โ„’, ๐’ฐ, ๐’ž) inds = [lookup[s] for s in segs] if !isempty(segs) push!(points, p) - push!(seginds, unique(inds)) + push!(seginds, inds) end end From f5ff53e57ff27921090a15b96d292615935b19fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 14 Mar 2025 12:48:15 -0300 Subject: [PATCH 30/60] More refactoring --- src/utils/intersect.jl | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/utils/intersect.jl b/src/utils/intersect.jl index 6d6e0fb65..d9d2fee13 100644 --- a/src/utils/intersect.jl +++ b/src/utils/intersect.jl @@ -39,10 +39,6 @@ function bentleyottmann(segments) BinaryTrees.insert!(๐’ฌ, b) haskey(โ„’, a) ? push!(โ„’[a], (a, b)) : (โ„’[a] = [(a, b)]) haskey(๐’ฐ, b) ? push!(๐’ฐ[b], (a, b)) : (๐’ฐ[b] = [(a, b)]) - haskey(โ„’, b) || (โ„’[b] = S[]) - haskey(๐’ฐ, a) || (๐’ฐ[a] = S[]) - haskey(๐’ž, a) || (๐’ž[a] = S[]) - haskey(๐’ž, b) || (๐’ž[b] = S[]) lookup[(a, b)] = i end From 0004c36e23071d7683ddc99d115d3b8379db6ca1 Mon Sep 17 00:00:00 2001 From: souma4 Date: Fri, 14 Mar 2025 10:43:11 -0600 Subject: [PATCH 31/60] realigned more with original algorithm and reduced recomputation --- src/utils/intersect.jl | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/utils/intersect.jl b/src/utils/intersect.jl index d9d2fee13..65d64b572 100644 --- a/src/utils/intersect.jl +++ b/src/utils/intersect.jl @@ -74,9 +74,7 @@ function _processbegin!(โ„ฌ, ๐’ฌ, โ„›, ๐’ž) end for s in โ„ฌ prev, next = BinaryTrees.prevnext(โ„›, s) - if !isnothing(prev) && !isnothing(next) - _newevent!(๐’ฌ, ๐’ž, BinaryTrees.key(next), BinaryTrees.key(prev)) - end + if !isnothing(prev) _newevent!(๐’ฌ, ๐’ž, BinaryTrees.key(prev), s) end @@ -101,7 +99,7 @@ function _processintersects!(โ„, ๐’ฌ, โ„›, ๐’ž) prev, _ = BinaryTrees.prevnext(โ„›, s) if !isnothing(prev) # find segments r and u - r, _ = BinaryTrees.prevnext(โ„›, s) + r = prev _, u = BinaryTrees.prevnext(โ„›, BinaryTrees.key(prev)) # remove crossing points rs and tu from event queue From dd8881348cf4c9b8ca473273ea350cd2605e8188 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 14 Mar 2025 14:30:18 -0300 Subject: [PATCH 32/60] More refactoring --- src/utils/intersect.jl | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/src/utils/intersect.jl b/src/utils/intersect.jl index 65d64b572..0843661f8 100644 --- a/src/utils/intersect.jl +++ b/src/utils/intersect.jl @@ -14,16 +14,14 @@ O(nโ‹…log(n)) time using Bentley-Ottmann sweep line algorithm. geometric intersections](https://ieeexplore.ieee.org/document/1675432) """ function bentleyottmann(segments) - # adjust vertices of segments + # orient segments segs = map(segments) do s a, b = extrema(s) - a > b ? reverse(s) : s + a > b ? (b, a) : (a, b) end # retrieve types - s = first(segs) - p = minimum(s) - P = typeof(p) + P = eltype(first(segs)) S = Tuple{P,P} # initialization @@ -33,8 +31,7 @@ function bentleyottmann(segments) ๐’ฐ = Dict{P,Vector{S}}() ๐’ž = Dict{P,Vector{S}}() lookup = Dict{S,Int}() - for (i, s) in enumerate(segs) - a, b = extrema(s) + for (i, (a, b)) in enumerate(segs) BinaryTrees.insert!(๐’ฌ, a) BinaryTrees.insert!(๐’ฌ, b) haskey(โ„’, a) ? push!(โ„’[a], (a, b)) : (โ„’[a] = [(a, b)]) @@ -45,7 +42,7 @@ function bentleyottmann(segments) # sweep line points = Vector{P}() seginds = Vector{Vector{Int}}() - while !isnothing(BinaryTrees.root(๐’ฌ)) + while !_isempty(๐’ฌ) p = BinaryTrees.key(BinaryTrees.minnode(๐’ฌ)) BinaryTrees.delete!(๐’ฌ, p) _handle!(points, seginds, lookup, p, S, ๐’ฌ, โ„›, โ„’, ๐’ฐ, ๐’ž) @@ -56,25 +53,23 @@ end function _handle!(points, seginds, lookup, p, S, ๐’ฌ, โ„›, โ„’, ๐’ฐ, ๐’ž) โ„ฌ = get(โ„’, p, S[]) โ„ฐ = get(๐’ฐ, p, S[]) - โ„ = get(๐’ž, p, S[]) + โ„ณ = get(๐’ž, p, S[]) _processend!(โ„ฐ, ๐’ฌ, โ„›, ๐’ž) - _processbegin!(โ„ฌ, ๐’ฌ, โ„›, ๐’ž) - _processintersects!(โ„, ๐’ฌ, โ„›, ๐’ž) - segs = โ„ฌ โˆช โ„ฐ โˆช โ„ - inds = [lookup[s] for s in segs] - if !isempty(segs) + _processbeg!(โ„ฌ, ๐’ฌ, โ„›, ๐’ž) + _processmid!(โ„ณ, ๐’ฌ, โ„›, ๐’ž) + inds = [lookup[s] for s in โ„ฌ โˆช โ„ฐ โˆช โ„ณ] + if !isempty(inds) push!(points, p) push!(seginds, inds) end end -function _processbegin!(โ„ฌ, ๐’ฌ, โ„›, ๐’ž) +function _processbeg!(โ„ฌ, ๐’ฌ, โ„›, ๐’ž) for s in โ„ฌ BinaryTrees.insert!(โ„›, s) end for s in โ„ฌ prev, next = BinaryTrees.prevnext(โ„›, s) - if !isnothing(prev) _newevent!(๐’ฌ, ๐’ž, BinaryTrees.key(prev), s) end @@ -94,8 +89,8 @@ function _processend!(โ„ฐ, ๐’ฌ, โ„›, ๐’ž) end end -function _processintersects!(โ„, ๐’ฌ, โ„›, ๐’ž) - for s in โ„ +function _processmid!(โ„ณ, ๐’ฌ, โ„›, ๐’ž) + for s in โ„ณ prev, _ = BinaryTrees.prevnext(โ„›, s) if !isnothing(prev) # find segments r and u @@ -132,8 +127,8 @@ function _newevent!(๐’ฌ, ๐’ž, (aโ‚, bโ‚), (aโ‚‚, bโ‚‚)) ๐’ž[p] = [(aโ‚, bโ‚), (aโ‚‚, bโ‚‚)] end end - nothing end + nothing end function _rmevent!(๐’ฌ, (aโ‚, bโ‚), (aโ‚‚, bโ‚‚)) @@ -141,6 +136,8 @@ function _rmevent!(๐’ฌ, (aโ‚, bโ‚), (aโ‚‚, bโ‚‚)) if type(I) == Crossing || type(I) == EdgeTouching BinaryTrees.delete!(๐’ฌ, get(I)) end - nothing end + nothing end + +_isempty(๐’ฌ) = isnothing(BinaryTrees.root(๐’ฌ)) From 2fb7e10a8c196a49e8b1330e28f34d4a12b9c352 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 14 Mar 2025 14:43:38 -0300 Subject: [PATCH 33/60] More refactoring --- src/utils/intersect.jl | 60 +++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/src/utils/intersect.jl b/src/utils/intersect.jl index 0843661f8..1daed42bf 100644 --- a/src/utils/intersect.jl +++ b/src/utils/intersect.jl @@ -27,15 +27,15 @@ function bentleyottmann(segments) # initialization ๐’ฌ = BinaryTrees.AVLTree{P}() โ„› = BinaryTrees.AVLTree{S}() - โ„’ = Dict{P,Vector{S}}() - ๐’ฐ = Dict{P,Vector{S}}() - ๐’ž = Dict{P,Vector{S}}() + โ„ฌ = Dict{P,Vector{S}}() + โ„ฐ = Dict{P,Vector{S}}() + โ„ณ = Dict{P,Vector{S}}() lookup = Dict{S,Int}() for (i, (a, b)) in enumerate(segs) BinaryTrees.insert!(๐’ฌ, a) BinaryTrees.insert!(๐’ฌ, b) - haskey(โ„’, a) ? push!(โ„’[a], (a, b)) : (โ„’[a] = [(a, b)]) - haskey(๐’ฐ, b) ? push!(๐’ฐ[b], (a, b)) : (๐’ฐ[b] = [(a, b)]) + haskey(โ„ฌ, a) ? push!(โ„ฌ[a], (a, b)) : (โ„ฌ[a] = [(a, b)]) + haskey(โ„ฐ, b) ? push!(โ„ฐ[b], (a, b)) : (โ„ฐ[b] = [(a, b)]) lookup[(a, b)] = i end @@ -45,52 +45,52 @@ function bentleyottmann(segments) while !_isempty(๐’ฌ) p = BinaryTrees.key(BinaryTrees.minnode(๐’ฌ)) BinaryTrees.delete!(๐’ฌ, p) - _handle!(points, seginds, lookup, p, S, ๐’ฌ, โ„›, โ„’, ๐’ฐ, ๐’ž) + _handle!(points, seginds, lookup, p, S, ๐’ฌ, โ„›, โ„ฌ, โ„ฐ, โ„ณ) end points, seginds end -function _handle!(points, seginds, lookup, p, S, ๐’ฌ, โ„›, โ„’, ๐’ฐ, ๐’ž) - โ„ฌ = get(โ„’, p, S[]) - โ„ฐ = get(๐’ฐ, p, S[]) - โ„ณ = get(๐’ž, p, S[]) - _processend!(โ„ฐ, ๐’ฌ, โ„›, ๐’ž) - _processbeg!(โ„ฌ, ๐’ฌ, โ„›, ๐’ž) - _processmid!(โ„ณ, ๐’ฌ, โ„›, ๐’ž) - inds = [lookup[s] for s in โ„ฌ โˆช โ„ฐ โˆช โ„ณ] +function _handle!(points, seginds, lookup, p, S, ๐’ฌ, โ„›, โ„ฌ, โ„ฐ, โ„ณ) + โ„ฌโ‚š = get(โ„ฌ, p, S[]) + โ„ฐโ‚š = get(โ„ฐ, p, S[]) + โ„ณโ‚š = get(โ„ณ, p, S[]) + _processend!(โ„ฐโ‚š, ๐’ฌ, โ„›, โ„ณ) + _processbeg!(โ„ฌโ‚š, ๐’ฌ, โ„›, โ„ณ) + _processmid!(โ„ณโ‚š, ๐’ฌ, โ„›, โ„ณ) + inds = [lookup[s] for s in โ„ฌโ‚š โˆช โ„ฐโ‚š โˆช โ„ณโ‚š] if !isempty(inds) push!(points, p) push!(seginds, inds) end end -function _processbeg!(โ„ฌ, ๐’ฌ, โ„›, ๐’ž) - for s in โ„ฌ +function _processbeg!(โ„ฌโ‚š, ๐’ฌ, โ„›, โ„ณ) + for s in โ„ฌโ‚š BinaryTrees.insert!(โ„›, s) end - for s in โ„ฌ + for s in โ„ฌโ‚š prev, next = BinaryTrees.prevnext(โ„›, s) if !isnothing(prev) - _newevent!(๐’ฌ, ๐’ž, BinaryTrees.key(prev), s) + _newevent!(๐’ฌ, โ„ณ, BinaryTrees.key(prev), s) end if !isnothing(next) - _newevent!(๐’ฌ, ๐’ž, s, BinaryTrees.key(next)) + _newevent!(๐’ฌ, โ„ณ, s, BinaryTrees.key(next)) end end end -function _processend!(โ„ฐ, ๐’ฌ, โ„›, ๐’ž) - for s in โ„ฐ +function _processend!(โ„ฐโ‚š, ๐’ฌ, โ„›, โ„ณ) + for s in โ„ฐโ‚š prev, next = BinaryTrees.prevnext(โ„›, s) if !isnothing(prev) && !isnothing(next) - _newevent!(๐’ฌ, ๐’ž, BinaryTrees.key(next), BinaryTrees.key(prev)) + _newevent!(๐’ฌ, โ„ณ, BinaryTrees.key(next), BinaryTrees.key(prev)) end BinaryTrees.delete!(โ„›, s) end end -function _processmid!(โ„ณ, ๐’ฌ, โ„›, ๐’ž) - for s in โ„ณ +function _processmid!(โ„ณโ‚š, ๐’ฌ, โ„›, โ„ณ) + for s in โ„ณโ‚š prev, _ = BinaryTrees.prevnext(โ„›, s) if !isnothing(prev) # find segments r and u @@ -107,24 +107,24 @@ function _processmid!(โ„ณ, ๐’ฌ, โ„›, ๐’ž) # add crossing points rt and su to event queue if !isnothing(r) - _newevent!(๐’ฌ, ๐’ž, BinaryTrees.key(r), BinaryTrees.key(prev)) + _newevent!(๐’ฌ, โ„ณ, BinaryTrees.key(r), BinaryTrees.key(prev)) end if !isnothing(u) - _newevent!(๐’ฌ, ๐’ž, BinaryTrees.key(u), s) + _newevent!(๐’ฌ, โ„ณ, BinaryTrees.key(u), s) end end end end -function _newevent!(๐’ฌ, ๐’ž, (aโ‚, bโ‚), (aโ‚‚, bโ‚‚)) +function _newevent!(๐’ฌ, โ„ณ, (aโ‚, bโ‚), (aโ‚‚, bโ‚‚)) intersection(Segment(aโ‚, bโ‚), Segment(aโ‚‚, bโ‚‚)) do I if type(I) == Crossing || type(I) == EdgeTouching p = get(I) BinaryTrees.insert!(๐’ฌ, p) - if haskey(๐’ž, p) - push!(๐’ž[p], (aโ‚, bโ‚), (aโ‚‚, bโ‚‚)) + if haskey(โ„ณ, p) + push!(โ„ณ[p], (aโ‚, bโ‚), (aโ‚‚, bโ‚‚)) else - ๐’ž[p] = [(aโ‚, bโ‚), (aโ‚‚, bโ‚‚)] + โ„ณ[p] = [(aโ‚, bโ‚), (aโ‚‚, bโ‚‚)] end end end From 8e618e0efa8047ad19710dc5176ed2a255cd5966 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 14 Mar 2025 16:58:11 -0300 Subject: [PATCH 34/60] More refactoring --- src/utils/intersect.jl | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/utils/intersect.jl b/src/utils/intersect.jl index 1daed42bf..3ae32a189 100644 --- a/src/utils/intersect.jl +++ b/src/utils/intersect.jl @@ -39,21 +39,21 @@ function bentleyottmann(segments) lookup[(a, b)] = i end - # sweep line + # sweep line algorithm points = Vector{P}() seginds = Vector{Vector{Int}}() while !_isempty(๐’ฌ) p = BinaryTrees.key(BinaryTrees.minnode(๐’ฌ)) BinaryTrees.delete!(๐’ฌ, p) - _handle!(points, seginds, lookup, p, S, ๐’ฌ, โ„›, โ„ฌ, โ„ฐ, โ„ณ) + _handle!(points, seginds, lookup, p, ๐’ฌ, โ„›, โ„ฌ, โ„ฐ, โ„ณ) end points, seginds end -function _handle!(points, seginds, lookup, p, S, ๐’ฌ, โ„›, โ„ฌ, โ„ฐ, โ„ณ) - โ„ฌโ‚š = get(โ„ฌ, p, S[]) - โ„ฐโ‚š = get(โ„ฐ, p, S[]) - โ„ณโ‚š = get(โ„ณ, p, S[]) +function _handle!(points, seginds, lookup, p, ๐’ฌ, โ„›, โ„ฌ, โ„ฐ, โ„ณ) + โ„ฌโ‚š = _segmentswith(โ„ฌ, p) + โ„ฐโ‚š = _segmentswith(โ„ฐ, p) + โ„ณโ‚š = _segmentswith(โ„ณ, p) _processend!(โ„ฐโ‚š, ๐’ฌ, โ„›, โ„ณ) _processbeg!(โ„ฌโ‚š, ๐’ฌ, โ„›, โ„ณ) _processmid!(โ„ณโ‚š, ๐’ฌ, โ„›, โ„ณ) @@ -116,28 +116,34 @@ function _processmid!(โ„ณโ‚š, ๐’ฌ, โ„›, โ„ณ) end end -function _newevent!(๐’ฌ, โ„ณ, (aโ‚, bโ‚), (aโ‚‚, bโ‚‚)) - intersection(Segment(aโ‚, bโ‚), Segment(aโ‚‚, bโ‚‚)) do I +function _newevent!(๐’ฌ, โ„ณ, sโ‚, sโ‚‚) + intersection(Segment(sโ‚), Segment(sโ‚‚)) do I if type(I) == Crossing || type(I) == EdgeTouching p = get(I) BinaryTrees.insert!(๐’ฌ, p) if haskey(โ„ณ, p) - push!(โ„ณ[p], (aโ‚, bโ‚), (aโ‚‚, bโ‚‚)) + push!(โ„ณ[p], sโ‚, sโ‚‚) else - โ„ณ[p] = [(aโ‚, bโ‚), (aโ‚‚, bโ‚‚)] + โ„ณ[p] = [sโ‚, sโ‚‚] end end + nothing end - nothing end -function _rmevent!(๐’ฌ, (aโ‚, bโ‚), (aโ‚‚, bโ‚‚)) - intersection(Segment(aโ‚, bโ‚), Segment(aโ‚‚, bโ‚‚)) do I +function _rmevent!(๐’ฌ, sโ‚, sโ‚‚) + intersection(Segment(sโ‚), Segment(sโ‚‚)) do I if type(I) == Crossing || type(I) == EdgeTouching BinaryTrees.delete!(๐’ฌ, get(I)) end + nothing end - nothing end _isempty(๐’ฌ) = isnothing(BinaryTrees.root(๐’ฌ)) + +function _segmentswith(๐’Ÿ, p) + P = typeof(p) + S = Tuple{P,P} + get(๐’Ÿ, p, S[]) +end From 28505389ac866949cdde190e484473d8a7cd4598 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 14 Mar 2025 18:19:15 -0300 Subject: [PATCH 35/60] More refactoring --- src/utils/intersect.jl | 46 ++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/src/utils/intersect.jl b/src/utils/intersect.jl index 3ae32a189..6665bbb07 100644 --- a/src/utils/intersect.jl +++ b/src/utils/intersect.jl @@ -43,28 +43,32 @@ function bentleyottmann(segments) points = Vector{P}() seginds = Vector{Vector{Int}}() while !_isempty(๐’ฌ) + # current point (or event) p = BinaryTrees.key(BinaryTrees.minnode(๐’ฌ)) + + # delete point from event queue BinaryTrees.delete!(๐’ฌ, p) - _handle!(points, seginds, lookup, p, ๐’ฌ, โ„›, โ„ฌ, โ„ฐ, โ„ณ) - end - points, seginds -end -function _handle!(points, seginds, lookup, p, ๐’ฌ, โ„›, โ„ฌ, โ„ฐ, โ„ณ) - โ„ฌโ‚š = _segmentswith(โ„ฌ, p) - โ„ฐโ‚š = _segmentswith(โ„ฐ, p) - โ„ณโ‚š = _segmentswith(โ„ณ, p) - _processend!(โ„ฐโ‚š, ๐’ฌ, โ„›, โ„ณ) - _processbeg!(โ„ฌโ‚š, ๐’ฌ, โ„›, โ„ณ) - _processmid!(โ„ณโ‚š, ๐’ฌ, โ„›, โ„ณ) - inds = [lookup[s] for s in โ„ฌโ‚š โˆช โ„ฐโ‚š โˆช โ„ณโ‚š] - if !isempty(inds) - push!(points, p) - push!(seginds, inds) + # handle event, i.e. update ๐’ฌ, โ„› and โ„ณ + โ„ฌโ‚š = get(โ„ฌ, p, S[]) # segments with p at the begin + โ„ฐโ‚š = get(โ„ฐ, p, S[]) # segments with p at the end + โ„ณโ‚š = get(โ„ณ, p, S[]) # segments with p at the middle + _handlebeg!(โ„ฌโ‚š, ๐’ฌ, โ„›, โ„ณ) + _handleend!(โ„ฐโ‚š, ๐’ฌ, โ„›, โ„ณ) + _handlemid!(โ„ณโ‚š, ๐’ฌ, โ„›, โ„ณ) + + # report intersection point and segment indices + inds = [lookup[s] for s in โ„ฌโ‚š โˆช โ„ฐโ‚š โˆช โ„ณโ‚š] + if !isempty(inds) + push!(points, p) + push!(seginds, inds) + end end + + points, seginds end -function _processbeg!(โ„ฌโ‚š, ๐’ฌ, โ„›, โ„ณ) +function _handlebeg!(โ„ฌโ‚š, ๐’ฌ, โ„›, โ„ณ) for s in โ„ฌโ‚š BinaryTrees.insert!(โ„›, s) end @@ -79,7 +83,7 @@ function _processbeg!(โ„ฌโ‚š, ๐’ฌ, โ„›, โ„ณ) end end -function _processend!(โ„ฐโ‚š, ๐’ฌ, โ„›, โ„ณ) +function _handleend!(โ„ฐโ‚š, ๐’ฌ, โ„›, โ„ณ) for s in โ„ฐโ‚š prev, next = BinaryTrees.prevnext(โ„›, s) if !isnothing(prev) && !isnothing(next) @@ -89,7 +93,7 @@ function _processend!(โ„ฐโ‚š, ๐’ฌ, โ„›, โ„ณ) end end -function _processmid!(โ„ณโ‚š, ๐’ฌ, โ„›, โ„ณ) +function _handlemid!(โ„ณโ‚š, ๐’ฌ, โ„›, โ„ณ) for s in โ„ณโ‚š prev, _ = BinaryTrees.prevnext(โ„›, s) if !isnothing(prev) @@ -141,9 +145,3 @@ function _rmevent!(๐’ฌ, sโ‚, sโ‚‚) end _isempty(๐’ฌ) = isnothing(BinaryTrees.root(๐’ฌ)) - -function _segmentswith(๐’Ÿ, p) - P = typeof(p) - S = Tuple{P,P} - get(๐’Ÿ, p, S[]) -end From 7762044076425536e09f72f6d5599a6397236b87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 14 Mar 2025 18:26:11 -0300 Subject: [PATCH 36/60] Add more tests --- test/utils.jl | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/test/utils.jl b/test/utils.jl index 5de828703..05c1791cd 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -65,13 +65,20 @@ end Segment(cart(0, 1), cart(1, 1)), Segment(cart(1, 0), cart(1, 1)) ] - points, seginds = Meshes.bentleyottmann(segs) - - @inferred Meshes.bentleyottmann(segs) @test all(points .โ‰ˆ [cart(0, 0), cart(0, 1), cart(0.5, 0.5), cart(1, 0), cart(1, 1), cart(1.1, 1.1)]) @test length(points) == 6 @test length(seginds) == 6 @test maximum(reduce(vcat, seginds)) == length(segs) @test seginds == [[1, 3, 4], [2, 5, 3], [1, 2], [6, 2, 4], [5, 6, 1], [1]] + + # finds all intersections in a grid + segs = facets(cartgrid(10, 10)) + points, seginds = Meshes.bentleyottmann(segs) + @test length(points) == 121 + @test length(seginds) == 121 + @test unique(length.(seginds)) == [2, 3, 4] + + # inference test + @inferred Meshes.bentleyottmann(segs) end From e288e238407052c30729c0e55a795298b89fd0b2 Mon Sep 17 00:00:00 2001 From: souma4 Date: Fri, 14 Mar 2025 17:31:26 -0600 Subject: [PATCH 37/60] edited the processing of intersections to better reflect the original algorithm and to reduce redundancy. --- src/utils/intersect.jl | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/src/utils/intersect.jl b/src/utils/intersect.jl index 6665bbb07..8d06e364e 100644 --- a/src/utils/intersect.jl +++ b/src/utils/intersect.jl @@ -95,26 +95,23 @@ end function _handlemid!(โ„ณโ‚š, ๐’ฌ, โ„›, โ„ณ) for s in โ„ณโ‚š - prev, _ = BinaryTrees.prevnext(โ„›, s) - if !isnothing(prev) - # find segments r and u - r = prev - _, u = BinaryTrees.prevnext(โ„›, BinaryTrees.key(prev)) - - # remove crossing points rs and tu from event queue - if !isnothing(r) - _rmevent!(๐’ฌ, BinaryTrees.key(r), s) - end - if !isnothing(u) - _rmevent!(๐’ฌ, BinaryTrees.key(u), BinaryTrees.key(prev)) - end - - # add crossing points rt and su to event queue - if !isnothing(r) - _newevent!(๐’ฌ, โ„ณ, BinaryTrees.key(r), BinaryTrees.key(prev)) + prev, next = BinaryTrees.prevnext(โ„›, s) + r = !isnothing(prev) ? BinaryTrees.key(prev) : nothing + t = !isnothing(next) ? BinaryTrees.key(next) : nothing + if !isnothing(r) + _rmevent!(๐’ฌ, r, s) + if !isnothing(t) + _newevent!(๐’ฌ, โ„ณ, r, t) end + end + if !isnothing(t) + _, next = BinaryTrees.prevnext(โ„›, BinaryTrees.key(next)) + u = !isnothing(next) ? BinaryTrees.key(next) : nothing if !isnothing(u) - _newevent!(๐’ฌ, โ„ณ, BinaryTrees.key(u), s) + _rmevent!(๐’ฌ, t, u) + if !isnothing(r) + _newevent!(๐’ฌ, โ„ณ, r, u) + end end end end From 5a21d3aad475d712c906536c7463a0a8ce214e92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 14 Mar 2025 21:10:10 -0300 Subject: [PATCH 38/60] More refactoring --- src/utils/intersect.jl | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/utils/intersect.jl b/src/utils/intersect.jl index 8d06e364e..8521d63f5 100644 --- a/src/utils/intersect.jl +++ b/src/utils/intersect.jl @@ -74,21 +74,15 @@ function _handlebeg!(โ„ฌโ‚š, ๐’ฌ, โ„›, โ„ณ) end for s in โ„ฌโ‚š prev, next = BinaryTrees.prevnext(โ„›, s) - if !isnothing(prev) - _newevent!(๐’ฌ, โ„ณ, BinaryTrees.key(prev), s) - end - if !isnothing(next) - _newevent!(๐’ฌ, โ„ณ, s, BinaryTrees.key(next)) - end + isnothing(prev) || _newevent!(๐’ฌ, โ„ณ, BinaryTrees.key(prev), s) + isnothing(next) || _newevent!(๐’ฌ, โ„ณ, s, BinaryTrees.key(next)) end end function _handleend!(โ„ฐโ‚š, ๐’ฌ, โ„›, โ„ณ) for s in โ„ฐโ‚š prev, next = BinaryTrees.prevnext(โ„›, s) - if !isnothing(prev) && !isnothing(next) - _newevent!(๐’ฌ, โ„ณ, BinaryTrees.key(next), BinaryTrees.key(prev)) - end + isnothing(prev) || isnothing(next) || _newevent!(๐’ฌ, โ„ณ, BinaryTrees.key(next), BinaryTrees.key(prev)) BinaryTrees.delete!(โ„›, s) end end From 165ddae9fc86b970b1be91998fe322041b1d8f0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 14 Mar 2025 21:10:20 -0300 Subject: [PATCH 39/60] Refactor tests --- test/utils.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/utils.jl b/test/utils.jl index 05c1791cd..29b5b5632 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -57,6 +57,7 @@ end @testitem "Bentley-Ottmann" setup = [Setup] begin + # basic check with a small number of segments segs = [ Segment(cart(0, 0), cart(1.1, 1.1)), Segment(cart(1, 0), cart(0, 1)), @@ -69,7 +70,6 @@ end @test all(points .โ‰ˆ [cart(0, 0), cart(0, 1), cart(0.5, 0.5), cart(1, 0), cart(1, 1), cart(1.1, 1.1)]) @test length(points) == 6 @test length(seginds) == 6 - @test maximum(reduce(vcat, seginds)) == length(segs) @test seginds == [[1, 3, 4], [2, 5, 3], [1, 2], [6, 2, 4], [5, 6, 1], [1]] # finds all intersections in a grid From 3ed853db63966eeba4d544be4b9bd546e70c99e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 14 Mar 2025 21:19:14 -0300 Subject: [PATCH 40/60] Add more tests --- test/utils.jl | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/utils.jl b/test/utils.jl index 29b5b5632..086eec830 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -79,6 +79,15 @@ end @test length(seginds) == 121 @test unique(length.(seginds)) == [2, 3, 4] + # result is invariant under rotations + segs = collect(segs) + for ฮธ in T(ฯ€/6):T(ฯ€/6):T(2ฯ€-ฯ€/6) + points, seginds = Meshes.bentleyottmann(segs |> Rotate(ฮธ)) + @test length(points) == 121 + @test length(seginds) == 121 + @test unique(length.(seginds)) == [2, 3, 4] + end + # inference test @inferred Meshes.bentleyottmann(segs) end From 859c846d9e3a27e9599fa56b3b4c2db2569b5797 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 14 Mar 2025 22:51:50 -0300 Subject: [PATCH 41/60] Improve tests --- test/utils.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/utils.jl b/test/utils.jl index 086eec830..eda26b3ef 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -77,7 +77,7 @@ end points, seginds = Meshes.bentleyottmann(segs) @test length(points) == 121 @test length(seginds) == 121 - @test unique(length.(seginds)) == [2, 3, 4] + @test Set(length.(seginds)) == Set([2, 3, 4]) # result is invariant under rotations segs = collect(segs) @@ -85,7 +85,7 @@ end points, seginds = Meshes.bentleyottmann(segs |> Rotate(ฮธ)) @test length(points) == 121 @test length(seginds) == 121 - @test unique(length.(seginds)) == [2, 3, 4] + @test Set(length.(seginds)) == Set([2, 3, 4]) end # inference test From 9732322d1989991bef22e3c7d0acfb5dc44c0592 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 14 Mar 2025 22:53:55 -0300 Subject: [PATCH 42/60] Fix formatting --- test/utils.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/utils.jl b/test/utils.jl index eda26b3ef..314135ea5 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -81,7 +81,7 @@ end # result is invariant under rotations segs = collect(segs) - for ฮธ in T(ฯ€/6):T(ฯ€/6):T(2ฯ€-ฯ€/6) + for ฮธ in T(ฯ€ / 6):T(ฯ€ / 6):T(2ฯ€ - ฯ€ / 6) points, seginds = Meshes.bentleyottmann(segs |> Rotate(ฮธ)) @test length(points) == 121 @test length(seginds) == 121 From 98f0786c623260b9d6d7bbb71aa8406953ea8082 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Sat, 15 Mar 2025 07:09:23 -0300 Subject: [PATCH 43/60] More refactoring --- Project.toml | 2 +- src/utils/intersect.jl | 6 ++---- test/utils.jl | 8 ++++---- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/Project.toml b/Project.toml index 1e94a1e05..bbbcd70e8 100644 --- a/Project.toml +++ b/Project.toml @@ -31,7 +31,7 @@ MeshesMakieExt = "Makie" [compat] Bessels = "0.2" -BinaryTrees = "0.1.1" +BinaryTrees = "0.1.2" CircularArrays = "1.3" Colorfy = "1.1" CoordRefSystems = "0.16" diff --git a/src/utils/intersect.jl b/src/utils/intersect.jl index 8521d63f5..22c41272a 100644 --- a/src/utils/intersect.jl +++ b/src/utils/intersect.jl @@ -42,7 +42,7 @@ function bentleyottmann(segments) # sweep line algorithm points = Vector{P}() seginds = Vector{Vector{Int}}() - while !_isempty(๐’ฌ) + while !BinaryTrees.isempty(๐’ฌ) # current point (or event) p = BinaryTrees.key(BinaryTrees.minnode(๐’ฌ)) @@ -82,7 +82,7 @@ end function _handleend!(โ„ฐโ‚š, ๐’ฌ, โ„›, โ„ณ) for s in โ„ฐโ‚š prev, next = BinaryTrees.prevnext(โ„›, s) - isnothing(prev) || isnothing(next) || _newevent!(๐’ฌ, โ„ณ, BinaryTrees.key(next), BinaryTrees.key(prev)) + isnothing(prev) || isnothing(next) || _newevent!(๐’ฌ, โ„ณ, BinaryTrees.key(prev), BinaryTrees.key(next)) BinaryTrees.delete!(โ„›, s) end end @@ -134,5 +134,3 @@ function _rmevent!(๐’ฌ, sโ‚, sโ‚‚) nothing end end - -_isempty(๐’ฌ) = isnothing(BinaryTrees.root(๐’ฌ)) diff --git a/test/utils.jl b/test/utils.jl index 314135ea5..5a52ab0f5 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -82,10 +82,10 @@ end # result is invariant under rotations segs = collect(segs) for ฮธ in T(ฯ€ / 6):T(ฯ€ / 6):T(2ฯ€ - ฯ€ / 6) - points, seginds = Meshes.bentleyottmann(segs |> Rotate(ฮธ)) - @test length(points) == 121 - @test length(seginds) == 121 - @test Set(length.(seginds)) == Set([2, 3, 4]) + ฮธpoints, ฮธseginds = Meshes.bentleyottmann(segs |> Rotate(ฮธ)) + @test length(ฮธpoints) == 121 + @test length(ฮธseginds) == 121 + @test Set(length.(ฮธseginds)) == Set([2, 3, 4]) end # inference test From 042e6904d023b17aa0b97492e3ec5730f84ab258 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Sat, 15 Mar 2025 09:41:44 -0300 Subject: [PATCH 44/60] Add test with infinite loop --- test/utils.jl | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/test/utils.jl b/test/utils.jl index 5a52ab0f5..484121ad4 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -58,20 +58,32 @@ end @testitem "Bentley-Ottmann" setup = [Setup] begin # basic check with a small number of segments - segs = [ - Segment(cart(0, 0), cart(1.1, 1.1)), - Segment(cart(1, 0), cart(0, 1)), - Segment(cart(0, 0), cart(0, 1)), - Segment(cart(0, 0), cart(1, 0)), - Segment(cart(0, 1), cart(1, 1)), - Segment(cart(1, 0), cart(1, 1)) - ] + segs = Segment.([ + (cart(0, 0), cart(1.1, 1.1)), + (cart(1, 0), cart(0, 1)), + (cart(0, 0), cart(0, 1)), + (cart(0, 0), cart(1, 0)), + (cart(0, 1), cart(1, 1)), + (cart(1, 0), cart(1, 1)) + ]) points, seginds = Meshes.bentleyottmann(segs) @test all(points .โ‰ˆ [cart(0, 0), cart(0, 1), cart(0.5, 0.5), cart(1, 0), cart(1, 1), cart(1.1, 1.1)]) @test length(points) == 6 @test length(seginds) == 6 @test seginds == [[1, 3, 4], [2, 5, 3], [1, 2], [6, 2, 4], [5, 6, 1], [1]] + segs = Segment.([ + (cart(9, 13), cart(6, 9)), + (cart(2, 12), cart(5.2, 8.5)), + (cart(12, 11), cart(4, 7)), + (cart(2.5, 10), cart(12.5, 2)), + (cart(13, 6), cart(10, 4)), + (cart(10.5, 5.5), cart(9, 1)), + (cart(10, 4), cart(11, -1)) + ]) + # TODO: fix loop + #points, seginds = Meshes.bentleyottmann(segs) + # finds all intersections in a grid segs = facets(cartgrid(10, 10)) points, seginds = Meshes.bentleyottmann(segs) @@ -89,5 +101,6 @@ end end # inference test + segs = facets(cartgrid(10, 10)) @inferred Meshes.bentleyottmann(segs) end From c9d933f3524db5cf935374201a23fd1107d18000 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Sat, 15 Mar 2025 09:42:48 -0300 Subject: [PATCH 45/60] Fix formatting --- test/utils.jl | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/test/utils.jl b/test/utils.jl index 484121ad4..5a8cc4e96 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -58,29 +58,31 @@ end @testitem "Bentley-Ottmann" setup = [Setup] begin # basic check with a small number of segments - segs = Segment.([ - (cart(0, 0), cart(1.1, 1.1)), - (cart(1, 0), cart(0, 1)), - (cart(0, 0), cart(0, 1)), - (cart(0, 0), cart(1, 0)), - (cart(0, 1), cart(1, 1)), - (cart(1, 0), cart(1, 1)) - ]) + segs = + Segment.([ + (cart(0, 0), cart(1.1, 1.1)), + (cart(1, 0), cart(0, 1)), + (cart(0, 0), cart(0, 1)), + (cart(0, 0), cart(1, 0)), + (cart(0, 1), cart(1, 1)), + (cart(1, 0), cart(1, 1)) + ]) points, seginds = Meshes.bentleyottmann(segs) @test all(points .โ‰ˆ [cart(0, 0), cart(0, 1), cart(0.5, 0.5), cart(1, 0), cart(1, 1), cart(1.1, 1.1)]) @test length(points) == 6 @test length(seginds) == 6 @test seginds == [[1, 3, 4], [2, 5, 3], [1, 2], [6, 2, 4], [5, 6, 1], [1]] - segs = Segment.([ - (cart(9, 13), cart(6, 9)), - (cart(2, 12), cart(5.2, 8.5)), - (cart(12, 11), cart(4, 7)), - (cart(2.5, 10), cart(12.5, 2)), - (cart(13, 6), cart(10, 4)), - (cart(10.5, 5.5), cart(9, 1)), - (cart(10, 4), cart(11, -1)) - ]) + segs = + Segment.([ + (cart(9, 13), cart(6, 9)), + (cart(2, 12), cart(5.2, 8.5)), + (cart(12, 11), cart(4, 7)), + (cart(2.5, 10), cart(12.5, 2)), + (cart(13, 6), cart(10, 4)), + (cart(10.5, 5.5), cart(9, 1)), + (cart(10, 4), cart(11, -1)) + ]) # TODO: fix loop #points, seginds = Meshes.bentleyottmann(segs) From 219add320ceb384f6954365062da2c2e6020b024 Mon Sep 17 00:00:00 2001 From: souma4 Date: Sat, 15 Mar 2025 15:23:04 -0600 Subject: [PATCH 46/60] update to stop infinite loops, but has major TODOs to handle floating point issues --- src/utils/intersect.jl | 38 +++++++++++++++++++++++++++----------- test/utils.jl | 19 +++++++++++++++++-- 2 files changed, 44 insertions(+), 13 deletions(-) diff --git a/src/utils/intersect.jl b/src/utils/intersect.jl index 22c41272a..18dc54076 100644 --- a/src/utils/intersect.jl +++ b/src/utils/intersect.jl @@ -41,11 +41,12 @@ function bentleyottmann(segments) # sweep line algorithm points = Vector{P}() + visited = Dict{P,Int}() seginds = Vector{Vector{Int}}() + i = 1 while !BinaryTrees.isempty(๐’ฌ) # current point (or event) p = BinaryTrees.key(BinaryTrees.minnode(๐’ฌ)) - # delete point from event queue BinaryTrees.delete!(๐’ฌ, p) @@ -53,16 +54,24 @@ function bentleyottmann(segments) โ„ฌโ‚š = get(โ„ฌ, p, S[]) # segments with p at the begin โ„ฐโ‚š = get(โ„ฐ, p, S[]) # segments with p at the end โ„ณโ‚š = get(โ„ณ, p, S[]) # segments with p at the middle - _handlebeg!(โ„ฌโ‚š, ๐’ฌ, โ„›, โ„ณ) - _handleend!(โ„ฐโ‚š, ๐’ฌ, โ„›, โ„ณ) - _handlemid!(โ„ณโ‚š, ๐’ฌ, โ„›, โ„ณ) + Meshes._handlebeg!(โ„ฌโ‚š, ๐’ฌ, โ„›, โ„ณ) + Meshes._handleend!(โ„ฐโ‚š, ๐’ฌ, โ„›, โ„ณ) + Meshes._handlemid!(โ„ณโ‚š, ๐’ฌ, โ„›, โ„ณ) # report intersection point and segment indices inds = [lookup[s] for s in โ„ฌโ‚š โˆช โ„ฐโ‚š โˆช โ„ณโ‚š] if !isempty(inds) - push!(points, p) - push!(seginds, inds) + if p โˆˆ keys(visited) + seginds[visited[p]] = inds + else + push!(points, p) + push!(seginds, inds) + push!(visited, p => i) + i += 1 + end end + ๐’ฌ + hcat(points, seginds) end points, seginds @@ -93,7 +102,7 @@ function _handlemid!(โ„ณโ‚š, ๐’ฌ, โ„›, โ„ณ) r = !isnothing(prev) ? BinaryTrees.key(prev) : nothing t = !isnothing(next) ? BinaryTrees.key(next) : nothing if !isnothing(r) - _rmevent!(๐’ฌ, r, s) + _newevent!(๐’ฌ, โ„ณ, r, s) if !isnothing(t) _newevent!(๐’ฌ, โ„ณ, r, t) end @@ -102,7 +111,7 @@ function _handlemid!(โ„ณโ‚š, ๐’ฌ, โ„›, โ„ณ) _, next = BinaryTrees.prevnext(โ„›, BinaryTrees.key(next)) u = !isnothing(next) ? BinaryTrees.key(next) : nothing if !isnothing(u) - _rmevent!(๐’ฌ, t, u) + _newevent!(๐’ฌ, โ„ณ, t, u) if !isnothing(r) _newevent!(๐’ฌ, โ„ณ, r, u) end @@ -111,21 +120,28 @@ function _handlemid!(โ„ณโ‚š, ๐’ฌ, โ„›, โ„ณ) end end +#TODO potentially include visited here, to nudge values to existing points +# TODO Maybe just check if p near a key in \scrM function _newevent!(๐’ฌ, โ„ณ, sโ‚, sโ‚‚) intersection(Segment(sโ‚), Segment(sโ‚‚)) do I if type(I) == Crossing || type(I) == EdgeTouching p = get(I) - BinaryTrees.insert!(๐’ฌ, p) if haskey(โ„ณ, p) - push!(โ„ณ[p], sโ‚, sโ‚‚) + if sโ‚ โˆ‰ โ„ณ[p] + push!(โ„ณ[p], sโ‚) + BinaryTrees.insert!(๐’ฌ, p) + end + if sโ‚‚ โˆ‰ โ„ณ[p] + push!(โ„ณ[p], sโ‚‚) + end else โ„ณ[p] = [sโ‚, sโ‚‚] + BinaryTrees.insert!(๐’ฌ, p) end end nothing end end - function _rmevent!(๐’ฌ, sโ‚, sโ‚‚) intersection(Segment(sโ‚), Segment(sโ‚‚)) do I if type(I) == Crossing || type(I) == EdgeTouching diff --git a/test/utils.jl b/test/utils.jl index 5a8cc4e96..a676b6a72 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -73,6 +73,7 @@ end @test length(seginds) == 6 @test seginds == [[1, 3, 4], [2, 5, 3], [1, 2], [6, 2, 4], [5, 6, 1], [1]] + #TODO original complex test segs = Segment.([ (cart(9, 13), cart(6, 9)), @@ -83,8 +84,22 @@ end (cart(10.5, 5.5), cart(9, 1)), (cart(10, 4), cart(11, -1)) ]) - # TODO: fix loop - #points, seginds = Meshes.bentleyottmann(segs) + # points, seginds = Meshes.bentleyottmann(segs) + + #TODO more intensive test + segs = + Segment.([ + (cart(9, 13), cart(6, 9)), + (cart(2, 12), cart(9, 4.8)), + (cart(12, 11), cart(4, 7)), + (cart(2.5, 10), cart(12.5, 2)), + (cart(13, 6), cart(10, 4)), + (cart(10.5, 5.5), cart(9, 1)), + (cart(10, 4), cart(11, -1)), + (cart(10, 3), cart(10, 5)) + ]) + # points, seginds = Meshes.bentleyottmann(segs) + # hcat(points, seginds) # finds all intersections in a grid segs = facets(cartgrid(10, 10)) From 89a8d98a8b89af3b50b5c6e89d8f61b60a6935ec Mon Sep 17 00:00:00 2001 From: souma4 Date: Sun, 16 Mar 2025 15:40:25 -0600 Subject: [PATCH 47/60] reverting prior to dictionary implementation --- src/utils/intersect.jl | 38 +++++++++++--------------------------- test/utils.jl | 19 ++----------------- 2 files changed, 13 insertions(+), 44 deletions(-) diff --git a/src/utils/intersect.jl b/src/utils/intersect.jl index 18dc54076..22c41272a 100644 --- a/src/utils/intersect.jl +++ b/src/utils/intersect.jl @@ -41,12 +41,11 @@ function bentleyottmann(segments) # sweep line algorithm points = Vector{P}() - visited = Dict{P,Int}() seginds = Vector{Vector{Int}}() - i = 1 while !BinaryTrees.isempty(๐’ฌ) # current point (or event) p = BinaryTrees.key(BinaryTrees.minnode(๐’ฌ)) + # delete point from event queue BinaryTrees.delete!(๐’ฌ, p) @@ -54,24 +53,16 @@ function bentleyottmann(segments) โ„ฌโ‚š = get(โ„ฌ, p, S[]) # segments with p at the begin โ„ฐโ‚š = get(โ„ฐ, p, S[]) # segments with p at the end โ„ณโ‚š = get(โ„ณ, p, S[]) # segments with p at the middle - Meshes._handlebeg!(โ„ฌโ‚š, ๐’ฌ, โ„›, โ„ณ) - Meshes._handleend!(โ„ฐโ‚š, ๐’ฌ, โ„›, โ„ณ) - Meshes._handlemid!(โ„ณโ‚š, ๐’ฌ, โ„›, โ„ณ) + _handlebeg!(โ„ฌโ‚š, ๐’ฌ, โ„›, โ„ณ) + _handleend!(โ„ฐโ‚š, ๐’ฌ, โ„›, โ„ณ) + _handlemid!(โ„ณโ‚š, ๐’ฌ, โ„›, โ„ณ) # report intersection point and segment indices inds = [lookup[s] for s in โ„ฌโ‚š โˆช โ„ฐโ‚š โˆช โ„ณโ‚š] if !isempty(inds) - if p โˆˆ keys(visited) - seginds[visited[p]] = inds - else - push!(points, p) - push!(seginds, inds) - push!(visited, p => i) - i += 1 - end + push!(points, p) + push!(seginds, inds) end - ๐’ฌ - hcat(points, seginds) end points, seginds @@ -102,7 +93,7 @@ function _handlemid!(โ„ณโ‚š, ๐’ฌ, โ„›, โ„ณ) r = !isnothing(prev) ? BinaryTrees.key(prev) : nothing t = !isnothing(next) ? BinaryTrees.key(next) : nothing if !isnothing(r) - _newevent!(๐’ฌ, โ„ณ, r, s) + _rmevent!(๐’ฌ, r, s) if !isnothing(t) _newevent!(๐’ฌ, โ„ณ, r, t) end @@ -111,7 +102,7 @@ function _handlemid!(โ„ณโ‚š, ๐’ฌ, โ„›, โ„ณ) _, next = BinaryTrees.prevnext(โ„›, BinaryTrees.key(next)) u = !isnothing(next) ? BinaryTrees.key(next) : nothing if !isnothing(u) - _newevent!(๐’ฌ, โ„ณ, t, u) + _rmevent!(๐’ฌ, t, u) if !isnothing(r) _newevent!(๐’ฌ, โ„ณ, r, u) end @@ -120,28 +111,21 @@ function _handlemid!(โ„ณโ‚š, ๐’ฌ, โ„›, โ„ณ) end end -#TODO potentially include visited here, to nudge values to existing points -# TODO Maybe just check if p near a key in \scrM function _newevent!(๐’ฌ, โ„ณ, sโ‚, sโ‚‚) intersection(Segment(sโ‚), Segment(sโ‚‚)) do I if type(I) == Crossing || type(I) == EdgeTouching p = get(I) + BinaryTrees.insert!(๐’ฌ, p) if haskey(โ„ณ, p) - if sโ‚ โˆ‰ โ„ณ[p] - push!(โ„ณ[p], sโ‚) - BinaryTrees.insert!(๐’ฌ, p) - end - if sโ‚‚ โˆ‰ โ„ณ[p] - push!(โ„ณ[p], sโ‚‚) - end + push!(โ„ณ[p], sโ‚, sโ‚‚) else โ„ณ[p] = [sโ‚, sโ‚‚] - BinaryTrees.insert!(๐’ฌ, p) end end nothing end end + function _rmevent!(๐’ฌ, sโ‚, sโ‚‚) intersection(Segment(sโ‚), Segment(sโ‚‚)) do I if type(I) == Crossing || type(I) == EdgeTouching diff --git a/test/utils.jl b/test/utils.jl index a676b6a72..5a8cc4e96 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -73,7 +73,6 @@ end @test length(seginds) == 6 @test seginds == [[1, 3, 4], [2, 5, 3], [1, 2], [6, 2, 4], [5, 6, 1], [1]] - #TODO original complex test segs = Segment.([ (cart(9, 13), cart(6, 9)), @@ -84,22 +83,8 @@ end (cart(10.5, 5.5), cart(9, 1)), (cart(10, 4), cart(11, -1)) ]) - # points, seginds = Meshes.bentleyottmann(segs) - - #TODO more intensive test - segs = - Segment.([ - (cart(9, 13), cart(6, 9)), - (cart(2, 12), cart(9, 4.8)), - (cart(12, 11), cart(4, 7)), - (cart(2.5, 10), cart(12.5, 2)), - (cart(13, 6), cart(10, 4)), - (cart(10.5, 5.5), cart(9, 1)), - (cart(10, 4), cart(11, -1)), - (cart(10, 3), cart(10, 5)) - ]) - # points, seginds = Meshes.bentleyottmann(segs) - # hcat(points, seginds) + # TODO: fix loop + #points, seginds = Meshes.bentleyottmann(segs) # finds all intersections in a grid segs = facets(cartgrid(10, 10)) From fdb936ba5926a970532589adef755296f4c0f0dc Mon Sep 17 00:00:00 2001 From: souma4 Date: Mon, 17 Mar 2025 13:33:15 -0600 Subject: [PATCH 48/60] fully functioning not infinitely looping bentleyottman algorithm all passing tests locally --- src/utils/intersect.jl | 75 +++++++++++++++++++++++------------------- test/utils.jl | 22 ++++++++----- 2 files changed, 56 insertions(+), 41 deletions(-) diff --git a/src/utils/intersect.jl b/src/utils/intersect.jl index 22c41272a..c9c0424b9 100644 --- a/src/utils/intersect.jl +++ b/src/utils/intersect.jl @@ -3,17 +3,18 @@ # ------------------------------------------------------------------ """ - bentleyottmann(segments) + bentleyottmann(segments; digits) -Compute pairwise intersections between n `segments` in -O(nโ‹…log(n)) time using Bentley-Ottmann sweep line algorithm. +Compute pairwise intersections between n `segments` +with `digits` precision in O(nโ‹…log(n)) time using +Bentley-Ottmann sweep line algorithm. ## References * Bentley & Ottmann 1979. [Algorithms for reporting and counting geometric intersections](https://ieeexplore.ieee.org/document/1675432) """ -function bentleyottmann(segments) +function bentleyottmann(segments; kwargs...) # orient segments segs = map(segments) do s a, b = extrema(s) @@ -42,6 +43,8 @@ function bentleyottmann(segments) # sweep line algorithm points = Vector{P}() seginds = Vector{Vector{Int}}() + visited = Dict{P,Int}() + i = 1 while !BinaryTrees.isempty(๐’ฌ) # current point (or event) p = BinaryTrees.key(BinaryTrees.minnode(๐’ฌ)) @@ -53,84 +56,90 @@ function bentleyottmann(segments) โ„ฌโ‚š = get(โ„ฌ, p, S[]) # segments with p at the begin โ„ฐโ‚š = get(โ„ฐ, p, S[]) # segments with p at the end โ„ณโ‚š = get(โ„ณ, p, S[]) # segments with p at the middle - _handlebeg!(โ„ฌโ‚š, ๐’ฌ, โ„›, โ„ณ) - _handleend!(โ„ฐโ‚š, ๐’ฌ, โ„›, โ„ณ) - _handlemid!(โ„ณโ‚š, ๐’ฌ, โ„›, โ„ณ) + _handlebeg!(โ„ฌโ‚š, ๐’ฌ, โ„›, โ„ณ; kwargs...) + _handleend!(โ„ฐโ‚š, ๐’ฌ, โ„›, โ„ณ; kwargs...) + _handlemid!(โ„ณโ‚š, ๐’ฌ, โ„›, โ„ณ; kwargs...) # report intersection point and segment indices inds = [lookup[s] for s in โ„ฌโ‚š โˆช โ„ฐโ‚š โˆช โ„ณโ‚š] if !isempty(inds) - push!(points, p) - push!(seginds, inds) + if p โˆˆ keys(visited) + seginds[visited[p]] = inds + else + push!(points, p) + push!(seginds, inds) + push!(visited, p => i) + i += 1 + end end + ๐’ฌ + hcat(points, seginds) end points, seginds end -function _handlebeg!(โ„ฌโ‚š, ๐’ฌ, โ„›, โ„ณ) +function _handlebeg!(โ„ฌโ‚š, ๐’ฌ, โ„›, โ„ณ; kwargs...) for s in โ„ฌโ‚š BinaryTrees.insert!(โ„›, s) end for s in โ„ฌโ‚š prev, next = BinaryTrees.prevnext(โ„›, s) - isnothing(prev) || _newevent!(๐’ฌ, โ„ณ, BinaryTrees.key(prev), s) - isnothing(next) || _newevent!(๐’ฌ, โ„ณ, s, BinaryTrees.key(next)) + isnothing(prev) || _newevent!(๐’ฌ, โ„ณ, BinaryTrees.key(prev), s; kwargs...) + isnothing(next) || _newevent!(๐’ฌ, โ„ณ, s, BinaryTrees.key(next); kwargs...) end end -function _handleend!(โ„ฐโ‚š, ๐’ฌ, โ„›, โ„ณ) +function _handleend!(โ„ฐโ‚š, ๐’ฌ, โ„›, โ„ณ; kwargs...) for s in โ„ฐโ‚š prev, next = BinaryTrees.prevnext(โ„›, s) - isnothing(prev) || isnothing(next) || _newevent!(๐’ฌ, โ„ณ, BinaryTrees.key(prev), BinaryTrees.key(next)) + isnothing(prev) || isnothing(next) || _newevent!(๐’ฌ, โ„ณ, BinaryTrees.key(prev), BinaryTrees.key(next); kwargs...) BinaryTrees.delete!(โ„›, s) end end -function _handlemid!(โ„ณโ‚š, ๐’ฌ, โ„›, โ„ณ) +function _handlemid!(โ„ณโ‚š, ๐’ฌ, โ„›, โ„ณ; kwargs...) for s in โ„ณโ‚š prev, next = BinaryTrees.prevnext(โ„›, s) r = !isnothing(prev) ? BinaryTrees.key(prev) : nothing t = !isnothing(next) ? BinaryTrees.key(next) : nothing if !isnothing(r) - _rmevent!(๐’ฌ, r, s) + _newevent!(๐’ฌ, โ„ณ, r, s; kwargs...) if !isnothing(t) - _newevent!(๐’ฌ, โ„ณ, r, t) + _newevent!(๐’ฌ, โ„ณ, r, t; kwargs...) end end if !isnothing(t) _, next = BinaryTrees.prevnext(โ„›, BinaryTrees.key(next)) u = !isnothing(next) ? BinaryTrees.key(next) : nothing if !isnothing(u) - _rmevent!(๐’ฌ, t, u) + _newevent!(๐’ฌ, โ„ณ, t, u; kwargs...) if !isnothing(r) - _newevent!(๐’ฌ, โ„ณ, r, u) + _newevent!(๐’ฌ, โ„ณ, r, u; kwargs...) end end end end end -function _newevent!(๐’ฌ, โ„ณ, sโ‚, sโ‚‚) +function _newevent!(๐’ฌ, โ„ณ, sโ‚, sโ‚‚; kwargs...) intersection(Segment(sโ‚), Segment(sโ‚‚)) do I if type(I) == Crossing || type(I) == EdgeTouching p = get(I) - BinaryTrees.insert!(๐’ฌ, p) - if haskey(โ„ณ, p) - push!(โ„ณ[p], sโ‚, sโ‚‚) + pโ€ฒ = roundcoords(p; kwargs...) + if haskey(โ„ณ, pโ€ฒ) + if sโ‚ โˆ‰ โ„ณ[pโ€ฒ] + push!(โ„ณ[pโ€ฒ], sโ‚) + BinaryTrees.insert!(๐’ฌ, pโ€ฒ) + end + if sโ‚‚ โˆ‰ โ„ณ[pโ€ฒ] + push!(โ„ณ[pโ€ฒ], sโ‚‚) + end else - โ„ณ[p] = [sโ‚, sโ‚‚] + โ„ณ[pโ€ฒ] = [sโ‚, sโ‚‚] + BinaryTrees.insert!(๐’ฌ, pโ€ฒ) end end nothing end end - -function _rmevent!(๐’ฌ, sโ‚, sโ‚‚) - intersection(Segment(sโ‚), Segment(sโ‚‚)) do I - if type(I) == Crossing || type(I) == EdgeTouching - BinaryTrees.delete!(๐’ฌ, get(I)) - end - nothing - end -end diff --git a/test/utils.jl b/test/utils.jl index 33d82d18b..879459d96 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -74,7 +74,7 @@ end (cart(0, 1), cart(1, 1)), (cart(1, 0), cart(1, 1)) ]) - points, seginds = Meshes.bentleyottmann(segs) + points, seginds = Meshes.bentleyottmann(segs; digits=10) @test all(points .โ‰ˆ [cart(0, 0), cart(0, 1), cart(0.5, 0.5), cart(1, 0), cart(1, 1), cart(1.1, 1.1)]) @test length(points) == 6 @test length(seginds) == 6 @@ -83,19 +83,25 @@ end segs = Segment.([ (cart(9, 13), cart(6, 9)), - (cart(2, 12), cart(5.2, 8.5)), + (cart(2, 12), cart(9, 4.8)), (cart(12, 11), cart(4, 7)), (cart(2.5, 10), cart(12.5, 2)), (cart(13, 6), cart(10, 4)), (cart(10.5, 5.5), cart(9, 1)), - (cart(10, 4), cart(11, -1)) + (cart(10, 4), cart(11, -1)), + (cart(10, 3), cart(10, 5)) ]) - # TODO: fix loop - #points, seginds = Meshes.bentleyottmann(segs) + points, seginds = Meshes.bentleyottmann(segs; digits=10) + @test length(points) == 17 + @test length(seginds) == 17 + @test Set(reduce(vcat, seginds)) == Set(1:8) + @test points[findfirst(p -> p โ‰ˆ cart(10, 4), points)] โ‰ˆ cart(10, 4) + @test Set(seginds[findfirst(p -> p โ‰ˆ cart(10, 4), points)]) == Set([4, 5, 6, 7, 8]) + @test Set(seginds[findfirst(p -> p โ‰ˆ cart(9, 4.8), points)]) == Set([4, 2]) # finds all intersections in a grid segs = facets(cartgrid(10, 10)) - points, seginds = Meshes.bentleyottmann(segs) + points, seginds = Meshes.bentleyottmann(segs; digits=10) @test length(points) == 121 @test length(seginds) == 121 @test Set(length.(seginds)) == Set([2, 3, 4]) @@ -103,7 +109,7 @@ end # result is invariant under rotations segs = collect(segs) for ฮธ in T(ฯ€ / 6):T(ฯ€ / 6):T(2ฯ€ - ฯ€ / 6) - ฮธpoints, ฮธseginds = Meshes.bentleyottmann(segs |> Rotate(ฮธ)) + ฮธpoints, ฮธseginds = Meshes.bentleyottmann(segs |> Rotate(ฮธ); digits=10) @test length(ฮธpoints) == 121 @test length(ฮธseginds) == 121 @test Set(length.(ฮธseginds)) == Set([2, 3, 4]) @@ -111,5 +117,5 @@ end # inference test segs = facets(cartgrid(10, 10)) - @inferred Meshes.bentleyottmann(segs) + @inferred Meshes.bentleyottmann(segs; digits=10) end From 5ef650544d17e225424ed3b68ec7c7a92c45a713 Mon Sep 17 00:00:00 2001 From: souma4 Date: Mon, 17 Mar 2025 14:53:57 -0600 Subject: [PATCH 49/60] cleaned up test outputs and fixed tests to be T agnostic --- src/utils/intersect.jl | 2 -- test/utils.jl | 9 +++++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/utils/intersect.jl b/src/utils/intersect.jl index c9c0424b9..4dc290465 100644 --- a/src/utils/intersect.jl +++ b/src/utils/intersect.jl @@ -72,8 +72,6 @@ function bentleyottmann(segments; kwargs...) i += 1 end end - ๐’ฌ - hcat(points, seginds) end points, seginds diff --git a/test/utils.jl b/test/utils.jl index 879459d96..90137b606 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -65,6 +65,7 @@ end @testitem "Bentley-Ottmann" setup = [Setup] begin # basic check with a small number of segments + digits = -Int(log10(atol(T))) segs = Segment.([ (cart(0, 0), cart(1.1, 1.1)), @@ -74,7 +75,7 @@ end (cart(0, 1), cart(1, 1)), (cart(1, 0), cart(1, 1)) ]) - points, seginds = Meshes.bentleyottmann(segs; digits=10) + points, seginds = Meshes.bentleyottmann(segs; digits=digits) @test all(points .โ‰ˆ [cart(0, 0), cart(0, 1), cart(0.5, 0.5), cart(1, 0), cart(1, 1), cart(1.1, 1.1)]) @test length(points) == 6 @test length(seginds) == 6 @@ -91,7 +92,7 @@ end (cart(10, 4), cart(11, -1)), (cart(10, 3), cart(10, 5)) ]) - points, seginds = Meshes.bentleyottmann(segs; digits=10) + points, seginds = Meshes.bentleyottmann(segs; digits=digits) @test length(points) == 17 @test length(seginds) == 17 @test Set(reduce(vcat, seginds)) == Set(1:8) @@ -101,7 +102,7 @@ end # finds all intersections in a grid segs = facets(cartgrid(10, 10)) - points, seginds = Meshes.bentleyottmann(segs; digits=10) + points, seginds = Meshes.bentleyottmann(segs; digits=digits) @test length(points) == 121 @test length(seginds) == 121 @test Set(length.(seginds)) == Set([2, 3, 4]) @@ -109,7 +110,7 @@ end # result is invariant under rotations segs = collect(segs) for ฮธ in T(ฯ€ / 6):T(ฯ€ / 6):T(2ฯ€ - ฯ€ / 6) - ฮธpoints, ฮธseginds = Meshes.bentleyottmann(segs |> Rotate(ฮธ); digits=10) + ฮธpoints, ฮธseginds = Meshes.bentleyottmann(segs |> Rotate(ฮธ); digits=digits) @test length(ฮธpoints) == 121 @test length(ฮธseginds) == 121 @test Set(length.(ฮธseginds)) == Set([2, 3, 4]) From 33f2fba39b903131823c6f520f532420e1f51fa9 Mon Sep 17 00:00:00 2001 From: souma4 Date: Wed, 19 Mar 2025 16:44:02 -0600 Subject: [PATCH 50/60] hopefully fixed failing test on Float32 values --- test/utils.jl | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/test/utils.jl b/test/utils.jl index 90137b606..2db7e8607 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -65,7 +65,7 @@ end @testitem "Bentley-Ottmann" setup = [Setup] begin # basic check with a small number of segments - digits = -Int(log10(atol(T))) + precision = -Int(log10(atol(T))) segs = Segment.([ (cart(0, 0), cart(1.1, 1.1)), @@ -75,11 +75,12 @@ end (cart(0, 1), cart(1, 1)), (cart(1, 0), cart(1, 1)) ]) - points, seginds = Meshes.bentleyottmann(segs; digits=digits) + points, seginds = Meshes.bentleyottmann(segs; digits=precision) @test all(points .โ‰ˆ [cart(0, 0), cart(0, 1), cart(0.5, 0.5), cart(1, 0), cart(1, 1), cart(1.1, 1.1)]) @test length(points) == 6 @test length(seginds) == 6 @test seginds == [[1, 3, 4], [2, 5, 3], [1, 2], [6, 2, 4], [5, 6, 1], [1]] + @inferred Meshes.bentleyottmann(segs; digits=precision) segs = Segment.([ @@ -92,7 +93,7 @@ end (cart(10, 4), cart(11, -1)), (cart(10, 3), cart(10, 5)) ]) - points, seginds = Meshes.bentleyottmann(segs; digits=digits) + points, seginds = Meshes.bentleyottmann(segs; digits=precision) @test length(points) == 17 @test length(seginds) == 17 @test Set(reduce(vcat, seginds)) == Set(1:8) @@ -102,7 +103,7 @@ end # finds all intersections in a grid segs = facets(cartgrid(10, 10)) - points, seginds = Meshes.bentleyottmann(segs; digits=digits) + points, seginds = Meshes.bentleyottmann(segs; digits=precision) @test length(points) == 121 @test length(seginds) == 121 @test Set(length.(seginds)) == Set([2, 3, 4]) @@ -110,7 +111,7 @@ end # result is invariant under rotations segs = collect(segs) for ฮธ in T(ฯ€ / 6):T(ฯ€ / 6):T(2ฯ€ - ฯ€ / 6) - ฮธpoints, ฮธseginds = Meshes.bentleyottmann(segs |> Rotate(ฮธ); digits=digits) + ฮธpoints, ฮธseginds = Meshes.bentleyottmann(segs |> Rotate(ฮธ); digits=precision) @test length(ฮธpoints) == 121 @test length(ฮธseginds) == 121 @test Set(length.(ฮธseginds)) == Set([2, 3, 4]) @@ -118,5 +119,5 @@ end # inference test segs = facets(cartgrid(10, 10)) - @inferred Meshes.bentleyottmann(segs; digits=10) + @inferred Meshes.bentleyottmann(segs; digits=precision) end From e933e9c5868277a50e84063781649d97d2b8de9e Mon Sep 17 00:00:00 2001 From: souma4 Date: Wed, 19 Mar 2025 17:37:59 -0600 Subject: [PATCH 51/60] I think it's a precision issue, but T wasn't being handled how I thought it would so I'm just hard coding the Float32 tolerance. --- test/utils.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/utils.jl b/test/utils.jl index 2db7e8607..cdd1eaf7a 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -65,7 +65,7 @@ end @testitem "Bentley-Ottmann" setup = [Setup] begin # basic check with a small number of segments - precision = -Int(log10(atol(T))) + precision = Int(4) segs = Segment.([ (cart(0, 0), cart(1.1, 1.1)), From 5e972fc2380681d3fed46ee5fa74773ac5fde16a Mon Sep 17 00:00:00 2001 From: souma4 Date: Wed, 19 Mar 2025 19:21:14 -0600 Subject: [PATCH 52/60] shuffled to reduce redundant computations, particularly specifying the removal of ending segments from the status structure. My local tests suggest roughly logarithmic time, although caching from garbage collection makes it sort of stepwise --- src/utils/intersect.jl | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/src/utils/intersect.jl b/src/utils/intersect.jl index 4dc290465..6d28f5c77 100644 --- a/src/utils/intersect.jl +++ b/src/utils/intersect.jl @@ -59,6 +59,9 @@ function bentleyottmann(segments; kwargs...) _handlebeg!(โ„ฌโ‚š, ๐’ฌ, โ„›, โ„ณ; kwargs...) _handleend!(โ„ฐโ‚š, ๐’ฌ, โ„›, โ„ณ; kwargs...) _handlemid!(โ„ณโ‚š, ๐’ฌ, โ„›, โ„ณ; kwargs...) + # Meshes._handlebeg!(โ„ฌโ‚š, ๐’ฌ, โ„›, โ„ณ; digits=digits) + # Meshes._handleend!(โ„ฐโ‚š, ๐’ฌ, โ„›, โ„ณ; digits=digits) + # Meshes._handlemid!(โ„ณโ‚š, ๐’ฌ, โ„›, โ„ณ; digits=digits) # report intersection point and segment indices inds = [lookup[s] for s in โ„ฌโ‚š โˆช โ„ฐโ‚š โˆช โ„ณโ‚š] @@ -85,6 +88,7 @@ function _handlebeg!(โ„ฌโ‚š, ๐’ฌ, โ„›, โ„ณ; kwargs...) prev, next = BinaryTrees.prevnext(โ„›, s) isnothing(prev) || _newevent!(๐’ฌ, โ„ณ, BinaryTrees.key(prev), s; kwargs...) isnothing(next) || _newevent!(๐’ฌ, โ„ณ, s, BinaryTrees.key(next); kwargs...) + isnothing(prev) || isnothing(next) || _rmevent!(๐’ฌ, s, s; kwargs...) end end @@ -124,20 +128,31 @@ function _newevent!(๐’ฌ, โ„ณ, sโ‚, sโ‚‚; kwargs...) intersection(Segment(sโ‚), Segment(sโ‚‚)) do I if type(I) == Crossing || type(I) == EdgeTouching p = get(I) - pโ€ฒ = roundcoords(p; kwargs...) - if haskey(โ„ณ, pโ€ฒ) - if sโ‚ โˆ‰ โ„ณ[pโ€ฒ] - push!(โ„ณ[pโ€ฒ], sโ‚) - BinaryTrees.insert!(๐’ฌ, pโ€ฒ) + p = roundcoords(p; kwargs...) + if haskey(โ„ณ, p) + if sโ‚ โˆ‰ โ„ณ[p] + push!(โ„ณ[p], sโ‚) + BinaryTrees.insert!(๐’ฌ, p) end - if sโ‚‚ โˆ‰ โ„ณ[pโ€ฒ] - push!(โ„ณ[pโ€ฒ], sโ‚‚) + if sโ‚‚ โˆ‰ โ„ณ[p] + push!(โ„ณ[p], sโ‚‚) end else - โ„ณ[pโ€ฒ] = [sโ‚, sโ‚‚] - BinaryTrees.insert!(๐’ฌ, pโ€ฒ) + โ„ณ[p] = [sโ‚, sโ‚‚] + BinaryTrees.insert!(๐’ฌ, p) end end nothing end end + +function _rmevent!(๐’ฌ, sโ‚, sโ‚‚; kwargs...) + intersection(Segment(sโ‚), Segment(sโ‚‚)) do I + if type(I) == Crossing || type(I) == EdgeTouching + p = get(I) + p = roundcoords(p; kwargs...) + BinaryTrees.delete!(๐’ฌ, p) + end + nothing + end +end From 0ed8c5f903be5eebb8c3b147627d686a5bb025f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Thu, 20 Mar 2025 09:52:24 -0300 Subject: [PATCH 53/60] Minor adjustments --- src/utils/intersect.jl | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/utils/intersect.jl b/src/utils/intersect.jl index 6d28f5c77..f81801d5d 100644 --- a/src/utils/intersect.jl +++ b/src/utils/intersect.jl @@ -127,8 +127,7 @@ end function _newevent!(๐’ฌ, โ„ณ, sโ‚, sโ‚‚; kwargs...) intersection(Segment(sโ‚), Segment(sโ‚‚)) do I if type(I) == Crossing || type(I) == EdgeTouching - p = get(I) - p = roundcoords(p; kwargs...) + p = roundcoords(get(I); kwargs...) if haskey(โ„ณ, p) if sโ‚ โˆ‰ โ„ณ[p] push!(โ„ณ[p], sโ‚) @@ -149,8 +148,7 @@ end function _rmevent!(๐’ฌ, sโ‚, sโ‚‚; kwargs...) intersection(Segment(sโ‚), Segment(sโ‚‚)) do I if type(I) == Crossing || type(I) == EdgeTouching - p = get(I) - p = roundcoords(p; kwargs...) + p = roundcoords(get(I); kwargs...) BinaryTrees.delete!(๐’ฌ, p) end nothing From 0a188650d9c03a8abeabe68f6a643dd3467e09ed Mon Sep 17 00:00:00 2001 From: souma4 Date: Thu, 20 Mar 2025 10:34:53 -0600 Subject: [PATCH 54/60] removed commented out useless code --- src/utils/intersect.jl | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/utils/intersect.jl b/src/utils/intersect.jl index f81801d5d..0f7c55151 100644 --- a/src/utils/intersect.jl +++ b/src/utils/intersect.jl @@ -59,9 +59,6 @@ function bentleyottmann(segments; kwargs...) _handlebeg!(โ„ฌโ‚š, ๐’ฌ, โ„›, โ„ณ; kwargs...) _handleend!(โ„ฐโ‚š, ๐’ฌ, โ„›, โ„ณ; kwargs...) _handlemid!(โ„ณโ‚š, ๐’ฌ, โ„›, โ„ณ; kwargs...) - # Meshes._handlebeg!(โ„ฌโ‚š, ๐’ฌ, โ„›, โ„ณ; digits=digits) - # Meshes._handleend!(โ„ฐโ‚š, ๐’ฌ, โ„›, โ„ณ; digits=digits) - # Meshes._handlemid!(โ„ณโ‚š, ๐’ฌ, โ„›, โ„ณ; digits=digits) # report intersection point and segment indices inds = [lookup[s] for s in โ„ฌโ‚š โˆช โ„ฐโ‚š โˆช โ„ณโ‚š] From e38d2032748e27f0babd1d9153f2e514fd9df53c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Fri, 21 Mar 2025 19:12:46 -0300 Subject: [PATCH 55/60] Minor adjustments --- src/utils/intersect.jl | 6 +++--- test/utils.jl | 17 +++++++++-------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/utils/intersect.jl b/src/utils/intersect.jl index 0f7c55151..6d97e53da 100644 --- a/src/utils/intersect.jl +++ b/src/utils/intersect.jl @@ -41,10 +41,10 @@ function bentleyottmann(segments; kwargs...) end # sweep line algorithm + counter = 1 points = Vector{P}() seginds = Vector{Vector{Int}}() visited = Dict{P,Int}() - i = 1 while !BinaryTrees.isempty(๐’ฌ) # current point (or event) p = BinaryTrees.key(BinaryTrees.minnode(๐’ฌ)) @@ -68,8 +68,8 @@ function bentleyottmann(segments; kwargs...) else push!(points, p) push!(seginds, inds) - push!(visited, p => i) - i += 1 + push!(visited, p => counter) + counter += 1 end end end diff --git a/test/utils.jl b/test/utils.jl index cdd1eaf7a..727902d1f 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -63,9 +63,10 @@ @inferred Meshes.roundcoords(pโ‚, digits=10) end -@testitem "Bentley-Ottmann" setup = [Setup] begin +@testitem "bentleyottmann" setup = [Setup] begin + digits = Int(4) # precision in number of digits + # basic check with a small number of segments - precision = Int(4) segs = Segment.([ (cart(0, 0), cart(1.1, 1.1)), @@ -75,12 +76,12 @@ end (cart(0, 1), cart(1, 1)), (cart(1, 0), cart(1, 1)) ]) - points, seginds = Meshes.bentleyottmann(segs; digits=precision) + points, seginds = Meshes.bentleyottmann(segs; digits) @test all(points .โ‰ˆ [cart(0, 0), cart(0, 1), cart(0.5, 0.5), cart(1, 0), cart(1, 1), cart(1.1, 1.1)]) @test length(points) == 6 @test length(seginds) == 6 @test seginds == [[1, 3, 4], [2, 5, 3], [1, 2], [6, 2, 4], [5, 6, 1], [1]] - @inferred Meshes.bentleyottmann(segs; digits=precision) + @inferred Meshes.bentleyottmann(segs; digits) segs = Segment.([ @@ -93,7 +94,7 @@ end (cart(10, 4), cart(11, -1)), (cart(10, 3), cart(10, 5)) ]) - points, seginds = Meshes.bentleyottmann(segs; digits=precision) + points, seginds = Meshes.bentleyottmann(segs; digits) @test length(points) == 17 @test length(seginds) == 17 @test Set(reduce(vcat, seginds)) == Set(1:8) @@ -103,7 +104,7 @@ end # finds all intersections in a grid segs = facets(cartgrid(10, 10)) - points, seginds = Meshes.bentleyottmann(segs; digits=precision) + points, seginds = Meshes.bentleyottmann(segs; digits) @test length(points) == 121 @test length(seginds) == 121 @test Set(length.(seginds)) == Set([2, 3, 4]) @@ -111,7 +112,7 @@ end # result is invariant under rotations segs = collect(segs) for ฮธ in T(ฯ€ / 6):T(ฯ€ / 6):T(2ฯ€ - ฯ€ / 6) - ฮธpoints, ฮธseginds = Meshes.bentleyottmann(segs |> Rotate(ฮธ); digits=precision) + ฮธpoints, ฮธseginds = Meshes.bentleyottmann(segs |> Rotate(ฮธ); digits) @test length(ฮธpoints) == 121 @test length(ฮธseginds) == 121 @test Set(length.(ฮธseginds)) == Set([2, 3, 4]) @@ -119,5 +120,5 @@ end # inference test segs = facets(cartgrid(10, 10)) - @inferred Meshes.bentleyottmann(segs; digits=precision) + @inferred Meshes.bentleyottmann(segs; digits) end From 893fafdacba7ceb826ff37a6c61ba7edda92135a Mon Sep 17 00:00:00 2001 From: Jeffrey Chandler Date: Fri, 21 Mar 2025 22:33:25 -0600 Subject: [PATCH 56/60] Int is default type for integer literals Co-authored-by: Elias Carvalho <73039601+eliascarv@users.noreply.github.com> --- test/utils.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/utils.jl b/test/utils.jl index 727902d1f..702ee8056 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -64,7 +64,7 @@ end @testitem "bentleyottmann" setup = [Setup] begin - digits = Int(4) # precision in number of digits + digits = 4 # precision in number of digits # basic check with a small number of segments segs = From a10680b3c3abd9326a0d1dae59f8413291b0af6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Sat, 22 Mar 2025 10:13:40 -0300 Subject: [PATCH 57/60] Set digits by default using exponent of absolute tolerance --- src/utils/intersect.jl | 50 +++++++++++++++++++++++++----------------- test/utils.jl | 14 +++++------- 2 files changed, 36 insertions(+), 28 deletions(-) diff --git a/src/utils/intersect.jl b/src/utils/intersect.jl index 6d97e53da..c1633e297 100644 --- a/src/utils/intersect.jl +++ b/src/utils/intersect.jl @@ -3,18 +3,21 @@ # ------------------------------------------------------------------ """ - bentleyottmann(segments; digits) + bentleyottmann(segments; [digits]) Compute pairwise intersections between n `segments` with `digits` precision in O(nโ‹…log(n)) time using Bentley-Ottmann sweep line algorithm. +By default, set `digits` based on the absolute +tolerance of the length type of the segments. + ## References * Bentley & Ottmann 1979. [Algorithms for reporting and counting geometric intersections](https://ieeexplore.ieee.org/document/1675432) """ -function bentleyottmann(segments; kwargs...) +function bentleyottmann(segments; digits=_digits(segments)) # orient segments segs = map(segments) do s a, b = extrema(s) @@ -56,9 +59,9 @@ function bentleyottmann(segments; kwargs...) โ„ฌโ‚š = get(โ„ฌ, p, S[]) # segments with p at the begin โ„ฐโ‚š = get(โ„ฐ, p, S[]) # segments with p at the end โ„ณโ‚š = get(โ„ณ, p, S[]) # segments with p at the middle - _handlebeg!(โ„ฌโ‚š, ๐’ฌ, โ„›, โ„ณ; kwargs...) - _handleend!(โ„ฐโ‚š, ๐’ฌ, โ„›, โ„ณ; kwargs...) - _handlemid!(โ„ณโ‚š, ๐’ฌ, โ„›, โ„ณ; kwargs...) + _handlebeg!(โ„ฌโ‚š, ๐’ฌ, โ„›, โ„ณ, digits) + _handleend!(โ„ฐโ‚š, ๐’ฌ, โ„›, โ„ณ, digits) + _handlemid!(โ„ณโ‚š, ๐’ฌ, โ„›, โ„ณ, digits) # report intersection point and segment indices inds = [lookup[s] for s in โ„ฌโ‚š โˆช โ„ฐโ‚š โˆช โ„ณโ‚š] @@ -77,54 +80,54 @@ function bentleyottmann(segments; kwargs...) points, seginds end -function _handlebeg!(โ„ฌโ‚š, ๐’ฌ, โ„›, โ„ณ; kwargs...) +function _handlebeg!(โ„ฌโ‚š, ๐’ฌ, โ„›, โ„ณ, digits) for s in โ„ฌโ‚š BinaryTrees.insert!(โ„›, s) end for s in โ„ฌโ‚š prev, next = BinaryTrees.prevnext(โ„›, s) - isnothing(prev) || _newevent!(๐’ฌ, โ„ณ, BinaryTrees.key(prev), s; kwargs...) - isnothing(next) || _newevent!(๐’ฌ, โ„ณ, s, BinaryTrees.key(next); kwargs...) - isnothing(prev) || isnothing(next) || _rmevent!(๐’ฌ, s, s; kwargs...) + isnothing(prev) || _newevent!(๐’ฌ, โ„ณ, BinaryTrees.key(prev), s, digits) + isnothing(next) || _newevent!(๐’ฌ, โ„ณ, s, BinaryTrees.key(next), digits) + isnothing(prev) || isnothing(next) || _rmevent!(๐’ฌ, s, s, digits) end end -function _handleend!(โ„ฐโ‚š, ๐’ฌ, โ„›, โ„ณ; kwargs...) +function _handleend!(โ„ฐโ‚š, ๐’ฌ, โ„›, โ„ณ, digits) for s in โ„ฐโ‚š prev, next = BinaryTrees.prevnext(โ„›, s) - isnothing(prev) || isnothing(next) || _newevent!(๐’ฌ, โ„ณ, BinaryTrees.key(prev), BinaryTrees.key(next); kwargs...) + isnothing(prev) || isnothing(next) || _newevent!(๐’ฌ, โ„ณ, BinaryTrees.key(prev), BinaryTrees.key(next), digits) BinaryTrees.delete!(โ„›, s) end end -function _handlemid!(โ„ณโ‚š, ๐’ฌ, โ„›, โ„ณ; kwargs...) +function _handlemid!(โ„ณโ‚š, ๐’ฌ, โ„›, โ„ณ, digits) for s in โ„ณโ‚š prev, next = BinaryTrees.prevnext(โ„›, s) r = !isnothing(prev) ? BinaryTrees.key(prev) : nothing t = !isnothing(next) ? BinaryTrees.key(next) : nothing if !isnothing(r) - _newevent!(๐’ฌ, โ„ณ, r, s; kwargs...) + _newevent!(๐’ฌ, โ„ณ, r, s, digits) if !isnothing(t) - _newevent!(๐’ฌ, โ„ณ, r, t; kwargs...) + _newevent!(๐’ฌ, โ„ณ, r, t, digits) end end if !isnothing(t) _, next = BinaryTrees.prevnext(โ„›, BinaryTrees.key(next)) u = !isnothing(next) ? BinaryTrees.key(next) : nothing if !isnothing(u) - _newevent!(๐’ฌ, โ„ณ, t, u; kwargs...) + _newevent!(๐’ฌ, โ„ณ, t, u, digits) if !isnothing(r) - _newevent!(๐’ฌ, โ„ณ, r, u; kwargs...) + _newevent!(๐’ฌ, โ„ณ, r, u, digits) end end end end end -function _newevent!(๐’ฌ, โ„ณ, sโ‚, sโ‚‚; kwargs...) +function _newevent!(๐’ฌ, โ„ณ, sโ‚, sโ‚‚, digits) intersection(Segment(sโ‚), Segment(sโ‚‚)) do I if type(I) == Crossing || type(I) == EdgeTouching - p = roundcoords(get(I); kwargs...) + p = roundcoords(get(I); digits) if haskey(โ„ณ, p) if sโ‚ โˆ‰ โ„ณ[p] push!(โ„ณ[p], sโ‚) @@ -142,12 +145,19 @@ function _newevent!(๐’ฌ, โ„ณ, sโ‚, sโ‚‚; kwargs...) end end -function _rmevent!(๐’ฌ, sโ‚, sโ‚‚; kwargs...) +function _rmevent!(๐’ฌ, sโ‚, sโ‚‚, digits) intersection(Segment(sโ‚), Segment(sโ‚‚)) do I if type(I) == Crossing || type(I) == EdgeTouching - p = roundcoords(get(I); kwargs...) + p = roundcoords(get(I); digits) BinaryTrees.delete!(๐’ฌ, p) end nothing end end + +function _digits(segments) + s = first(segments) + โ„’ = Meshes.lentype(s) + ฯ„ = ustrip(atol(โ„’)) + round(Int, -log10(ฯ„)) +end diff --git a/test/utils.jl b/test/utils.jl index 702ee8056..3d2f7a740 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -64,8 +64,6 @@ end @testitem "bentleyottmann" setup = [Setup] begin - digits = 4 # precision in number of digits - # basic check with a small number of segments segs = Segment.([ @@ -76,12 +74,12 @@ end (cart(0, 1), cart(1, 1)), (cart(1, 0), cart(1, 1)) ]) - points, seginds = Meshes.bentleyottmann(segs; digits) + points, seginds = Meshes.bentleyottmann(segs) @test all(points .โ‰ˆ [cart(0, 0), cart(0, 1), cart(0.5, 0.5), cart(1, 0), cart(1, 1), cart(1.1, 1.1)]) @test length(points) == 6 @test length(seginds) == 6 @test seginds == [[1, 3, 4], [2, 5, 3], [1, 2], [6, 2, 4], [5, 6, 1], [1]] - @inferred Meshes.bentleyottmann(segs; digits) + @inferred Meshes.bentleyottmann(segs) segs = Segment.([ @@ -94,7 +92,7 @@ end (cart(10, 4), cart(11, -1)), (cart(10, 3), cart(10, 5)) ]) - points, seginds = Meshes.bentleyottmann(segs; digits) + points, seginds = Meshes.bentleyottmann(segs) @test length(points) == 17 @test length(seginds) == 17 @test Set(reduce(vcat, seginds)) == Set(1:8) @@ -104,7 +102,7 @@ end # finds all intersections in a grid segs = facets(cartgrid(10, 10)) - points, seginds = Meshes.bentleyottmann(segs; digits) + points, seginds = Meshes.bentleyottmann(segs) @test length(points) == 121 @test length(seginds) == 121 @test Set(length.(seginds)) == Set([2, 3, 4]) @@ -112,7 +110,7 @@ end # result is invariant under rotations segs = collect(segs) for ฮธ in T(ฯ€ / 6):T(ฯ€ / 6):T(2ฯ€ - ฯ€ / 6) - ฮธpoints, ฮธseginds = Meshes.bentleyottmann(segs |> Rotate(ฮธ); digits) + ฮธpoints, ฮธseginds = Meshes.bentleyottmann(segs |> Rotate(ฮธ)) @test length(ฮธpoints) == 121 @test length(ฮธseginds) == 121 @test Set(length.(ฮธseginds)) == Set([2, 3, 4]) @@ -120,5 +118,5 @@ end # inference test segs = facets(cartgrid(10, 10)) - @inferred Meshes.bentleyottmann(segs; digits) + @inferred Meshes.bentleyottmann(segs) end From 20178a354a430b91e7319a1da8f34c7e173ceefa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BAlio=20Hoffimann?= Date: Sat, 22 Mar 2025 16:11:03 -0300 Subject: [PATCH 58/60] Decrease number of digits by one --- src/utils/intersect.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/intersect.jl b/src/utils/intersect.jl index c1633e297..bfb603cef 100644 --- a/src/utils/intersect.jl +++ b/src/utils/intersect.jl @@ -157,7 +157,7 @@ end function _digits(segments) s = first(segments) - โ„’ = Meshes.lentype(s) + โ„’ = lentype(s) ฯ„ = ustrip(atol(โ„’)) - round(Int, -log10(ฯ„)) + round(Int, -log10(ฯ„)) - 1 end From 01604f140753c55d0a9fac842272b66460b69d00 Mon Sep 17 00:00:00 2001 From: souma4 Date: Sun, 23 Mar 2025 10:38:27 -0600 Subject: [PATCH 59/60] updated script to be simpler. updated tests. THIS FAILS THE GRID TEST. EVENTS ARENT BEING HANDLED PROPERLY. I DONT HAVE TIME TO ADDRESS PROMPTLY --- src/utils/intersect.jl | 4 +--- test/utils.jl | 22 ++++++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/utils/intersect.jl b/src/utils/intersect.jl index bfb603cef..e0b8182d4 100644 --- a/src/utils/intersect.jl +++ b/src/utils/intersect.jl @@ -64,7 +64,7 @@ function bentleyottmann(segments; digits=_digits(segments)) _handlemid!(โ„ณโ‚š, ๐’ฌ, โ„›, โ„ณ, digits) # report intersection point and segment indices - inds = [lookup[s] for s in โ„ฌโ‚š โˆช โ„ฐโ‚š โˆช โ„ณโ‚š] + inds = [lookup[s] for s in โ„ณโ‚š] if !isempty(inds) if p โˆˆ keys(visited) seginds[visited[p]] = inds @@ -83,8 +83,6 @@ end function _handlebeg!(โ„ฌโ‚š, ๐’ฌ, โ„›, โ„ณ, digits) for s in โ„ฌโ‚š BinaryTrees.insert!(โ„›, s) - end - for s in โ„ฌโ‚š prev, next = BinaryTrees.prevnext(โ„›, s) isnothing(prev) || _newevent!(๐’ฌ, โ„ณ, BinaryTrees.key(prev), s, digits) isnothing(next) || _newevent!(๐’ฌ, โ„ณ, s, BinaryTrees.key(next), digits) diff --git a/test/utils.jl b/test/utils.jl index 3d2f7a740..9cb2d85e0 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -75,10 +75,10 @@ end (cart(1, 0), cart(1, 1)) ]) points, seginds = Meshes.bentleyottmann(segs) - @test all(points .โ‰ˆ [cart(0, 0), cart(0, 1), cart(0.5, 0.5), cart(1, 0), cart(1, 1), cart(1.1, 1.1)]) - @test length(points) == 6 - @test length(seginds) == 6 - @test seginds == [[1, 3, 4], [2, 5, 3], [1, 2], [6, 2, 4], [5, 6, 1], [1]] + @test all(points .โ‰ˆ [cart(0.5, 0.5), cart(1, 1)]) + @test length(points) == 2 + @test length(seginds) == 2 + @test seginds == [[1, 2], [1, 5, 6]] @inferred Meshes.bentleyottmann(segs) segs = @@ -93,19 +93,21 @@ end (cart(10, 3), cart(10, 5)) ]) points, seginds = Meshes.bentleyottmann(segs) - @test length(points) == 17 - @test length(seginds) == 17 - @test Set(reduce(vcat, seginds)) == Set(1:8) + @test length(points) == 4 + @test length(seginds) == 4 + @test Set(reduce(vcat, seginds)) == Set(2:8) @test points[findfirst(p -> p โ‰ˆ cart(10, 4), points)] โ‰ˆ cart(10, 4) @test Set(seginds[findfirst(p -> p โ‰ˆ cart(10, 4), points)]) == Set([4, 5, 6, 7, 8]) @test Set(seginds[findfirst(p -> p โ‰ˆ cart(9, 4.8), points)]) == Set([4, 2]) # finds all intersections in a grid - segs = facets(cartgrid(10, 10)) + horizontal = [Segment((1, i), (n, i)) for i in 1:n] + vertical = [Segment((i, 1), (i, n)) for i in 1:n] + segs = [horizontal; vertical] points, seginds = Meshes.bentleyottmann(segs) @test length(points) == 121 @test length(seginds) == 121 - @test Set(length.(seginds)) == Set([2, 3, 4]) + @test Set(length.(seginds)) == Set([2]) # result is invariant under rotations segs = collect(segs) @@ -113,7 +115,7 @@ end ฮธpoints, ฮธseginds = Meshes.bentleyottmann(segs |> Rotate(ฮธ)) @test length(ฮธpoints) == 121 @test length(ฮธseginds) == 121 - @test Set(length.(ฮธseginds)) == Set([2, 3, 4]) + @test Set(length.(ฮธseginds)) == Set([2]) end # inference test From 8345d1ee7e80ad292f8a2a6eb8685b663b19278d Mon Sep 17 00:00:00 2001 From: souma4 Date: Fri, 28 Mar 2025 14:28:20 -0600 Subject: [PATCH 60/60] WIP, With what little time I've had and will have for a bit, here's as far as I got with De Berg implementation. All tests fail. I think this is occuring between the status structure isn't reflecting the order where the plane crosses, but the total order in x and y (so a line with a low x will always be the minimum even if where it intersects the plane is the maximum). I tried having a sort method to maybe get it but that didn't work. I'm sort of at a loss outside of computing intersections to the plane, which I can't think of an efficient way of doing that right now. Maybe y'all will have some ideas --- src/utils/intersect.jl | 195 ++++++++++++++++++++++++++++------------- 1 file changed, 135 insertions(+), 60 deletions(-) diff --git a/src/utils/intersect.jl b/src/utils/intersect.jl index e0b8182d4..4911028f5 100644 --- a/src/utils/intersect.jl +++ b/src/utils/intersect.jl @@ -44,99 +44,174 @@ function bentleyottmann(segments; digits=_digits(segments)) end # sweep line algorithm - counter = 1 points = Vector{P}() seginds = Vector{Vector{Int}}() - visited = Dict{P,Int}() while !BinaryTrees.isempty(๐’ฌ) # current point (or event) p = BinaryTrees.key(BinaryTrees.minnode(๐’ฌ)) # delete point from event queue BinaryTrees.delete!(๐’ฌ, p) - # handle event, i.e. update ๐’ฌ, โ„› and โ„ณ โ„ฌโ‚š = get(โ„ฌ, p, S[]) # segments with p at the begin โ„ฐโ‚š = get(โ„ฐ, p, S[]) # segments with p at the end โ„ณโ‚š = get(โ„ณ, p, S[]) # segments with p at the middle - _handlebeg!(โ„ฌโ‚š, ๐’ฌ, โ„›, โ„ณ, digits) - _handleend!(โ„ฐโ‚š, ๐’ฌ, โ„›, โ„ณ, digits) - _handlemid!(โ„ณโ‚š, ๐’ฌ, โ„›, โ„ณ, digits) - - # report intersection point and segment indices - inds = [lookup[s] for s in โ„ณโ‚š] - if !isempty(inds) - if p โˆˆ keys(visited) - seginds[visited[p]] = inds - else - push!(points, p) - push!(seginds, inds) - push!(visited, p => counter) - counter += 1 + # handle status line + _handlestatus!(โ„›, โ„ฌโ‚š, โ„ณโ‚š, โ„ฐโ‚š) + + if length(โ„ณโ‚š) > 0 + push!(points, p) + inds = [lookup[s] for s in โ„ณโ‚š] + push!(seginds, inds) + end + + activesegs = โ„ฌโ‚š โˆช โ„ณโ‚š + + if isempty(activesegs) + for s in โ„ฐโ‚š + sโ‚—, sแตฃ = BinaryTrees.prevnext(โ„›, s) + isnothing(sโ‚—) || isnothing(sแตฃ) || Meshes._newevent!(๐’ฌ, โ„ณ, p, BinaryTrees.key(sโ‚—), BinaryTrees.key(sแตฃ), digits) end + else + _handlebottom!(activesegs[1], โ„›, ๐’ฌ, โ„ณ, p, digits) + + _handletop!(activesegs[end], โ„›, ๐’ฌ, โ„ณ, p, digits) end end points, seginds end -function _handlebeg!(โ„ฌโ‚š, ๐’ฌ, โ„›, โ„ณ, digits) - for s in โ„ฌโ‚š +function _handlestatus!(โ„›, โ„ฌโ‚š, โ„ณโ‚š, โ„ฐโ‚š) + for s in โ„ฐโ‚š + !isnothing(BinaryTrees.search(โ„›, s)) || deleteat!(โ„ฐโ‚š, findfirst(isequal(s), โ„ฐโ‚š)) || BinaryTrees.delete!(โ„›, s) + end + + for s in โ„ณโ‚š + !isnothing(BinaryTrees.search(โ„›, s)) || deleteat!(โ„ณโ‚š, findfirst(isequal(s), โ„ณโ‚š)) || BinaryTrees.delete!(โ„›, s) + end + + for s in โ„ฌโ‚š โˆช โ„ณโ‚š BinaryTrees.insert!(โ„›, s) - prev, next = BinaryTrees.prevnext(โ„›, s) - isnothing(prev) || _newevent!(๐’ฌ, โ„ณ, BinaryTrees.key(prev), s, digits) - isnothing(next) || _newevent!(๐’ฌ, โ„ณ, s, BinaryTrees.key(next), digits) - isnothing(prev) || isnothing(next) || _rmevent!(๐’ฌ, s, s, digits) end end -function _handleend!(โ„ฐโ‚š, ๐’ฌ, โ„›, โ„ณ, digits) - for s in โ„ฐโ‚š - prev, next = BinaryTrees.prevnext(โ„›, s) - isnothing(prev) || isnothing(next) || _newevent!(๐’ฌ, โ„ณ, BinaryTrees.key(prev), BinaryTrees.key(next), digits) - BinaryTrees.delete!(โ„›, s) +function _handlebottom!(s, โ„›, ๐’ฌ, โ„ณ, p, digits) + sโ€ฒ = BinaryTrees.key(BinaryTrees.search(โ„›, s)) + + sโ‚—, _ = BinaryTrees.prevnext(โ„›, sโ€ฒ) + if !isnothing(sโ‚—) + _newevent!(๐’ฌ, โ„ณ, p, BinaryTrees.key(sโ‚—), sโ€ฒ, digits) end end -function _handlemid!(โ„ณโ‚š, ๐’ฌ, โ„›, โ„ณ, digits) - for s in โ„ณโ‚š - prev, next = BinaryTrees.prevnext(โ„›, s) - r = !isnothing(prev) ? BinaryTrees.key(prev) : nothing - t = !isnothing(next) ? BinaryTrees.key(next) : nothing - if !isnothing(r) - _newevent!(๐’ฌ, โ„ณ, r, s, digits) - if !isnothing(t) - _newevent!(๐’ฌ, โ„ณ, r, t, digits) - end - end - if !isnothing(t) - _, next = BinaryTrees.prevnext(โ„›, BinaryTrees.key(next)) - u = !isnothing(next) ? BinaryTrees.key(next) : nothing - if !isnothing(u) - _newevent!(๐’ฌ, โ„ณ, t, u, digits) - if !isnothing(r) - _newevent!(๐’ฌ, โ„ณ, r, u, digits) - end - end +function _handletop!(s, โ„›, ๐’ฌ, โ„ณ, p, digits) + sโ€ฒโ€ฒ = BinaryTrees.search(โ„›, s) |> BinaryTrees.key + + if !isnothing(sโ€ฒโ€ฒ) + _, sแตฃ = BinaryTrees.prevnext(โ„›, sโ€ฒโ€ฒ) + if !isnothing(sแตฃ) + _newevent!(๐’ฌ, โ„ณ, p, sโ€ฒโ€ฒ, BinaryTrees.key(sแตฃ), digits) end end end -function _newevent!(๐’ฌ, โ„ณ, sโ‚, sโ‚‚, digits) +# #sort by where segment intersects plane then check to โ„›? #doesn't seem to work +# function _sort(segs, p) +# segs = copy(segs) +# if isempty(segs) +# return segs +# end +# _sort!(segs, p) +# end +# function _sort!(segs, p) +# T = lentype(eltype(first(segs))) +# ys = Vector{T}() +# pโ‚“, _ = coords(p) |> CoordRefSystems.values +# for s in segs +# a, b = s +# xโ‚, yโ‚ = coords(a) |> CoordRefSystems.values +# xโ‚‚, yโ‚‚ = coords(b) |> CoordRefSystems.values +# if xโ‚ == xโ‚‚ +# push!(ys, yโ‚‚) # Vertical segment, use yโ‚‚ directly to place at end +# else +# t = (pโ‚“ - xโ‚) / (xโ‚‚ - xโ‚) +# c = yโ‚ + t * (yโ‚‚ - yโ‚) +# push!(ys, c) +# end +# end +# segs[sortperm(ys)] +# end + +# _handlebeg!(โ„ฌโ‚š, ๐’ฌ, โ„›, โ„ณ, digits) +# _handleend!(โ„ฐโ‚š, ๐’ฌ, โ„›, โ„ณ, digits) +# _handlemid!(โ„ณโ‚š, ๐’ฌ, โ„›, โ„ณ, digits) + +# report intersection point and segment indices +# inds = [lookup[s] for s in โ„ณโ‚š] +# if !isempty(inds) +# if p โˆˆ keys(visited) +# seginds[visited[p]] = inds +# else +# push!(points, p) +# push!(seginds, inds) +# push!(visited, p => counter) +# counter += 1 +# end +# end +# function _handlebeg!(โ„ฌโ‚š, ๐’ฌ, โ„›, โ„ณ, digits) +# for s in โ„ฌโ‚š +# BinaryTrees.insert!(โ„›, s) +# prev, next = BinaryTrees.prevnext(โ„›, s) +# isnothing(prev) || _newevent!(๐’ฌ, โ„ณ, BinaryTrees.key(prev), s, digits) +# isnothing(next) || _newevent!(๐’ฌ, โ„ณ, s, BinaryTrees.key(next), digits) +# isnothing(prev) || isnothing(next) || _rmevent!(๐’ฌ, s, s, digits) +# end +# end + +# function _handleend!(โ„ฐโ‚š, ๐’ฌ, โ„›, โ„ณ, digits) +# for s in โ„ฐโ‚š +# prev, next = BinaryTrees.prevnext(โ„›, s) +# isnothing(prev) || isnothing(next) || _newevent!(๐’ฌ, โ„ณ, BinaryTrees.key(prev), BinaryTrees.key(next), digits) +# BinaryTrees.delete!(โ„›, s) +# end +# end + +# function _handlemid!(โ„ณโ‚š, ๐’ฌ, โ„›, โ„ณ, digits) +# for s in โ„ณโ‚š +# prev, next = BinaryTrees.prevnext(โ„›, s) +# r = !isnothing(prev) ? BinaryTrees.key(prev) : nothing +# t = !isnothing(next) ? BinaryTrees.key(next) : nothing +# if !isnothing(r) +# _newevent!(๐’ฌ, โ„ณ, r, s, digits) +# if !isnothing(t) +# _newevent!(๐’ฌ, โ„ณ, r, t, digits) +# end +# end +# if !isnothing(t) +# _, next = BinaryTrees.prevnext(โ„›, BinaryTrees.key(next)) +# u = !isnothing(next) ? BinaryTrees.key(next) : nothing +# if !isnothing(u) +# _newevent!(๐’ฌ, โ„ณ, t, u, digits) +# if !isnothing(r) +# _newevent!(๐’ฌ, โ„ณ, r, u, digits) +# end +# end +# end +# end +# end + +function _newevent!(๐’ฌ, โ„ณ, p, sโ‚, sโ‚‚, digits) intersection(Segment(sโ‚), Segment(sโ‚‚)) do I if type(I) == Crossing || type(I) == EdgeTouching - p = roundcoords(get(I); digits) - if haskey(โ„ณ, p) - if sโ‚ โˆ‰ โ„ณ[p] - push!(โ„ณ[p], sโ‚) - BinaryTrees.insert!(๐’ฌ, p) - end - if sโ‚‚ โˆ‰ โ„ณ[p] - push!(โ„ณ[p], sโ‚‚) + pโ€ฒ = roundcoords(get(I); digits) + if pโ€ฒ > p + !isnothing(BinaryTrees.search(๐’ฌ, pโ€ฒ)) || BinaryTrees.insert!(๐’ฌ, pโ€ฒ) + if haskey(โ„ณ, pโ€ฒ) + push!(โ„ณ[pโ€ฒ], sโ‚, sโ‚‚) + else + โ„ณ[pโ€ฒ] = [sโ‚, sโ‚‚] end - else - โ„ณ[p] = [sโ‚, sโ‚‚] - BinaryTrees.insert!(๐’ฌ, p) end end nothing