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 diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a1411b..270f5dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,12 @@ # 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 @@ -60,9 +65,9 @@ All notable changes to this project since V1.0 will be documented in this file. - 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. +- 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 diff --git a/Project.toml b/Project.toml index 58bb6bc..0260998 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "TetGen" uuid = "c5d3f3f7-f850-59f6-8a2e-ffc6dc1317ea" +version = "3.0" authors = ["SimonDanisch ", "Juergen Fuhrmann 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,15 @@ 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) +end + # JF: probably this case is included in the case above # """ # $(SIGNATURES) diff --git a/src/cpptetgenio.jl b/src/cpptetgenio.jl index b820811..d88810b 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. @@ -155,3 +160,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_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 +bug reports 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..2082d85 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}} @@ -339,3 +344,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) + GC.@preserve cinput save_tetgen(cinput[1], fstub) + return nothing +end diff --git a/src/rawtetgenio.jl b/src/rawtetgenio.jl index 008beaf..4c4c379 100644 --- a/src/rawtetgenio.jl +++ b/src/rawtetgenio.jl @@ -13,8 +13,7 @@ struct RawFacet{T} polygonlist::Array{Array{Cint, 1}, 1} """ - 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 @@ -357,6 +356,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,8 +702,14 @@ function tetrahedralize(stlfile::String, flags::String) return RawTetGenIO(coutput) end +function save_tetgen(input::RawTetGenIO{Float64}, fstub::String) + cinput, flist, plist = CPPTetGenIO(input) + save_tetgen(cinput, fstub) + return nothing +end + """ -$(TYPEDSIGNATURES) +$(TYPEDSIGNATURES) Create GeometryBasics.Mesh from the triface list (for quick visualization purposes using Makie's wireframe). 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 2e1f374..3f46471 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,311 +1,10 @@ using ExplicitImports, Aqua -using TetGen: TetGen, JLPolygon, JLFacet, Point, tetrahedralize -using GeometryBasics: GeometryBasics -using GeometryBasics: Mesh, Triangle, Tetrahedron, TriangleFace, QuadFace, faces +using TetGen: TetGen using Test -@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), - ] +include("tetgentests.jl") - 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 - 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 - 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 - 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 - 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) == 56 - @test numberoftetrahedra(result) == 168 - @test numberofedges(result) == 36 - @test numberoftrifaces(result) == 104 - generic_test(result) - - result = prism() - @test numberofpoints(result) == 8 - @test numberofedges(result) == 11 - @test numberoftrifaces(result) == 12 - @test numberoftetrahedra(result) == 6 - 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) > 100 - 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 - ]' - ) - - cinput, x1, x2 = TetGen.CPPTetGenIO(input) - 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..2e750f5 --- /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(tetmesh, "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