From 032236dc01457d66649fd5887d07b1c28885656d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Fuhrmann?= Date: Wed, 23 Sep 2026 15:57:45 +0200 Subject: [PATCH 01/35] Update upstream version to v1.6.1 * adapt runtests to slightly different grid output --- Project.toml | 4 ++-- test/runtests.jl | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Project.toml b/Project.toml index 58bb6bc..d3e26c7 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "TetGen" uuid = "c5d3f3f7-f850-59f6-8a2e-ffc6dc1317ea" -authors = ["SimonDanisch ", "Juergen Fuhrmann ", "Juergen Fuhrmann 100 + @test numberoftetrahedra(result) > 20 generic_test(result) end From a39ecba8298a2bd8a542a7544c2c9da3a876ee0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Fuhrmann?= Date: Wed, 23 Sep 2026 15:59:14 +0200 Subject: [PATCH 02/35] Add save_poly and save_nodes methods Check for size() of point list in RawFacet --- src/rawtetgenio.jl | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/rawtetgenio.jl b/src/rawtetgenio.jl index 008beaf..2a59733 100644 --- a/src/rawtetgenio.jl +++ b/src/rawtetgenio.jl @@ -13,7 +13,7 @@ struct RawFacet{T} polygonlist::Array{Array{Cint, 1}, 1} """ - Array of points given by their coordinates + `3 x npoints` array of points given by their coordinates marking polygons describing holes in the facet. """ holelist::Array{T, 2} @@ -357,6 +357,9 @@ function CPPTetGenIO(tio::RawTetGenIO{T}) where {T} for ipolygon in 1:numberofpolygons polygonlist[ipolygon] = CPolygon(pointer(facet.polygonlist[ipolygon]), size(facet.polygonlist[ipolygon], 1)) end + if length(facet.holelist) > 0 + @assert size(facet.holelist, 1) == 3 + end numberofholes = size(facet.holelist, 2) push!(polygonlist_array, polygonlist) push!( @@ -700,6 +703,16 @@ function tetrahedralize(stlfile::String, flags::String) return RawTetGenIO(coutput) end +function save_poly(input::RawTetGenIO{Float64}, fstub::String) + cinput, flist, plist = CPPTetGenIO(input) + return ccall((:save_poly, libtet), Cvoid, (CPPTetGenIO{Float64}, Cstring), cinput, fstub) +end + +function save_nodes(input::RawTetGenIO{Float64}, fstub::String) + cinput, flist, plist = CPPTetGenIO(input) + return ccall((:save_nodes, libtet), Cvoid, (CPPTetGenIO{Float64}, Cstring), cinput, fstub) +end + """ $(TYPEDSIGNATURES) From 729e62bc270c387fde1bb620ce88e3533ad5fca4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Fuhrmann?= Date: Wed, 23 Sep 2026 16:09:27 +0200 Subject: [PATCH 03/35] fix cutprism example --- examples/examples.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/examples.jl b/examples/examples.jl index de5f288..fc670d2 100644 --- a/examples/examples.jl +++ b/examples/examples.jl @@ -224,8 +224,8 @@ function cutprism(; vol = 0.05) -1 2 10 ]' - push!(input.facetlist, RawFacet([Cint[1, 2, 3], Cint[7, 8, 9, 10]], [0.1 0.1 0.0;])) - push!(input.facetlist, RawFacet([Cint[4, 5, 6], Cint[11, 12, 13, 14]], [0.1 0.1 1.0;])) + push!(input.facetlist, RawFacet([Cint[1, 2, 3], Cint[7, 8, 9, 10]], Matrix([0.1 0.1 0.0;]'))) + push!(input.facetlist, RawFacet([Cint[4, 5, 6], Cint[11, 12, 13, 14]], Matrix([0.1 0.1 1.0;]'))) push!(input.facetlist, RawFacet([Cint[1, 2, 5, 4]], Array{Cdouble, 2}(undef, 0, 0))) push!(input.facetlist, RawFacet([Cint[2, 3, 6, 5]], Array{Cdouble, 2}(undef, 0, 0))) push!(input.facetlist, RawFacet([Cint[3, 1, 4, 6]], Array{Cdouble, 2}(undef, 0, 0))) @@ -236,5 +236,5 @@ function cutprism(; vol = 0.05) input.facetmarkerlist = [1, 2, 3, 3, 3, 4, 5, 6, 7] input.regionlist = [-0.1 -0.1 0.1 1 vol;]' - return tetrahedralize(input, "paqAQ") + return tetrahedralize(input, "p") #aqAQ") end From 2753ee33c4fcd7f4716c4d4ea0c7233ee630c0db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Fuhrmann?= Date: Thu, 24 Sep 2026 12:05:23 +0200 Subject: [PATCH 04/35] Vheck for TetGen_jll version in save_ methods. * throw an error if it is 1.5. With an updated build for TetGen_jll 1.5.4+1 or 1.5.5, we may remove this. --- src/rawtetgenio.jl | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/rawtetgenio.jl b/src/rawtetgenio.jl index 2a59733..d07a006 100644 --- a/src/rawtetgenio.jl +++ b/src/rawtetgenio.jl @@ -704,17 +704,23 @@ function tetrahedralize(stlfile::String, flags::String) end function save_poly(input::RawTetGenIO{Float64}, fstub::String) + if pkgversion(TetGen_jll) < v"1.6.1" + error("save_poly is only available with TetGen_jll >=v1.6.1") + end cinput, flist, plist = CPPTetGenIO(input) return ccall((:save_poly, libtet), Cvoid, (CPPTetGenIO{Float64}, Cstring), cinput, fstub) end function save_nodes(input::RawTetGenIO{Float64}, fstub::String) + if pkgversion(TetGen_jll) < v"1.6.1" + error("save_nodes is only available with TetGen_jll >=v1.6.1") + end cinput, flist, plist = CPPTetGenIO(input) return ccall((:save_nodes, libtet), Cvoid, (CPPTetGenIO{Float64}, Cstring), cinput, fstub) end """ -$(TYPEDSIGNATURES) +$(TYPEDSIGNATURES) Create GeometryBasics.Mesh from the triface list (for quick visualization purposes using Makie's wireframe). From 7b9f63381173c4c5293a86a3ca4537fafe2478a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Fuhrmann?= Date: Fri, 25 Sep 2026 23:14:50 +0200 Subject: [PATCH 05/35] Remove 1.6 check as we now can use the 1.5.4 jll --- CHANGELOG.md | 5 +++++ src/TetGen.jl | 2 ++ src/api.jl | 30 ++++++++++++++++++++---------- src/cpptetgenio.jl | 16 ++++++++++++++++ src/jltetgenio.jl | 6 ++++++ src/rawtetgenio.jl | 16 +++------------- test/runtests.jl | 18 +++++++++++++++++- 7 files changed, 69 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a1411b..ae3bdc2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ # Changelog All notable changes to this project since V1.0 will be documented in this file. +## [3.0.0] - 2026-09-28 +- Update TetGen_jll.jl dependency to 1.6.1 (corresponding to upstream TetGen version 1.6.1). Keep supporting TetGen_jll.jl + version 1.5.3 (corresponding to upstream TetGen version 1.5.1) +- Modify test infrastructure which runs tests for both jll versions +- Add `save_poly` and `save_nodes` methods for `RawTetgenIO` ## [2.0.2] - 2026-05-11 - Cap TetGen_jll.jl version to ~1.5.3, to avoid unexpected behavior in CI tests. diff --git a/src/TetGen.jl b/src/TetGen.jl index 5e4cbc7..b572558 100644 --- a/src/TetGen.jl +++ b/src/TetGen.jl @@ -15,6 +15,7 @@ using Printf: Printf using StaticArrays: StaticArrays, SVector using TetGen_jll: TetGen_jll, libtet + include("cpptetgenio.jl") include("jltetgenio.jl") include("rawtetgenio.jl") @@ -27,5 +28,6 @@ export TetGenError export RawTetGenIO, facetlist!, RawFacet export numberofpoints, numberoftetrahedra, numberoftrifaces, numberofedges export volumemesh, surfacemesh +export save_tetgen end # module diff --git a/src/api.jl b/src/api.jl index a545742..ac39d80 100644 --- a/src/api.jl +++ b/src/api.jl @@ -10,6 +10,20 @@ function voronoi(points::Vector{Point{3, T}}) where {T <: AbstractFloat} return Mesh{Triangle}(result) end + +function JLTetGenIO(mesh; marker = :markers, holes = Point{3, Float64}[]) + f = faces(mesh) + if pkgversion(GeometryBasics) < v"0.5" + kw_args = Any[:facets => GeometryBasics.metafree(f), :holes => holes] + else + kw_args = Any[:facets => f, :holes => holes] + end + if hasproperty(f, marker) + push!(kw_args, :facetmarkers => getproperty(f, marker)) + end + return JLTetGenIO(coordinates(mesh); kw_args...) +end + """ $(SIGNATURES) @@ -27,20 +41,16 @@ function tetrahedralize( mesh, command = "Qp"; marker = :markers, holes = Point{3, Float64}[] ) - f = faces(mesh) - if pkgversion(GeometryBasics) < v"0.5" - kw_args = Any[:facets => GeometryBasics.metafree(f), :holes => holes] - else - kw_args = Any[:facets => f, :holes => holes] - end - if hasproperty(f, marker) - push!(kw_args, :facetmarkers => getproperty(f, marker)) - end - tio = JLTetGenIO(coordinates(mesh); kw_args...) + tio = JLTetGenIO(mesh; marker, holes) result = tetrahedralize(tio, command) return Mesh{Tetrahedron}(result) end +function save_tetgen(mesh, fstub; marker = :markers, holes = Point{3, Float64}[]) + return save_tetgen(JLTetGenIO(mesh; marker, holes), fstub) + return true +end + # JF: probably this case is included in the case above # """ # $(SIGNATURES) diff --git a/src/cpptetgenio.jl b/src/cpptetgenio.jl index b820811..f4cf98d 100644 --- a/src/cpptetgenio.jl +++ b/src/cpptetgenio.jl @@ -155,3 +155,19 @@ function tetunsuitable!(unsuitable::Function; check_signature = true) c_wrap_tetunsuitable = @cfunction(jl_wrap_tetunsuitable, Cint, (Ptr{Cdouble}, Ptr{Cdouble}, Ptr{Cdouble}, Ptr{Cdouble})) return ccall((:tetunsuitable_callback, libtet), Cvoid, (Ptr{Cvoid},), c_wrap_tetunsuitable) end + + +""" + save(input, fstub) + +Save input (mesh, [`CPPTetGenIO`](@ref), [`JLTetGenIO`](@ref) or [`RawTetGenIO`](@ref)) +to the files `fstub.node` and `fstub.poly`. These can be read by the tetgen executable and are suited for submission of +bug reportst to upstream tetgen. +""" +function save_tetgen end + +function save_tetgen(cinput::CPPTetGenIO{Float64}, fstub::String) + ccall((:save_poly, libtet), Cvoid, (CPPTetGenIO{Float64}, Cstring), cinput, fstub) + ccall((:save_nodes, libtet), Cvoid, (CPPTetGenIO{Float64}, Cstring), cinput, fstub) + return nothing +end diff --git a/src/jltetgenio.jl b/src/jltetgenio.jl index 5a3b812..4e2abbd 100644 --- a/src/jltetgenio.jl +++ b/src/jltetgenio.jl @@ -339,3 +339,9 @@ function tetrahedralize(input::JLTetGenIO{Float64}, command::String) end return convert(JLTetGenIO, cres) end + +function save_tetgen(input::JLTetGenIO{Float64}, fstub::String) + cinput = Base.cconvert(CPPTetGenIO{Float64}, input) + save_tetgen(cinput[1], fstub) + return nothing +end diff --git a/src/rawtetgenio.jl b/src/rawtetgenio.jl index d07a006..653e609 100644 --- a/src/rawtetgenio.jl +++ b/src/rawtetgenio.jl @@ -703,20 +703,10 @@ function tetrahedralize(stlfile::String, flags::String) return RawTetGenIO(coutput) end -function save_poly(input::RawTetGenIO{Float64}, fstub::String) - if pkgversion(TetGen_jll) < v"1.6.1" - error("save_poly is only available with TetGen_jll >=v1.6.1") - end - cinput, flist, plist = CPPTetGenIO(input) - return ccall((:save_poly, libtet), Cvoid, (CPPTetGenIO{Float64}, Cstring), cinput, fstub) -end - -function save_nodes(input::RawTetGenIO{Float64}, fstub::String) - if pkgversion(TetGen_jll) < v"1.6.1" - error("save_nodes is only available with TetGen_jll >=v1.6.1") - end +function save_tetgen(input::RawTetGenIO{Float64}, fstub::String) cinput, flist, plist = CPPTetGenIO(input) - return ccall((:save_nodes, libtet), Cvoid, (CPPTetGenIO{Float64}, Cstring), cinput, fstub) + save_tetgen(cinput, fstub) + return nothing end """ diff --git a/test/runtests.jl b/test/runtests.jl index 7dc1e0e..fb31a9a 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,9 +1,19 @@ using ExplicitImports, Aqua -using TetGen: TetGen, JLPolygon, JLFacet, Point, tetrahedralize +using TetGen: TetGen, JLPolygon, JLFacet, Point, tetrahedralize, save_tetgen using GeometryBasics: GeometryBasics using GeometryBasics: Mesh, Triangle, Tetrahedron, TriangleFace, QuadFace, faces using Test +function testsave(input, fstub) + return mktempdir() do dir + cd(dir) do + save_tetgen(input, fstub) + sleep(1) + isfile(fstub * ".node") && isfile(fstub * ".poly") + end + end +end + @testset "mesh based API" begin # Construct a cube out of Quads points = Point{3, Float64}[ @@ -29,6 +39,7 @@ using Test else mesh = GeometryBasics.MetaMesh(points, facets; markers) end + @test testsave(mesh, "mesh") result = tetrahedralize(mesh) @test result isa Mesh @@ -38,6 +49,7 @@ using Test else mesh = GeometryBasics.MetaMesh(points, facets) end + @test testsave(mesh, "mesh") result = tetrahedralize(mesh, "vpq1.414a0.1") @test result isa Mesh @@ -64,6 +76,7 @@ using Test else tetmesh = GeometryBasics.MetaMesh(tetpoints, tetfacets) end + @test testsave(mesh, "tetmesh") result = tetrahedralize(tetmesh, "pQqAa0.01") @test result isa Mesh @@ -108,6 +121,7 @@ using Test else mesh = GeometryBasics.MetaMesh(points, facets; markers = markers) end + @test testsave(mesh, "mesh") result = tetrahedralize(mesh, "pQqAa1.0"; holes = [Point{3, Float64}(0, 0, 0)]) @test result isa Mesh @@ -293,7 +307,9 @@ end ]' ) + @test testsave(input, "input") cinput, x1, x2 = TetGen.CPPTetGenIO(input) + @test testsave(cinput, "cinput") coutput = tetrahedralize(cinput, "pQa") @test coutput.numberofpoints == 8 From 903c9124cdd6344b032c215307ca9bc88b5f225b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Fuhrmann?= Date: Fri, 25 Sep 2026 23:20:24 +0200 Subject: [PATCH 06/35] Update version to 3.0 --- CHANGELOG.md | 6 +++--- Project.toml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae3bdc2..4fb024b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,11 @@ # Changelog All notable changes to this project since V1.0 will be documented in this file. + ## [3.0.0] - 2026-09-28 - Update TetGen_jll.jl dependency to 1.6.1 (corresponding to upstream TetGen version 1.6.1). Keep supporting TetGen_jll.jl - version 1.5.3 (corresponding to upstream TetGen version 1.5.1) -- Modify test infrastructure which runs tests for both jll versions -- Add `save_poly` and `save_nodes` methods for `RawTetgenIO` + version 1.5.4 (corresponding to upstream TetGen version 1.5.1) +- Add `save_tetgen` method to write out tetgen input to allow easy bug reporting to upstream ## [2.0.2] - 2026-05-11 - Cap TetGen_jll.jl version to ~1.5.3, to avoid unexpected behavior in CI tests. diff --git a/Project.toml b/Project.toml index d3e26c7..0260998 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "TetGen" uuid = "c5d3f3f7-f850-59f6-8a2e-ffc6dc1317ea" -version = "2.0.2" +version = "3.0" authors = ["SimonDanisch ", "Juergen Fuhrmann Date: Fri, 25 Sep 2026 23:51:36 +0200 Subject: [PATCH 07/35] Test with both 1.5.1 and 1.6.1 tetgen versions --- test/runtests-tetgen-1.5.1.jl | 10 ++ test/runtests.jl | 323 +-------------------------------- test/tetgentests.jl | 324 ++++++++++++++++++++++++++++++++++ 3 files changed, 337 insertions(+), 320 deletions(-) create mode 100644 test/runtests-tetgen-1.5.1.jl create mode 100644 test/tetgentests.jl diff --git a/test/runtests-tetgen-1.5.1.jl b/test/runtests-tetgen-1.5.1.jl new file mode 100644 index 0000000..ab2a77f --- /dev/null +++ b/test/runtests-tetgen-1.5.1.jl @@ -0,0 +1,10 @@ +push!(LOAD_PATH, "@v#.#", "@stdlib") +using Pkg +Pkg.activate(; temp = true) +Pkg.add("GeometryBasics") +Pkg.add(name = "TetGen_jll", version = "1.5.4") +Pkg.develop(path = joinpath(@__DIR__, "..")) +using Test +@testset "tetgen 1.5.1" begin + include("tetgentests.jl") +end diff --git a/test/runtests.jl b/test/runtests.jl index fb31a9a..3f46471 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,327 +1,10 @@ using ExplicitImports, Aqua -using TetGen: TetGen, JLPolygon, JLFacet, Point, tetrahedralize, save_tetgen -using GeometryBasics: GeometryBasics -using GeometryBasics: Mesh, Triangle, Tetrahedron, TriangleFace, QuadFace, faces +using TetGen: TetGen using Test -function testsave(input, fstub) - return mktempdir() do dir - cd(dir) do - save_tetgen(input, fstub) - sleep(1) - isfile(fstub * ".node") && isfile(fstub * ".poly") - end - end -end - -@testset "mesh based API" begin - # Construct a cube out of Quads - points = Point{3, Float64}[ - (0.0, 0.0, 0.0), (2.0, 0.0, 0.0), - (2.0, 2.0, 0.0), (0.0, 2.0, 0.0), - (0.0, 0.0, 12.0), (2.0, 0.0, 12.0), - (2.0, 2.0, 12.0), (0.0, 2.0, 12.0), - ] - - facets = QuadFace{Cint}[ - 1:4, - 5:8, - [1, 5, 6, 2], - [2, 6, 7, 3], - [3, 7, 8, 4], - [4, 8, 5, 1], - ] - - markers = Cint[-1, -2, 0, 0, 0, 0] - # attach some additional information to our faces! - if pkgversion(GeometryBasics) < v"0.5" - mesh = Mesh(points, GeometryBasics.meta(facets; markers)) - else - mesh = GeometryBasics.MetaMesh(points, facets; markers) - end - @test testsave(mesh, "mesh") - result = tetrahedralize(mesh) - @test result isa Mesh - - # Make it similar to the README example - if pkgversion(GeometryBasics) < v"0.5" - mesh = Mesh(points, facets) - else - mesh = GeometryBasics.MetaMesh(points, facets) - end - @test testsave(mesh, "mesh") - result = tetrahedralize(mesh, "vpq1.414a0.1") - @test result isa Mesh - - points = rand(Point{3, Float64}, 100) - result = TetGen.voronoi(points) - @test result isa Mesh - - tetpoints = Point{3, Float64}[ - (0.0, 0.0, 0.0), - (1.0, 0.0, 0.0), - (0.0, 1.0, 0.0), - (0.0, 0.0, 1.0), - ] - - tetfacets = TriangleFace{Cint}[ - [1, 2, 3], - [1, 2, 4], - [1, 3, 4], - [2, 3, 4], - ] - - if pkgversion(GeometryBasics) < v"0.5" - tetmesh = Mesh(tetpoints, tetfacets) - else - tetmesh = GeometryBasics.MetaMesh(tetpoints, tetfacets) - end - @test testsave(mesh, "tetmesh") - result = tetrahedralize(tetmesh, "pQqAa0.01") - @test result isa Mesh - - ################# cube with hole example - # Construct a cube out of Quads - points = Point{3, Float64}[ - # outer cube: - (-2.0, -2.0, -2.0), (2.0, -2.0, -2.0), - (2.0, 2.0, -2.0), (-2.0, 2.0, -2.0), - (-2.0, -2.0, 2.0), (2.0, -2.0, 2.0), - (2.0, 2.0, 2.0), (-2.0, 2.0, 2.0), - - # inner cube: - (-1.0, -1.0, -1.0), (1.0, -1.0, -1.0), - (1.0, 1.0, -1.0), (-1.0, 1.0, -1.0), - (-1.0, -1.0, 1.0), (1.0, -1.0, 1.0), - (1.0, 1.0, 1.0), (-1.0, 1.0, 1.0), - ] - - facets = QuadFace{Cint}[ - # outer cube: - [1, 2, 3, 4], - [5, 6, 7, 8], - [1, 5, 6, 2], - [2, 6, 7, 3], - [3, 7, 8, 4], - [4, 8, 5, 1], - - # inner cube: - [1, 2, 3, 4] .+ 8, - [5, 6, 7, 8] .+ 8, - [1, 5, 6, 2] .+ 8, - [2, 6, 7, 3] .+ 8, - [3, 7, 8, 4] .+ 8, - [4, 8, 5, 1] .+ 8, - ] - - markers = ones(Cint, 12) - - if pkgversion(GeometryBasics) < v"0.5" - mesh = Mesh(points, GeometryBasics.meta(facets; markers = markers)) - else - mesh = GeometryBasics.MetaMesh(points, facets; markers = markers) - end - @test testsave(mesh, "mesh") - result = tetrahedralize(mesh, "pQqAa1.0"; holes = [Point{3, Float64}(0, 0, 0)]) - @test result isa Mesh - - # s = Sphere{Float64}(Point(0.0, 0.0, 0.0), 2.0) - # - # x = PlainMesh{Float64, Triangle{Cint}}(s) - # - # function Mesh{T, Dim, FaceType}(a::AbstractGeometry; resolution = nothing) - # P = Point{T, Dim} - # Mesh( - # coordinates(a, P; resolution = resolution), - # faces(a, FaceType; resolution = resolution) - # ) - # end - # - # s = Sphere{Float64}(Point(0.0, 0.0, 0.0), 1.0) - # - # y = PlainMesh{Float64, Triangle{Cint}}(s) - -end - -@testset "examples.jl" begin - include("../examples/examples.jl") - function generic_test(result::RawTetGenIO) - @test volumemesh(result) isa Mesh - - if numberoftrifaces(result) > 0 - @test surfacemesh(result) isa Mesh - end - - buf = IOBuffer() - Base.show(buf, result) - @test length(buf.data) > 0 - - ctio, flist, plist = TetGen.CPPTetGenIO(result) - @test ctio.numberofpoints == numberofpoints(result) - @test ctio.numberoftetrahedra == numberoftetrahedra(result) - @test ctio.numberoftrifaces == numberoftrifaces(result) - end +include("tetgentests.jl") - result = random_delaunay(; npoints = 100) - @test numberofpoints(result) == 100 - generic_test(result) - - result = cube() - @test numberofpoints(result) == 8 - @test numberoftetrahedra(result) == 6 - @test numberofedges(result) == 12 - @test numberoftrifaces(result) == 12 - generic_test(result) - - result = cube_stl() - @test numberofpoints(result) == 8 - @test numberoftetrahedra(result) == 6 - @test numberofedges(result) == 12 - @test numberoftrifaces(result) == 12 - generic_test(result) - - result = cubewithhole() - @test numberofpoints(result) in [56, 52] - @test numberoftetrahedra(result) in [168, 137] - @test numberofedges(result) == 36 - @test numberoftrifaces(result) in [104, 96] - generic_test(result) - - result = prism() - @test numberofpoints(result) in [8, 6] - @test numberofedges(result) in [11, 9] - @test numberoftrifaces(result) in [12, 8] - @test numberoftetrahedra(result) in [6, 3] - generic_test(result) - - # exact numbers depend on FP arithmetic and - # compiler optimizations - - result = cube_localref() - generic_test(result) - - result = material_prism() - @test numberoftetrahedra(result) > 100 - generic_test(result) - - result = cutprism() - @test numberoftetrahedra(result) > 20 - generic_test(result) -end - -@testset "error handling" begin - function badcube1(; vol = 1) - input = TetGen.RawTetGenIO{Cdouble}() - input.pointlist = [ - 0 0 0; - 1 0 0; - 1 1 0; - 0 1 0; - 0 0 1; - 1 0 2; - 1 1 1; - 0 1 2 - ]' - - TetGen.facetlist!( - input, [ - 1 2 3 4; - 5 6 7 8; - 1 2 6 5; - 2 3 7 6; - 3 4 8 7; - 4 1 5 8 - ]' - ) - tetrahedralize(input, "pQa$(vol)") - end - - function badcube2(; vol = 1) - input = TetGen.RawTetGenIO{Cdouble}() - input.pointlist = [ - 0 0 0; - 1 0 0; - 1 1 0; - 0 1 0; - 0 0 1; - 1 0 1; - 1 1 1; - 0 1 1; - 0 1 1 - 1.0e-5 - ]' - - TetGen.facetlist!( - input, [ - 1 2 3 4; - 5 6 7 8; - 1 2 6 5; - 2 3 7 6; - 3 4 8 7; - 4 1 5 8 - ]' - ) - tetrahedralize(input, "pQa$(vol)") - end - - function test_catch_error(geom) - try - result = geom() - catch err - if typeof(err) == TetGenError - println("Caught TetGenError") - println(err) - return true - end - end - false - end - if !Sys.iswindows() - @test test_catch_error(badcube2) - end -end - -############################################## -# Solely for increasing codecov - -@testset "codecov" begin - input = TetGen.RawTetGenIO{Cdouble}() - input.pointlist = [ - 0 0 0; - 1 0 0; - 1 1 0; - 0 1 0; - 0 0 1; - 1 0 1; - 1 1 1; - 0 1 1 - ]' - - TetGen.facetlist!( - input, [ - 1 2 3 4; - 5 6 7 8; - 1 2 6 5; - 2 3 7 6; - 3 4 8 7; - 4 1 5 8 - ]' - ) - - @test testsave(input, "input") - cinput, x1, x2 = TetGen.CPPTetGenIO(input) - @test testsave(cinput, "cinput") - coutput = tetrahedralize(cinput, "pQa") - - @test coutput.numberofpoints == 8 - - function test_error_output() - for i in 1:10 - println(TetGenError(i)) - end - true - end - @test test_error_output() -end +run(`julia $(joinpath(@__DIR__, "runtests-tetgen-1.5.1.jl"))`) @testset "ExplicitImports" begin @test ExplicitImports.check_no_implicit_imports(TetGen, skip = (Base, Core)) === nothing diff --git a/test/tetgentests.jl b/test/tetgentests.jl new file mode 100644 index 0000000..2ea1e2f --- /dev/null +++ b/test/tetgentests.jl @@ -0,0 +1,324 @@ +using TetGen: TetGen, JLPolygon, JLFacet, Point, tetrahedralize, save_tetgen +using GeometryBasics: GeometryBasics +using GeometryBasics: Mesh, Triangle, Tetrahedron, TriangleFace, QuadFace, faces +using Test + +function testsave(input, fstub) + return mktempdir() do dir + cd(dir) do + save_tetgen(input, fstub) + sleep(1) + isfile(fstub * ".node") && isfile(fstub * ".poly") + end + end +end + + +@testset "mesh based API" begin + # Construct a cube out of Quads + points = Point{3, Float64}[ + (0.0, 0.0, 0.0), (2.0, 0.0, 0.0), + (2.0, 2.0, 0.0), (0.0, 2.0, 0.0), + (0.0, 0.0, 12.0), (2.0, 0.0, 12.0), + (2.0, 2.0, 12.0), (0.0, 2.0, 12.0), + ] + + facets = QuadFace{Cint}[ + 1:4, + 5:8, + [1, 5, 6, 2], + [2, 6, 7, 3], + [3, 7, 8, 4], + [4, 8, 5, 1], + ] + + markers = Cint[-1, -2, 0, 0, 0, 0] + # attach some additional information to our faces! + if pkgversion(GeometryBasics) < v"0.5" + mesh = Mesh(points, GeometryBasics.meta(facets; markers)) + else + mesh = GeometryBasics.MetaMesh(points, facets; markers) + end + @test testsave(mesh, "mesh") + result = tetrahedralize(mesh) + @test result isa Mesh + + # Make it similar to the README example + if pkgversion(GeometryBasics) < v"0.5" + mesh = Mesh(points, facets) + else + mesh = GeometryBasics.MetaMesh(points, facets) + end + @test testsave(mesh, "mesh") + result = tetrahedralize(mesh, "vpq1.414a0.1") + @test result isa Mesh + + points = rand(Point{3, Float64}, 100) + result = TetGen.voronoi(points) + @test result isa Mesh + + tetpoints = Point{3, Float64}[ + (0.0, 0.0, 0.0), + (1.0, 0.0, 0.0), + (0.0, 1.0, 0.0), + (0.0, 0.0, 1.0), + ] + + tetfacets = TriangleFace{Cint}[ + [1, 2, 3], + [1, 2, 4], + [1, 3, 4], + [2, 3, 4], + ] + + if pkgversion(GeometryBasics) < v"0.5" + tetmesh = Mesh(tetpoints, tetfacets) + else + tetmesh = GeometryBasics.MetaMesh(tetpoints, tetfacets) + end + @test testsave(mesh, "tetmesh") + result = tetrahedralize(tetmesh, "pQqAa0.01") + @test result isa Mesh + + ################# cube with hole example + # Construct a cube out of Quads + points = Point{3, Float64}[ + # outer cube: + (-2.0, -2.0, -2.0), (2.0, -2.0, -2.0), + (2.0, 2.0, -2.0), (-2.0, 2.0, -2.0), + (-2.0, -2.0, 2.0), (2.0, -2.0, 2.0), + (2.0, 2.0, 2.0), (-2.0, 2.0, 2.0), + + # inner cube: + (-1.0, -1.0, -1.0), (1.0, -1.0, -1.0), + (1.0, 1.0, -1.0), (-1.0, 1.0, -1.0), + (-1.0, -1.0, 1.0), (1.0, -1.0, 1.0), + (1.0, 1.0, 1.0), (-1.0, 1.0, 1.0), + ] + + facets = QuadFace{Cint}[ + # outer cube: + [1, 2, 3, 4], + [5, 6, 7, 8], + [1, 5, 6, 2], + [2, 6, 7, 3], + [3, 7, 8, 4], + [4, 8, 5, 1], + + # inner cube: + [1, 2, 3, 4] .+ 8, + [5, 6, 7, 8] .+ 8, + [1, 5, 6, 2] .+ 8, + [2, 6, 7, 3] .+ 8, + [3, 7, 8, 4] .+ 8, + [4, 8, 5, 1] .+ 8, + ] + + markers = ones(Cint, 12) + + if pkgversion(GeometryBasics) < v"0.5" + mesh = Mesh(points, GeometryBasics.meta(facets; markers = markers)) + else + mesh = GeometryBasics.MetaMesh(points, facets; markers = markers) + end + @test testsave(mesh, "mesh") + result = tetrahedralize(mesh, "pQqAa1.0"; holes = [Point{3, Float64}(0, 0, 0)]) + @test result isa Mesh + + # s = Sphere{Float64}(Point(0.0, 0.0, 0.0), 2.0) + # + # x = PlainMesh{Float64, Triangle{Cint}}(s) + # + # function Mesh{T, Dim, FaceType}(a::AbstractGeometry; resolution = nothing) + # P = Point{T, Dim} + # Mesh( + # coordinates(a, P; resolution = resolution), + # faces(a, FaceType; resolution = resolution) + # ) + # end + # + # s = Sphere{Float64}(Point(0.0, 0.0, 0.0), 1.0) + # + # y = PlainMesh{Float64, Triangle{Cint}}(s) + +end + +@testset "examples.jl" begin + include("../examples/examples.jl") + function generic_test(result::RawTetGenIO) + @test volumemesh(result) isa Mesh + + if numberoftrifaces(result) > 0 + @test surfacemesh(result) isa Mesh + end + + buf = IOBuffer() + Base.show(buf, result) + @test length(buf.data) > 0 + + ctio, flist, plist = TetGen.CPPTetGenIO(result) + @test ctio.numberofpoints == numberofpoints(result) + @test ctio.numberoftetrahedra == numberoftetrahedra(result) + @test ctio.numberoftrifaces == numberoftrifaces(result) + end + + result = random_delaunay(; npoints = 100) + @test numberofpoints(result) == 100 + generic_test(result) + + result = cube() + @test numberofpoints(result) == 8 + @test numberoftetrahedra(result) == 6 + @test numberofedges(result) == 12 + @test numberoftrifaces(result) == 12 + generic_test(result) + + result = cube_stl() + @test numberofpoints(result) == 8 + @test numberoftetrahedra(result) == 6 + @test numberofedges(result) == 12 + @test numberoftrifaces(result) == 12 + generic_test(result) + + result = cubewithhole() + @test numberofpoints(result) in [56, 52] + @test numberoftetrahedra(result) in [168, 137] + @test numberofedges(result) == 36 + @test numberoftrifaces(result) in [104, 96] + generic_test(result) + + result = prism() + @test numberofpoints(result) in [8, 6] + @test numberofedges(result) in [11, 9] + @test numberoftrifaces(result) in [12, 8] + @test numberoftetrahedra(result) in [6, 3] + generic_test(result) + + # exact numbers depend on FP arithmetic and + # compiler optimizations + + result = cube_localref() + generic_test(result) + + result = material_prism() + @test numberoftetrahedra(result) > 100 + generic_test(result) + + result = cutprism() + @test numberoftetrahedra(result) > 20 + generic_test(result) +end + +@testset "error handling" begin + function badcube1(; vol = 1) + input = TetGen.RawTetGenIO{Cdouble}() + input.pointlist = [ + 0 0 0; + 1 0 0; + 1 1 0; + 0 1 0; + 0 0 1; + 1 0 2; + 1 1 1; + 0 1 2 + ]' + + TetGen.facetlist!( + input, [ + 1 2 3 4; + 5 6 7 8; + 1 2 6 5; + 2 3 7 6; + 3 4 8 7; + 4 1 5 8 + ]' + ) + tetrahedralize(input, "pQa$(vol)") + end + + function badcube2(; vol = 1) + input = TetGen.RawTetGenIO{Cdouble}() + input.pointlist = [ + 0 0 0; + 1 0 0; + 1 1 0; + 0 1 0; + 0 0 1; + 1 0 1; + 1 1 1; + 0 1 1; + 0 1 1 - 1.0e-5 + ]' + + TetGen.facetlist!( + input, [ + 1 2 3 4; + 5 6 7 8; + 1 2 6 5; + 2 3 7 6; + 3 4 8 7; + 4 1 5 8 + ]' + ) + tetrahedralize(input, "pQa$(vol)") + end + + function test_catch_error(geom) + try + result = geom() + catch err + if typeof(err) == TetGenError + println("Caught TetGenError") + println(err) + return true + end + end + false + end + if !Sys.iswindows() + @test test_catch_error(badcube2) + end +end + +############################################## +# Solely for increasing codecov + +@testset "codecov" begin + input = TetGen.RawTetGenIO{Cdouble}() + input.pointlist = [ + 0 0 0; + 1 0 0; + 1 1 0; + 0 1 0; + 0 0 1; + 1 0 1; + 1 1 1; + 0 1 1 + ]' + + TetGen.facetlist!( + input, [ + 1 2 3 4; + 5 6 7 8; + 1 2 6 5; + 2 3 7 6; + 3 4 8 7; + 4 1 5 8 + ]' + ) + + @test testsave(input, "input") + cinput, x1, x2 = TetGen.CPPTetGenIO(input) + @test testsave(cinput, "cinput") + coutput = tetrahedralize(cinput, "pQa") + + @test coutput.numberofpoints == 8 + + function test_error_output() + for i in 1:10 + println(TetGenError(i)) + end + true + end + @test test_error_output() +end From 1727189eb2ccee9d3566c4aa4c4e642596d26300 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Fuhrmann?= Date: Sat, 26 Sep 2026 00:14:09 +0200 Subject: [PATCH 08/35] Update README, docs --- README.md | 13 ++++++++++++- src/cpptetgenio.jl | 7 ++++++- src/jltetgenio.jl | 5 +++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9463700..1a6383c 100644 --- a/README.md +++ b/README.md @@ -6,12 +6,23 @@ [![](https://img.shields.io/badge/docs-dev-blue.svg)](https://JuliaGeometry.github.io/TetGen.jl/dev) [![Aqua QA](https://raw.githubusercontent.com/JuliaTesting/Aqua.jl/master/badge.svg)](https://github.com/JuliaTesting/Aqua.jl) -The `TetGen.jl` package is a Julia wrapper for the C++ project [TetGen](https://wias-berlin.de/software/index.jsp?id=TetGen&lang=1). This wrapper enables TetGen based tetrahedral meshing, and (constrained) 3D Delaunay and Voronoi tessellation. +The `TetGen.jl` package is a Julia wrapper for the C++ project [TetGen](https://codeberg.org/TetGen/TetGen). This wrapper enables TetGen based tetrahedral meshing, and (constrained) 3D Delaunay and Voronoi tessellation. If you find TetGen useful, please consider citing Hang Si: "TetGen, a Delaunay-Based Quality Tetrahedral Mesh Generator" ACM Trans. on Mathematical Software. 41 (2), 2015 [http://doi.acm.org/10.1145/2629697](http://doi.acm.org/10.1145/2629697). +## Supported upstream versions +By choosing corresponding versions of the [TetGen_jll.jl](https://github.com/JuliaBinaryWrappers/TetGen_jll.jl) binary package, +it is possible to choose between different TetGen upstream versions: + +| TetGen version | TetGen_jll version | +|:--------------:|:------------------:| +| 1.5.1 | 1.5.4 | +| 1.6.1 | 1.6.1 | + +By default, version 1.6.1 is installed. + ## License When installing TetGen.jl, a compiled library version of the TetGen library will be downloaded from the [TetGen_jll.jl](https://github.com/JuliaBinaryWrappers/TetGen_jll.jl) repository. This library is bound to the [Affero Gnu Public License v3 (AGPL)](https://www.gnu.org/licenses/agpl-3.0.html), but the bindings to the library in this package, TetGen.jl, are licensed under MIT. This means that code using the TetGen library via the TetGen.jl bindings is subject to TetGen's licensing terms. If you distribute covered work, i.e. a program that links to and is distributed with the TetGen library, then that distribution falls under the terms of the AGPLv3. diff --git a/src/cpptetgenio.jl b/src/cpptetgenio.jl index f4cf98d..435653f 100644 --- a/src/cpptetgenio.jl +++ b/src/cpptetgenio.jl @@ -10,6 +10,11 @@ struct CFacet{T} numberofholes::Cint end +""" + struct CPPTetGenIO + +Julia "mirror" structure to be passed to the C wrapper code. +""" struct CPPTetGenIO{T} firstnumber::Cint # 0 or 1, default 0. mesh_dim::Cint # must be 3. @@ -158,7 +163,7 @@ end """ - save(input, fstub) + save_tetgen(input, fstub) Save input (mesh, [`CPPTetGenIO`](@ref), [`JLTetGenIO`](@ref) or [`RawTetGenIO`](@ref)) to the files `fstub.node` and `fstub.poly`. These can be read by the tetgen executable and are suited for submission of diff --git a/src/jltetgenio.jl b/src/jltetgenio.jl index 4e2abbd..b24aadc 100644 --- a/src/jltetgenio.jl +++ b/src/jltetgenio.jl @@ -55,6 +55,11 @@ end inttype(::Type{Float64}) = Int64 inttype(::Type{Float32}) = Int32 +""" + struct JLTetGenIO + +Intermediate structure for TetGen data for interaction with GeometryBasics.Mesh. +""" struct JLTetGenIO{T, NSimplex, NAttributes, NMTr, IT, FT} points::Vector{Point{3, T}} pointattributes::Vector{SVector{NAttributes, T}} From 86c4643370f90fda0a484ddd0999f3e8e35199b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Fuhrmann?= Date: Sat, 26 Sep 2026 22:55:59 +0200 Subject: [PATCH 09/35] Update Readme + changelog --- CHANGELOG.md | 5 +++-- README.md | 10 +++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fb024b..ab82d49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,11 @@ All notable changes to this project since V1.0 will be documented in this file. -## [3.0.0] - 2026-09-28 +## [3.0.0] - unreleased - Update TetGen_jll.jl dependency to 1.6.1 (corresponding to upstream TetGen version 1.6.1). Keep supporting TetGen_jll.jl version 1.5.4 (corresponding to upstream TetGen version 1.5.1) -- Add `save_tetgen` method to write out tetgen input to allow easy bug reporting to upstream +- Maintain CI tests for both upstream versions +- Add `save_tetgen` method to write out tetgen input to suppoirt creation of MWE for upstream bug reports ## [2.0.2] - 2026-05-11 - Cap TetGen_jll.jl version to ~1.5.3, to avoid unexpected behavior in CI tests. diff --git a/README.md b/README.md index 1a6383c..3e80b7e 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,15 @@ it is possible to choose between different TetGen upstream versions: | 1.5.1 | 1.5.4 | | 1.6.1 | 1.6.1 | -By default, version 1.6.1 is installed. +By default, the package manager installs version 1.6.1. + +## Submitting bug reports +Bug reports regarding the Julia wrapper can be submitted just by opening an issue in this repo. Please try to attach a minimal +working example (MWE) to your report. + +Bug reports to upstream TetGen (v1.6.1) can be submitted to the [codeberg upstream repository](https://codeberg.org/TetGen/TetGen). +The `save_tetgen(input, fstub)` converts and writes the Julia input description to a pair of TetGen `.node` and `.poly` files which can serve +as an MWE. ## License From c262a6d4eea9b4985555dc38042fcaa1bc25d55c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Fuhrmann?= Date: Sat, 26 Sep 2026 23:25:06 +0200 Subject: [PATCH 10/35] Preserve cinput during save_tetgen call Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/jltetgenio.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jltetgenio.jl b/src/jltetgenio.jl index b24aadc..2082d85 100644 --- a/src/jltetgenio.jl +++ b/src/jltetgenio.jl @@ -347,6 +347,6 @@ end function save_tetgen(input::JLTetGenIO{Float64}, fstub::String) cinput = Base.cconvert(CPPTetGenIO{Float64}, input) - save_tetgen(cinput[1], fstub) + GC.@preserve cinput save_tetgen(cinput[1], fstub) return nothing end From cfc50f70aadb5137535c1ba9e39601bdb9c47735 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Fuhrmann?= Date: Sat, 26 Sep 2026 23:25:23 +0200 Subject: [PATCH 11/35] Fix test to save tetmesh instead of mesh Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- test/tetgentests.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/tetgentests.jl b/test/tetgentests.jl index 2ea1e2f..2e750f5 100644 --- a/test/tetgentests.jl +++ b/test/tetgentests.jl @@ -76,7 +76,7 @@ end else tetmesh = GeometryBasics.MetaMesh(tetpoints, tetfacets) end - @test testsave(mesh, "tetmesh") + @test testsave(tetmesh, "tetmesh") result = tetrahedralize(tetmesh, "pQqAa0.01") @test result isa Mesh From c3ff73dfee90d8f0fc54fd899ac62442a221be66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Fuhrmann?= Date: Sat, 26 Sep 2026 23:25:46 +0200 Subject: [PATCH 12/35] Correct typo in save_tetgen function comment Fix typo in comment regarding bug reports. Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/cpptetgenio.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cpptetgenio.jl b/src/cpptetgenio.jl index 435653f..d88810b 100644 --- a/src/cpptetgenio.jl +++ b/src/cpptetgenio.jl @@ -167,7 +167,7 @@ end Save input (mesh, [`CPPTetGenIO`](@ref), [`JLTetGenIO`](@ref) or [`RawTetGenIO`](@ref)) to the files `fstub.node` and `fstub.poly`. These can be read by the tetgen executable and are suited for submission of -bug reportst to upstream tetgen. +bug reports to upstream tetgen. """ function save_tetgen end From 29147eaed49743e2e4ecf65cbf2c5c5038357d03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Fuhrmann?= Date: Sat, 26 Sep 2026 23:25:58 +0200 Subject: [PATCH 13/35] Update documentation for holelist array in rawtetgenio.jl Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/rawtetgenio.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rawtetgenio.jl b/src/rawtetgenio.jl index 653e609..4c4c379 100644 --- a/src/rawtetgenio.jl +++ b/src/rawtetgenio.jl @@ -13,8 +13,7 @@ struct RawFacet{T} polygonlist::Array{Array{Cint, 1}, 1} """ - `3 x npoints` array of points given by their coordinates - marking polygons describing holes in the facet. + `3 x nholes` array of points marking holes in the facet. """ holelist::Array{T, 2} end From ac28aa084ffd5d35501a05823b539c42c04a6b17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Fuhrmann?= Date: Sat, 26 Sep 2026 23:26:13 +0200 Subject: [PATCH 14/35] Refactor save_tetgen to remove unnecessary return Removed redundant return statement in save_tetgen function. Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/api.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/api.jl b/src/api.jl index ac39d80..e1ac653 100644 --- a/src/api.jl +++ b/src/api.jl @@ -48,7 +48,6 @@ end function save_tetgen(mesh, fstub; marker = :markers, holes = Point{3, Float64}[]) return save_tetgen(JLTetGenIO(mesh; marker, holes), fstub) - return true end # JF: probably this case is included in the case above From 7020e31c970f6b3b056e037e103f82b116486731 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Fuhrmann?= Date: Sat, 26 Sep 2026 23:26:29 +0200 Subject: [PATCH 15/35] Change tetrahedralize parameter from 'p' to 'paqAQ' Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- examples/examples.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/examples.jl b/examples/examples.jl index fc670d2..0dc6a2e 100644 --- a/examples/examples.jl +++ b/examples/examples.jl @@ -236,5 +236,5 @@ function cutprism(; vol = 0.05) input.facetmarkerlist = [1, 2, 3, 3, 3, 4, 5, 6, 7] input.regionlist = [-0.1 -0.1 0.1 1 vol;]' - return tetrahedralize(input, "p") #aqAQ") + return tetrahedralize(input, "paqAQ") end From 90ddad474a4f9a5cda52626353606e6b08bac67d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Fuhrmann?= Date: Sat, 26 Sep 2026 23:26:39 +0200 Subject: [PATCH 16/35] Revise CHANGELOG.md for clarity and updates Update changelog to include recent changes and maintain formatting. Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- CHANGELOG.md | 146 +++++++++++++++++++++++++-------------------------- 1 file changed, 73 insertions(+), 73 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab82d49..3aa0f1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,76 +1,76 @@ -# Changelog - -All notable changes to this project since V1.0 will be documented in this file. - -## [3.0.0] - unreleased -- Update TetGen_jll.jl dependency to 1.6.1 (corresponding to upstream TetGen version 1.6.1). Keep supporting TetGen_jll.jl - version 1.5.4 (corresponding to upstream TetGen version 1.5.1) -- Maintain CI tests for both upstream versions -- Add `save_tetgen` method to write out tetgen input to suppoirt creation of MWE for upstream bug reports - -## [2.0.2] - 2026-05-11 -- Cap TetGen_jll.jl version to ~1.5.3, to avoid unexpected behavior in CI tests. - See #47 for a discussion of the background - -## [2.0.1] - 2026-01-13 -- modernize tests and CI - -## [2.0.1] - 2024-11-28 -- Allow for GeometryBasics v0.5 along with 0.4 -- Breaking: with GeometryBasics v0.5, a MetaMesh has - to be provided as input for Tetrahedralize. - The RawTetGenIO based API is not affected. Users who - rely on the RawTetGenIO based API can use `TetGen="1,2"` - in their `[compat]` section. -- Breaking: remove deprecated `tetunsuitable`. Use `tetunsuitable!` instead - -## [1.5.1] - 2024-02-27 - -- Fix version check for `MemoryRef` -- Add ci for apple silicon - -## [1.5.0] - 2023-11-15 - -- Fix imports/named using from GeometryBasics -- add juliaformatter -- @deprecate tetunsuitable tetunsuitable! -- Pass pointer to unsafe_convert -- Test for type of return value of unsuitable function -- fix for 1.11 memory handling - -## [1.4.0] - 2022-06-02 -- allow to input stl files - -## [1.3.0] - 2021-10-05 - -- Switch Julia package to MIT license (after the FFTW model). -- Remove checking of error catching from tests due to problems on windows. -- Point3f0->Point3f (GeometryBasics version) -- Add stl loading functionality -- Switch Julia package to MIT license (after the FFTW model). - -## [1.2.1] - 2021-09-13 -- allow for GeometryBasics 0.4 - -## [1.2.0] - 2021-05-26 - -- Added example for RawTetGenIO and holes -- Created holes for tetrahedralize(::Mesh, ...) -- Added test example for mesh API with holes - -## [1.1.1] - 2021-01-02 - -- Bump version compat for StaticArrays - -## [1.1.0] - 2020-12-22 - -- Thank you travis and bye bye -- Update of upstream patch version of TetGen to "August 18, 2018". +# Changelog + +All notable changes to this project since V1.0 will be documented in this file. + +## [3.0.0] - unreleased +- Update TetGen_jll.jl dependency to 1.6.1 (corresponding to upstream TetGen version 1.6.1). Keep supporting TetGen_jll.jl + version 1.5.4 (corresponding to upstream TetGen version 1.5.1) +- Maintain CI tests for both upstream versions +- Add `save_tetgen` method to write out tetgen input to support creation of MWE for upstream bug reports + +## [2.0.2] - 2026-05-11 +- Cap TetGen_jll.jl version to ~1.5.3, to avoid unexpected behavior in CI tests. + See #47 for a discussion of the background + +## [2.0.1] - 2026-01-13 +- modernize tests and CI + +## [2.0.1] - 2024-11-28 +- Allow for GeometryBasics v0.5 along with 0.4 +- Breaking: with GeometryBasics v0.5, a MetaMesh has + to be provided as input for Tetrahedralize. + The RawTetGenIO based API is not affected. Users who + rely on the RawTetGenIO based API can use `TetGen="1,2"` + in their `[compat]` section. +- Breaking: remove deprecated `tetunsuitable`. Use `tetunsuitable!` instead + +## [1.5.1] - 2024-02-27 + +- Fix version check for `MemoryRef` +- Add ci for apple silicon + +## [1.5.0] - 2023-11-15 + +- Fix imports/named using from GeometryBasics +- add juliaformatter +- @deprecate tetunsuitable tetunsuitable! +- Pass pointer to unsafe_convert +- Test for type of return value of unsuitable function +- fix for 1.11 memory handling + +## [1.4.0] - 2022-06-02 +- allow to input stl files + +## [1.3.0] - 2021-10-05 + +- Switch Julia package to MIT license (after the FFTW model). +- Remove checking of error catching from tests due to problems on windows. +- Point3f0->Point3f (GeometryBasics version) +- Add stl loading functionality +- Switch Julia package to MIT license (after the FFTW model). + +## [1.2.1] - 2021-09-13 +- allow for GeometryBasics 0.4 + +## [1.2.0] - 2021-05-26 + +- Added example for RawTetGenIO and holes +- Created holes for tetrahedralize(::Mesh, ...) +- Added test example for mesh API with holes + +## [1.1.1] - 2021-01-02 + +- Bump version compat for StaticArrays + +## [1.1.0] - 2020-12-22 + +- Thank you travis and bye bye +- Update of upstream patch version of TetGen to "August 18, 2018". - Formally this still appears to be 1.5.1 , and it is manifest in TetGen_jll 1.5.1+1 - Catch errors thrown by TetGen in a rc variable, throw julia error if rc!=0 - Handle unsuitable callback to allow local refinement. - -## [1.0.0] - 2020-11-30 -- Dependency on artifacts -- Refactoring. API of API.jl stays the same. -- Provide array based API. and RawTetGenIO + +## [1.0.0] - 2020-11-30 +- Dependency on artifacts +- Refactoring. API of API.jl stays the same. +- Provide array based API. and RawTetGenIO From 2d572bb717a69d1418417328c7a48660ed0eadde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Fuhrmann?= Date: Sat, 26 Sep 2026 23:37:07 +0200 Subject: [PATCH 17/35] fix dos line endings in changelog introduced by copilot --- CHANGELOG.md | 151 +++++++++++++++++++++++++-------------------------- 1 file changed, 75 insertions(+), 76 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3aa0f1c..270f5dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,76 +1,75 @@ -# Changelog - -All notable changes to this project since V1.0 will be documented in this file. - -## [3.0.0] - unreleased -- Update TetGen_jll.jl dependency to 1.6.1 (corresponding to upstream TetGen version 1.6.1). Keep supporting TetGen_jll.jl - version 1.5.4 (corresponding to upstream TetGen version 1.5.1) -- Maintain CI tests for both upstream versions -- Add `save_tetgen` method to write out tetgen input to support creation of MWE for upstream bug reports - -## [2.0.2] - 2026-05-11 -- Cap TetGen_jll.jl version to ~1.5.3, to avoid unexpected behavior in CI tests. - See #47 for a discussion of the background - -## [2.0.1] - 2026-01-13 -- modernize tests and CI - -## [2.0.1] - 2024-11-28 -- Allow for GeometryBasics v0.5 along with 0.4 -- Breaking: with GeometryBasics v0.5, a MetaMesh has - to be provided as input for Tetrahedralize. - The RawTetGenIO based API is not affected. Users who - rely on the RawTetGenIO based API can use `TetGen="1,2"` - in their `[compat]` section. -- Breaking: remove deprecated `tetunsuitable`. Use `tetunsuitable!` instead - -## [1.5.1] - 2024-02-27 - -- Fix version check for `MemoryRef` -- Add ci for apple silicon - -## [1.5.0] - 2023-11-15 - -- Fix imports/named using from GeometryBasics -- add juliaformatter -- @deprecate tetunsuitable tetunsuitable! -- Pass pointer to unsafe_convert -- Test for type of return value of unsuitable function -- fix for 1.11 memory handling - -## [1.4.0] - 2022-06-02 -- allow to input stl files - -## [1.3.0] - 2021-10-05 - -- Switch Julia package to MIT license (after the FFTW model). -- Remove checking of error catching from tests due to problems on windows. -- Point3f0->Point3f (GeometryBasics version) -- Add stl loading functionality -- Switch Julia package to MIT license (after the FFTW model). - -## [1.2.1] - 2021-09-13 -- allow for GeometryBasics 0.4 - -## [1.2.0] - 2021-05-26 - -- Added example for RawTetGenIO and holes -- Created holes for tetrahedralize(::Mesh, ...) -- Added test example for mesh API with holes - -## [1.1.1] - 2021-01-02 - -- Bump version compat for StaticArrays - -## [1.1.0] - 2020-12-22 - -- Thank you travis and bye bye -- Update of upstream patch version of TetGen to "August 18, 2018". -- Formally this still appears to be 1.5.1 , and it is manifest in TetGen_jll 1.5.1+1 -- Catch errors thrown by TetGen in a rc variable, throw julia error if rc!=0 -- Handle unsuitable callback to allow local refinement. - -## [1.0.0] - 2020-11-30 -- Dependency on artifacts -- Refactoring. API of API.jl stays the same. -- Provide array based API. and RawTetGenIO +# Changelog +All notable changes to this project since V1.0 will be documented in this file. + +## [3.0.0] - unreleased +- Update TetGen_jll.jl dependency to 1.6.1 (corresponding to upstream TetGen version 1.6.1). Keep supporting TetGen_jll.jl + version 1.5.4 (corresponding to upstream TetGen version 1.5.1) +- Maintain CI tests for both upstream versions +- Add `save_tetgen` method to write out tetgen input to support creation of MWE for upstream bug reports + +## [2.0.2] - 2026-05-11 +- Cap TetGen_jll.jl version to ~1.5.3, to avoid unexpected behavior in CI tests. + See #47 for a discussion of the background + +## [2.0.1] - 2026-01-13 +- modernize tests and CI + +## [2.0.1] - 2024-11-28 +- Allow for GeometryBasics v0.5 along with 0.4 +- Breaking: with GeometryBasics v0.5, a MetaMesh has + to be provided as input for Tetrahedralize. + The RawTetGenIO based API is not affected. Users who + rely on the RawTetGenIO based API can use `TetGen="1,2"` + in their `[compat]` section. +- Breaking: remove deprecated `tetunsuitable`. Use `tetunsuitable!` instead + +## [1.5.1] - 2024-02-27 + +- Fix version check for `MemoryRef` +- Add ci for apple silicon + +## [1.5.0] - 2023-11-15 + +- Fix imports/named using from GeometryBasics +- add juliaformatter +- @deprecate tetunsuitable tetunsuitable! +- Pass pointer to unsafe_convert +- Test for type of return value of unsuitable function +- fix for 1.11 memory handling + +## [1.4.0] - 2022-06-02 +- allow to input stl files + +## [1.3.0] - 2021-10-05 + +- Switch Julia package to MIT license (after the FFTW model). +- Remove checking of error catching from tests due to problems on windows. +- Point3f0->Point3f (GeometryBasics version) +- Add stl loading functionality +- Switch Julia package to MIT license (after the FFTW model). + +## [1.2.1] - 2021-09-13 +- allow for GeometryBasics 0.4 + +## [1.2.0] - 2021-05-26 + +- Added example for RawTetGenIO and holes +- Created holes for tetrahedralize(::Mesh, ...) +- Added test example for mesh API with holes + +## [1.1.1] - 2021-01-02 + +- Bump version compat for StaticArrays + +## [1.1.0] - 2020-12-22 + +- Thank you travis and bye bye +- Update of upstream patch version of TetGen to "August 18, 2018". +- Formally this still appears to be 1.5.1 , and it is manifest in TetGen_jll 1.5.1+1 +- Catch errors thrown by TetGen in a rc variable, throw julia error if rc!=0 +- Handle unsuitable callback to allow local refinement. + +## [1.0.0] - 2020-11-30 +- Dependency on artifacts +- Refactoring. API of API.jl stays the same. +- Provide array based API. and RawTetGenIO From d867344e13e7c6bd4cd441099750c7dd55af014d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Oct 2026 00:43:34 +0000 Subject: [PATCH 18/35] Bump JuliaRegistries/TagBot from 1.25.11 to 1.25.13 Bumps [JuliaRegistries/TagBot](https://github.com/juliaregistries/tagbot) from 1.25.11 to 1.25.13. - [Release notes](https://github.com/juliaregistries/tagbot/releases) - [Commits](https://github.com/juliaregistries/tagbot/compare/cded32665b34ca0496d3ac10a67d08ddfc726963...ffef3282bcacf1055e4d621a5c010959dfbe59ff) --- updated-dependencies: - dependency-name: JuliaRegistries/TagBot dependency-version: 1.25.13 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/TagBot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/TagBot.yml b/.github/workflows/TagBot.yml index 248a8c0..138a626 100644 --- a/.github/workflows/TagBot.yml +++ b/.github/workflows/TagBot.yml @@ -14,7 +14,7 @@ jobs: # ubuntu-slim doesn't support containers, and thus won't work runs-on: ubuntu-latest steps: - - uses: JuliaRegistries/TagBot@cded32665b34ca0496d3ac10a67d08ddfc726963 # v1.25.11 + - uses: JuliaRegistries/TagBot@ffef3282bcacf1055e4d621a5c010959dfbe59ff # v1.25.13 with: token: ${{ secrets.GITHUB_TOKEN }} # For commits that modify workflow files: SSH key enables tagging, but From f6974f50586123748a2d3685f258681942eae6aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Fuhrmann?= Date: Wed, 23 Sep 2026 15:57:45 +0200 Subject: [PATCH 19/35] Update upstream version to v1.6.1 * adapt runtests to slightly different grid output --- Project.toml | 4 ++-- test/runtests.jl | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Project.toml b/Project.toml index 58bb6bc..d3e26c7 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "TetGen" uuid = "c5d3f3f7-f850-59f6-8a2e-ffc6dc1317ea" -authors = ["SimonDanisch ", "Juergen Fuhrmann ", "Juergen Fuhrmann 100 + @test numberoftetrahedra(result) > 20 generic_test(result) end From e5cc4594ba562a5016df0677de96b5ddfcd2fded Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Fuhrmann?= Date: Wed, 23 Sep 2026 15:59:14 +0200 Subject: [PATCH 20/35] Add save_poly and save_nodes methods Check for size() of point list in RawFacet --- src/rawtetgenio.jl | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/rawtetgenio.jl b/src/rawtetgenio.jl index 008beaf..2a59733 100644 --- a/src/rawtetgenio.jl +++ b/src/rawtetgenio.jl @@ -13,7 +13,7 @@ struct RawFacet{T} polygonlist::Array{Array{Cint, 1}, 1} """ - Array of points given by their coordinates + `3 x npoints` array of points given by their coordinates marking polygons describing holes in the facet. """ holelist::Array{T, 2} @@ -357,6 +357,9 @@ function CPPTetGenIO(tio::RawTetGenIO{T}) where {T} for ipolygon in 1:numberofpolygons polygonlist[ipolygon] = CPolygon(pointer(facet.polygonlist[ipolygon]), size(facet.polygonlist[ipolygon], 1)) end + if length(facet.holelist) > 0 + @assert size(facet.holelist, 1) == 3 + end numberofholes = size(facet.holelist, 2) push!(polygonlist_array, polygonlist) push!( @@ -700,6 +703,16 @@ function tetrahedralize(stlfile::String, flags::String) return RawTetGenIO(coutput) end +function save_poly(input::RawTetGenIO{Float64}, fstub::String) + cinput, flist, plist = CPPTetGenIO(input) + return ccall((:save_poly, libtet), Cvoid, (CPPTetGenIO{Float64}, Cstring), cinput, fstub) +end + +function save_nodes(input::RawTetGenIO{Float64}, fstub::String) + cinput, flist, plist = CPPTetGenIO(input) + return ccall((:save_nodes, libtet), Cvoid, (CPPTetGenIO{Float64}, Cstring), cinput, fstub) +end + """ $(TYPEDSIGNATURES) From 44099c888ff9b6cc67166bafe994be511fe78388 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Fuhrmann?= Date: Wed, 23 Sep 2026 16:09:27 +0200 Subject: [PATCH 21/35] fix cutprism example --- examples/examples.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/examples.jl b/examples/examples.jl index de5f288..fc670d2 100644 --- a/examples/examples.jl +++ b/examples/examples.jl @@ -224,8 +224,8 @@ function cutprism(; vol = 0.05) -1 2 10 ]' - push!(input.facetlist, RawFacet([Cint[1, 2, 3], Cint[7, 8, 9, 10]], [0.1 0.1 0.0;])) - push!(input.facetlist, RawFacet([Cint[4, 5, 6], Cint[11, 12, 13, 14]], [0.1 0.1 1.0;])) + push!(input.facetlist, RawFacet([Cint[1, 2, 3], Cint[7, 8, 9, 10]], Matrix([0.1 0.1 0.0;]'))) + push!(input.facetlist, RawFacet([Cint[4, 5, 6], Cint[11, 12, 13, 14]], Matrix([0.1 0.1 1.0;]'))) push!(input.facetlist, RawFacet([Cint[1, 2, 5, 4]], Array{Cdouble, 2}(undef, 0, 0))) push!(input.facetlist, RawFacet([Cint[2, 3, 6, 5]], Array{Cdouble, 2}(undef, 0, 0))) push!(input.facetlist, RawFacet([Cint[3, 1, 4, 6]], Array{Cdouble, 2}(undef, 0, 0))) @@ -236,5 +236,5 @@ function cutprism(; vol = 0.05) input.facetmarkerlist = [1, 2, 3, 3, 3, 4, 5, 6, 7] input.regionlist = [-0.1 -0.1 0.1 1 vol;]' - return tetrahedralize(input, "paqAQ") + return tetrahedralize(input, "p") #aqAQ") end From 8c917ceda008b293dca1be8e84509820e48c59be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Fuhrmann?= Date: Thu, 24 Sep 2026 12:05:23 +0200 Subject: [PATCH 22/35] Vheck for TetGen_jll version in save_ methods. * throw an error if it is 1.5. With an updated build for TetGen_jll 1.5.4+1 or 1.5.5, we may remove this. --- src/rawtetgenio.jl | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/rawtetgenio.jl b/src/rawtetgenio.jl index 2a59733..d07a006 100644 --- a/src/rawtetgenio.jl +++ b/src/rawtetgenio.jl @@ -704,17 +704,23 @@ function tetrahedralize(stlfile::String, flags::String) end function save_poly(input::RawTetGenIO{Float64}, fstub::String) + if pkgversion(TetGen_jll) < v"1.6.1" + error("save_poly is only available with TetGen_jll >=v1.6.1") + end cinput, flist, plist = CPPTetGenIO(input) return ccall((:save_poly, libtet), Cvoid, (CPPTetGenIO{Float64}, Cstring), cinput, fstub) end function save_nodes(input::RawTetGenIO{Float64}, fstub::String) + if pkgversion(TetGen_jll) < v"1.6.1" + error("save_nodes is only available with TetGen_jll >=v1.6.1") + end cinput, flist, plist = CPPTetGenIO(input) return ccall((:save_nodes, libtet), Cvoid, (CPPTetGenIO{Float64}, Cstring), cinput, fstub) end """ -$(TYPEDSIGNATURES) +$(TYPEDSIGNATURES) Create GeometryBasics.Mesh from the triface list (for quick visualization purposes using Makie's wireframe). From 85d2184354269e96358372484ea453fd7f9c5888 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Fuhrmann?= Date: Fri, 25 Sep 2026 23:14:50 +0200 Subject: [PATCH 23/35] Remove 1.6 check as we now can use the 1.5.4 jll --- CHANGELOG.md | 5 +++++ src/TetGen.jl | 2 ++ src/api.jl | 30 ++++++++++++++++++++---------- src/cpptetgenio.jl | 16 ++++++++++++++++ src/jltetgenio.jl | 6 ++++++ src/rawtetgenio.jl | 16 +++------------- test/runtests.jl | 18 +++++++++++++++++- 7 files changed, 69 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a1411b..ae3bdc2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,11 @@ # Changelog All notable changes to this project since V1.0 will be documented in this file. +## [3.0.0] - 2026-09-28 +- Update TetGen_jll.jl dependency to 1.6.1 (corresponding to upstream TetGen version 1.6.1). Keep supporting TetGen_jll.jl + version 1.5.3 (corresponding to upstream TetGen version 1.5.1) +- Modify test infrastructure which runs tests for both jll versions +- Add `save_poly` and `save_nodes` methods for `RawTetgenIO` ## [2.0.2] - 2026-05-11 - Cap TetGen_jll.jl version to ~1.5.3, to avoid unexpected behavior in CI tests. diff --git a/src/TetGen.jl b/src/TetGen.jl index 5e4cbc7..b572558 100644 --- a/src/TetGen.jl +++ b/src/TetGen.jl @@ -15,6 +15,7 @@ using Printf: Printf using StaticArrays: StaticArrays, SVector using TetGen_jll: TetGen_jll, libtet + include("cpptetgenio.jl") include("jltetgenio.jl") include("rawtetgenio.jl") @@ -27,5 +28,6 @@ export TetGenError export RawTetGenIO, facetlist!, RawFacet export numberofpoints, numberoftetrahedra, numberoftrifaces, numberofedges export volumemesh, surfacemesh +export save_tetgen end # module diff --git a/src/api.jl b/src/api.jl index a545742..ac39d80 100644 --- a/src/api.jl +++ b/src/api.jl @@ -10,6 +10,20 @@ function voronoi(points::Vector{Point{3, T}}) where {T <: AbstractFloat} return Mesh{Triangle}(result) end + +function JLTetGenIO(mesh; marker = :markers, holes = Point{3, Float64}[]) + f = faces(mesh) + if pkgversion(GeometryBasics) < v"0.5" + kw_args = Any[:facets => GeometryBasics.metafree(f), :holes => holes] + else + kw_args = Any[:facets => f, :holes => holes] + end + if hasproperty(f, marker) + push!(kw_args, :facetmarkers => getproperty(f, marker)) + end + return JLTetGenIO(coordinates(mesh); kw_args...) +end + """ $(SIGNATURES) @@ -27,20 +41,16 @@ function tetrahedralize( mesh, command = "Qp"; marker = :markers, holes = Point{3, Float64}[] ) - f = faces(mesh) - if pkgversion(GeometryBasics) < v"0.5" - kw_args = Any[:facets => GeometryBasics.metafree(f), :holes => holes] - else - kw_args = Any[:facets => f, :holes => holes] - end - if hasproperty(f, marker) - push!(kw_args, :facetmarkers => getproperty(f, marker)) - end - tio = JLTetGenIO(coordinates(mesh); kw_args...) + tio = JLTetGenIO(mesh; marker, holes) result = tetrahedralize(tio, command) return Mesh{Tetrahedron}(result) end +function save_tetgen(mesh, fstub; marker = :markers, holes = Point{3, Float64}[]) + return save_tetgen(JLTetGenIO(mesh; marker, holes), fstub) + return true +end + # JF: probably this case is included in the case above # """ # $(SIGNATURES) diff --git a/src/cpptetgenio.jl b/src/cpptetgenio.jl index b820811..f4cf98d 100644 --- a/src/cpptetgenio.jl +++ b/src/cpptetgenio.jl @@ -155,3 +155,19 @@ function tetunsuitable!(unsuitable::Function; check_signature = true) c_wrap_tetunsuitable = @cfunction(jl_wrap_tetunsuitable, Cint, (Ptr{Cdouble}, Ptr{Cdouble}, Ptr{Cdouble}, Ptr{Cdouble})) return ccall((:tetunsuitable_callback, libtet), Cvoid, (Ptr{Cvoid},), c_wrap_tetunsuitable) end + + +""" + save(input, fstub) + +Save input (mesh, [`CPPTetGenIO`](@ref), [`JLTetGenIO`](@ref) or [`RawTetGenIO`](@ref)) +to the files `fstub.node` and `fstub.poly`. These can be read by the tetgen executable and are suited for submission of +bug reportst to upstream tetgen. +""" +function save_tetgen end + +function save_tetgen(cinput::CPPTetGenIO{Float64}, fstub::String) + ccall((:save_poly, libtet), Cvoid, (CPPTetGenIO{Float64}, Cstring), cinput, fstub) + ccall((:save_nodes, libtet), Cvoid, (CPPTetGenIO{Float64}, Cstring), cinput, fstub) + return nothing +end diff --git a/src/jltetgenio.jl b/src/jltetgenio.jl index 5a3b812..4e2abbd 100644 --- a/src/jltetgenio.jl +++ b/src/jltetgenio.jl @@ -339,3 +339,9 @@ function tetrahedralize(input::JLTetGenIO{Float64}, command::String) end return convert(JLTetGenIO, cres) end + +function save_tetgen(input::JLTetGenIO{Float64}, fstub::String) + cinput = Base.cconvert(CPPTetGenIO{Float64}, input) + save_tetgen(cinput[1], fstub) + return nothing +end diff --git a/src/rawtetgenio.jl b/src/rawtetgenio.jl index d07a006..653e609 100644 --- a/src/rawtetgenio.jl +++ b/src/rawtetgenio.jl @@ -703,20 +703,10 @@ function tetrahedralize(stlfile::String, flags::String) return RawTetGenIO(coutput) end -function save_poly(input::RawTetGenIO{Float64}, fstub::String) - if pkgversion(TetGen_jll) < v"1.6.1" - error("save_poly is only available with TetGen_jll >=v1.6.1") - end - cinput, flist, plist = CPPTetGenIO(input) - return ccall((:save_poly, libtet), Cvoid, (CPPTetGenIO{Float64}, Cstring), cinput, fstub) -end - -function save_nodes(input::RawTetGenIO{Float64}, fstub::String) - if pkgversion(TetGen_jll) < v"1.6.1" - error("save_nodes is only available with TetGen_jll >=v1.6.1") - end +function save_tetgen(input::RawTetGenIO{Float64}, fstub::String) cinput, flist, plist = CPPTetGenIO(input) - return ccall((:save_nodes, libtet), Cvoid, (CPPTetGenIO{Float64}, Cstring), cinput, fstub) + save_tetgen(cinput, fstub) + return nothing end """ diff --git a/test/runtests.jl b/test/runtests.jl index 7dc1e0e..fb31a9a 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,9 +1,19 @@ using ExplicitImports, Aqua -using TetGen: TetGen, JLPolygon, JLFacet, Point, tetrahedralize +using TetGen: TetGen, JLPolygon, JLFacet, Point, tetrahedralize, save_tetgen using GeometryBasics: GeometryBasics using GeometryBasics: Mesh, Triangle, Tetrahedron, TriangleFace, QuadFace, faces using Test +function testsave(input, fstub) + return mktempdir() do dir + cd(dir) do + save_tetgen(input, fstub) + sleep(1) + isfile(fstub * ".node") && isfile(fstub * ".poly") + end + end +end + @testset "mesh based API" begin # Construct a cube out of Quads points = Point{3, Float64}[ @@ -29,6 +39,7 @@ using Test else mesh = GeometryBasics.MetaMesh(points, facets; markers) end + @test testsave(mesh, "mesh") result = tetrahedralize(mesh) @test result isa Mesh @@ -38,6 +49,7 @@ using Test else mesh = GeometryBasics.MetaMesh(points, facets) end + @test testsave(mesh, "mesh") result = tetrahedralize(mesh, "vpq1.414a0.1") @test result isa Mesh @@ -64,6 +76,7 @@ using Test else tetmesh = GeometryBasics.MetaMesh(tetpoints, tetfacets) end + @test testsave(mesh, "tetmesh") result = tetrahedralize(tetmesh, "pQqAa0.01") @test result isa Mesh @@ -108,6 +121,7 @@ using Test else mesh = GeometryBasics.MetaMesh(points, facets; markers = markers) end + @test testsave(mesh, "mesh") result = tetrahedralize(mesh, "pQqAa1.0"; holes = [Point{3, Float64}(0, 0, 0)]) @test result isa Mesh @@ -293,7 +307,9 @@ end ]' ) + @test testsave(input, "input") cinput, x1, x2 = TetGen.CPPTetGenIO(input) + @test testsave(cinput, "cinput") coutput = tetrahedralize(cinput, "pQa") @test coutput.numberofpoints == 8 From e36c1a9da211dd6b15130d65cb5f86dbc964d5da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Fuhrmann?= Date: Fri, 25 Sep 2026 23:20:24 +0200 Subject: [PATCH 24/35] Update version to 3.0 --- CHANGELOG.md | 6 +++--- Project.toml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae3bdc2..4fb024b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,11 @@ # Changelog All notable changes to this project since V1.0 will be documented in this file. + ## [3.0.0] - 2026-09-28 - Update TetGen_jll.jl dependency to 1.6.1 (corresponding to upstream TetGen version 1.6.1). Keep supporting TetGen_jll.jl - version 1.5.3 (corresponding to upstream TetGen version 1.5.1) -- Modify test infrastructure which runs tests for both jll versions -- Add `save_poly` and `save_nodes` methods for `RawTetgenIO` + version 1.5.4 (corresponding to upstream TetGen version 1.5.1) +- Add `save_tetgen` method to write out tetgen input to allow easy bug reporting to upstream ## [2.0.2] - 2026-05-11 - Cap TetGen_jll.jl version to ~1.5.3, to avoid unexpected behavior in CI tests. diff --git a/Project.toml b/Project.toml index d3e26c7..0260998 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "TetGen" uuid = "c5d3f3f7-f850-59f6-8a2e-ffc6dc1317ea" -version = "2.0.2" +version = "3.0" authors = ["SimonDanisch ", "Juergen Fuhrmann Date: Fri, 25 Sep 2026 23:51:36 +0200 Subject: [PATCH 25/35] Test with both 1.5.1 and 1.6.1 tetgen versions --- test/runtests-tetgen-1.5.1.jl | 10 ++ test/runtests.jl | 323 +-------------------------------- test/tetgentests.jl | 324 ++++++++++++++++++++++++++++++++++ 3 files changed, 337 insertions(+), 320 deletions(-) create mode 100644 test/runtests-tetgen-1.5.1.jl create mode 100644 test/tetgentests.jl diff --git a/test/runtests-tetgen-1.5.1.jl b/test/runtests-tetgen-1.5.1.jl new file mode 100644 index 0000000..ab2a77f --- /dev/null +++ b/test/runtests-tetgen-1.5.1.jl @@ -0,0 +1,10 @@ +push!(LOAD_PATH, "@v#.#", "@stdlib") +using Pkg +Pkg.activate(; temp = true) +Pkg.add("GeometryBasics") +Pkg.add(name = "TetGen_jll", version = "1.5.4") +Pkg.develop(path = joinpath(@__DIR__, "..")) +using Test +@testset "tetgen 1.5.1" begin + include("tetgentests.jl") +end diff --git a/test/runtests.jl b/test/runtests.jl index fb31a9a..3f46471 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,327 +1,10 @@ using ExplicitImports, Aqua -using TetGen: TetGen, JLPolygon, JLFacet, Point, tetrahedralize, save_tetgen -using GeometryBasics: GeometryBasics -using GeometryBasics: Mesh, Triangle, Tetrahedron, TriangleFace, QuadFace, faces +using TetGen: TetGen using Test -function testsave(input, fstub) - return mktempdir() do dir - cd(dir) do - save_tetgen(input, fstub) - sleep(1) - isfile(fstub * ".node") && isfile(fstub * ".poly") - end - end -end - -@testset "mesh based API" begin - # Construct a cube out of Quads - points = Point{3, Float64}[ - (0.0, 0.0, 0.0), (2.0, 0.0, 0.0), - (2.0, 2.0, 0.0), (0.0, 2.0, 0.0), - (0.0, 0.0, 12.0), (2.0, 0.0, 12.0), - (2.0, 2.0, 12.0), (0.0, 2.0, 12.0), - ] - - facets = QuadFace{Cint}[ - 1:4, - 5:8, - [1, 5, 6, 2], - [2, 6, 7, 3], - [3, 7, 8, 4], - [4, 8, 5, 1], - ] - - markers = Cint[-1, -2, 0, 0, 0, 0] - # attach some additional information to our faces! - if pkgversion(GeometryBasics) < v"0.5" - mesh = Mesh(points, GeometryBasics.meta(facets; markers)) - else - mesh = GeometryBasics.MetaMesh(points, facets; markers) - end - @test testsave(mesh, "mesh") - result = tetrahedralize(mesh) - @test result isa Mesh - - # Make it similar to the README example - if pkgversion(GeometryBasics) < v"0.5" - mesh = Mesh(points, facets) - else - mesh = GeometryBasics.MetaMesh(points, facets) - end - @test testsave(mesh, "mesh") - result = tetrahedralize(mesh, "vpq1.414a0.1") - @test result isa Mesh - - points = rand(Point{3, Float64}, 100) - result = TetGen.voronoi(points) - @test result isa Mesh - - tetpoints = Point{3, Float64}[ - (0.0, 0.0, 0.0), - (1.0, 0.0, 0.0), - (0.0, 1.0, 0.0), - (0.0, 0.0, 1.0), - ] - - tetfacets = TriangleFace{Cint}[ - [1, 2, 3], - [1, 2, 4], - [1, 3, 4], - [2, 3, 4], - ] - - if pkgversion(GeometryBasics) < v"0.5" - tetmesh = Mesh(tetpoints, tetfacets) - else - tetmesh = GeometryBasics.MetaMesh(tetpoints, tetfacets) - end - @test testsave(mesh, "tetmesh") - result = tetrahedralize(tetmesh, "pQqAa0.01") - @test result isa Mesh - - ################# cube with hole example - # Construct a cube out of Quads - points = Point{3, Float64}[ - # outer cube: - (-2.0, -2.0, -2.0), (2.0, -2.0, -2.0), - (2.0, 2.0, -2.0), (-2.0, 2.0, -2.0), - (-2.0, -2.0, 2.0), (2.0, -2.0, 2.0), - (2.0, 2.0, 2.0), (-2.0, 2.0, 2.0), - - # inner cube: - (-1.0, -1.0, -1.0), (1.0, -1.0, -1.0), - (1.0, 1.0, -1.0), (-1.0, 1.0, -1.0), - (-1.0, -1.0, 1.0), (1.0, -1.0, 1.0), - (1.0, 1.0, 1.0), (-1.0, 1.0, 1.0), - ] - - facets = QuadFace{Cint}[ - # outer cube: - [1, 2, 3, 4], - [5, 6, 7, 8], - [1, 5, 6, 2], - [2, 6, 7, 3], - [3, 7, 8, 4], - [4, 8, 5, 1], - - # inner cube: - [1, 2, 3, 4] .+ 8, - [5, 6, 7, 8] .+ 8, - [1, 5, 6, 2] .+ 8, - [2, 6, 7, 3] .+ 8, - [3, 7, 8, 4] .+ 8, - [4, 8, 5, 1] .+ 8, - ] - - markers = ones(Cint, 12) - - if pkgversion(GeometryBasics) < v"0.5" - mesh = Mesh(points, GeometryBasics.meta(facets; markers = markers)) - else - mesh = GeometryBasics.MetaMesh(points, facets; markers = markers) - end - @test testsave(mesh, "mesh") - result = tetrahedralize(mesh, "pQqAa1.0"; holes = [Point{3, Float64}(0, 0, 0)]) - @test result isa Mesh - - # s = Sphere{Float64}(Point(0.0, 0.0, 0.0), 2.0) - # - # x = PlainMesh{Float64, Triangle{Cint}}(s) - # - # function Mesh{T, Dim, FaceType}(a::AbstractGeometry; resolution = nothing) - # P = Point{T, Dim} - # Mesh( - # coordinates(a, P; resolution = resolution), - # faces(a, FaceType; resolution = resolution) - # ) - # end - # - # s = Sphere{Float64}(Point(0.0, 0.0, 0.0), 1.0) - # - # y = PlainMesh{Float64, Triangle{Cint}}(s) - -end - -@testset "examples.jl" begin - include("../examples/examples.jl") - function generic_test(result::RawTetGenIO) - @test volumemesh(result) isa Mesh - - if numberoftrifaces(result) > 0 - @test surfacemesh(result) isa Mesh - end - - buf = IOBuffer() - Base.show(buf, result) - @test length(buf.data) > 0 - - ctio, flist, plist = TetGen.CPPTetGenIO(result) - @test ctio.numberofpoints == numberofpoints(result) - @test ctio.numberoftetrahedra == numberoftetrahedra(result) - @test ctio.numberoftrifaces == numberoftrifaces(result) - end +include("tetgentests.jl") - result = random_delaunay(; npoints = 100) - @test numberofpoints(result) == 100 - generic_test(result) - - result = cube() - @test numberofpoints(result) == 8 - @test numberoftetrahedra(result) == 6 - @test numberofedges(result) == 12 - @test numberoftrifaces(result) == 12 - generic_test(result) - - result = cube_stl() - @test numberofpoints(result) == 8 - @test numberoftetrahedra(result) == 6 - @test numberofedges(result) == 12 - @test numberoftrifaces(result) == 12 - generic_test(result) - - result = cubewithhole() - @test numberofpoints(result) in [56, 52] - @test numberoftetrahedra(result) in [168, 137] - @test numberofedges(result) == 36 - @test numberoftrifaces(result) in [104, 96] - generic_test(result) - - result = prism() - @test numberofpoints(result) in [8, 6] - @test numberofedges(result) in [11, 9] - @test numberoftrifaces(result) in [12, 8] - @test numberoftetrahedra(result) in [6, 3] - generic_test(result) - - # exact numbers depend on FP arithmetic and - # compiler optimizations - - result = cube_localref() - generic_test(result) - - result = material_prism() - @test numberoftetrahedra(result) > 100 - generic_test(result) - - result = cutprism() - @test numberoftetrahedra(result) > 20 - generic_test(result) -end - -@testset "error handling" begin - function badcube1(; vol = 1) - input = TetGen.RawTetGenIO{Cdouble}() - input.pointlist = [ - 0 0 0; - 1 0 0; - 1 1 0; - 0 1 0; - 0 0 1; - 1 0 2; - 1 1 1; - 0 1 2 - ]' - - TetGen.facetlist!( - input, [ - 1 2 3 4; - 5 6 7 8; - 1 2 6 5; - 2 3 7 6; - 3 4 8 7; - 4 1 5 8 - ]' - ) - tetrahedralize(input, "pQa$(vol)") - end - - function badcube2(; vol = 1) - input = TetGen.RawTetGenIO{Cdouble}() - input.pointlist = [ - 0 0 0; - 1 0 0; - 1 1 0; - 0 1 0; - 0 0 1; - 1 0 1; - 1 1 1; - 0 1 1; - 0 1 1 - 1.0e-5 - ]' - - TetGen.facetlist!( - input, [ - 1 2 3 4; - 5 6 7 8; - 1 2 6 5; - 2 3 7 6; - 3 4 8 7; - 4 1 5 8 - ]' - ) - tetrahedralize(input, "pQa$(vol)") - end - - function test_catch_error(geom) - try - result = geom() - catch err - if typeof(err) == TetGenError - println("Caught TetGenError") - println(err) - return true - end - end - false - end - if !Sys.iswindows() - @test test_catch_error(badcube2) - end -end - -############################################## -# Solely for increasing codecov - -@testset "codecov" begin - input = TetGen.RawTetGenIO{Cdouble}() - input.pointlist = [ - 0 0 0; - 1 0 0; - 1 1 0; - 0 1 0; - 0 0 1; - 1 0 1; - 1 1 1; - 0 1 1 - ]' - - TetGen.facetlist!( - input, [ - 1 2 3 4; - 5 6 7 8; - 1 2 6 5; - 2 3 7 6; - 3 4 8 7; - 4 1 5 8 - ]' - ) - - @test testsave(input, "input") - cinput, x1, x2 = TetGen.CPPTetGenIO(input) - @test testsave(cinput, "cinput") - coutput = tetrahedralize(cinput, "pQa") - - @test coutput.numberofpoints == 8 - - function test_error_output() - for i in 1:10 - println(TetGenError(i)) - end - true - end - @test test_error_output() -end +run(`julia $(joinpath(@__DIR__, "runtests-tetgen-1.5.1.jl"))`) @testset "ExplicitImports" begin @test ExplicitImports.check_no_implicit_imports(TetGen, skip = (Base, Core)) === nothing diff --git a/test/tetgentests.jl b/test/tetgentests.jl new file mode 100644 index 0000000..2ea1e2f --- /dev/null +++ b/test/tetgentests.jl @@ -0,0 +1,324 @@ +using TetGen: TetGen, JLPolygon, JLFacet, Point, tetrahedralize, save_tetgen +using GeometryBasics: GeometryBasics +using GeometryBasics: Mesh, Triangle, Tetrahedron, TriangleFace, QuadFace, faces +using Test + +function testsave(input, fstub) + return mktempdir() do dir + cd(dir) do + save_tetgen(input, fstub) + sleep(1) + isfile(fstub * ".node") && isfile(fstub * ".poly") + end + end +end + + +@testset "mesh based API" begin + # Construct a cube out of Quads + points = Point{3, Float64}[ + (0.0, 0.0, 0.0), (2.0, 0.0, 0.0), + (2.0, 2.0, 0.0), (0.0, 2.0, 0.0), + (0.0, 0.0, 12.0), (2.0, 0.0, 12.0), + (2.0, 2.0, 12.0), (0.0, 2.0, 12.0), + ] + + facets = QuadFace{Cint}[ + 1:4, + 5:8, + [1, 5, 6, 2], + [2, 6, 7, 3], + [3, 7, 8, 4], + [4, 8, 5, 1], + ] + + markers = Cint[-1, -2, 0, 0, 0, 0] + # attach some additional information to our faces! + if pkgversion(GeometryBasics) < v"0.5" + mesh = Mesh(points, GeometryBasics.meta(facets; markers)) + else + mesh = GeometryBasics.MetaMesh(points, facets; markers) + end + @test testsave(mesh, "mesh") + result = tetrahedralize(mesh) + @test result isa Mesh + + # Make it similar to the README example + if pkgversion(GeometryBasics) < v"0.5" + mesh = Mesh(points, facets) + else + mesh = GeometryBasics.MetaMesh(points, facets) + end + @test testsave(mesh, "mesh") + result = tetrahedralize(mesh, "vpq1.414a0.1") + @test result isa Mesh + + points = rand(Point{3, Float64}, 100) + result = TetGen.voronoi(points) + @test result isa Mesh + + tetpoints = Point{3, Float64}[ + (0.0, 0.0, 0.0), + (1.0, 0.0, 0.0), + (0.0, 1.0, 0.0), + (0.0, 0.0, 1.0), + ] + + tetfacets = TriangleFace{Cint}[ + [1, 2, 3], + [1, 2, 4], + [1, 3, 4], + [2, 3, 4], + ] + + if pkgversion(GeometryBasics) < v"0.5" + tetmesh = Mesh(tetpoints, tetfacets) + else + tetmesh = GeometryBasics.MetaMesh(tetpoints, tetfacets) + end + @test testsave(mesh, "tetmesh") + result = tetrahedralize(tetmesh, "pQqAa0.01") + @test result isa Mesh + + ################# cube with hole example + # Construct a cube out of Quads + points = Point{3, Float64}[ + # outer cube: + (-2.0, -2.0, -2.0), (2.0, -2.0, -2.0), + (2.0, 2.0, -2.0), (-2.0, 2.0, -2.0), + (-2.0, -2.0, 2.0), (2.0, -2.0, 2.0), + (2.0, 2.0, 2.0), (-2.0, 2.0, 2.0), + + # inner cube: + (-1.0, -1.0, -1.0), (1.0, -1.0, -1.0), + (1.0, 1.0, -1.0), (-1.0, 1.0, -1.0), + (-1.0, -1.0, 1.0), (1.0, -1.0, 1.0), + (1.0, 1.0, 1.0), (-1.0, 1.0, 1.0), + ] + + facets = QuadFace{Cint}[ + # outer cube: + [1, 2, 3, 4], + [5, 6, 7, 8], + [1, 5, 6, 2], + [2, 6, 7, 3], + [3, 7, 8, 4], + [4, 8, 5, 1], + + # inner cube: + [1, 2, 3, 4] .+ 8, + [5, 6, 7, 8] .+ 8, + [1, 5, 6, 2] .+ 8, + [2, 6, 7, 3] .+ 8, + [3, 7, 8, 4] .+ 8, + [4, 8, 5, 1] .+ 8, + ] + + markers = ones(Cint, 12) + + if pkgversion(GeometryBasics) < v"0.5" + mesh = Mesh(points, GeometryBasics.meta(facets; markers = markers)) + else + mesh = GeometryBasics.MetaMesh(points, facets; markers = markers) + end + @test testsave(mesh, "mesh") + result = tetrahedralize(mesh, "pQqAa1.0"; holes = [Point{3, Float64}(0, 0, 0)]) + @test result isa Mesh + + # s = Sphere{Float64}(Point(0.0, 0.0, 0.0), 2.0) + # + # x = PlainMesh{Float64, Triangle{Cint}}(s) + # + # function Mesh{T, Dim, FaceType}(a::AbstractGeometry; resolution = nothing) + # P = Point{T, Dim} + # Mesh( + # coordinates(a, P; resolution = resolution), + # faces(a, FaceType; resolution = resolution) + # ) + # end + # + # s = Sphere{Float64}(Point(0.0, 0.0, 0.0), 1.0) + # + # y = PlainMesh{Float64, Triangle{Cint}}(s) + +end + +@testset "examples.jl" begin + include("../examples/examples.jl") + function generic_test(result::RawTetGenIO) + @test volumemesh(result) isa Mesh + + if numberoftrifaces(result) > 0 + @test surfacemesh(result) isa Mesh + end + + buf = IOBuffer() + Base.show(buf, result) + @test length(buf.data) > 0 + + ctio, flist, plist = TetGen.CPPTetGenIO(result) + @test ctio.numberofpoints == numberofpoints(result) + @test ctio.numberoftetrahedra == numberoftetrahedra(result) + @test ctio.numberoftrifaces == numberoftrifaces(result) + end + + result = random_delaunay(; npoints = 100) + @test numberofpoints(result) == 100 + generic_test(result) + + result = cube() + @test numberofpoints(result) == 8 + @test numberoftetrahedra(result) == 6 + @test numberofedges(result) == 12 + @test numberoftrifaces(result) == 12 + generic_test(result) + + result = cube_stl() + @test numberofpoints(result) == 8 + @test numberoftetrahedra(result) == 6 + @test numberofedges(result) == 12 + @test numberoftrifaces(result) == 12 + generic_test(result) + + result = cubewithhole() + @test numberofpoints(result) in [56, 52] + @test numberoftetrahedra(result) in [168, 137] + @test numberofedges(result) == 36 + @test numberoftrifaces(result) in [104, 96] + generic_test(result) + + result = prism() + @test numberofpoints(result) in [8, 6] + @test numberofedges(result) in [11, 9] + @test numberoftrifaces(result) in [12, 8] + @test numberoftetrahedra(result) in [6, 3] + generic_test(result) + + # exact numbers depend on FP arithmetic and + # compiler optimizations + + result = cube_localref() + generic_test(result) + + result = material_prism() + @test numberoftetrahedra(result) > 100 + generic_test(result) + + result = cutprism() + @test numberoftetrahedra(result) > 20 + generic_test(result) +end + +@testset "error handling" begin + function badcube1(; vol = 1) + input = TetGen.RawTetGenIO{Cdouble}() + input.pointlist = [ + 0 0 0; + 1 0 0; + 1 1 0; + 0 1 0; + 0 0 1; + 1 0 2; + 1 1 1; + 0 1 2 + ]' + + TetGen.facetlist!( + input, [ + 1 2 3 4; + 5 6 7 8; + 1 2 6 5; + 2 3 7 6; + 3 4 8 7; + 4 1 5 8 + ]' + ) + tetrahedralize(input, "pQa$(vol)") + end + + function badcube2(; vol = 1) + input = TetGen.RawTetGenIO{Cdouble}() + input.pointlist = [ + 0 0 0; + 1 0 0; + 1 1 0; + 0 1 0; + 0 0 1; + 1 0 1; + 1 1 1; + 0 1 1; + 0 1 1 - 1.0e-5 + ]' + + TetGen.facetlist!( + input, [ + 1 2 3 4; + 5 6 7 8; + 1 2 6 5; + 2 3 7 6; + 3 4 8 7; + 4 1 5 8 + ]' + ) + tetrahedralize(input, "pQa$(vol)") + end + + function test_catch_error(geom) + try + result = geom() + catch err + if typeof(err) == TetGenError + println("Caught TetGenError") + println(err) + return true + end + end + false + end + if !Sys.iswindows() + @test test_catch_error(badcube2) + end +end + +############################################## +# Solely for increasing codecov + +@testset "codecov" begin + input = TetGen.RawTetGenIO{Cdouble}() + input.pointlist = [ + 0 0 0; + 1 0 0; + 1 1 0; + 0 1 0; + 0 0 1; + 1 0 1; + 1 1 1; + 0 1 1 + ]' + + TetGen.facetlist!( + input, [ + 1 2 3 4; + 5 6 7 8; + 1 2 6 5; + 2 3 7 6; + 3 4 8 7; + 4 1 5 8 + ]' + ) + + @test testsave(input, "input") + cinput, x1, x2 = TetGen.CPPTetGenIO(input) + @test testsave(cinput, "cinput") + coutput = tetrahedralize(cinput, "pQa") + + @test coutput.numberofpoints == 8 + + function test_error_output() + for i in 1:10 + println(TetGenError(i)) + end + true + end + @test test_error_output() +end From 0e1cd5981c77506fd70124cf57cb2308a8c7a08f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Fuhrmann?= Date: Sat, 26 Sep 2026 00:14:09 +0200 Subject: [PATCH 26/35] Update README, docs --- README.md | 13 ++++++++++++- src/cpptetgenio.jl | 7 ++++++- src/jltetgenio.jl | 5 +++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9463700..1a6383c 100644 --- a/README.md +++ b/README.md @@ -6,12 +6,23 @@ [![](https://img.shields.io/badge/docs-dev-blue.svg)](https://JuliaGeometry.github.io/TetGen.jl/dev) [![Aqua QA](https://raw.githubusercontent.com/JuliaTesting/Aqua.jl/master/badge.svg)](https://github.com/JuliaTesting/Aqua.jl) -The `TetGen.jl` package is a Julia wrapper for the C++ project [TetGen](https://wias-berlin.de/software/index.jsp?id=TetGen&lang=1). This wrapper enables TetGen based tetrahedral meshing, and (constrained) 3D Delaunay and Voronoi tessellation. +The `TetGen.jl` package is a Julia wrapper for the C++ project [TetGen](https://codeberg.org/TetGen/TetGen). This wrapper enables TetGen based tetrahedral meshing, and (constrained) 3D Delaunay and Voronoi tessellation. If you find TetGen useful, please consider citing Hang Si: "TetGen, a Delaunay-Based Quality Tetrahedral Mesh Generator" ACM Trans. on Mathematical Software. 41 (2), 2015 [http://doi.acm.org/10.1145/2629697](http://doi.acm.org/10.1145/2629697). +## Supported upstream versions +By choosing corresponding versions of the [TetGen_jll.jl](https://github.com/JuliaBinaryWrappers/TetGen_jll.jl) binary package, +it is possible to choose between different TetGen upstream versions: + +| TetGen version | TetGen_jll version | +|:--------------:|:------------------:| +| 1.5.1 | 1.5.4 | +| 1.6.1 | 1.6.1 | + +By default, version 1.6.1 is installed. + ## License When installing TetGen.jl, a compiled library version of the TetGen library will be downloaded from the [TetGen_jll.jl](https://github.com/JuliaBinaryWrappers/TetGen_jll.jl) repository. This library is bound to the [Affero Gnu Public License v3 (AGPL)](https://www.gnu.org/licenses/agpl-3.0.html), but the bindings to the library in this package, TetGen.jl, are licensed under MIT. This means that code using the TetGen library via the TetGen.jl bindings is subject to TetGen's licensing terms. If you distribute covered work, i.e. a program that links to and is distributed with the TetGen library, then that distribution falls under the terms of the AGPLv3. diff --git a/src/cpptetgenio.jl b/src/cpptetgenio.jl index f4cf98d..435653f 100644 --- a/src/cpptetgenio.jl +++ b/src/cpptetgenio.jl @@ -10,6 +10,11 @@ struct CFacet{T} numberofholes::Cint end +""" + struct CPPTetGenIO + +Julia "mirror" structure to be passed to the C wrapper code. +""" struct CPPTetGenIO{T} firstnumber::Cint # 0 or 1, default 0. mesh_dim::Cint # must be 3. @@ -158,7 +163,7 @@ end """ - save(input, fstub) + save_tetgen(input, fstub) Save input (mesh, [`CPPTetGenIO`](@ref), [`JLTetGenIO`](@ref) or [`RawTetGenIO`](@ref)) to the files `fstub.node` and `fstub.poly`. These can be read by the tetgen executable and are suited for submission of diff --git a/src/jltetgenio.jl b/src/jltetgenio.jl index 4e2abbd..b24aadc 100644 --- a/src/jltetgenio.jl +++ b/src/jltetgenio.jl @@ -55,6 +55,11 @@ end inttype(::Type{Float64}) = Int64 inttype(::Type{Float32}) = Int32 +""" + struct JLTetGenIO + +Intermediate structure for TetGen data for interaction with GeometryBasics.Mesh. +""" struct JLTetGenIO{T, NSimplex, NAttributes, NMTr, IT, FT} points::Vector{Point{3, T}} pointattributes::Vector{SVector{NAttributes, T}} From 47a081b4ed73800e237a42d7813a36daa4451fd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Fuhrmann?= Date: Sat, 26 Sep 2026 22:55:59 +0200 Subject: [PATCH 27/35] Update Readme + changelog --- CHANGELOG.md | 5 +++-- README.md | 10 +++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fb024b..ab82d49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,11 @@ All notable changes to this project since V1.0 will be documented in this file. -## [3.0.0] - 2026-09-28 +## [3.0.0] - unreleased - Update TetGen_jll.jl dependency to 1.6.1 (corresponding to upstream TetGen version 1.6.1). Keep supporting TetGen_jll.jl version 1.5.4 (corresponding to upstream TetGen version 1.5.1) -- Add `save_tetgen` method to write out tetgen input to allow easy bug reporting to upstream +- Maintain CI tests for both upstream versions +- Add `save_tetgen` method to write out tetgen input to suppoirt creation of MWE for upstream bug reports ## [2.0.2] - 2026-05-11 - Cap TetGen_jll.jl version to ~1.5.3, to avoid unexpected behavior in CI tests. diff --git a/README.md b/README.md index 1a6383c..3e80b7e 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,15 @@ it is possible to choose between different TetGen upstream versions: | 1.5.1 | 1.5.4 | | 1.6.1 | 1.6.1 | -By default, version 1.6.1 is installed. +By default, the package manager installs version 1.6.1. + +## Submitting bug reports +Bug reports regarding the Julia wrapper can be submitted just by opening an issue in this repo. Please try to attach a minimal +working example (MWE) to your report. + +Bug reports to upstream TetGen (v1.6.1) can be submitted to the [codeberg upstream repository](https://codeberg.org/TetGen/TetGen). +The `save_tetgen(input, fstub)` converts and writes the Julia input description to a pair of TetGen `.node` and `.poly` files which can serve +as an MWE. ## License From 346cd149584e23a85817c93ac14dbb1f514a0d38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Fuhrmann?= Date: Sat, 26 Sep 2026 23:25:06 +0200 Subject: [PATCH 28/35] Preserve cinput during save_tetgen call Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/jltetgenio.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jltetgenio.jl b/src/jltetgenio.jl index b24aadc..2082d85 100644 --- a/src/jltetgenio.jl +++ b/src/jltetgenio.jl @@ -347,6 +347,6 @@ end function save_tetgen(input::JLTetGenIO{Float64}, fstub::String) cinput = Base.cconvert(CPPTetGenIO{Float64}, input) - save_tetgen(cinput[1], fstub) + GC.@preserve cinput save_tetgen(cinput[1], fstub) return nothing end From afb4751118997cba04b1aad216ed49d6a6588757 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Fuhrmann?= Date: Sat, 26 Sep 2026 23:25:23 +0200 Subject: [PATCH 29/35] Fix test to save tetmesh instead of mesh Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- test/tetgentests.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/tetgentests.jl b/test/tetgentests.jl index 2ea1e2f..2e750f5 100644 --- a/test/tetgentests.jl +++ b/test/tetgentests.jl @@ -76,7 +76,7 @@ end else tetmesh = GeometryBasics.MetaMesh(tetpoints, tetfacets) end - @test testsave(mesh, "tetmesh") + @test testsave(tetmesh, "tetmesh") result = tetrahedralize(tetmesh, "pQqAa0.01") @test result isa Mesh From f4d22bb35bb51a08f49303281d4ed5a75ce2e7ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Fuhrmann?= Date: Sat, 26 Sep 2026 23:25:46 +0200 Subject: [PATCH 30/35] Correct typo in save_tetgen function comment Fix typo in comment regarding bug reports. Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/cpptetgenio.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cpptetgenio.jl b/src/cpptetgenio.jl index 435653f..d88810b 100644 --- a/src/cpptetgenio.jl +++ b/src/cpptetgenio.jl @@ -167,7 +167,7 @@ end Save input (mesh, [`CPPTetGenIO`](@ref), [`JLTetGenIO`](@ref) or [`RawTetGenIO`](@ref)) to the files `fstub.node` and `fstub.poly`. These can be read by the tetgen executable and are suited for submission of -bug reportst to upstream tetgen. +bug reports to upstream tetgen. """ function save_tetgen end From bd14a93b73293dc039056066560128f0262dfedd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Fuhrmann?= Date: Sat, 26 Sep 2026 23:25:58 +0200 Subject: [PATCH 31/35] Update documentation for holelist array in rawtetgenio.jl Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/rawtetgenio.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rawtetgenio.jl b/src/rawtetgenio.jl index 653e609..4c4c379 100644 --- a/src/rawtetgenio.jl +++ b/src/rawtetgenio.jl @@ -13,8 +13,7 @@ struct RawFacet{T} polygonlist::Array{Array{Cint, 1}, 1} """ - `3 x npoints` array of points given by their coordinates - marking polygons describing holes in the facet. + `3 x nholes` array of points marking holes in the facet. """ holelist::Array{T, 2} end From b621398934b8ef5486cebd034d73bbd89c1aaca0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Fuhrmann?= Date: Sat, 26 Sep 2026 23:26:13 +0200 Subject: [PATCH 32/35] Refactor save_tetgen to remove unnecessary return Removed redundant return statement in save_tetgen function. Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- src/api.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/src/api.jl b/src/api.jl index ac39d80..e1ac653 100644 --- a/src/api.jl +++ b/src/api.jl @@ -48,7 +48,6 @@ end function save_tetgen(mesh, fstub; marker = :markers, holes = Point{3, Float64}[]) return save_tetgen(JLTetGenIO(mesh; marker, holes), fstub) - return true end # JF: probably this case is included in the case above From 72ab225e6c334507c3af182e0a23d688c04fc738 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Fuhrmann?= Date: Sat, 26 Sep 2026 23:26:29 +0200 Subject: [PATCH 33/35] Change tetrahedralize parameter from 'p' to 'paqAQ' Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- examples/examples.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/examples.jl b/examples/examples.jl index fc670d2..0dc6a2e 100644 --- a/examples/examples.jl +++ b/examples/examples.jl @@ -236,5 +236,5 @@ function cutprism(; vol = 0.05) input.facetmarkerlist = [1, 2, 3, 3, 3, 4, 5, 6, 7] input.regionlist = [-0.1 -0.1 0.1 1 vol;]' - return tetrahedralize(input, "p") #aqAQ") + return tetrahedralize(input, "paqAQ") end From 1e79635611952a30a217fbd565f969b227d95b44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Fuhrmann?= Date: Sat, 26 Sep 2026 23:26:39 +0200 Subject: [PATCH 34/35] Revise CHANGELOG.md for clarity and updates Update changelog to include recent changes and maintain formatting. Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- CHANGELOG.md | 146 +++++++++++++++++++++++++-------------------------- 1 file changed, 73 insertions(+), 73 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab82d49..3aa0f1c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,76 +1,76 @@ -# Changelog - -All notable changes to this project since V1.0 will be documented in this file. - -## [3.0.0] - unreleased -- Update TetGen_jll.jl dependency to 1.6.1 (corresponding to upstream TetGen version 1.6.1). Keep supporting TetGen_jll.jl - version 1.5.4 (corresponding to upstream TetGen version 1.5.1) -- Maintain CI tests for both upstream versions -- Add `save_tetgen` method to write out tetgen input to suppoirt creation of MWE for upstream bug reports - -## [2.0.2] - 2026-05-11 -- Cap TetGen_jll.jl version to ~1.5.3, to avoid unexpected behavior in CI tests. - See #47 for a discussion of the background - -## [2.0.1] - 2026-01-13 -- modernize tests and CI - -## [2.0.1] - 2024-11-28 -- Allow for GeometryBasics v0.5 along with 0.4 -- Breaking: with GeometryBasics v0.5, a MetaMesh has - to be provided as input for Tetrahedralize. - The RawTetGenIO based API is not affected. Users who - rely on the RawTetGenIO based API can use `TetGen="1,2"` - in their `[compat]` section. -- Breaking: remove deprecated `tetunsuitable`. Use `tetunsuitable!` instead - -## [1.5.1] - 2024-02-27 - -- Fix version check for `MemoryRef` -- Add ci for apple silicon - -## [1.5.0] - 2023-11-15 - -- Fix imports/named using from GeometryBasics -- add juliaformatter -- @deprecate tetunsuitable tetunsuitable! -- Pass pointer to unsafe_convert -- Test for type of return value of unsuitable function -- fix for 1.11 memory handling - -## [1.4.0] - 2022-06-02 -- allow to input stl files - -## [1.3.0] - 2021-10-05 - -- Switch Julia package to MIT license (after the FFTW model). -- Remove checking of error catching from tests due to problems on windows. -- Point3f0->Point3f (GeometryBasics version) -- Add stl loading functionality -- Switch Julia package to MIT license (after the FFTW model). - -## [1.2.1] - 2021-09-13 -- allow for GeometryBasics 0.4 - -## [1.2.0] - 2021-05-26 - -- Added example for RawTetGenIO and holes -- Created holes for tetrahedralize(::Mesh, ...) -- Added test example for mesh API with holes - -## [1.1.1] - 2021-01-02 - -- Bump version compat for StaticArrays - -## [1.1.0] - 2020-12-22 - -- Thank you travis and bye bye -- Update of upstream patch version of TetGen to "August 18, 2018". +# Changelog + +All notable changes to this project since V1.0 will be documented in this file. + +## [3.0.0] - unreleased +- Update TetGen_jll.jl dependency to 1.6.1 (corresponding to upstream TetGen version 1.6.1). Keep supporting TetGen_jll.jl + version 1.5.4 (corresponding to upstream TetGen version 1.5.1) +- Maintain CI tests for both upstream versions +- Add `save_tetgen` method to write out tetgen input to support creation of MWE for upstream bug reports + +## [2.0.2] - 2026-05-11 +- Cap TetGen_jll.jl version to ~1.5.3, to avoid unexpected behavior in CI tests. + See #47 for a discussion of the background + +## [2.0.1] - 2026-01-13 +- modernize tests and CI + +## [2.0.1] - 2024-11-28 +- Allow for GeometryBasics v0.5 along with 0.4 +- Breaking: with GeometryBasics v0.5, a MetaMesh has + to be provided as input for Tetrahedralize. + The RawTetGenIO based API is not affected. Users who + rely on the RawTetGenIO based API can use `TetGen="1,2"` + in their `[compat]` section. +- Breaking: remove deprecated `tetunsuitable`. Use `tetunsuitable!` instead + +## [1.5.1] - 2024-02-27 + +- Fix version check for `MemoryRef` +- Add ci for apple silicon + +## [1.5.0] - 2023-11-15 + +- Fix imports/named using from GeometryBasics +- add juliaformatter +- @deprecate tetunsuitable tetunsuitable! +- Pass pointer to unsafe_convert +- Test for type of return value of unsuitable function +- fix for 1.11 memory handling + +## [1.4.0] - 2022-06-02 +- allow to input stl files + +## [1.3.0] - 2021-10-05 + +- Switch Julia package to MIT license (after the FFTW model). +- Remove checking of error catching from tests due to problems on windows. +- Point3f0->Point3f (GeometryBasics version) +- Add stl loading functionality +- Switch Julia package to MIT license (after the FFTW model). + +## [1.2.1] - 2021-09-13 +- allow for GeometryBasics 0.4 + +## [1.2.0] - 2021-05-26 + +- Added example for RawTetGenIO and holes +- Created holes for tetrahedralize(::Mesh, ...) +- Added test example for mesh API with holes + +## [1.1.1] - 2021-01-02 + +- Bump version compat for StaticArrays + +## [1.1.0] - 2020-12-22 + +- Thank you travis and bye bye +- Update of upstream patch version of TetGen to "August 18, 2018". - Formally this still appears to be 1.5.1 , and it is manifest in TetGen_jll 1.5.1+1 - Catch errors thrown by TetGen in a rc variable, throw julia error if rc!=0 - Handle unsuitable callback to allow local refinement. - -## [1.0.0] - 2020-11-30 -- Dependency on artifacts -- Refactoring. API of API.jl stays the same. -- Provide array based API. and RawTetGenIO + +## [1.0.0] - 2020-11-30 +- Dependency on artifacts +- Refactoring. API of API.jl stays the same. +- Provide array based API. and RawTetGenIO From 661c6732faa55afd0d85d12c8e50d790ad469b74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Fuhrmann?= Date: Sat, 26 Sep 2026 23:37:07 +0200 Subject: [PATCH 35/35] fix dos line endings in changelog introduced by copilot --- CHANGELOG.md | 151 +++++++++++++++++++++++++-------------------------- 1 file changed, 75 insertions(+), 76 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3aa0f1c..270f5dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,76 +1,75 @@ -# Changelog - -All notable changes to this project since V1.0 will be documented in this file. - -## [3.0.0] - unreleased -- Update TetGen_jll.jl dependency to 1.6.1 (corresponding to upstream TetGen version 1.6.1). Keep supporting TetGen_jll.jl - version 1.5.4 (corresponding to upstream TetGen version 1.5.1) -- Maintain CI tests for both upstream versions -- Add `save_tetgen` method to write out tetgen input to support creation of MWE for upstream bug reports - -## [2.0.2] - 2026-05-11 -- Cap TetGen_jll.jl version to ~1.5.3, to avoid unexpected behavior in CI tests. - See #47 for a discussion of the background - -## [2.0.1] - 2026-01-13 -- modernize tests and CI - -## [2.0.1] - 2024-11-28 -- Allow for GeometryBasics v0.5 along with 0.4 -- Breaking: with GeometryBasics v0.5, a MetaMesh has - to be provided as input for Tetrahedralize. - The RawTetGenIO based API is not affected. Users who - rely on the RawTetGenIO based API can use `TetGen="1,2"` - in their `[compat]` section. -- Breaking: remove deprecated `tetunsuitable`. Use `tetunsuitable!` instead - -## [1.5.1] - 2024-02-27 - -- Fix version check for `MemoryRef` -- Add ci for apple silicon - -## [1.5.0] - 2023-11-15 - -- Fix imports/named using from GeometryBasics -- add juliaformatter -- @deprecate tetunsuitable tetunsuitable! -- Pass pointer to unsafe_convert -- Test for type of return value of unsuitable function -- fix for 1.11 memory handling - -## [1.4.0] - 2022-06-02 -- allow to input stl files - -## [1.3.0] - 2021-10-05 - -- Switch Julia package to MIT license (after the FFTW model). -- Remove checking of error catching from tests due to problems on windows. -- Point3f0->Point3f (GeometryBasics version) -- Add stl loading functionality -- Switch Julia package to MIT license (after the FFTW model). - -## [1.2.1] - 2021-09-13 -- allow for GeometryBasics 0.4 - -## [1.2.0] - 2021-05-26 - -- Added example for RawTetGenIO and holes -- Created holes for tetrahedralize(::Mesh, ...) -- Added test example for mesh API with holes - -## [1.1.1] - 2021-01-02 - -- Bump version compat for StaticArrays - -## [1.1.0] - 2020-12-22 - -- Thank you travis and bye bye -- Update of upstream patch version of TetGen to "August 18, 2018". -- Formally this still appears to be 1.5.1 , and it is manifest in TetGen_jll 1.5.1+1 -- Catch errors thrown by TetGen in a rc variable, throw julia error if rc!=0 -- Handle unsuitable callback to allow local refinement. - -## [1.0.0] - 2020-11-30 -- Dependency on artifacts -- Refactoring. API of API.jl stays the same. -- Provide array based API. and RawTetGenIO +# Changelog +All notable changes to this project since V1.0 will be documented in this file. + +## [3.0.0] - unreleased +- Update TetGen_jll.jl dependency to 1.6.1 (corresponding to upstream TetGen version 1.6.1). Keep supporting TetGen_jll.jl + version 1.5.4 (corresponding to upstream TetGen version 1.5.1) +- Maintain CI tests for both upstream versions +- Add `save_tetgen` method to write out tetgen input to support creation of MWE for upstream bug reports + +## [2.0.2] - 2026-05-11 +- Cap TetGen_jll.jl version to ~1.5.3, to avoid unexpected behavior in CI tests. + See #47 for a discussion of the background + +## [2.0.1] - 2026-01-13 +- modernize tests and CI + +## [2.0.1] - 2024-11-28 +- Allow for GeometryBasics v0.5 along with 0.4 +- Breaking: with GeometryBasics v0.5, a MetaMesh has + to be provided as input for Tetrahedralize. + The RawTetGenIO based API is not affected. Users who + rely on the RawTetGenIO based API can use `TetGen="1,2"` + in their `[compat]` section. +- Breaking: remove deprecated `tetunsuitable`. Use `tetunsuitable!` instead + +## [1.5.1] - 2024-02-27 + +- Fix version check for `MemoryRef` +- Add ci for apple silicon + +## [1.5.0] - 2023-11-15 + +- Fix imports/named using from GeometryBasics +- add juliaformatter +- @deprecate tetunsuitable tetunsuitable! +- Pass pointer to unsafe_convert +- Test for type of return value of unsuitable function +- fix for 1.11 memory handling + +## [1.4.0] - 2022-06-02 +- allow to input stl files + +## [1.3.0] - 2021-10-05 + +- Switch Julia package to MIT license (after the FFTW model). +- Remove checking of error catching from tests due to problems on windows. +- Point3f0->Point3f (GeometryBasics version) +- Add stl loading functionality +- Switch Julia package to MIT license (after the FFTW model). + +## [1.2.1] - 2021-09-13 +- allow for GeometryBasics 0.4 + +## [1.2.0] - 2021-05-26 + +- Added example for RawTetGenIO and holes +- Created holes for tetrahedralize(::Mesh, ...) +- Added test example for mesh API with holes + +## [1.1.1] - 2021-01-02 + +- Bump version compat for StaticArrays + +## [1.1.0] - 2020-12-22 + +- Thank you travis and bye bye +- Update of upstream patch version of TetGen to "August 18, 2018". +- Formally this still appears to be 1.5.1 , and it is manifest in TetGen_jll 1.5.1+1 +- Catch errors thrown by TetGen in a rc variable, throw julia error if rc!=0 +- Handle unsuitable callback to allow local refinement. + +## [1.0.0] - 2020-11-30 +- Dependency on artifacts +- Refactoring. API of API.jl stays the same. +- Provide array based API. and RawTetGenIO