# Systems *Core and viewer systems architecture.* Applies to: `packages/core/src/systems/**`, `packages/viewer/src/systems/**`. Systems own business logic, geometry generation, and constraints. They run in the Three.js frame loop and are never rendered directly. ## Two Kinds of Systems ### Core Systems — `packages/core/src/systems/` Pure logic: no rendering, no Three.js objects. They read nodes from `useScene`, compute derived values (geometry, constraints), and write results back. | System | Responsibility | |---|---| | `WallSystem` | Wall mitering, corner joints | | `SlabSystem` | Polygon-based floor/roof generation | | `CeilingSystem` | Polygon-based ceiling generation | | `RoofSystem` | Pitched roof shape | | `DoorSystem` | Placement constraints on walls | | `WindowSystem` | Placement constraints on walls | | `ItemSystem` | Item transforms, collision | ### Viewer Systems — `packages/viewer/src/systems/` Access Three.js objects (via `useRegistry`) and manage rendering side-effects. | System | Responsibility | |---|---| | `LevelSystem` | Stacked / exploded / solo / manual level positions | | `WallCutout` | Cuts door/window holes in wall geometry | | `ZoneSystem` | Zone display and label placement | | `InteractiveSystem` | Item toggles and sliders in the scene | | `GuideSystem` | Temporary helper geometry | | `ScanSystem` | Point cloud rendering | ## Pattern Systems are React components that render nothing (`return null`) and use `useFrame` for per-frame logic. ```tsx // packages/core/src/systems/my-system.tsx import { useFrame } from '@react-three/fiber' import { useScene } from '../store/use-scene' export function MySystem() { const nodes = useScene(s => s.nodes) useFrame(() => { // compute and write back derived state }) return null } ``` Core and viewer systems are mounted inside `` alongside renderers. See `packages/viewer/src/components/viewer/index.tsx` for the mount order. **Systems are a customization point.** Any consumer of `` — the editor app, an embed, a read-only preview — can inject its own systems as children. This is how editor-specific behaviour (space detection, tool feedback) is added without touching the viewer package. ## Rules - **Core systems must not import Three.js** — they work with plain data. - **Viewer systems must not contain business logic** — delegate to core if the rule is domain-level. - **Never duplicate logic** between a system and a renderer — if the renderer needs it, the system should compute and store it, and the renderer reads the result. - Systems should be **idempotent**: given the same nodes, they produce the same output. - Mark nodes as `dirty` in the scene store to signal that a system should re-run. Avoid running expensive logic every frame without a dirty check. ## Adding a New System 1. Decide the scope: - **Domain logic** → `packages/core/src/systems/` - **Viewer rendering side-effect** → `packages/viewer/src/systems/` — mount in `packages/viewer/src/components/viewer/index.tsx` - **Editor-specific or integration-specific** → keep it in the consuming app (e.g. `apps/editor/components/systems/`) and inject it as a child of `` 2. Create `-system.tsx` in the appropriate directory. 3. Mount it in the right place: - Viewer-internal systems go in `packages/viewer/src/components/viewer/index.tsx` - App-specific systems are injected as children from outside: ```tsx // apps/editor — editor injects its own systems without modifying the viewer ``` 4. **Mount order matters.** Most viewer systems run *after* renderers in the JSX tree — they consume `sceneRegistry` data that renderers populate on mount. Only place a system before renderers if it explicitly does not read the registry.