# API

## Quick Start - isosurface

Given 3D levelset data such as a CT scan, we can do:

```
using Meshing
A = rand(50,50,50) # 3D Matrix
points,faces = isosurface(A)
```

An iso-level is specified within the algorithm specification as follows:

```
using Meshing
A = rand(50,50,50) # 3D Matrix
points,faces = isosurface(A, MarchingCubes(iso=1))
```

## Quick Start - GeometryBasics

Meshing is well-integrated with GeometryBasics.jl by extending the mesh constructors for convience.

The algorithms operate on a `Function`

, `AbstractArray`

, or `SignedDistanceField`

and output a concrete `AbstractMesh`

. For example, we can use the GeometryBasics API as follows, using `Rect`

to specify the bounds:

```
using Meshing
using GeometryBasics
using LinearAlgebra: dot, norm
using FileIO
# Mesh an equation of sphere in the Axis-Aligned Bounding box starting
# at -1,-1,-1 and widths of 2,2,2 using Marching Cubes
m = GLNormalMesh(Rect(Vec(-1,-1,-1.), Vec(2,2,2.)), MarchingCubes()) do v
sqrt(sum(dot(v,v))) - 1
end
# save the Sphere as a PLY file
save("sphere.ply",m)
```

For a full listing of concrete `AbstractMesh`

types see GeometryBasics.jl mesh documentation.

Alternatively, we can use the `isosurface`

API to sample a function:

```
using Meshing
using LinearAlgebra
using StaticArrays
points, faces = isosurface(origin=SVector(-1,-1,-1.), widths = SVector(2,2,2.), samples = (40,40,40)) do v
sqrt(sum(dot(v,v))) - 1
end
# by default MarchingCubes() is used, but we may specify a different algorithm as follows
points, faces = isosurface(MarchingTetrahedra(), origin=SVector(-1,-1,-1.), widths = SVector(2,2,2.), samples = (40,40,40)) do v
sqrt(sum(dot(v,v))) - 1
end
```

## Quick Start - GeometryTypes

GeometryTypes is in the process of being deprecated across the Julia ecosystem. GeometryBasics is the recommended replacement.

Meshing is well-integrated with GeometryTypes.jl by extending the mesh constructors for convience.

The algorithms operate on a `Function`

, `AbstractArray`

, or `SignedDistanceField`

and output a concrete `AbstractMesh`

. For example, we can use the GeometryTypes API as follows, using `HyperRectangle`

to specify the bounds:

```
using Meshing
using GeometryTypes
using LinearAlgebra: dot, norm
using FileIO
# Mesh an equation of sphere in the Axis-Aligned Bounding box starting
# at -1,-1,-1 and widths of 2,2,2 using Marching Cubes
m = GLNormalMesh(HyperRectangle(Vec(-1,-1,-1.), Vec(2,2,2.)), MarchingCubes()) do v
sqrt(sum(dot(v,v))) - 1
end
# save the Sphere as a PLY file
save("sphere.ply",m)
```

For a full listing of concrete `AbstractMesh`

types see GeometryTypes.jl mesh documentation.

Alternatively, we can use the `isosurface`

API to sample a function:

```
using Meshing
using LinearAlgebra
using StaticArrays
points, faces = isosurface(origin=SVector(-1,-1,-1.), widths = SVector(2,2,2.), samples = (40,40,40)) do v
sqrt(sum(dot(v,v))) - 1
end
# by default MarchingCubes() is used, but we may specify a different algorithm as follows
points, faces = isosurface(MarchingTetrahedra(), origin=SVector(-1,-1,-1.), widths = SVector(2,2,2.), samples = (40,40,40)) do v
sqrt(sum(dot(v,v))) - 1
end
```

## Meshing Algorithms

Three meshing algorithms exist:

`MarchingCubes()`

`MarchingTetrahedra()`

`NaiveSurfaceNets()`

Each takes optional `iso`

, `eps`

, and `insidepositive`

parameters, e.g. `MarchingCubes(iso=0.0,eps=1e-6,insidepositive=false)`

.

Here `iso`

controls the offset for the boundary detection. By default this is set to 0. `eps`

is the detection tolerance for a voxel edge intersection. `insidepositive`

sets the sign convention for inside/outside the surface (default: false).

Users must construct an algorithm type and use it as an argument to a GeometryTypes mesh call or `isosurface`

call.

Below is a comparison of the algorithms:

Algorithm Type | Face Type | Unique Vertices | Performance | Interpolation |
---|---|---|---|---|

Naive Surface Nets | Quad | Yes | ~1x | Voxel Edge Weight |

Marching Cubes | Triangle | No/Partial | 1x | Linear on Edge |

Marching Tetrahedra | Triangle | Yes | 3x | Linear on Edge |

Visual Comparison: From left: Marching Cubes, Naive Surface Nets, Marching Tetrahedra

`Meshing.MarchingCubes`

— Type.```
MarchingCubes(iso=0.0, eps=1e-3, reduceverts=true, insidepositive=false)
MarchingCubes(;iso=0.0, eps=1e-3, reduceverts=true, insidepositive=false)
MarchingCubes(iso)
MarchingCubes(iso,eps)
```

Specifies the use of the Marching Cubes algorithm for isosurface extraction. This algorithm provides a good balance between performance and vertex count. In contrast to the other algorithms, vertices may be repeated, so mesh size may be large and it will be difficult to extract topological/connectivity information.

`iso`

(default: 0.0) specifies the iso level to use for surface extraction.`eps`

(default: 1e-3) is the tolerence around a voxel corner to ensure manifold mesh generation.`reduceverts`

(default: true) if true will merge vertices within a voxel to reduce mesh size by around 30% and with slight performance improvement.`insidepositive`

(default: false) set true if the sign convention inside the surface is positive, common for NRRD and DICOM data

`Meshing.MarchingTetrahedra`

— Type.```
MarchingTetrahedra(iso=0.0, eps=1e-3, reduceverts=true, insidepositive=false)
MarchingTetrahedra(;iso=0.0, eps=1e-3, reduceverts=true, insidepositive=false)
MarchingTetrahedra(iso)
MarchingTetrahedra(iso,eps)
```

Specifies the use of the Marching Tetrahedra algorithm for isosurface extraction. This algorithm has a roughly 2x performance penalty compared to Marching Cubes, and generates more faces. However, each vertex is guaranteed to not be repeated, making this algorithm useful for topological analysis.

`iso`

specifies the iso level to use for surface extraction.`eps`

is the tolerence around a voxel corner to ensure manifold mesh generation.`reduceverts`

(default: true) if false, vertices will not be unique and have repeated face references.`insidepositive`

(default: false) set true if the sign convention inside the surface is positive, common for NRRD and DICOM data

`Meshing.NaiveSurfaceNets`

— Type.```
NaiveSurfaceNets(iso=0.0, eps=1e-3, reduceverts=true, insidepositive=false)
NaiveSurfaceNets(;iso=0.0, eps=1e-3, reduceverts=true, insidepositive=false)
NaiveSurfaceNets(iso)
NaiveSurfaceNets(iso,eps)
```

Specifies the use of the Naive Surface Nets algorithm for isosurface extraction. This algorithm has a slight performance advantage in some cases over Marching Cubes. Each vertex is guaranteed to not be repeated (useful for topological analysis), however the algorithm does not guarantee accuracy and generates quad faces.

`iso`

specifies the iso level to use for surface extraction.`eps`

is the tolerence around a voxel corner to ensure manifold mesh generation.`reduceverts`

reserved for future use.`insidepositive`

(default: false) set true if the sign convention inside the surface is positive, common for NRRD and DICOM data

`Meshing.AbstractMeshingAlgorithm`

— Type.`AbstractMeshingAlgorithm`

Abstract type to specify an algorithm for isosurface extraction. See:

- MarchingCubes
- MarchingTetrahedra
- NaiveSurfaceNets

## Isosurface

`isosurface`

is the common and generic API for isosurface extraction with any type of abstract vector/vertex/face type.

`Meshing.isosurface`

— Function.```
function isosurface(sdf::AbstractArray{T, 3}, [ method::AbstractMeshingAlgorithm ],
[ VertType = SVector{3,Float64} ], [ FaceType} = SVector{3, Int} ] ;
origin = SVector(-1.0,-1.0,-1.0), widths = SVector(2.0,2.0,2.0))
function isosurface(f::Function, [ method::AbstractMeshingAlgorithm ],
[ VertType = SVector{3,Float64} ], [FaceType = SVector{3, Int} ] ;
origin = SVector(-1.0,-1.0,-1.0), widths = SVector(2.0,2.0,2.0)
samples=(24,24,24))`
```

`isosurface`

is the general interface to all isosurface extraction algorithms.

Returns: (Vector{VertType}, Vector{FaceType})

Defaults:

- VertType = SVector{3,Float64} (positional)
- FaceType = SVector{3, Int} ] ; (positional)
- origin = SVector(-1.0,-1.0,-1.0) (keyword)
- widths = SVector(2.0,2.0,2.0) (keyword)
- samples=(24,24,24) (keyword, function sampling only)

`method`

must be an instance of an `AbstractMeshingAlgorithm`

, e.g.:

- MarchingCubes()
- MarchingTetrahedra()
- NaiveSurfaceNets()

If `isosurface`

is called without a specified algorithm, it will default to MarchingCubes.

If a subtype of `AbstractArray`

is specified, the mesh will by default be centered at the origin between (-1,1) in each axis. This may be overridden by specifying a new origin and widths for the axis-aligned bounding box using keywords of the same names. For example if we want our vertices in the range of (0,1), we can specify `origin=SVector(0,0,0)`

and `widths = SVector(1,1,1)`

.

If a function is specified, it will be uniformly sampled in each axis by the amount specified in `samples`

. The function is called using the specifed `VertType`

.

Performance Tips:

- ensure
`VertType`

,`origin`

, and`widths`

are all of the same type - ensure the element type of
`VertType`

is the same as the specified isolevel

See also:

- MarchingCubes
- MarchingTetrahedra
- NaiveSurfaceNets

## GeometryBasics

Meshing extends the mesh types in GeometryBasics for convenience and use with visualization tools such as Makie and MeshCat. Any instance of a `Mesh`

may be created as follows:

```
mesh(df::SignedDistanceField{3,ST,FT}, method::AbstractMeshingAlgorithm; pointtype=nothing, facetype=nothing) where {ST, FT}
mesh(f::Function, h::Rect, samples::NTuple{3,T}, method::AbstractMeshingAlgorithm; pointtype=nothing, facetype=nothing) where {T <: Integer}
mesh(f::Function, h::Rect, method::AbstractMeshingAlgorithm; pointtype=nothing, facetype=nothing, samples::NTuple{3,T}=_DEFAULT_SAMPLES) where {T <: Integer}
mesh(volume::AbstractArray{T, 3}, method::AbstractMeshingAlgorithm; pointtype=nothing, facetype=nothing, vargs...) where {T}
```

With the GeometryBasics API, the bounding box is specified by a `Rect`

, or keyword specified `origin`

and `widths`

.

Some notes on pointtype and facetype. They can be used to create a mesh with the desired point & face types. If they're left at nothing, the element type best matching the method and signed distancefield is used. Both the element type of the volume, element type of the `vertextype`

, and type of `iso`

in the `AbstractMeshingAlgorithm`

are all promoted. This also allows the use of auto differentiation tools on the isosurface construction.

## GeometryTypes

Meshing extends the mesh types in GeometryTypes for convience and use with visualization tools such as Makie and MeshCat. Any instance of an `AbstractMesh`

may be called with arguments as follows:

```
(::Type{MT})(df::SignedDistanceField{3,ST,FT}, method::AbstractMeshingAlgorithm)::MT where {MT <: AbstractMesh, ST, FT}
(::Type{MT})(f::Function, h::HyperRectangle, samples::NTuple{3,T}, method::AbstractMeshingAlgorithm)::MT where {MT <: AbstractMesh, T <: Integer}
(::Type{MT})(f::Function, h::HyperRectangle, method::AbstractMeshingAlgorithm; samples::NTuple{3,T}=_DEFAULT_SAMPLES)::MT where {MT <: AbstractMesh, T <: Integer}
(::Type{MT})(volume::AbstractArray{T, 3}, method::AbstractMeshingAlgorithm; vargs...) where {MT <: AbstractMesh, T}
```

With the GeometryTypes API, the bounding box is specified by a `HyperRectangle`

, or keyword specified `origin`

and `widths`

.

Some notes on VertType and FaceType. Since it is common to simply call `HomogenousMesh`

or `GLNormalMesh`

, we have added promotion and default type logic to the GeometryTypes API to improve type stability and therefore performance. Both the element type of the volume, element type of the `vertextype`

, and type of `iso`

in the `AbstractMeshingAlgorithm`

are all promoted. This also allows the use of auto differentiation tools on the isosurface construction.

If for example a `HomogenousMesh`

is requested, the default types will be `Point{3,Float64}`

and `Face{3,Int}`

Similarly, a `GLNormalMesh`

specifies `Point{3, Float32}`

and `Face{3, OffsetInteger{-1,UIn32}}`

so these these types will be used.

See: `isosurface`

for the generic API.