Skip to content

Commit

Permalink
Delaunay recipes (#3159)
Browse files Browse the repository at this point in the history
* Add the new plot recipes

* Use RNG

* Shrink the default markersize

* Don't need the name reference

* Fixed the wrong file for DelaunayTriangulation ref

* Add needs_tight_limits for triplot/voronoiplot, and add bounding boxes to both plot types

* Dropped a name

* News

* Missing RNG

* Update to the new DelaunayTriangulation release, and add customiable bounding box

* Left in deved folders accidentally

* Fix some other edge cases

* Missed a nothing check

* Wait a bit longer to convert the bounding box coordinates

* Add doc pages

* allow passing PointBased data to voronoiplot

* make show_generators updateable

* simplify conversion

* fix conversions, type error, add color passthrough

* fix error due to plot nesting

* rename polygon_color -> color

* fix tests failure

* roll back color name change

* Make sure color order gets respected

* Add conversiosn for triplot, and some more tests

* Remove N

* Tense

* rename polygon_color to color & simplify

* get Colorbar working

* mention voronoiplot in PolarAxis docs

* document other plot signatures

* add PointBased conversions for multiple Matrices

* prototype default boundingbox alternative

* update docs

* fix polygon_color in docstring

* simplify docs

* tweak docstring, scatter render

* point_color -> markercolor, render order tweaks

* show off triangle_color

* fix color hanbdling, tweak render order in GLMakie

* cleanup tests

remove some rendundancies, emphasize details

* prototype arbitrary bounding boxes

* prototype circle clipping

* Change some of the defaults, remove old clipping code

* Fix empty polygon plotting

* Test custom bounding boxes

* simplify conversion

* minor test tweaks

* clip generators to bbox

* allow Rect2 bounding boxes and mixed type tuples

* bring circle clip back & use it by default for PolarAxis

* undo generator clipping change

* Language in docs

* Remove reverse!(cs) so that moving a point slightly gives consistent colors after updating

* Add tests with observables

* Undo reverse! change until consistent rule for local colors implemented

* fix test failure

* clean up observable bookkeeping

* clean up observable updates

* cleanup map in refimg

* merge refimg

* simplify refimg test

* replace color sorting refimg with unit test

* - Use better default markersize
- Use a coordinate-independent definition of color so that they don't rapidly change when moving points slightly

* Fix triplot for disjoint domains

* Fix interior curve detection

* Use method from DT for detecting interior curves

* Remove global declaration

* Wasn't supposed to be a testset

* update name to not error

* fix typo

* Apply suggestions from code review

Co-authored-by: Daniel VandenHeuvel <95613936+DanielVandH@users.noreply.github.com>

* tweak docs

* Update docs/examples/plotting_functions/triplot.md

Co-authored-by: Daniel VandenHeuvel <95613936+DanielVandH@users.noreply.github.com>

* rename bounding_box -> clip

* add Rect2 -> tuple conversion

* add link to this pr to NEWS [skip ci]

---------

Co-authored-by: DanielVandH <danj.vandenheuvel@gmail.com>
Co-authored-by: ffreyer <frederic481994@hotmail.de>
Co-authored-by: Daniel VandenHeuvel <95613936+DanielVandH@users.noreply.github.com>
  • Loading branch information
4 people authored Aug 22, 2023
1 parent 7ad6ad2 commit a4af178
Show file tree
Hide file tree
Showing 14 changed files with 1,007 additions and 31 deletions.
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- Added `xreversed`, `yreversed` and `zreversed` attributes to `Axis3` [#3138](https://github.com/MakieOrg/Makie.jl/pull/3138).
- Fixed incorrect placement of contourlabels with transform functions [#3083](https://github.com/MakieOrg/Makie.jl/pull/3083)
- Fixed automatic normal generation for meshes with shading and no normals [#3041](https://github.com/MakieOrg/Makie.jl/pull/3041).
- Added the `triplot` and `voronoiplot` recipes from DelaunayTriangulation.jl [#3102](https://github.com/MakieOrg/Makie.jl/pull/3102), [#3159](https://github.com/MakieOrg/Makie.jl/pull/3159).

## v0.19.7

Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ ColorSchemes = "3.5"
ColorTypes = "0.8, 0.9, 0.10, 0.11"
Colors = "0.9, 0.10, 0.11, 0.12"
Contour = "0.5, 0.6"
DelaunayTriangulation = "0.6.2, 0.7"
DelaunayTriangulation = "0.8.7"
Distributions = "0.17, 0.18, 0.19, 0.20, 0.21, 0.22, 0.23, 0.24, 0.25"
DocStringExtensions = "0.8, 0.9"
FileIO = "1.6"
Expand Down
196 changes: 194 additions & 2 deletions ReferenceTests/src/tests/examples2d.jl
Original file line number Diff line number Diff line change
Expand Up @@ -732,7 +732,7 @@ end
inner = [n:-1:1; n] # clockwise inner
outer = [(n+1):(2n); n+1] # counter-clockwise outer
boundary_nodes = [[outer], [inner]]
tri = DelaunayTriangulation.triangulate([x'; y'], boundary_nodes = boundary_nodes)
tri = triangulate([x'; y'], boundary_nodes = boundary_nodes)
f, ax, _ = tricontourf(tri, z)
scatter!(x, y, color = z, strokewidth = 1, strokecolor = :black)
f
Expand Down Expand Up @@ -1130,7 +1130,7 @@ end
poly!(Axis(fig[3,1]), Makie.MultiPolygon([p]), color = :green)
poly!(Axis(fig[3,2]), Makie.MultiPolygon([p, q]), color = [:black, :red])
fig
end
end

@reference_test "lines (some with NaNs) with array colors" begin
f = Figure()
Expand All @@ -1157,3 +1157,195 @@ end
z = @. sin(x) * cos(x')
fig, ax = contour(x, x, z, color=RGBAf(1,0,0,0.4), linewidth=6)
end

@reference_test "Triplot with points, ghost edges, and convex hull" begin
pts = RNG.rand(2, 50)
tri = triangulate(pts; rng = RNG.STABLE_RNG)
fig, ax, sc = triplot(tri,
triangle_color = :lightgray, strokewidth = 4,
show_points=true, markersize = 20, markercolor = :orange,
show_ghost_edges=true, ghost_edge_linewidth = 4,
show_convex_hull=true, convex_hull_linewidth = 4

)
fig
end

@reference_test "Triplot of a constrained triangulation with holes and a custom bounding box" begin
curve_1 = [[
(0.0, 0.0), (4.0, 0.0), (8.0, 0.0), (12.0, 0.0), (12.0, 4.0),
(12.0, 8.0), (14.0, 10.0), (16.0, 12.0), (16.0, 16.0),
(14.0, 18.0), (12.0, 20.0), (12.0, 24.0), (12.0, 28.0),
(8.0, 28.0), (4.0, 28.0), (0.0, 28.0), (-2.0, 26.0), (0.0, 22.0),
(0.0, 18.0), (0.0, 10.0), (0.0, 8.0), (0.0, 4.0), (-4.0, 4.0),
(-4.0, 0.0), (0.0, 0.0),
]]
curve_2 = [[
(4.0, 26.0), (8.0, 26.0), (10.0, 26.0), (10.0, 24.0),
(10.0, 22.0), (10.0, 20.0), (8.0, 20.0), (6.0, 20.0),
(4.0, 20.0), (4.0, 22.0), (4.0, 24.0), (4.0, 26.0)
]]
curve_3 = [[(4.0, 16.0), (12.0, 16.0), (12.0, 14.0), (4.0, 14.0), (4.0, 16.0)]]
curve_4 = [[(4.0, 8.0), (10.0, 8.0), (8.0, 6.0), (6.0, 6.0), (4.0, 8.0)]]
curves = [curve_1, curve_2, curve_3, curve_4]
points = [
(2.0, 26.0), (2.0, 24.0), (6.0, 24.0), (6.0, 22.0), (8.0, 24.0), (8.0, 22.0),
(2.0, 22.0), (0.0, 26.0), (10.0, 18.0), (8.0, 18.0), (4.0, 18.0), (2.0, 16.0),
(2.0, 12.0), (6.0, 12.0), (2.0, 8.0), (2.0, 4.0), (4.0, 2.0),
(-2.0, 2.0), (4.0, 6.0), (10.0, 2.0), (10.0, 6.0), (8.0, 10.0), (4.0, 10.0),
(10.0, 12.0), (12.0, 12.0), (14.0, 26.0), (16.0, 24.0), (18.0, 28.0),
(16.0, 20.0), (18.0, 12.0), (16.0, 8.0), (14.0, 4.0), (14.0, -2.0),
(6.0, -2.0), (2.0, -4.0), (-4.0, -2.0), (-2.0, 8.0), (-2.0, 16.0),
(-4.0, 22.0), (-4.0, 26.0), (-2.0, 28.0), (6.0, 15.0), (7.0, 15.0),
(8.0, 15.0), (9.0, 15.0), (10.0, 15.0), (6.2, 7.8),
(5.6, 7.8), (5.6, 7.6), (5.6, 7.4), (6.2, 7.4), (6.0, 7.6),
(7.0, 7.8), (7.0, 7.4)]
boundary_nodes, points = convert_boundary_points_to_indices(curves; existing_points=points)
tri = triangulate(points; boundary_nodes=boundary_nodes, rng = RNG.STABLE_RNG)
refine!(tri, max_area = 1e-3get_total_area(tri), rng = RNG.STABLE_RNG)
fig, ax, sc = triplot(tri,
show_points=true,
show_constrained_edges=true,
constrained_edge_linewidth=2,
strokewidth=0.2,
markersize=15,
point_color=:blue,
show_ghost_edges=true, # not as good because the outer boundary is not convex, but just testing
marker='x',
bounding_box = (-5,20,-5,35)) # also testing the conversion to Float64 for bbox here
fig
end

@reference_test "Triplot with nonlinear transformation" begin
f = Figure()
ax = PolarAxis(f[1, 1])
points = Point2f[(r, phi) for r in 1:10 for phi in range(0, 2pi, length=36)[1:35]]
tr = triplot!(ax, points)
f
end

@reference_test "Triplot after adding points and make sure the representative_point_list is correctly updated" begin
points = [(0.0,0.0),(0.95,0.0),(1.0,1.4),(0.0,1.0)] # not 1 so that we have a unique triangulation
tri = Observable(triangulate(points; delete_ghosts = false))
fig, ax, sc = triplot(tri, show_points = true, markersize = 14, show_ghost_edges = true, recompute_centers = true)
for p in [(0.3, 0.5), (-1.5, 2.3), (0.2, 0.2), (0.2, 0.5)]
add_point!(tri[], p)
end
convex_hull!(tri[])
notify(tri)
ax = Axis(fig[1, 2])
triplot!(ax, tri[], show_points = true, markersize = 14, show_ghost_edges = true, recompute_centers = true)
fig
end

@reference_test "Triplot Showing ghost edges for a triangulation with disjoint boundaries" begin
θ = LinRange(0, 2π, 20) |> collect
θ[end] = 0 # need to make sure that 2π gives the exact same coordinates as 0
xy = Vector{Vector{Vector{NTuple{2,Float64}}}}()
cx = 0.0
for i in 1:2
## Make the exterior circle
push!(xy, [[(cx + cos(θ), sin(θ)) for θ in θ]])
## Now the interior circle - clockwise
push!(xy, [[(cx + 0.5cos(θ), 0.5sin(θ)) for θ in reverse(θ)]])
cx += 3.0
end
boundary_nodes, points = convert_boundary_points_to_indices(xy)
tri = triangulate(points; boundary_nodes=boundary_nodes, check_arguments=false)
fig, ax, sc = triplot(tri, show_ghost_edges=true)
fig
end

@reference_test "Voronoiplot for a centroidal tessellation with an automatic colormap" begin
points = [(0.0,0.0),(1.0,0.0),(1.0,1.0),(0.0,1.0)]
tri = triangulate(points; boundary_nodes = [1,2,3,4,1], rng = RNG.STABLE_RNG)
refine!(tri; max_area=1e-2, min_angle = 29.871, rng = RNG.STABLE_RNG)
vorn = voronoi(tri)
smooth_vorn = centroidal_smooth(vorn; maxiters = 250, rng = RNG.STABLE_RNG)
cmap = cgrad(:matter)
fig, ax, sc = voronoiplot(smooth_vorn, markersize=10, strokewidth = 4, markercolor = :red)
fig
end

@reference_test "Voronoiplot for a tessellation with a custom bounding box" begin
pts = 25RNG.randn(2, 50)
tri = triangulate(pts; rng = RNG.STABLE_RNG)
vorn = voronoi(tri, false)
fig, ax, sc = voronoiplot(vorn,
show_generators=true,
colormap=:RdBu,
strokecolor=:white,
strokewidth=4,
markersize=25,
marker = 'x',
markercolor=:green,
unbounded_edge_extension_factor=5.0)
xlims!(ax, -120, 120)
ylims!(ax, -120, 120)
fig
end

@reference_test "Voronoiplots with clipped tessellation and unbounded polygons" begin
pts = 25RNG.randn(2, 10)
tri = triangulate(pts; rng = RNG.STABLE_RNG)
vorn = voronoi(tri, true)
fig, ax, sc = voronoiplot(vorn, color = (:blue,0.2), markersize = 20, strokewidth = 4)

# used to be bugged
points = [(0.0, 1.0), (-1.0, 2.0), (-2.0, -1.0)]
tri = triangulate(points)
vorn = voronoi(tri)
voronoiplot(fig[1,2], vorn, show_generators = true, strokewidth = 4,
color = [:red, :blue, :green], markercolor = :white, markersize = 20)

fig
end

@reference_test "Voronoiplot with a nonlinear transform" begin
f = Figure()
ax = PolarAxis(f[1, 1])
points = Point2f[(r, phi) for r in 1:10 for phi in range(0, 2pi, length=36)[1:35]]
polygon_color = [r for r in 1:10 for phi in range(0, 2pi, length=36)[1:35]]
polygon_color_2 = [phi for r in 1:10 for phi in range(0, 2pi, length=36)[1:35]]
tr = voronoiplot!(ax, points, smooth = false, show_generators = false, color = polygon_color)
Makie.rlims!(ax, 12) # to make rect clip visible if circular clip doesn't happen
ax = PolarAxis(f[1, 2])
tr = voronoiplot!(ax, points, smooth = true, show_generators = false, color = polygon_color_2)
Makie.rlims!(ax, 12)
f
end

@reference_test "Voronoiplot with some custom bounding boxes may not contain all data sites" begin
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)]
tri = triangulate(points)
vorn = voronoi(tri)
color = [:red, :blue, :green, :yellow, :cyan, :magenta, :black, :brown] # the polygon colors should not change even if some are not included (because they're outside of the box)
fig = Figure()
ax1 = Axis(fig[1, 1], title = "Default")
voronoiplot!(ax1, vorn, show_generators = true, markersize=14, strokewidth = 4, color = color)
ax2 = Axis(fig[1, 2], title = "Some excluded")
voronoiplot!(ax2, vorn, show_generators = true, markersize=14, strokewidth = 4, color = color, clip = BBox(0.0, 5.0, -15.0, 15.0))
ax3 = Axis(fig[2, 1], title = "Bigger range")
voronoiplot!(ax3, vorn, show_generators = true, markersize=14, strokewidth = 4, color = color, clip = (-15.0, 15.0, -15.0, 15.0))
ax4 = Axis(fig[2, 2], title = "Only one polygon")
voronoiplot!(ax4, vorn, show_generators = true, markersize=14, strokewidth = 4, color = color, clip = (10.0, 12.0, 2.0, 5.0))
for ax in fig.content
xlims!(ax4, -15, 15)
ylims!(ax4, -15, 15)
end
fig
end

@reference_test "Voronoiplot after adding points" begin
points = Observable([(0.0,0.0), (1.0,0.0), (1.0,1.0), (0.0,1.0)])
fig, ax, sc = voronoiplot(points, show_generators=true, markersize=36) # make sure any regressions with missing generators are identified, so use 36
push!(points[], (2.0, 2.0), (0.5, 0.5), (0.25, 0.25), (0.25, 0.75), (0.75, 0.25), (0.75, 0.75))
notify(points)
ax2 = Axis(fig[1, 2])
voronoiplot!(ax2, voronoi(triangulate(points[])), show_generators=true, markersize=36)
xlims!(ax,-0.5,2.5)
ylims!(ax,-0.5,2.5)
xlims!(ax2,-0.5,2.5)
ylims!(ax2,-0.5,2.5) # need to make sure all generators are shown, and the bounding box is automatically updated
fig
end
56 changes: 36 additions & 20 deletions docs/examples/blocks/polaraxis.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# PolarAxis

The `PolarAxis` is an axis for data in polar coordinates `(radius, angle)`. It
The `PolarAxis` is an axis for data in polar coordinates `(radius, angle)`. It
is currently an experimental feature, meaning that some functionality might be
missing or broken, and that the `PolarAxis` is (more) open to breaking changes.

Expand All @@ -23,7 +23,7 @@ f

## Plotting into an PolarAxis

Like with an `Axis` you can use mutating 2D plot functions directly on a
Like with an `Axis` you can use mutating 2D plot functions directly on a
`PolarAxis`. The input arguments of the plot functions will then be interpreted
in polar coordinates, i.e. as a radius and angle (in radians).

Expand All @@ -36,20 +36,36 @@ f
```
\end{examplefigure}

Note that not every plot type is compatible with polar transforms. For example
`image` is not as it expects to be drawn on a rectangle. `heatmap` works to a
degree in CairoMakie, but not GLMakie due to differences in the backend
implementation. `surface` works in both, as it is designed to generate a
triangle mesh.
Note that not every plot type is compatible with polar transforms. For example
`image` is not as it expects to be drawn on a rectangle. `heatmap` works to a
degree in CairoMakie, but not GLMakie due to differences in the backend
implementation.
`surface` can be used as a replacement for `image` as it generates a triangle
mesh. However it also has a component in z-direction which will affect drawing
order. You can use `translate!(plot, 0, 0, z_shift)` to work around that.
As a replacement for `heatmap` you can use `voronoiplot`, which generates cells
of arbitrary shape around points given to it. Here you will generally need to
set `rlims!(ax, rmax)` yourself.

\begin{examplefigure}{svg = false}
```julia
f = Figure()
ax = PolarAxis(f[1, 1])
# The first two arguments should be set to a sensible radial and angular range
zs = [r*cos(phi) for r in range(1, 2, length=100), phi in range(0, 4pi, length=100)]
p = surface!(ax, 0..10, 0..2pi, zs, shading = false, colormap = :coolwarm, colorrange=(-2, 2))
Colorbar(f[1, 2], p)
f = Figure(resolution = (800, 500))

ax = PolarAxis(f[1, 1], title = "Surface")
rs = 0:10
phis = range(0, 2pi, 37)
cs = [r+cos(4phi) for r in rs, phi in phis]
p = surface!(ax, 0..10, 0..2pi, cs, shading = false, colormap = :coolwarm)
Colorbar(f[2, 1], p, vertical = false, flipaxis = false)

ax = PolarAxis(f[1, 2], title = "Voronoi")
rs = 1:10
phis = range(0, 2pi, 37)[1:36]
cs = [r+cos(4phi) for r in rs, phi in phis]
p = voronoiplot!(ax, rs, phis, cs, show_generators = false, strokewidth = 0)
Makie.rlims!(ax, 10.5)
Colorbar(f[2, 2], p, vertical = false, flipaxis = false)

f
```
\end{examplefigure}
Expand All @@ -75,17 +91,17 @@ f
```
\end{examplefigure}

Decorations such as grid lines and tick labels can be adjusted through
Decorations such as grid lines and tick labels can be adjusted through
attributes in much the same way.

\begin{examplefigure}{svg = true}
```julia
f = Figure(resolution = (600, 600), backgroundcolor = :black)
ax = PolarAxis(
f[1, 1],
f[1, 1],
backgroundcolor = :black,
# r minor grid
rminorgridvisible = true, rminorgridcolor = :red,
rminorgridvisible = true, rminorgridcolor = :red,
rminorgridwidth = 1.0, rminorgridstyle = :dash,
# theta minor grid
thetaminorgridvisible = true, thetaminorgridcolor = :lightblue,
Expand All @@ -106,12 +122,12 @@ f

## Interactivity

The `PolarAxis` currently implements zooming by scrolling and allows you to
The `PolarAxis` currently implements zooming by scrolling and allows you to
reset the view with left control + left mouse button. You can change the key
combination for resetting the view with the `reset_button` attribute, which
combination for resetting the view with the `reset_button` attribute, which
accepts anything `ispressed` accepts.

Note that `PolarAxis` currently does not implement the interaction itnerface
Note that `PolarAxis` currently does not implement the interaction itnerface
used by `Axis`.

## Other Notes
Expand All @@ -121,7 +137,7 @@ used by `Axis`.
Currently there is a scatter and poly plot outside the area of the `PolarAxis`
which clips the content to the relevant area. If you want to draw outside the
circle limiting the polar axis but still within it's scene area, you will need
to translate those plots to a z range between `9000` and `10_000` or disable
to translate those plots to a z range between `9000` and `10_000` or disable
clipping via the `clip` attribute.

## Attributes
Expand Down
Loading

0 comments on commit a4af178

Please sign in to comment.