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 comments explaining compute_bowyer_envelope! #38

Merged
merged 3 commits into from
Jul 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
28 changes: 25 additions & 3 deletions src/interpolation/utils.jl
Original file line number Diff line number Diff line change
@@ -1,14 +1,30 @@
function compute_bowyer_envelope!(envelope, tri::Triangulation, history::InsertionEventHistory, temp_adjacent::Adjacent, point; kwargs...) #kwargs are add_point! kwargs
#=
`add_point!` uses the Bowyer-Watson algorithm to add points.
The Bowyer-Watson algorithm removes all triangles whose circumcenters contain
the point to be inserted, creating a hole which is then retriangulated with the
new point included.

What happens here is that we record the new triangles added in `history`, and then
extract those triangles - we know that these changed after the new point was added,
so they are the "natural neighbours" of the new point.
=#
empty!(history)
empty!(envelope)
empty!(get_adjacent(temp_adjacent))
n = num_points(tri)
I = integer_type(tri)
# Note that the `peek` keyword here indicates that the original triangulation
# is never modified - but the new triangles are added to `history`.
V = add_point!(tri, point; store_event_history=Val(true), event_history=history, peek=Val(true), kwargs...)
all_triangles = each_added_triangle(history)
if isempty(all_triangles) # This is possible for constrained triangulations
# To clarify, this means that no triangles were added. This means the point
# has no natural neighbours.
return envelope, temp_adjacent, history, V
end
# Add triangles to the `DelaunayTriangulation.Adjacent` dict,
# which stores the ((u, v)::Edge => w::Point` adjacency relationship.
for T in all_triangles
add_triangle!(temp_adjacent, T)
end
Expand All @@ -17,10 +33,16 @@ function compute_bowyer_envelope!(envelope, tri::Triangulation, history::Inserti
v = i == I(n + 1) ? j : i # get a vertex on the envelope
push!(envelope, v)
for i in 2:num_triangles(all_triangles)
v = get_adjacent(temp_adjacent, I(n + 1), v)
#=
Get the vertex associated with the edge `(point, points[v])` such that the
returned point (the new `v`) forms a positively oriented triangle
with that edge. Then, add the newly found `v` to the envelope.
To be clear, `n+1` is the index of the newly inserted vertex in the triangulation.
=#
v = get_adjacent(temp_adjacent, I(n + 1), v)
push!(envelope, v)
end
Comment on lines 35 to 44
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not 100% sure what this loop is doing - is there some guarantee that all triangles added have n+1 as a vertex, and we're just getting each individual natural neighbour from this loop?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That loop is building up the "envelope". All triangles will have n+1 as a vertex. I'll draw a picture

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha - that's what I thought was happening but just wanted to confirm. Thanks!

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

By pivoting around n+1 (which I've called i+1 in the image for some reason), we can traverse the boundary of the envelope counter-clockwise by looping over the triangles. The orientation is important since we compute areas later

push!(envelope, envelope[begin])
push!(envelope, envelope[begin]) # Close out the envelope polygon
return envelope, temp_adjacent, history, V
end
function compute_bowyer_envelope!(envelope, tri::Triangulation, point; kwargs...)
Expand Down Expand Up @@ -241,4 +263,4 @@ function hessian_form(tri, u, v, w, N₀, H) # zᵢ,ⱼₖ
dxᵢₖ = xₖ[1] - xᵢ[1]
dyᵢₖ = xₖ[2] - xᵢ[2]
return dxᵢⱼ * (dxᵢₖ * B₁₁ + dyᵢₖ * B₁₂) + dyᵢⱼ * (dxᵢₖ * B₁₂ + dyᵢₖ * B₂₂)
end
end
20 changes: 10 additions & 10 deletions test/doc_examples/differentiation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,12 @@ tri = triangulate(points)
∇g = generate_gradients(tri, z)
fig, ε = plot_gradients(∇g, tri, f′, x, y)
@test_reference normpath(@__DIR__, "../..", "docs", "src", "figures", "gradient_data.png") fig
@test ε ≈ 10.2511800
@test ε ≈ 10.2511800 rtol=1e-3

∇gr, _ = generate_derivatives(tri, z; method=Direct())
fig, ε = plot_gradients(∇gr, tri, f′, x, y)
@test_reference normpath(@__DIR__, "../..", "docs", "src", "figures", "joint_gradient_data.png") fig
@test ε ≈ 7.7177915977
@test ε ≈ 7.7177915977 rtol=1e-3

# Hessians
to_mat(H::NTuple{3,Float64}) = [H[1] H[3]; H[3] H[2]]
Expand All @@ -128,17 +128,17 @@ function plot_hessians(H, tri, f′′, x, y)
end
_, Hg = generate_derivatives(tri, z)
fig, ε = plot_hessians(Hg, tri, f′′, x, y)
@test ε ≈ 42.085578794
@test ε ≈ 42.085578794 rtol=1e-3
@test_reference normpath(@__DIR__, "../..", "docs", "src", "figures", "hessian_data.png") fig

_, Hg = generate_derivatives(tri, z, use_cubic_terms=false)
fig, ε = plot_hessians(Hg, tri, f′′, x, y)
@test ε ≈ 35.20873081
@test ε ≈ 35.20873081 rtol=1e-3
@test_reference normpath(@__DIR__, "../..", "docs", "src", "figures", "hessian_data_no_cubic.png") fig

_, Hg = generate_derivatives(tri, z, method=Iterative()) # the gradients will be generated first automatically
fig, ε = plot_hessians(Hg, tri, f′′, x, y)
@test ε ≈ 39.584816
@test ε ≈ 39.584816 rtol=1e-3
@test_reference normpath(@__DIR__, "../..", "docs", "src", "figures", "hessian_data_iterative.png") fig

# Away from the data sites
Expand Down Expand Up @@ -190,7 +190,7 @@ function plot_gradients(∇g, f′, xg, yg)
end
fig, ε = plot_gradients(∇g, f′, xg, yg)
@test_reference normpath(@__DIR__, "../..", "docs", "src", "figures", "gradient_surface.png") fig
@test ε ≈ 13.1857476
@test ε ≈ 13.1857476 rtol=1e-1

other_methods = [Sibson(), Laplace(), Nearest(), Triangle()]
∇gs = [∂(_x, _y; interpolant_method=method) for method in other_methods]
Expand Down Expand Up @@ -236,8 +236,8 @@ figH, εH = plot_hessians(Hg, f′′, xg, yg)
zlims!(figH.content[4], -25, 25)
@test_reference normpath(@__DIR__, "../..", "docs", "src", "figures", "hessian_surface_direct.png") figH
@test_reference normpath(@__DIR__, "../..", "docs", "src", "figures", "gradient_surface_2_direct.png") fig∇
@test εH ≈ 46.7610990050276
@test ε∇ ≈ 9.853286514069882
@test εH ≈ 46.7610990050276 rtol=1e-1
@test ε∇ ≈ 9.853286514069882 rtol=1e-1

function rrmserr(z, ẑ, ∂, x, y)
tri = ∂.interpolant.triangulation
Expand All @@ -259,5 +259,5 @@ end

εH_nohull = rrmserr(f′′.(_x, _y), to_mat.(Hg), ∂, _x, _y)
ε∇_nohull = rrmserr(f′.(_x, _y), ∇g, ∂, _x, _y)
@test ε∇_nohull ≈ 7.479964687673911
@test εH_nohull ≈ 38.884740966379056
@test ε∇_nohull ≈ 7.479964687673911 rtol=1e-2
@test εH_nohull ≈ 38.884740966379056 rtol=1e-2
Loading