Skip to content

Commit da9f582

Browse files
authored
Skip duplicates by default (#218)
1 parent 9134c8e commit da9f582

24 files changed

+132
-77
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,5 @@ docs/Manifest.toml
99
/.vscode
1010
LocalPreferences.toml
1111
docs/src/applications/cell_simulation.mp4
12+
varying_weight_power.mp4
13+
varying_weight.mp4

NEWS.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## 1.6.4
4+
5+
- An error is no longer thrown for inputs with duplicate points. Instead, a warning is thrown and any duplicates are merged into the `skip_points` keyword argument. With this, `DuplicatePointsError` has been removed. To silence the new warning, use `DelaunayTriangulation.toggle_warn_on_dupes!()`.
6+
37
## 1.6.1
48
- Fix issue with clipping Voronoi tessellation dual to a single right-angled triangle. See [#207](https://github.com/JuliaGeometry/DelaunayTriangulation.jl/pull/207)
59

Project.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "DelaunayTriangulation"
22
uuid = "927a84f5-c5f4-47a5-9785-b46e178433df"
33
authors = ["Daniel VandenHeuvel <[email protected]>"]
4-
version = "1.6.3"
4+
version = "1.6.4"
55

66
[deps]
77
AdaptivePredicates = "35492f91-a3bd-45ad-95db-fcad7dcfedb7"

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ ax = Axis(fig[2, 3]; title = "Curve-Bounded", wh...); triplot!(ax, tr
124124
ax = Axis(fig[2, 4]; title = "Disjoint Curve-Bounded", wh...); triplot!(ax, tri8)
125125
ax = Axis(fig[3, 1]; title = "Weighted", wh...); triplot!(ax, tri9)
126126
ax = Axis(fig[3, 2]; title = "Power Diagram", wh...); voronoiplot!(ax, vorn10)
127-
ax = Axis(fig[3, 3]; title = "Generic Clipped Voronoi", wh...); voronoiplot!(ax, vorn11)
127+
ax = Axis(fig[3, 3]; title = "Clipped Voronoi", wh...); voronoiplot!(ax, vorn11, color=:white, strokewidth = 4)
128128
```
129129

130130
![](readme.png)

docs/src/api/triangulation.md

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ refine!
1212
retriangulate
1313
convert_boundary_points_to_indices
1414
check_args
15+
toggle_warn_on_dupes!
1516
get_points
1617
get_triangles
1718
get_boundary_nodes
Loading
Loading
-97 Bytes
Loading

docs/src/figures/voronoi_ex_1.png

-93 Bytes
Loading

docs/src/figures/voronoi_ex_2.png

-755 Bytes
Loading

docs/src/figures/voronoi_ex_3.png

79 Bytes
Loading

docs/src/figures/voronoi_ex_5.png

-1.14 KB
Loading

docs/src/literate_tutorials/clipped_polygon.jl

+4-4
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,11 @@ clipped_vorn = voronoi(tri, clip = true, clip_polygon = clip_polygon)
3636
fig = Figure()
3737
ax1 = Axis(fig[1, 1], title = "Unclipped", width = 600, height = 400)
3838
ax2 = Axis(fig[1, 2], title = "Clipped", width = 600, height = 400)
39-
voronoiplot!(ax1, vorn, show_generators = false, colormap = :matter, strokewidth = 4)
39+
voronoiplot!(ax1, vorn, show_generators = false, color = :white, strokewidth = 4)
4040
xlims!(ax1, -2, 2)
4141
ylims!(ax1, -2, 2)
4242
lines!(ax1, [clip_points..., clip_points[begin]], color = :black, linewidth = 4, linestyle = :dash)
43-
voronoiplot!(ax2, clipped_vorn, show_generators = false, colormap = :matter, strokewidth = 4)
43+
voronoiplot!(ax2, clipped_vorn, show_generators = false, color = :white, strokewidth = 4)
4444
xlims!(ax2, -2, 2)
4545
ylims!(ax2, -2, 2)
4646
resize_to_layout!(fig)
@@ -61,11 +61,11 @@ clipped_vorn = voronoi(tri, clip = true, clip_polygon = clip_polygon)
6161
fig = Figure()
6262
ax1 = Axis(fig[1, 1], title = "Unclipped", width = 600, height = 400)
6363
ax2 = Axis(fig[1, 2], title = "Clipped", width = 600, height = 400)
64-
voronoiplot!(ax1, vorn, show_generators = false, colormap = :matter, strokewidth = 4)
64+
voronoiplot!(ax1, vorn, show_generators = false, color = :white, strokewidth = 4)
6565
xlims!(ax1, -2, 2)
6666
ylims!(ax1, -2, 2)
6767
lines!(ax1, [clip_points..., clip_points[begin]], color = :black, linewidth = 4, linestyle = :dash)
68-
voronoiplot!(ax2, clipped_vorn, show_generators = false, colormap = :matter, strokewidth = 4)
68+
voronoiplot!(ax2, clipped_vorn, show_generators = false, color = :white, strokewidth = 4)
6969
xlims!(ax2, -2, 2)
7070
ylims!(ax2, -2, 2)
7171
resize_to_layout!(fig)

readme.png

27.9 KB
Loading

src/algorithms/triangulation/check_args.jl

+28-25
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
"""
2-
check_args(points, boundary_nodes, hierarchy::PolygonHierarchy, boundary_curves = ()) -> Bool
2+
check_args(points, boundary_nodes, hierarchy::PolygonHierarchy, boundary_curves = (); skip_points = Set{Int}()) -> Bool
33
44
Check that the arguments `points` and `boundary_nodes` to [`triangulate`](@ref), and a constructed
55
[`PolygonHierarchy`](@ref) given by `hierarchy`, are valid. In particular, the function checks:
66
77
- The dimension of the points. If the points are not 2D, a warning is thrown.
8-
- The points are all unique. If they are not, a `DuplicatePointsError` is thrown.
8+
- The points are all unique. If they are not, a warning is thrown and the indices of the duplicates are merged into `skip_points`.
99
- There are at least three points. If there are not, an `InsufficientPointsError` is thrown.
1010
11+
If any duplicate points are found, the indices of the duplicates are merged into `skip_points` in-place.
12+
1113
If `boundary_nodes` are provided, meaning [`has_boundary_nodes`](@ref), then the function also checks:
1214
1315
- If the boundary curves all connect consistently. Here, this means that each section of a boundary curve ends at the start of the next boundary section;
@@ -21,9 +23,9 @@ If `boundary_nodes` are provided, meaning [`has_boundary_nodes`](@ref), then the
2123
Another requirement for [`triangulate`](@ref) is that none of the boundaries intersect in their interior, which also prohibits
2224
interior self-intersections. This is NOT checked. Similarly, segments should not intersect in their interior, which is not checked.
2325
"""
24-
function check_args(points, boundary_nodes, hierarchy, boundary_curves = ())
26+
function check_args(points, boundary_nodes, hierarchy, boundary_curves = (); skip_points = Set{Int}())
2527
check_dimension(points)
26-
has_unique_points(points)
28+
has_unique_points!(skip_points, points)
2729
has_enough_points(points)
2830
has_bnd = has_boundary_nodes(boundary_nodes)
2931
if has_bnd
@@ -33,9 +35,6 @@ function check_args(points, boundary_nodes, hierarchy, boundary_curves = ())
3335
return true
3436
end
3537

36-
struct DuplicatePointsError{P} <: Exception
37-
points::P
38-
end
3938
struct InsufficientPointsError{P} <: Exception
4039
points::P
4140
end
@@ -52,22 +51,6 @@ struct InconsistentOrientationError{I} <: Exception
5251
is_sectioned::Bool
5352
is_curve_bounded::Bool
5453
end
55-
function Base.showerror(io::IO, err::DuplicatePointsError)
56-
points = err.points
57-
dup_seen = find_duplicate_points(points)
58-
println(io, "DuplicatePointsError: There were duplicate points. The following points are duplicated:")
59-
n = length(dup_seen)
60-
ctr = 1
61-
for (p, ivec) in dup_seen
62-
if ctr < n
63-
println(io, " ", p, " at indices ", ivec)
64-
else
65-
print(io, " ", p, " at indices ", ivec, ".")
66-
end
67-
ctr += 1
68-
end
69-
return io
70-
end
7154
function Base.showerror(io::IO, err::InsufficientPointsError)
7255
points = err.points
7356
print(io, "InsufficientPointsError: The provided point set has ", num_points(points), " points, but triangulations require at least three points.")
@@ -116,9 +99,29 @@ function check_dimension(points)
11699
return valid
117100
end
118101

119-
function has_unique_points(points)
102+
function has_unique_points!(skip_points, points)
120103
all_unique = points_are_unique(points)
121-
!all_unique && throw(DuplicatePointsError(points))
104+
if !all_unique
105+
dup_seen = find_duplicate_points(points)
106+
if WARN_ON_DUPES[]
107+
io = IOBuffer()
108+
println(io, "There were duplicate points. Only one of each duplicate will be used, and all other duplicates will be skipped. The indices of the duplicates are:")
109+
end
110+
for (p, ivec) in dup_seen
111+
for j in 2:lastindex(ivec)
112+
push!(skip_points, ivec[j])
113+
end
114+
if WARN_ON_DUPES[]
115+
println(io, " ", p, " at indices ", ivec)
116+
end
117+
end
118+
if WARN_ON_DUPES[]
119+
println(io, "To suppress this warning, call `DelaunayTriangulation.toggle_warn_on_dupes!()`.")
120+
end
121+
if WARN_ON_DUPES[]
122+
@warn String(take!(io))
123+
end
124+
end
122125
return true
123126
end
124127

src/algorithms/triangulation/main.jl

+7-2
Original file line numberDiff line numberDiff line change
@@ -151,10 +151,15 @@ function triangulate(
151151
if isnothing(full_polygon_hierarchy)
152152
full_polygon_hierarchy = construct_polygon_hierarchy(points, boundary_nodes; IntegerType)
153153
end
154-
check_arguments && check_args(points, boundary_nodes, full_polygon_hierarchy, boundary_curves)
154+
skip_points_set = Set{IntegerType}(skip_points)
155+
n = length(skip_points_set)
156+
check_arguments && check_args(points, boundary_nodes, full_polygon_hierarchy, boundary_curves; skip_points = skip_points_set)
157+
if length(skip_points_set) > n
158+
setdiff!(insertion_order, skip_points_set)
159+
end
155160
tri = Triangulation(points; IntegerType, EdgeType, TriangleType, EdgesType, TrianglesType, weights, boundary_curves, boundary_enricher, build_cache = Val(true))
156161
return _triangulate!(
157-
tri, segments, boundary_nodes, predicates, randomise, try_last_inserted_point, skip_points, num_sample_rule, rng, insertion_order,
162+
tri, segments, boundary_nodes, predicates, randomise, try_last_inserted_point, skip_points_set, num_sample_rule, rng, insertion_order,
158163
recompute_representative_points, delete_holes, full_polygon_hierarchy, delete_ghosts, delete_empty_features,
159164
)
160165
end

src/public.jl

+1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
num_generators,
5353
polygon_features,
5454
toggle_inf_warn!,
55+
toggle_warn_on_dupes!,
5556
AbstractParametricCurve,
5657
angle_between,
5758
arc_length,

src/setup.jl

+9
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,15 @@ By default, this warning is enabled.
4848
"""
4949
toggle_inf_warn!() = (INF_WARN[] = !INF_WARN[])
5050

51+
const WARN_ON_DUPES = Ref(true)
52+
"""
53+
toggle_warn_on_dupes!()
54+
55+
Toggle the warning for duplicate points in the input data.
56+
By default, this warning is enabled.
57+
"""
58+
toggle_warn_on_dupes!() = (WARN_ON_DUPES[] = !WARN_ON_DUPES[])
59+
5160
@eval macro $(Symbol("const"))(field)
5261
if VERSION >= v"1.8.0-DEV.1148"
5362
return Expr(:const, esc(field))

test/Project.toml

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
66
ColorSchemes = "35d6a980-a343-548e-a6ea-1d62b119f2f4"
77
DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
88
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
9+
DelaunayTriangulation = "927a84f5-c5f4-47a5-9785-b46e178433df"
910
DelimitedFiles = "8bb1440f-4735-579b-a4ab-409b98df4dab"
1011
Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
1112
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
-212 Bytes
Loading
Loading

test/makie/makie.jl

-1
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,6 @@ end
302302
f
303303
end
304304

305-
306305
@test_reference "Voronoiplot with some custom bounding boxes may not contain all data sites.png" begin
307306
reseed!()
308307
points = [(-3.0, 7.0), (1.0, 6.0), (-1.0, 3.0), (-2.0, 4.0), (3.0, -2.0), (5.0, 5.0), (-4.0, -3.0), (3.0, 8.0)]

test/readme_example.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ ax = Axis(fig[2, 3]; title = "Curve-Bounded", wh...); triplot!(ax, tr
9393
ax = Axis(fig[2, 4]; title = "Disjoint Curve-Bounded", wh...); triplot!(ax, tri8)
9494
ax = Axis(fig[3, 1]; title = "Weighted", wh...); triplot!(ax, tri9)
9595
ax = Axis(fig[3, 2]; title = "Power Diagram", wh...); voronoiplot!(ax, vorn10)
96-
ax = Axis(fig[3, 3]; title = "Clipped Voronoi", wh...); voronoiplot!(ax, vorn11)
96+
ax = Axis(fig[3, 3]; title = "Clipped Voronoi", wh...); voronoiplot!(ax, vorn11, color=:white, strokewidth = 4)
9797

9898
readme_img = joinpath(dirname(dirname(pathof(DelaunayTriangulation))), "readme.png")
9999
@test_reference readme_img fig by = psnr_equality(10)

0 commit comments

Comments
 (0)