.rasmah: The Parametric Model Format
using RasmahOverview
.rasmah is Rasmah’s own format. Unlike STL/VTK (a frozen mesh) or STEP (a frozen boundary representation), a .rasmah file serialises the parametric model itself — the feature operations and parameters that build the model — as a JSON (JavaScript Object Notation) document. It is the portable, human-readable source of truth: read one back and you get the full parametric model, not a snapshot of its geometry.
write_rasmah(model, io/path)— serialize the parametric model to JSON.read_rasmah(io/path)— parse it back into the identical parametric model.
What gets stored
The model is built from features over plain values:
r = 0.5
model = difference(box(4.0, 2.0, 0.5), cylinder(r, 1.0))Difference{Box{Float64, Float64, Float64}, Cylinder{Float64, Float64}}(Box{Float64, Float64, Float64}(4.0, 2.0, 0.5), Cylinder{Float64, Float64}(0.5, 1.0))
write_rasmah encodes the model as nested JSON — each feature operation records its inputs, and each parameter value is stored inline:
io = IOBuffer()
write_rasmah(model, io)
println(String(take!(io))){"node":{"type":"difference","children":[{"type":"box","children":[{"value":4.0,"type":"value"},{"value":2.0,"type":"value"},{"value":0.5,"type":"value"}]},{"type":"cylinder","children":[{"value":0.5,"type":"value"},{"value":1.0,"type":"value"}]}]},"schema":"rasmah","version":1}
That JSON is the whole model: difference → box + cylinder, where the cylinder’s radius is the value 0.5.
Writing and reading back
read_rasmah reconstructs the model exactly — the values come back as plain numbers, so the model is still parametric and still rebuildable:
mktempdir() do dir
p = joinpath(dir, "bracket.rasmah")
write_rasmah(model, p)
model2 = read_rasmah(p)
println("round-trip field: ", field(model2, [1.0, 0.0, 0.0]))
endround-trip field: -0.25
The round-trip preserves the intent (a parametric model with a cylinder radius of 0.5), which a mesh or BRep format cannot.
Geometry is reproduced, not stored
Because the model is stored, the read-back model reproduces the exact same signed-distance field — no lossy re-sampling:
mktempdir() do dir
p = joinpath(dir, "bracket.rasmah")
write_rasmah(model, p)
model2 = read_rasmah(p)
println("field at origin: ", round(field(model, [0.0, 0.0, 0.0]); digits=6),
" vs ", round(field(model2, [0.0, 0.0, 0.0]); digits=6))
endfield at origin: 0.5 vs 0.5
Streams and files
Every entry point accepts an IO stream or a path, so the same serializer works for files, byte buffers, and network sockets alike:
buf = IOBuffer()
write_rasmah(model, buf)
seekstart(buf)
println("round-trip via stream: ", field(read_rasmah(buf), [1.0, 0.0, 0.0]))round-trip via stream: -0.25
When to use each format
| Format | Stores | Use it to… |
|---|---|---|
.rasmah |
parametric model | keep the editable parametric model |
.step |
exact BRep | exchange exact geometry with CAD kernels |
.vtk/.stl |
mesh | hand geometry to meshers, solvers, viewers |
.rasmah is the only one that keeps your design editable: the parametric model is reconstructed exactly, so the model can be re-optimised later.
Next steps
- Converting Representations — how the parametric model regenerates every other representation (SDF/BRep/mesh) and file format.
- Differentiable models: parameters, caching, and derivatives — the parametric-model-as-function style
.rasmahround-trips. - STEP: Exact Boundary Representation — the frozen-BRep counterpart.
- VTK: Legacy and XML — the mesh formats for solvers and viewers.
