Image showing three main
CSG boolean
operations
I've been an OpenSCAD user for quite some time now. It's the pragmatic choice for parametric 3D modeling and my go-to solution for mechanical parts.
The tool comes with a script -> geometry compiler which uses a dedicated
DSL. The language
(while having C inspired syntax) is functional - constants only, pure functions,
etc. Additionally, to produce any geometry, one must use its dedicated module
system:
// A `module` is a block,
// which evaluates (implicitly returns?) some geometry
module highlight() {
color("red") children();
}
// Modules can be chained
// leading to haskell style right-to-left syntax
scale(2.2) highlight() sphere(10.0);
While OpenSCAD does have it's quirks it generally gets the job done and is widely supported.
There are some things I don't like about it. Especially when it comes to building larger, more complex models or using some of its (also not so small) third party libraries:
is_num, is_list etc. Many of these errors could be inferred at
compile-time or via static analysis.cube(..) translate(..) instead of translate(...) cube(..) While the first
option might feel more "english" there are many benefits to chaining
operations after the object. You can read more about "pipelining" here:
Pipelining might be my favorite programming language feature.And boy there have been improvements - after switching to a developer snapshot compilation of my models went from tens of seconds to pretty much instantaneous! Like..huh? How is such improvement even possible?
A library by Emmett Lalish that's responsible for turning solids into a triangle mesh and supports 3mf export. As opposed to STL, this file format shares vertices between adjacent triangles2 which avoids broken, disjointed meshes and subsequent fixing. i.e. manifold always produces "watertight" models with exact known volume.
Additionally it has broad set of bindings which means we can use well supported general purpose language with mature tooling to generate our geometry.
Since the library can be compiled to wasm - there even is a reference web
interface ManifoldCAD. It's a great demo, but I
would like to create local setup, which can be launched from CLI, use my
$EDITOR of choice and have version control with git.

Choosing among the many available languages comes down to a few constraints imposed by our local setup:
Together, these constraints basically leaves us with Python. Luckily due to it's
use in robotics and ml-vision there are also many 3d viewers available. I chose
viser since it has good rendering of
semi-transparent models and updates are fast. After spending some time wiring
convenience wrappers, implementing hot-reload and file export we are left
syntax, which is quite a bit more expressive than scad language.
If you'd like to try this out - there is a repository
Given an n sided polygon inscribed in circle - this
formula calculates the required number of segments that satisfy some maximum
tolerance ε. Screenshot from
desmos playground
Since manifold works on triangulated meshes - any API for generating continuous shapes (cylinders, spheres) expects an explicit segment count.
Previously I'd eyeball the segment count, or set it globally with $fn. Neither is great since $fn applies the same segment count to every circular shape regardless of size, so small shapes end up overly dense.
For manufacturing, often we actually care about tolerance (ε). i.e. the maximum permissible deviation of the triangulated model from its SDF
For a simple circle the math is pretty straight forward it also approximates sphere close enough. Then - using formula from above and clamping it to something reasonable we get:
def segments(radius: float) -> int:
tolerance = 0.05
n = math.pi / math.acos(1.0 - tolerance / radius)
return min(1024, max(8, math.ceil(n)))
Then this function can be used as the default segments parameter