RasmahRasmah

More Interchange Formats: AMF, USD, X3D, Collada, SVG, UNV, EnSight

Overview

Beyond the core formats (STL, OBJ, PLY, VTK, MSH, INP, STEP, DICOM, NIfTI) and the modern interchange set (glTF, 3MF, XDMF, NRRD, IGES), Rasmah reads and writes a long tail of formats, each with a niche job:

  • AMF — the XML additive-manufacturing format (millimetres).
  • USD — Pixar’s scene-description format.
  • X3D — the ISO web-3D format (meshes and line sets).
  • Collada (DAE) — the digital-asset-exchange format.
  • SVG — 2D vector line art.
  • UNV / EnSight — legacy solver mesh formats (read-only).
using Rasmah

dir = mktempdir()
m = icosphere(1)
TriangleMesh{Matrix{Float64}, Matrix{Int64}, Nothing}([-0.5257311121191336 0.5257311121191336 … 0.8090169943749475 1.0; 0.85065080835204 0.85065080835204 … -0.5000000000000001 0.0; 0.0 0.0 … -0.30901699437494745 0.0], [1 12 … 2 42; 13 14 … 24 31; 15 13 … 31 24], nothing)

AMF, USD, X3D and Collada (triangle meshes)

These all round-trip a TriangleMesh through their own serialisation:

write_amf(m, joinpath(dir, "sphere.amf"))
write_usd(m, joinpath(dir, "sphere.usd"))
write_x3d(m, joinpath(dir, "sphere.x3d"))
write_dae(m, joinpath(dir, "sphere.dae"))

println("AMF round-trip:      ", length(read_amf(joinpath(dir, "sphere.amf")).vertices) == length(m.vertices))
println("USD round-trip:      ", length(read_usd(joinpath(dir, "sphere.usd")).vertices) == length(m.vertices))
println("X3D round-trip:      ", length(read_x3d(joinpath(dir, "sphere.x3d")).vertices) == length(m.vertices))
println("Collada round-trip:  ", length(read_dae(joinpath(dir, "sphere.dae")).vertices) == length(m.vertices))
AMF round-trip:      true
USD round-trip:      true
X3D round-trip:      true
Collada round-trip:  true

SVG (line art)

SVG stores 2D line sets. Build a LineMesh, write it, and read it back:

l = LineMesh([0.0 1.0 1.0 0.0; 0.0 0.0 1.0 1.0; 0.0 0.0 0.0 0.0], [1 2 3 4; 2 3 4 1])
write_svg(l, joinpath(dir, "square.svg"))
l2 = read_svg(joinpath(dir, "square.svg"))
println("SVG round-trip: ", l2 isa LineMesh)
SVG round-trip: true

UNV (read-only, legacy solver mesh)

The UNV reader parses the I-deas solver format. Here a single tetrahedron is read from an inline deck:

unv = """
    -1
  2411
        4
         1
  0.000000000000000D+00  0.000000000000000D+00  0.000000000000000D+00
         2
  1.000000000000000D+00  0.000000000000000D+00  0.000000000000000D+00
         3
  0.000000000000000D+00  1.000000000000000D+00  0.000000000000000D+00
         4
  0.000000000000000D+00  0.000000000000000D+00  1.000000000000000D+00
    -1
      2412
        1
         1       111         1         1         0         4
         1         2         3         4
"""
um = read_unv(IOBuffer(unv))
println("UNV: ", typeof(um), " with ", size(um.nodes, 2), " nodes, ", size(um.elements, 2), " element")
UNV: TetMesh{Matrix{Float64}, Matrix{Int64}, Nothing} with 4 nodes, 1 element

EnSight (read-only, CFD post-processing)

The EnSight reader parses the .geo geometry format:

geo = """
    node id off
    triangle
    two lines of description
    coordinates
    3
    0.0 1.0 0.0
    0.0 0.0 1.0
    0.0 0.0 0.0
    tria3
    part
    1
    part description
    line two
    1
    1
    1 2 3
"""
em = read_ensight(IOBuffer(geo))
println("EnSight: ", typeof(em), " with ", size(em.faces, 2), " face")
EnSight: TriangleMesh{Matrix{Float64}, Matrix{Int64}, Nothing} with 1 face

Checkpoint / restart

write_checkpoint / read_checkpoint serialise any solver state (field vectors, snapshots, (; u, p), …) so a long run can be interrupted and resumed:

state = (u = rand(10), p = rand(10), t = 0.5)
write_checkpoint(state, joinpath(dir, "state.ckpt"))
restored = read_checkpoint(joinpath(dir, "state.ckpt"))
println("checkpoint round-trip: ", restored.u == state.u && restored.t == state.t)
checkpoint round-trip: true

Next steps