Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add Bentley-Ottman Algorithm #1168

Open
wants to merge 62 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
c5a3b90
add test segment. proper test later
souma4 Feb 11, 2025
e004810
Clean single commit for BentleyOttman algorithm. Essentially, it outp…
souma4 Feb 11, 2025
491a130
test for valid answer
souma4 Feb 11, 2025
840c1a5
moved sweepline algorithm to Intersections since it is dependent on i…
souma4 Feb 11, 2025
6f018a3
Update Doc for bentley ottman implementation to specify output
souma4 Feb 17, 2025
eb0d56b
update project.toml to include BinaryTrees dependency
souma4 Feb 17, 2025
b869b6e
major commit. Moved sweepline from intersections to utils along with …
souma4 Feb 24, 2025
6be63f1
new_type and new_geom didn't meet style, so fixed. Removed an unneede…
souma4 Feb 24, 2025
ed09d87
forgot to rename _intersection to _pushintersection
souma4 Feb 24, 2025
2b83b1a
small commits to improve compatibility and fix mixed up old names
souma4 Mar 4, 2025
7484dc1
small updates to License text, cases, variable and function names, cl…
souma4 Mar 6, 2025
16791eb
Merge branch 'JRC_add_BentleyOttman' of https://github.com/souma4/Mes…
souma4 Mar 6, 2025
ec4c593
Updated algorithm to be simpler, output is points and segment indices…
souma4 Mar 7, 2025
e18eb99
updated tests to reflect changed output type
souma4 Mar 8, 2025
e27d9f1
add compat for BinaryTrees
souma4 Mar 9, 2025
56018d3
fixed names of variables and helper functions. removed output type f…
souma4 Mar 9, 2025
442861b
updated utils include path and refactored if else to new function for…
souma4 Mar 10, 2025
0cc5d91
Minor adjustments before review
juliohm Mar 10, 2025
1c03947
More fixes
juliohm Mar 11, 2025
00e2151
changed S = Tuple{P, P} to V and changed the empty vectors of S to ca…
souma4 Mar 11, 2025
83093e4
the s iter variable was modified to segment using the same name, so …
souma4 Mar 12, 2025
76d8be3
Rename V -> S
juliohm Mar 13, 2025
1e36abe
More refactoring
juliohm Mar 13, 2025
d590d36
Rename \scrT to \scrR to match paper notation, and avoid confusion wi…
juliohm Mar 14, 2025
93ce4a6
Refactor _newevent to improve readability
juliohm Mar 14, 2025
9fdaf93
More refactoring to improve readability
juliohm Mar 14, 2025
353cf70
More refactoring to improve readability and performance
juliohm Mar 14, 2025
d136b6e
More refactoring to improve readability
juliohm Mar 14, 2025
602a579
More refactoring
juliohm Mar 14, 2025
36e2497
More refactoring
juliohm Mar 14, 2025
f5ff53e
More refactoring
juliohm Mar 14, 2025
0004c36
realigned more with original algorithm and reduced recomputation
souma4 Mar 14, 2025
dd88813
More refactoring
juliohm Mar 14, 2025
2fb7e10
More refactoring
juliohm Mar 14, 2025
8e618e0
More refactoring
juliohm Mar 14, 2025
2850538
More refactoring
juliohm Mar 14, 2025
7762044
Add more tests
juliohm Mar 14, 2025
3f42e7d
Merge branch 'JuliaGeometry:master' into JRC_add_BentleyOttman
souma4 Mar 14, 2025
e288e23
edited the processing of intersections to better reflect the original…
souma4 Mar 14, 2025
5a21d3a
More refactoring
juliohm Mar 15, 2025
165ddae
Refactor tests
juliohm Mar 15, 2025
3ed853d
Add more tests
juliohm Mar 15, 2025
859c846
Improve tests
juliohm Mar 15, 2025
9732322
Fix formatting
juliohm Mar 15, 2025
98f0786
More refactoring
juliohm Mar 15, 2025
042e690
Add test with infinite loop
juliohm Mar 15, 2025
c9d933f
Fix formatting
juliohm Mar 15, 2025
219add3
update to stop infinite loops, but has major TODOs to handle floating…
souma4 Mar 15, 2025
89a8d98
reverting prior to dictionary implementation
souma4 Mar 16, 2025
3078412
Merge branch 'JuliaGeometry:master' into JRC_add_BentleyOttman
souma4 Mar 17, 2025
fdb936b
fully functioning not infinitely looping bentleyottman algorithm all …
souma4 Mar 17, 2025
5ef6505
cleaned up test outputs and fixed tests to be T agnostic
souma4 Mar 17, 2025
33f2fba
hopefully fixed failing test on Float32 values
souma4 Mar 19, 2025
e933e9c
I think it's a precision issue, but T wasn't being handled how I thou…
souma4 Mar 19, 2025
5e972fc
shuffled to reduce redundant computations, particularly specifying th…
souma4 Mar 20, 2025
0ed8c5f
Minor adjustments
juliohm Mar 20, 2025
0a18865
removed commented out useless code
souma4 Mar 20, 2025
e38d203
Minor adjustments
juliohm Mar 21, 2025
893fafd
Int is default type for integer literals
souma4 Mar 22, 2025
a10680b
Set digits by default using exponent of absolute tolerance
juliohm Mar 22, 2025
20178a3
Decrease number of digits by one
juliohm Mar 22, 2025
01604f1
updated script to be simpler. updated tests. THIS FAILS THE GRID TEST…
souma4 Mar 23, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -22,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"
Expand All @@ -42,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"
1 change: 1 addition & 0 deletions src/Meshes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

module Meshes

using BinaryTrees
using CoordRefSystems
using StaticArrays
using SparseArrays
Expand Down
1 change: 1 addition & 0 deletions src/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ include("utils/cmp.jl")
include("utils/units.jl")
include("utils/crs.jl")
include("utils/misc.jl")
include("utils/intersect.jl")
149 changes: 149 additions & 0 deletions src/utils/intersect.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
# ------------------------------------------------------------------
# Licensed under the MIT License. See LICENSE in the project root.
# ------------------------------------------------------------------

"""
bentleyottmann(segments)

Compute pairwise intersections between n `segments` 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)
# orient segments
segs = map(segments) do s
a, b = extrema(s)
a > b ? (b, a) : (a, b)
end

# retrieve types
P = eltype(first(segs))
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, (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)])
lookup[(a, b)] = i
end

# 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, 𝒬, ℛ, ℬ, ℰ, ℳ)
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)
end
end

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
if !isnothing(next)
_newevent!(𝒬, ℳ, s, BinaryTrees.key(next))
end
end
end

function _processend!(ℰₚ, 𝒬, ℛ, ℳ)
for s in ℰₚ
prev, next = BinaryTrees.prevnext(ℛ, s)
if !isnothing(prev) && !isnothing(next)
_newevent!(𝒬, ℳ, BinaryTrees.key(next), BinaryTrees.key(prev))
end
BinaryTrees.delete!(ℛ, s)
end
end

function _processmid!(ℳₚ, 𝒬, ℛ, ℳ)
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))
end
if !isnothing(u)
_newevent!(𝒬, ℳ, BinaryTrees.key(u), s)
end
end
end
end

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₂)
else
ℳ[p] = [s₁, s₂]
end
end
nothing

Check warning on line 130 in src/utils/intersect.jl

View check run for this annotation

Codecov / codecov/patch

src/utils/intersect.jl#L130

Added line #L130 was not covered by tests
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

Check warning on line 139 in src/utils/intersect.jl

View check run for this annotation

Codecov / codecov/patch

src/utils/intersect.jl#L139

Added line #L139 was not covered by tests
end
end

_isempty(𝒬) = isnothing(BinaryTrees.root(𝒬))

function _segmentswith(𝒟, p)
P = typeof(p)
S = Tuple{P,P}
get(𝒟, p, S[])
end
20 changes: 20 additions & 0 deletions test/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,23 @@
p = latlon(c) |> Proj(Cartesian)
@inferred Meshes.withcrs(p, c, LatLon)
end

@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)),
Segment(cart(0, 0), cart(1, 0)),
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]]
end
Loading