# Spatial assembly

Spatial assembly adds reusable plans, connection points, snapping, collision
rules, clearances, adjacency validation, bills of materials, quotes and
production exports to any Product 3D catalog.

The feature is domain-neutral. A component can represent a cabinet, machine,
exhibition panel, light, rack, pipe, vehicle option or any other spatial
object.

## 1. Describe reusable components

Assembly behavior belongs to the Product 3D element definition. Positions use
the same local world unit as the GLB model. Rotations exposed by the API use
radians.

```ts
const machineModule = {
  id: 'machine-module',
  name: 'Machine module',
  type: 'product-3d',
  product3d: {
    model: { url: '/models/machine-module.glb', format: 'glb' },
    parts: [],
  },
  assembly: {
    schemaVersion: 1,
    enabled: true,
    category: 'machine-module',
    tags: ['floor-mounted', 'connectable'],
    placement: {
      // Align the lowest collision point with plan.floorY.
      // Use `free` for suspended, wall-mounted or unconstrained components.
      support: 'floor',
      supportOffset: 0,
    },

    collision: {
      enabled: true,
      margin: 0.03,
      group: 'equipment',
      collidesWith: ['equipment'],
    },

    anchors: [
      {
        id: 'input',
        label: 'Input',
        role: 'target',
        position: { x: -0.8, y: 0.5, z: 0 },
        normal: { x: -1, y: 0, z: 0 },
        accepts: ['production-line'],
        snapDistance: 0.3,
        alignRotation: true,
      },
      {
        id: 'output',
        label: 'Output',
        role: 'source',
        position: { x: 0.8, y: 0.5, z: 0 },
        normal: { x: 1, y: 0, z: 0 },
        provides: ['production-line'],
        snapDistance: 0.3,
        alignRotation: true,
      },
    ],

    clearances: [
      {
        id: 'service-access',
        label: 'Service access',
        center: { x: 0, y: 1, z: -0.9 },
        size: { x: 1.4, y: 2, z: 0.8 },
        severity: 'error',
        message: 'Keep the service access area clear.',
      },
    ],

    adjacency: [
      {
        id: 'requires-output',
        type: 'requires-connection',
        target: { connectorTypes: ['production-line'] },
        severity: 'warning',
        message: 'Connect this module to the production line.',
      },
    ],

    bom: [
      {
        id: 'main-unit',
        sku: 'MACHINE-100',
        label: 'Machine module',
        quantity: 1,
        unit: 'piece',
        unitPrice: 2490,
        category: 'equipment',
      },
      {
        id: 'mounting-kit',
        sku: 'MOUNT-4',
        label: 'Mounting kit',
        quantity: 4,
        unit: 'piece',
        unitPrice: 12.5,
        category: 'hardware',
      },
    ],
  },
};
```

The Elements editor exposes these fields visually. Integrators do not need to
edit JSON to configure anchors, collision groups, clearances, adjacency rules
or BOM lines.

## 2. Configure a plan

Open the built-in **Assembly** module in a 3D view, or use the public API:

```ts
const result = await designer.assembly.configure(
  {
    enabled: true,
    plan: {
      // X/Z outline in world units.
      points: [
        { x: -4, z: -3 },
        { x: 4, z: -3 },
        { x: 4, z: 3 },
        { x: -4, z: 3 },
      ],
      floorY: 0,
      height: 2.8,
      wallThickness: 0.08,
      closed: true,
    },
    settings: {
      grid: { enabled: true, size: 0.1, subdivisions: 5 },
      snap: {
        enabled: true,
        anchors: true,
        boundaries: true,
        grid: true,
        rotation: true,
        autoConnect: true,
        distance: 0.3,
      },
      collision: {
        enabled: true,
        policy: 'prevent',
        margin: 0.02,
        includeClearances: true,
      },
      adjacency: {
        enabled: true,
        autoCorners: true,
      },
    },
  },
  { source: 'host.configure-room', commitHistory: true },
);

console.log(result.value);
```

The Assembly module provides rectangle, L-shaped and U-shaped presets. A host
application can pass any polygon through the API.

## 3. Place and connect components

Every placement is first evaluated by the pure assembly engine. Invalid
positions can be rejected while the previous valid transform is preserved.

```ts
const components = designer.assembly.listComponents();
const component = components[0];

const preview = designer.assembly.previewPlacement(component.id, {
  position: { x: 1.2, y: 0, z: -0.8 },
  snap: true,
});

if (preview.accepted) {
  await designer.assembly.place(
    component.id,
    { position: preview.applied.position, snap: true },
    { source: 'host.pointer-drop', commitHistory: true },
  );
}
```

Automatic placement chooses the closest compatible free anchor and falls back
to a valid wall, corner or grid position:

```ts
await designer.assembly.autoPlace(component.id, {
  source: 'host.auto-place',
  commitHistory: true,
});
```

Connections can also be explicit:

```ts
await designer.assembly.connect({
  source: { componentId: 'module-a', anchorId: 'output' },
  target: { componentId: 'module-b', anchorId: 'input' },
  connectorType: 'production-line',
});
```

## 4. Validate the complete scene

```ts
const validation = designer.assembly.validate();

for (const issue of validation.issues) {
  console.log(issue.severity, issue.code, issue.message, issue.componentIds);
}
```

Validation covers:

- product collisions and configurable collision groups;
- reserved clearance volumes;
- plan boundaries;
- required and forbidden neighbors;
- minimum and maximum gaps;
- required anchor connections;
- connector compatibility and anchor capacity.

## 5. BOM, quote and production export

```ts
const bom = designer.assembly.getBom({ currency: 'EUR' });

const quote = designer.assembly.getQuote({
  currency: 'EUR',
  adjustments: [
    { id: 'installation', label: 'Installation', amount: 350 },
  ],
  metadata: { projectId: 'PROJECT-42' },
});

const csv = await designer.assembly.export('bom-csv', {
  currency: 'EUR',
});

const productionPackage = await designer.assembly.export('production-json', {
  currency: 'EUR',
  metadata: { projectId: 'PROJECT-42' },
});
```

`production-json` contains the plan, settings, component transforms,
connections, validation result, BOM and quote. It is suitable as the canonical
handoff payload for ERP, manufacturing, installation or custom server
workflows.

## Renderer boundary

The assembly API is available only while a 3D view is active and the Babylon
addon is loaded. Calling it from a Fabric view throws an
`AsukaRendererError` with code `UNSUPPORTED_OPERATION`.

All values crossing `designer.assembly` are serializable. Babylon meshes,
materials, vectors and observables remain private to the 3D adapter.
