Skip to content
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
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Changes

## Unreleased

### Added
- `arc!` primitive to construct circular boundary geometries
- Example showing how to use `arc!`

### Fixed
- `circle!` primitive now constructs the circle based on polar coordinates, i.e. starting at `[r,0]`.

## v2.7.0 October 10, 2026
- Allow for TetGen.jl version 3. Consult the [TetGen.jl README](https://github.com/JuliaGeometry/TetGen.jl/blob/master/README.md) for
information on the different versions the upstream C++ TetGen code and its Julia binary package TetGen_jll.jl.
Expand Down
31 changes: 31 additions & 0 deletions examples/examples2d.jl
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,34 @@ end
# ![](swiss_cheese_2d.png)
#

# ## Domain with an arc boundary
# We can generate domains where a part of the boundary is described through an arc.
# This function constructs a quarter the well know "plate with a hole" geometry.
#
function holeplate_2d(H = 1.0, r = 0.33)
builder = SimplexGridBuilder(; Generator = Triangulate)
p1 = point!(builder, 0, r)
p2 = point!(builder, 0, H)
p3 = point!(builder, H, H)
p4 = point!(builder, H, 0)
p5 = point!(builder, r, 0)

facetregion!(builder, 1)
facet!(builder, p1, p2)
facetregion!(builder, 2)
facet!(builder, p2, p3)
facetregion!(builder, 3)
facet!(builder, p3, p4)
facetregion!(builder, 4)
facet!(builder, p4, p5)
facetregion!(builder, 5)
SimplexGridFactory.arc!(builder, p5, [0.0, 0.0], p1; n = 5)

return simplexgrid(builder, maxvolume = 0.0125)
end
#
# ![](holeplate_2d.png)
#

# ## Remeshing another grid
#
Expand Down Expand Up @@ -351,5 +379,8 @@ function generateplots(picdir; Plotter = nothing)

p = gridplot(glue_2d(); Plotter, size)
Plotter.save(joinpath(picdir, "glue_2d.png"), p)

p = gridplot(holeplate_2d(); Plotter, size)
Plotter.save(joinpath(picdir, "holeplate_2d.png"), p)
end
end
2 changes: 1 addition & 1 deletion src/SimplexGridFactory.jl
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ include("triangle.jl")
include("tetgen.jl")

include("primitives.jl")
export circle!, rect2d!, rect3d!, sphere!, bregions!, moveto!, lineto!
export circle!, arc!, rect2d!, rect3d!, sphere!, bregions!, moveto!, lineto!
export model3d!, mesh3d!

include("plot.jl")
Expand Down
61 changes: 60 additions & 1 deletion src/primitives.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,73 @@ circle!(builder, center, radius; n=20)
Add points and facets approximating a circle.
"""
function circle!(builder::SimplexGridBuilder, center, radius; n = 20)
points = [point!(builder, center[1] + radius * sin(t), center[2] + radius * cos(t)) for t in range(0, 2π; length = n)]
points = [point!(builder, center[1] + radius * cos(t), center[2] + radius * sin(t)) for t in range(0, 2π; length = n)]
for i in 1:(n - 1)
facet!(builder, points[i], points[i + 1])
end
facet!(builder, points[end], points[1])
return builder
end

"""
arc!(builder::SimplexGridBuilder, startpoint, center, endpoint; n = 5)

Add points and facets approximating a counterclockwise arc on a circle.

Note that you probably still need to connect the start- and endpoint to other points with facets.
"""
function arc!(builder, startpoint, center, endpoint; n = 5)
radius, θ = _arcdata(startpoint, center, endpoint, n)
points = [point!(builder, center[1] + radius * cos(t), center[2] + radius * sin(t)) for t in θ]
for i in 1:(length(points) - 1)
facet!(builder, points[i], points[i + 1])
end
return builder
end

"""
arc!(builder, startpoint::Integer, center, endpoint::Integer; n = 5)

Add points and facets approximating a counterclockwise arc on a circle.
Here, start- and endpoint are specified as point ids of points already known to the `builder`.
"""
function arc!(builder, startpoint::Integer, center, endpoint::Integer; n = 5)
radius, θ = _arcdata(builder.pointlist.points[:, startpoint], center, builder.pointlist.points[:, endpoint], n)
points = [point!(builder, center[1] + radius * cos(t), center[2] + radius * sin(t)) for t in θ[2:end]]
facet!(builder, startpoint, points[1])
for i in 1:(length(points) - 1)
facet!(builder, points[i], points[i + 1])
end
facet!(builder, points[end], endpoint)
return builder
end

function _arcdata(startpoint, center, endpoint, n)
a = startpoint - center
b = endpoint - center
radius = sqrt(a[1]^2 + a[2]^2)
if !isapprox(radius, sqrt(b[1]^2 + b[2]^2))
error("Can't construct arc: start and endpoint are not on the same circle.")
end
a /= radius
b /= radius
α = atan(a[2], a[1])
β = atan(b[2], b[1])
# If a point lies in the lower half of the circle, adjust angle
if a[2] < 0
α += 2π
end
if b[2] < 0
β += 2π
end
# Ensure the arc is clockwise
if α >= β
β += 2π
end
return radius, range(α, β; length = n)
end


"""
```
bregions!(builder::SimplexGridBuilder,grid, pairs...)
Expand Down
38 changes: 38 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -426,3 +426,41 @@ end
@info builderplot(t2d(); Plotter = CairoMakie) |> typeof
@test isa(builderplot(t2d(); Plotter = CairoMakie), CairoMakie.Scene)
end

@testset "2d arc primitive" begin
piquart = sqrt(2.0) / 2.0 #x,y value for π/4
function arc_vs_circle2d()
circbuilder = SimplexGridBuilder(; Generator = Triangulate)
arcbuilder = SimplexGridBuilder(; Generator = Triangulate)
center = rand(2)
radius = rand()
a = [center[1] + radius, center[2]]
n = Int64(ceil(rand() * 20.0)) + 3
circle!(circbuilder, center, radius; n = n)
SimplexGridFactory.arc!(arcbuilder, a, center, a; n = n)
for i in 1:(n - 1)
@test isapprox(circbuilder.pointlist.points[:, i], arcbuilder.pointlist.points[:, i])
end
end
function arcs_quadrant1_quadrant3()
b = SimplexGridBuilder(; Generator = Triangulate)
quad1_center = point!(b, piquart, piquart)
quad3_center = point!(b, -piquart, -piquart)
SimplexGridFactory.arc!(b, quad1_center, [0.0, 0.0], quad3_center; n = 3)
SimplexGridFactory.arc!(b, quad3_center, [0.0, 0.0], quad1_center; n = 3)
@test isapprox(b.pointlist.points[:, 3][1], -piquart)
@test isapprox(b.pointlist.points[:, 3][2], piquart)
@test isapprox(b.pointlist.points[:, 4][1], piquart)
@test isapprox(b.pointlist.points[:, 4][2], -piquart)
end
function arc_radius_mismatch()
b = SimplexGridBuilder(; Generator = Triangulate)
p1 = point!(b, piquart, piquart)
p2 = point!(b, -2 * piquart, 2 * piquart)
@test_throws "Can't construct arc: start and endpoint are not on the same circle." SimplexGridFactory.arc!(b, p1, [0.0, 0.0], p2)
end

arc_vs_circle2d()
arcs_quadrant1_quadrant3()
arc_radius_mismatch()
end
Loading