# CabCodeXF — CabCode eXchange Format (v1.0 DRAFT)

**Status:** working draft (2026-10-17: `Route` gained `Arc` and `toolSide`,
`Pocket` was added, every operation gained optional `name` / `note`, and the
optional `<Designs>` block was added — all additive, still 1.0) · **Started:** 2026-10-17 · **Home:** this is the
authoritative spec (CabCode owns CabCodeXF). Resources alongside: `schema/cabcodexf-1.0.xsd`,
`samples/*.cabcodexf`. Reference **producer** = CABcad's `/api/cabcodexf` (`server3/src/cabcodexf.ts`);
reference **viewer/importer** = cabmodel (`src/core/cabcodexfImport.ts`). Cutwrights is an
importer *customer* with its own CabCodeXF→MPR implementation (not in this repo).

A machine-neutral format describing **what to machine** on a flat-panel part —
board, edging per edge, and drilling/routing operations. It carries **intent,
not toolpaths**: the consuming factory's CabCode engine generates the machine
CAM (e.g. WoodWOP `.mpr`) from a CabCodeXF file.

Third-party CAD systems write post-processors that **emit** CabCodeXF; a CabCodeXF-enabled
factory **imports** it and machines from it. Cutwrights is the first (reference)
factory implementation.

---

## Why a new format (landscape, verified 2026-10)

No open, vendor-neutral, *semantic* part + machining-intent interchange has won
in **flat-panel / cabinet** furniture. The adjacent options each miss:

- **BTLx** — right concept (open, XML, machine-independent, CAD→CAM with
  post-processing downstream) but for **timber construction / beams**, not panels.
  CabCodeXF is explicitly modelled on its philosophy, one trade over.
- **WoodWOP `.mpr`/`.mprx` (Homag), Biesse `.cix`/`.cixl`** — semantic but
  **machine/vendor-specific CAM targets** (the *output* of a post-processor).
  `.mpr` is what CabCode **generates**, not what it imports.
- **DXF** — universal but semantically dumb (geometry + ad-hoc layers; every
  shop invents its own convention).
- **STEP-NC (ISO 14649)** — genuinely machine-neutral CAM intent, but effectively
  **unadopted in wood** (too heavy, metal-cutting-centric). The cautionary tale.
- **ETML** (EUMABOIS/VDMA, 2025) — *tool* data, not part/machining intent.
  Not a competitor; signals the industry is in an active standardisation mood.
- **imos iX (Homag) and Mozaik** — complete cabinet design + CAM suites. Their
  machining output goes through their own post-processors to **machine formats**:
  imos iX CAM names connections to woodWOP, B-Solid and Maestro; Mozaik lists
  `.cix`, `.mpr`, `.nc`, `.xcs`, `.xxl`, `.tcn`, `.tap`, `.prg` and others (its
  stock post files are free, open-source text files — scripts, not a data format).
  Their documented neutral exports carry part lists or geometry, not machining:
  imos iX CAD exports IFC (building models), GLTF/GLB and WRL, and part lists to
  ERP; Mozaik exports CSV cutlists. A search of their public documentation
  (2026-10-17) found **no published, openly specified export of parts plus
  machining intent** from either. So their output sits where MPR does — a CAM
  target — and neither can show an equivalent to CabCodeXF without publishing one.
  Each is also a natural CabCodeXF **producer**: a CabCodeXF post-processor is the adoption
  path (Mozaik's posts are editable text). Sources: imos3d.com iX CAM and iX CAD
  product pages; mozaiksoftware.com CNC router links and Mozaik CNC pages
  (checked 2026-10-17). Not verified: whether imos's XML / Integration Center
  interfaces or Mozaik's optimiser export are published or carry machining.

**Opportunity:** set the de-facto panel-machining-intent standard. The wedge that
beats STEP-NC's failure mode: CabCodeXF asks a CAD vendor only to emit *intent* — CabCode
**is the CAM** — so the post-processor they write is cheap. Low adoption cost is
the lever these standards usually lack.

## Ownership & licensing model

| Layer | Who | Open or paid |
|---|---|---|
| **CabCodeXF** — the neutral intent spec | CabCode owns & stewards | **Open, free to produce/consume** |
| **CabCode engine** — CabCodeXF → CAM (`.mpr`) | CabCode | **Paid — the moat** |
| **Cutwrights factory** — ingests CabCodeXF, cuts & drills | Cutwrights | Reference / flagship implementation |
| **3rd-party CAD post-processors** — emit CabCodeXF | CAD vendors | Free to produce |

Pattern: **"open spec, owned steward."** CabCode owns the trademark, spec roadmap
and versioning, but publishes a free, irrevocable licence to produce/consume CabCodeXF.
Money is in the CAM engine + a conformance/certification mark, never in the right
to emit CabCodeXF. (This is how BTLx-type formats get adopted while still being owned.)

**Open commercial question to settle early:** "first factory implementation"
implies others follow. Make Cutwrights' first-mover/exclusivity expectation
explicit up front (flagship + a head-start window, not forever-exclusive).

### Naming note
The format was first drafted as **CCX** and renamed **CabCodeXF** on 2026-10-17:
"CCX" is a registered trade mark of others in software classes (Severalnines,
Adobe), and `.ccx` is Adobe's Creative Cloud extension file. CabCodeXF builds on
the CabCode house mark, and "eXchange Format" echoes DXF, which every shop knows.
The file extension is `.cabcodexf`; the namespace is `https://cabcode.uk/cabcodexf/1.0`.
Consumers should still accept pre-rename files (`<CCX>` root, namespace
`https://cabcode.uk/ccx/1.0`, extension `.ccx`) — the content model is identical.

---

## Conventions (all explicit — a neutral format must leave nothing to guess)

- **Units** millimetres, angles in degrees; declared in `<Header>`.
- **Datum** origin at corner `(x=0, y=0)` of reference face **A**; X along
  Length `(0..L)`, Y along Width `(0..W)`, Z normal to A, positive away from part.
- **Length / Width are the MACHINING-frame extents, not the cutting-list labels.**
  `length` is the extent along which an op's `x` runs (`0..length`); `width` the
  extent along which `y` runs (`0..width`). This is deliberately **not** the
  saw/cutlist convention of "length = the longer side": a part cut 720×570 may be
  machined in a 570(x)×720(y) frame, so its CabCodeXF `length`=570, `width`=720. A
  producer must emit the frame its ops are expressed in (CABcad: `op.panelw`/
  `panelh`); a consumer places `length` along the part's local X. Grain is a
  separate `grain` attribute and does not change this.
- **Faces** `A` / `B` (the two faces) plus four edges named by the coordinate
  they sit on:
  - `X0` = edge at x=0 · `Xmax` = edge at x=L
  - `Y0` = edge at y=0 · `Ymax` = edge at y=W
  - Familiar panel labels map (CABcad convention, from `mprEnddrill`): **L1=X0,
    L2=Xmax, W1=Y0, W2=Ymax** (carried as `as`; the geometric `edge` is authoritative).
- **Depth** positive magnitude *into* the named face; `through="true"` overrides.
- **Edge positioning** on an edge, `u` = distance along the edge from its start
  (increasing-axis direction); `z` = height from face A.
- **Grain** stated explicitly on the part; never inferred. (Cutwrights' internal
  "Length = with-the-grain" rule becomes an explicit attribute here.)
- **Product frame** (assembly only) origin bottom-rear-left; X across, Y depth,
  Z up; mm. Placement reuses each part's existing local datum — no second
  coordinate system to define.

---

## Worked example — one part, three operation primitives

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!--
  CabCodeXF — CabCode eXchange Format, v1.0 (DRAFT)
  Describes WHAT to machine on a panel part (board, edging, holes, routes) —
  intent, not toolpaths. The consuming factory's CabCode engine generates the
  machine CAM (e.g. WoodWOP .mpr) from this file.
  Lineage: structurally modelled on BTLx (machine-independent XML, CAD->CAM with
  post-processing downstream) but for flat panels rather than timber members.
-->
<CabCodeXF xmlns="https://cabcode.uk/cabcodexf/1.0" version="1.0">

  <Header units="mm" angleUnit="deg"
          producer="AcmeCAD 2026 / CabCodeXF post-processor 1.0.3"
          created="2026-10-17T10:15:00Z">
    <Order ref="SO-48213"/>            <!-- opaque to CabCodeXF; for traceability only -->
  </Header>

  <!-- Shared definitions. Define each material/band ONCE; lines reference by id.
       Describe in NEUTRAL, resolvable terms (manufacturer/decor/finish/type/
       thickness) + the producer's own shorthand token. Do NOT bake a factory's
       internal code in — that would tie the file to one factory. The
       producer->factory code mapping lives factory-side (CabCode import). -->
  <Materials>
    <Material id="M2" token="U960"
              manufacturer="Egger" decor="U960" finish="ST9"
              type="MFC" thickness="18"
              description="18mm Egger U960 ST9 Onyx Grey MFC"/>
  </Materials>

  <EdgeMaterials>
    <EdgeMaterial id="E1" token="ABS2-U960" kind="ABS" thickness="2.0"
                  description="2mm ABS Onyx Grey"/>
  </EdgeMaterials>

  <Part id="P1" name="LH Gable" quantity="2"
        length="720" width="300" thickness="18" grain="length"
        material="M2">                      <!-- reference, not inline -->

    <!-- One entry per banded edge; unbanded edges are simply omitted.
         `edge` = geometric id, `as` = familiar panel label. -->
    <Edging>
      <Edge edge="Y0" as="W1" edgeMaterial="E1"/>
    </Edging>

    <Operations>

      <!-- 1) Shelf line-boring: vertical into face A. System-32 column —
            5 holes, 5mm dia x 12mm deep, 32mm pitch along Length,
            37mm in from the front edge. Pattern expands from the first At. -->
      <Bore face="A" diameter="5" depth="12">
        <At x="100" y="37"/>
        <Pattern count="5" dx="32" dy="0"/>
      </Bore>

      <!-- 2) Two construction dowels: horizontal into the top edge (Xmax),
            8mm dia x 30mm deep, centred in thickness (z=9 from face A).
            On an edge, `u` is distance ALONG the edge from its start. -->
      <Bore face="Xmax" diameter="8" depth="30" z="9">
        <At u="60"/>
        <At u="240"/>
      </Bore>

      <!-- 3) Back-panel groove: routed into face B, 4mm wide x 10mm deep,
            run 15mm in from the back edge (y=285), along the height. -->
      <Route face="B" toolWidth="4" depth="10">
        <Line x1="8" y1="285" x2="712" y2="285"/>
      </Route>

    </Operations>
  </Part>

  <!-- OPTIONAL (v1.1). Present only when the producer has model/placement data.
       A pure cut-list CabCodeXF omits this entirely and still validates. A consuming
       factory that only machines IGNORES it; a viewer (cabmodel) reads it. -->
  <Assembly id="CAB-1" name="600 Base Unit" units="mm">
    <!-- Product frame: origin bottom-rear-left; X across, Y depth, Z up.
         Placement positions each part's OWN local datum (corner of face A) into
         this frame — the same frame used for machining, so hole world-positions
         fall out for free. Parts repeat, so placement is per INSTANCE. -->
    <Instance part="P1" id="GABLE-L">
      <Position x="0" y="0" z="0"/>
      <!-- local X(length)->Z up, Y(depth)->Y, Z(thickness)->X across -->
      <Orientation localX="+Z" localY="+Y" localZ="+X"/>
    </Instance>
    <Instance part="P1" id="GABLE-R">
      <Position x="582" y="0" z="0"/>
      <Orientation localX="+Z" localY="+Y" localZ="+X"/>
    </Instance>
  </Assembly>

</CabCodeXF>
```

---

## Operation reference

All operation coordinates are in the part frame (see Conventions), seen from
face A. `face` is `A` or `B` (or an edge id, for edge bores).

Every operation may also carry two optional attributes for people:
- `name`: a short label ("Shelf pins", "Desk foot cut-out"). A factory may show
  it to the operator or use it as the machine program's operation name.
- `note`: free text for the operator ("Keep the offcut").

Neither changes what is machined. An importer must never read meaning from
them: a hole is a hole whatever it is called.

### `<Bore>` — drilling
Face bore (`face="A|B"`, positions `<At x y>`) or edge bore (`face="X0|Xmax|Y0|Ymax"`,
positions `<At u>` plus `z`). `diameter`, `depth` (or `through="true"`), and an
optional `<Pattern count dx dy>` that repeats from each `At`.

### `<Route>` — a routed path
Connected segments, each self-contained:
- `<Line x1 y1 x2 y2/>`
- `<Arc x1 y1 x2 y2 cx cy dir="cw|ccw"/>` — a circular arc about centre
  `(cx, cy)`; `dir` is clockwise / counter-clockwise in the part frame seen from
  face A.

Attributes: `toolWidth` (cutter diameter), `depth` or `through="true"`,
`closed="true"` when the path returns to its start, and `toolSide`:
- `left` / `right` — the path is the **finished edge**; the cutter runs on that
  side of it, relative to the direction of travel (cutter compensation).
- `centre` (default) — the cutter centre runs on the path: a groove `toolWidth`
  wide.

```xml
<!-- a shaped cut-out through a side: line, R30 corner, straight, R30 corner, line -->
<Route face="B" toolWidth="12" toolSide="right" depth="19" through="true">
  <Line x1="80"      y1="0"       x2="96.32"   y2="114.243"/>
  <Arc  x1="96.32"   y1="114.243" x2="126.019" y2="140" cx="126.019" cy="110" dir="cw"/>
  <Line x1="126.019" y1="140"     x2="373.981" y2="140"/>
  <Arc  x1="373.981" y1="140"     x2="403.68"  y2="114.243" cx="373.981" cy="110" dir="cw"/>
  <Line x1="403.68"  y1="114.243" x2="420"     y2="0"/>
</Route>
```

### `<Pocket>` — a rectangular recess
Given by its **centre**: `x`, `y`; its size along part X / Y, `sizeX`, `sizeY`
(before rotation); `cornerRadius` (default 0); `angle` (degrees,
counter-clockwise about the centre seen from face A, default 0); `depth` or
`through="true"`. A pocket may run past the part's edges (an open-ended groove);
any overcut at an open edge is the factory's choice.

```xml
<!-- an 8.25mm groove the full length of a 60 x 2350 end panel, 10 deep, open both ends -->
<Pocket face="B" x="9.875" y="1175" sizeX="8.25" sizeY="2370" depth="10"/>
```

---

## Materials & edge bands — shared definitions (reference library)

Define each material/band **once** in a document-level `<Materials>` /
`<EdgeMaterials>` library; every line references it by `id`. Two jobs:

1. **Normalization** — spell a material out once, not 100 times. Single edit point,
   smaller files. (Standard practice — BTLx/STEP do the same; reuse the `id`-ref
   idiom for bands and, later, tools.)
2. **A clean resolution boundary** — the producer speaks its own vocabulary
   (`token="W1000"`); the factory has its own code (`Egger W1000 MFC`). The
   definition is the seam between them.

**Design decision (flip-able):** keep the file **factory-neutral** —
the definition carries the producer's `token` + *resolvable* attributes
(`manufacturer` / `decor` / `finish` / `type` / `thickness`), NOT any one factory's
internal code. Baking in "Egger W1000 MFC" would tie the file to Cutwrights and
break "emit once, machine at any CabCodeXF factory." The producer→factory mapping lives
**factory-side** (CabCode import, a stored cross-reference keyed by producer +
token) — which gives a *stronger* "change once": fix it per producer and it applies
to every future job, not just one file.
- Richer neutral attributes → more jobs auto-resolve with no manual mapping at all.
- Optional escape hatch: a definition MAY carry a non-authoritative `factoryCode`
  hint/override for a known target — never required, never the key.
- Alternative (if ever wanted): embed the resolved factory code in the definition.
  Simpler per-file, but sacrifices neutrality. Not recommended.

## Design rationale

- **Materials are referenced, not inlined** — see above; neutral descriptors,
  factory resolves them. CabCodeXF never defines a factory's catalogue. Keeps it small.
- **Intent, not toolpaths** — no tool numbers, feeds, spindles. Those are CAM,
  generated by CabCode. A vendor only describes geometry + purpose: low adoption cost.
- **Four primitives cover most panel work** — vertical bore (+ pattern), edge
  bore, route (lines and arcs, with a cutter side) and pocket. A pocket *could*
  be described as a route, but it is its own operation: drilling centres such as
  the BHX 055 machine pockets with a native pocketing cycle (WoodWOP *Tasche*),
  and a factory should not have to recognise a pocket from a route's outline.
- **Cutting strategy stays with the factory** — how many passes a route takes,
  whether holding tabs are left, overcut at an open edge, tool choice. CabCodeXF says
  what the finished part looks like.
- **BTLx lineage visible** — `Part` with geometric attributes + an `Operations`
  block of parametric ops; machine-independent; one file, many parts.

## Assembly — optional v1.1 layer (placement for viewers)

Panel **position + orientation** so a viewer (cabmodel) can render the assembled,
machined product. Designed as an **optional, additive** block — never part of the
mandatory machining core — so it costs nothing to the vendors who only send a
cut-list, and preserves the low-adoption-cost wedge.

- **Reuses the part datum.** An instance places a part's own local frame (corner
  of face A, X=Length, Y=Width, Z=normal) into the product frame via a rigid
  transform. The frame that positions a drill hole positions the panel, so a
  feature's world position falls out for free.
- **Per-instance, not per-part.** Parts repeat (2 gables, 4 shelves); each
  placement references a part `id`. When `<Assembly>` is present the instance
  count is authoritative; `Part @quantity` is just the no-assembly convenience
  total. If both appear they must agree (validator check).
- **Orientation representation.** For cabinets (near-100% orthogonal) the
  axis-map form (`localX="+Z"` …) is the most readable and unambiguous. Canonical
  fallback for angled parts is a 4×4 `matrix` (or quaternion) — what a 3D viewer
  actually consumes; axis-map + Euler are convenience forms a validator expands.

**What it buys:** CabCodeXF can feed cabmodel end-to-end — geometry (box from L×W×T),
placement (assembly), *and* features (ops per face) in one file. Bonus: because
mating parts share a product frame, you can verify a gable's dowels line up with
the top's — catching a bad post-processor before it cuts.

**Guard-rail:** placed boxes with features for *visualisation* — NOT a CAD/BIM
model. No joint semantics, no parametrics, no constraints. The moment CabCodeXF tries to
be IFC it dies under its own weight.

## Designs — optional block (whole parametric cabinets)

A cabinet as its producing system **designed** it: a template plus height, width,
depth, boards and settings. It lets that system reopen, resize and re-price the
cabinet; every other consumer ignores the block and machines the Parts.

```xml
<Designs>
  <Design id="D1" system="cabcode" template="C101" name="Sink base" quantity="1"
          height="720" width="600" depth="560" x="0" y="0" z="0" rotation="0"
          code="TTXAK63EURFY">
    <Use role="carcass" material="M1"/>
    <Use role="front" material="M2"/>
    <Param key="doorhorz" value="2" type="number"/>
  </Design>
</Designs>
<Part id="P1" … design="D1">…</Part>
<Assembly id="A1" design="D1">…</Assembly>
```

- **`system`** names who owns the template ids (`cabcode`; another CAD system
  could use its own), so `template="C101"` never collides.
- **`height` / `width` / `depth`** are required: the overall size of the cabinet.
- **`<Use role=… material=…>`** names a board (or `edgeMaterial=` an edging) by
  role from the shared `<Materials>` / `<EdgeMaterials>` lists. Suggested roles:
  `carcass`, `front`, `back`, `edging`.
- **`<Param>`** is one of the system's own settings, opaque to everyone else.
  `type` is `string` (default), `number`, `boolean` or `json` (a structured value
  as JSON text).
- **`code`** is the system's own saved-design code, when it has one (CabCode's
  "unique code for this design"). **`catalogue`** records the template catalogue
  version, so a later resize rebuilds the same cabinet.
- **Links.** `Part @design` and `Assembly @design` say which design a part or
  placement came from.
- **Designs-only files.** A file may carry Designs and no Parts. Each such Design
  says `parts="omitted"`, and only a resolver for its `system` can turn it into
  parts (for CabCode: CABcad's `POST /api/cabcodexf/resolve`, which returns the
  file with Parts). A file must carry at least one Part or one Design.
  A consumer that cannot resolve a design should say so, not cut nothing silently.
- **Placement: `x` / `y` / `z` / `rotation`** (optional, mm and degrees) say where
  the cabinet sits in the job, the way a CabCode job places its cabinets: `x`
  along the wall, `y` up, `z` out from the wall; (x, y, z) is the cabinet's left,
  bottom, back corner, and `rotation` turns it about the vertical. On the Design
  they place its first copy; a `<Place x=… y=… z=… rotation=…/>` per copy, in
  order, places each copy and wins over the Design's own. Anything left out
  carries on from the copy before: `x` is that copy's `x` plus its width (copies
  stand side by side along the wall), `y`, `z` and `rotation` stay the same. So
  a file with no placement at all gives a straight run, left to right, in file
  order.
- **Inferred positions.** A file with Designs need not carry an `<Assembly>`: a
  resolver regenerates each design and places every copy as above. CABcad's
  resolve returns one `<Assembly>` holding every copy of every design.

This does not break the guard-rail above. CabCodeXF defines no parametric model:
the template, its settings and how sizes drive parts all belong to the named
system. The Parts remain the neutral, machinable truth.

CabCode producer mapping (`server3/src/cabcodexf.ts`): `kv.height/width/depth` →
the attributes, `kv.material` → `carcass`, `kv.materialfront` → `front`, `kv.name`
→ `name`; the job placement (`meta.position[]`, else the cab's own `x/y/z/rotation`)
→ the placement attributes or `<Place>`s; every other `kv` key → a `<Param>`. A part gets `design=` when the file
has one design, or when its name carries the design's `[name]` prefix (CABcad
prefixes each cabinet's panels with `kv.name`, else its `name`, else its `uid`). A
name two designs share, or a part CABcad merged across cabinets (identical sides
of two cabinets cut as one part, quantity 2), links to no design: `Part @design`
is optional, so it is left off rather than guessed.

## Scoped OUT of v1 (so it ships) — all clean v1.x additions later

- Compound macros (hinge = 35mm cup + 2× dowel as one op)
- Cutting strategy: passes, holding tabs, overcut, tool selection (factory-side by design)
- Non-rectangular pockets (a closed route with `toolSide` describes the outline today)
- Nesting / sheet layout
- Tolerances / fits
- Free-form NURBS edges
- Joint/constraint semantics (assembly carries placement only — see above)

## Open design forks (decide before hardening into an XSD)

1. **Datum** — corner-of-face-A origin (current) vs. BTLx-style reference-side +
   offset. Corner is simpler for panels.
2. **Edge-bore positioning** — `u` along edge + `z` (current) vs. full x/y/z
   always. `u` is cleaner but one more concept.
3. **Hinges / system holes** — raw bores (universal but dumb) vs. named macros
   early (richer but every implementer must support them).

## Next steps

- Lock the three forks → write a proper **XSD** + a second worked sample.
- Produce the **same part as JSON** (internal representation / optional secondary
  mapping) to demonstrate "model first, syntax second."
- Stand up a **conformance suite** (golden CabCodeXF files + expected CAM) — the real
  moat alongside the engine.
