Meshes

The two-dimensional embedded delta sets (EmbeddedDeltaSet2D) in CombinatorialSpaces can be converted to and from mesh objects (Mesh) in Meshes.jl. This is useful for interoperation with packages in the JuliaGeometry ecosystem.

Visualizing embedded delta sets

The following example shows how to import a mesh from an OBJ file, convert it into an embedded delta set, and render it as a 3D mesh using CairoMakie.

using FileIO, CairoMakie, CombinatorialSpaces
set_theme!(size=(800, 400))
catmesh = FileIO.load(assetpath("cat.obj"))

catmesh_dset = EmbeddedDeltaSet2D(catmesh)
mesh(catmesh_dset, shading=NoShading)
Example block output

Alternatively, the embedded delta set can be visualized as a wireframe:

wireframe(catmesh_dset)
Example block output

We can also construct and plot the dual complex for this mesh:

dual = EmbeddedDeltaDualComplex2D{Bool, Float32, Point{3,Float32}}(catmesh_dset)
subdivide_duals!(dual, Barycenter())
wireframe(dual)
Example block output

API docs

CombinatorialSpaces.CombMeshes.makeSphereMethod
makeSphere(minLat, maxLat, dLat, minLong, maxLong, dLong, radius)

Construct a spherical mesh (inclusively) bounded by the given latitudes and longitudes, discretized at dLat and dLong intervals, at the given radius from Earth's center.

Note that this construction returns a UV-sphere. DEC simulations are more accurate on meshes with (near) equilateral triangles, such as the icospheres available through loadmesh.

We say that:

  • 90°N is 0
  • 90°S is 180
  • Prime Meridian is 0
  • 10°W is 355

We say that:

  • (x=0,y=0,z=0) is at the center of the sphere
  • the x-axis points toward 0°,0°
  • the y-axis points toward 90°E,0°
  • the z-axis points toward the North Pole

References:

List of common coordinate transformations

Examples

# Regular octahedron.
julia> s, npi, spi = makeSphere(0, 180, 90, 0, 360, 90, 1)
# 72 points along the unit circle on the x-y plane.
julia> s, npi, spi = makeSphere(90, 90, 0, 0, 360, 5, 1)
# 72 points along the equator at 0km from Earth's surface.
julia> s, npi, spi = makeSphere(90, 90, 1, 0, 360, 5, 6371)
# TIE-GCM grid at 90km altitude (with no poles,   i.e. a bulbous cylinder).
julia> s, npi, spi = makeSphere(5, 175, 5, 0, 360, 5, 6371+90)
# TIE-GCM grid at 90km altitude (with South pole, i.e. a bowl).
julia> s, npi, spi = makeSphere(5, 180, 5, 0, 360, 5, 6371+90)
# TIE-GCM grid at 90km altitude (with poles,      i.e. a sphere).
julia> s, npi, spi = makeSphere(0, 180, 5, 0, 360, 5, 6371+90)
# The Northern hemisphere of the TIE-GCM grid at 90km altitude.
julia> s, npi, spi = makeSphere(0, 180, 5, 0, 360, 5, 6371+90)
source
CombinatorialSpaces.CombMeshes.parallelepipedMethod
function parallelepiped(;lx::Real = 1.0, ly::Real = 1.0, lz::Real = 1.0, dx::Real = 0.0, dy::Real = 0.0, point_type = Point3d, tetcmd::String = "vpq1.414a0.1")

Uses TetGen to generate turn a specificed parallelepiped to a tetrahedralized mesh. lx, ly, and lz kwargs control the side lengths in the respective dimensions and dx and dy kwargs will translate the top face relative to the bottom face.

Default TetGen command is "pQq1.414a0.1" and user desired cmds can be passed through the tetcmd kwarg.

source
CombinatorialSpaces.CombMeshes.triangulated_gridFunction
function triangulated_grid(lx, ly, dx, dy, point_type, compress=true)

Triangulate the rectangle [0,lx] x [0,ly] by approximately equilateral triangles of width dx and height dy.

If compress is true (default), then enforce that all rows of points are less than lx, otherwise, keep dx as is.

source
CombinatorialSpaces.CombMeshes.triangulated_gridMethod
triangulated_grid(lx::Real, ly::Real, nx::Int, ny::Int; point_type::DataType = Point3d, compress::Bool=true)

Generate a rectangle [0,lx] x [0,ly] with nx+1 by ny+1 vertices. This results in a mesh containing 2*nx*ny triangles. Setting the dx or dy keywords will override the respective nx or ny keywords.

source
CombinatorialSpaces.MeshInteropModule

Interoperation with mesh files.

This module enables delta sets to be imported from mesh files supported by MeshIO.jl and for delta sets to be converted to meshes, mainly for the purposes of plotting. Meshes are represented by the GeometryBasics.Mesh type.

source
CombinatorialSpaces.MeshOptimization.BoltzmannAcceptanceType
struct BoltzmannAcceptance <: AbstractAcceptance

Standard Boltzmann acceptance probability from simulated annealing.

A worse solution is accepted when rand() < exp(-(new_cost - old_cost) / temperature), so the magnitude of the cost increase and the current temperature both influence the likelihood of accepting a worse solution.

See also: DirectAcceptance.

source
CombinatorialSpaces.MeshOptimization.SimulatedAnnealingType

struct SimulatedAnnealing

The simulated annealing algorithm's parameters for mesh optimization.

Keyword arguments:

ϵ: Multiply the randomly-sampled j ~ N(0,1) by this number. (Defaults to 1e-3)

epochs: The number of times to iterate over each point in the mesh. (Defaults to 100)

debug_epochs: When debugging is active, print cost every debug_epochs epochs. (Defaults to 10)

hold_boundaries: Whether to hold the boundaries of the mesh fixed. (Defaults to true)

anneal: Whether to accept jitters than increase the cost function. (Defaults to true)

jitter3D: Whether to jitter in the z-dimension, too. (Defaults to false)

spherical: Whether to constrain the new jittered point to lie on the sphere. (Defaults to false)

radius: If spherical is true, the radius of the sphere. (Defaults to 1.0)

cost: The cost function to use when annealing. (Defaults to equilaterality.)

cooling_schedule: The cooling schedule to be used when annealing. (Defaults to linear_cooling_schedule.)

acceptance: The acceptance probability strategy to use when annealing. (Defaults to DirectAcceptance().)

See also: optimize_mesh!, DirectAcceptance, BoltzmannAcceptance.

source
CombinatorialSpaces.MeshOptimization.equilateralityMethod
function equilaterality(e0, e1, e2)

Given three edge lengths, compute the failure to be equilateral, using:

\[(E_0 - ar{E})^2 + (E_1 - ar{E})^2 + (E_1 - ar{E})^2,\]

where $E_0, E_1, E_2 = |e_0, e_1, e_2|_1$, and $ar{E}$ is their average.

0 implies all edge lengths are equal and the triangle is equilateral.

source
CombinatorialSpaces.MeshOptimization.exponential_cooling_scheduleMethod
exponential_cooling_schedule(epochs, epoch; T_init=1e-6, T_final=1e-10)

Geometrically decay the temperature from T_init to T_final over all epochs.

This schedule is appropriate for BoltzmannAcceptance, where the temperature must be on the same order of magnitude as the typical per-vertex cost difference for the acceptance probability to be discriminating.

        ⠀@. 1e-6 * (1e-10/1e-6)^((x - 1)/(100-1))⠀ 
        ┌────────────────────────────────────────┐ 
   1e⁻⁶ │⢣⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
        │⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
        │⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
        │⠀⢣⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
        │⠀⠸⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
        │⠀⠀⢇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
        │⠀⠀⠸⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
        │⠀⠀⠀⢇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
        │⠀⠀⠀⠘⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
        │⠀⠀⠀⠀⠱⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
        │⠀⠀⠀⠀⠀⢣⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
        │⠀⠀⠀⠀⠀⠀⠣⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
        │⠀⠀⠀⠀⠀⠀⠀⠈⢆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
        │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠦⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
      0 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠑⠒⠤⠤⠤⣄⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀⣀│ 
        └────────────────────────────────────────┘ 
        ⠀0⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀100⠀ 
source
CombinatorialSpaces.MeshOptimization.logarithmic_cooling_scheduleMethod
logarithmic_cooling_schedule(epochs, epoch; c=1e-6)

Decay the temperature as c / log(1 + epoch) (Geman & Geman, 1984).

Logarithmic cooling is the slowest standard schedule and provides a theoretical guarantee of convergence to the global optimum. The constant c should be chosen on the same order of magnitude as the typical per-vertex cost difference so that the Boltzmann acceptance probability is discriminating.

This schedule is appropriate for BoltzmannAcceptance.

        ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀@. c / log(1 + x)⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 
        ┌────────────────────────────────────────┐ 
   2e⁻⁶ │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
        │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
        │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
        │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
        │⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
        │⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
        │⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
        │⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
        │⠸⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
        │⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
        │⠀⠱⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
        │⠀⠀⠑⠤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
        │⠀⠀⠀⠀⠈⠉⠒⠒⠤⠤⠤⣀⣀⣀⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
        │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠉⠑⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒⠒│ 
      0 │⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│ 
        └────────────────────────────────────────┘ 
        ⠀0⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀100⠀ 
source
CombinatorialSpaces.MeshOptimization.optimize_mesh!Method
function optimize_mesh!(s::HasDeltaSet2D, alg::SimulatedAnnealing, noise_generator::Function)

Optimize the given mesh using a simulated annealing algorithm.

The acceptance probability strategy is determined by alg.acceptance via multiple dispatch on should_accept. Use DirectAcceptance for a temperature-only acceptance probability, or BoltzmannAcceptance for the standard Boltzmann acceptance that accounts for the magnitude of error change.

See also: SimulatedAnnealing, DirectAcceptance, BoltzmannAcceptance.

source