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

QuestionCall
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"}]
end

The 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/1 is where that promise is kept, and unproved/1 names the records whose evidence it could not verify.
  • A B-rep has no colour. Quiddity.Frame.rgba/2 is shading, so an hsv feature 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.