An Elixir wrapper of quiddity to bring feature extraction from CAD, with the geometry underneath it: rays, frames, meshes and surface samples. Wrapped via rustler.
It is a binding of quiddity and haecceity, both pure Rust with no OpenCascade and no CAD system: read a STEP file, and ask what its holes, pockets, slots, bosses, fillets and turned features are, where its surfaces are, and what a sensor placed on it would perceive.
{:ok, part} = Quiddity.open("bracket.step")
{:ok, recognition} = Quiddity.features(part)
Quiddity.Recognition.families(recognition)
#=> [{"holes", 4}, {"fillets", 6}, {"pockets", 1}]Everything is in millimetres, in the file's own coordinates, and the same file always gives the same answer.
What it answers
| Question | Call |
|---|---|
| What is this part? | Quiddity.open/1, Quiddity.features/1 |
| What is this face, and whose? | Quiddity.face/2, Quiddity.Recognition.owner/2 |
| What is at this point? | Quiddity.ray/4, Quiddity.nearest/3 |
| Inside, outside, or on it? | Quiddity.classify/2 |
| How heavy is it? | Quiddity.mass/2 |
| What would a camera see? | Quiddity.frame/2 |
| What does the file demand? | Quiddity.pmi/2 — dimensions, tolerances, datums |
| What changed between revisions? | Quiddity.correspond/2 |
| What is it made of? | Quiddity.mesh/2, Quiddity.samples/2 |
A frame is the interesting one: rays are cast against the exact B-rep, so every pixel knows the point on the surface, the outward normal there, and which face it landed on — not a depth buffer's approximation of it.
A note on the sensor model, and where it lives
A frame is a rendering: Quiddity.Camera builds the pinhole camera, including
the geometry of a 64 × 64 patch sensor with a 66° field of view, and
Quiddity.Frame decodes the answer into points, normals, face ids, depth,
shaded RGBA and semantic_3d rows.
What a perception or learning framework makes of that frame is that framework's
business, and it is not in this package. Quiddity.Camera.cam_to_world/1 and
Quiddity.Frame.semantic_3d/2 produce the two matrices such a framework
conventionally wants; nothing here interprets them.
As an MCP server
mix quiddity.mcp speaks the Model Context Protocol over stdio, so an assistant
can ask about a part directly:
{"mcpServers": {"quiddity": {"command": "mix", "args": ["run", "--no-halt", "quiddity.mcp"]}}}Seven tools: quiddity_part, quiddity_features, quiddity_face,
quiddity_probe, quiddity_sense, quiddity_correspond,
quiddity_export_mjcf.
Install
def deps do
[{:ex_quiddity, "~> 0.1"}]
endThe Rust engine is compiled when the dependency is: building wants a Rust
toolchain (scripts/install-rust.sh installs one), and running wants none.
The engine is generated from one table
Quiddity.Api.operations/0 lists every operation the Rust side has, and
Quiddity.Native is generated from it at compile time — the stubs, their
arities, and the MCP catalogue all come from the one list. A Rust function that
is not listed cannot be called, and a listed one that does not exist fails a
test rather than a call.
Limits, said out loud
- quiddity reports what it can prove from the geometry. A file that reads without
errors is not a promise every feature was found:
Quiddity.Recognition.unexplained_faces/1is where that promise is kept, andunproved/1names the records whose evidence it could not verify. - A B-rep has no colour.
Quiddity.Frame.rgba/2is shading, so anhsvfeature read from a CAD frame says how the surface turns, not what it is painted. - A geometry handle is a Rust resource: it serves every process on its node at once and cannot be sent to another node.
Licence
MPL-2.0 (LICENSE). The engine it binds is Apache-2.0 and stays a dependency;
NOTICE says what came from where.