Browse the docs
Examples
Copy one, change the file names and sizes, and run it. Every script here runs as shown with the current release.
There are three ways to put Betti to work on more than one step. Chains run commands one after another in one line. Scripts add loops, decisions and reports. Coding agents run the commands for you from a request in plain words.
Chains
betti run passes one mesh from step to step in memory and writes the last. A volume step names its own file; the surface goes to run's output.
Repair and remesh for simulation
betti run part.stl prepared.obj fix repair + mesh remesh -size 1%A scan to tetrahedra
betti run scan.stl surface.obj \
fix repair + mesh remesh -size 2% + volume tet -size 2% part.mshSimplify before meshing
betti run part.stl clean.obj fix repair + shape defeature -size 2 + mesh remesh -size 1%A recipe kept in a file
# print.txt: one step to a line
fix repair
save repaired.obj
additive placebetti run part.stl ready.3mf -recipe print.txtScripts
Run each with betti script, the script's name and its files, as its first comment shows. The scripting guide explains the language from the start.
Repair a folder and report it as CSV
Every part given on the command line is repaired and saved beside the original; the report goes to standard output, so it can be sent to a file.
# Repair every part given, save each, and report them as CSV.
# betti script -q batch.star parts/*.stl > report.csv
print("part,faces,holes,volume")
for path in args:
m = fix.repair(path)
print("%s,%d,%d,%s" % (path, m.faces, m.holes, math.round(m.volume * 1000) / 1000))
save(m, path.removesuffix(".stl") + "-fixed.obj")Remesh until close enough
Tries coarser sizes first and stops at the first within a set distance of the input, then fills the result with tetrahedra.
# Remesh finer until the result is within 0.2% of the part's size,
# then fill it with tetrahedra.
# betti script refine.star part.stl
m = fix.repair(read(args[0]))
for size in ["4%", "2%", "1%"]:
e = mesh.remesh(m, size = size)
d = verify(m, e)
print(size, e.faces, "faces, off by", d.hausdorff)
if d.hausdorff < 0.002:
break
v = volume.tet(e, size = size)
print("cells:", volume.info(v).cells)
save(e, "surface.obj")
save(v, "part.msh")Triangles or quads
Meshes the same part both ways and prints the smallest angle of each, to choose between them.
# Remesh a part in triangles and in quads, and compare their quality.
# betti script compare.star part.stl
m = fix.repair(read(args[0]))
for quads in [False, True]:
e = mesh.remesh(m, size = "2%", quads = quads)
q = info(e)
kind = "quads" if quads else "triangles"
print(kind, e.faces, "faces, smallest angle", math.round(q.min_angle * 10) / 10)
save(e, kind + ".obj")Cut a part in half
Finds the middle of the part's height from its bounds, cuts there and remeshes each half. A half that is empty comes back as None.
# Cut a part through the middle of its height and remesh each half.
# betti script split.star part.stl
m = fix.repair(read(args[0]))
lo, hi = m.bounds
middle = (lo[2] + hi[2]) / 2
below, above = shape.cut(m, plane = "z=%s" % middle)
for name, part in [("below", below), ("above", above)]:
if part == None:
print(name, "is empty")
continue
save(mesh.remesh(part, size = "2%"), name + ".obj")
print(name, part.faces, "faces")From STEP to a volume mesh
Heals the model, meshes its surface, stops if the surface is open, and writes both the healed model and the volume mesh.
# Heal a STEP model, mesh its surface and fill it with tetrahedra.
# betti script cadmesh.star part.step
model = cad.heal(read_cad(args[0]))
surface = cad.mesh(model, size = "3%")
if surface.holes:
fail("the surface of %s is open" % args[0])
r = volume.tet(surface, size = "3%", report = True)
print("cells:", r.cells)
save(model, "healed.step")
save(r.out, "part.msh")Prepare a part for printing
Repairs and orients the part, counts walls thinner than the printer can make, and writes the part as 3MF and its layers as SVG.
# Prepare a part for printing: repair it, orient it for the least
# support, report walls thinner than 0.8, and slice it.
# betti script print.star part.stl
m = additive.place(fix.repair(read(args[0])))
t = additive.thickness(m, min = 0.8)
print("walls thinner than 0.8:", t.thin_faces, "faces")
save(m, "placed.3mf")
save(additive.slice(m, layer = 0.2), "layers.svg")Hollow a solid
Hollows a solid with walls of 4% of its size and a drain hole at the lowest corner of its bounds, and checks the walls left.
# Hollow a solid for printing, with a drain hole at its lowest corner,
# and check that the walls left are thick enough.
# betti script hollow.star part.stl
m = fix.repair(read(args[0]))
lo, hi = m.bounds
shell = shape.hollow(m, wall = "4%", drain = [[lo[0], lo[1], lo[2], 1]])
t = additive.thickness(shell, min = 0.8)
print("volume", math.round(m.volume), "->", math.round(shell.volume))
save(shell, "hollow.obj")Coding agents
betti mcp gives a coding agent every command as a tool. Add it once:
claude mcp add betti -- betti mcp
codex mcp add betti -- betti mcpThen ask for the work in plain words, naming your files. The agent picks the commands, reads their reports and corrects course when one fails; each tool checks its module's licence as the command does. Requests that work well:
- Repair every STL in
parts/, remesh each to 1% of its size, and tell me which still have holes or crossings. - Mesh
bracket.stepwith tetrahedra of about 2 mm, check that the volume mesh is valid, and save it asbracket.inpfor Abaqus. - Find the walls of
case.stlthinner than 0.8 mm, then orient it for printing with the least support and save it as 3MF. - Compare
scan.stlwithdesign.stland tell me where they differ by more than 0.5 mm.
See betti mcp for how the tools take files, and how results are kept in memory between calls.