Packages

An Elixir wrapper of github.com/pzfreo/quiddity to bring feature extraction from CAD, with the geometry underneath it: rays, frames, meshes and surface samples. Wrapped via rustler.

Current section

Files

Jump to
ex_quiddity README.md
Raw

README.md

# ex_quiddity

An Elixir wrapper of [quiddity](https://github.com/pzfreo/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](https://github.com/pzfreo/quiddity/tree/main/crates/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.

```elixir
{: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:

```json
{"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

```elixir
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.