ex_quiddity

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

Licence

MPL-2.0 (LICENSE). The engine it binds is Apache-2.0 and stays a dependency; NOTICE says what came from where.