/** * `PartPlacement` — position a part on a surface location. * * A stable per-part component that places a part at its route's anchor (plus * the candidate's anchor when there is one) and applies the route's stacking * strategy via `useStacking`. Renders the part's mesh with `PartView`. */ import type { Package } from '@tts/bgm'; import { useStacking } from './stacking.js'; import type { Placement } from './state.js'; import { PartView } from './partView.js'; import { MM_TO_WORLD } from './part.js'; export function PartPlacement({ pkg, placement }: { pkg: Package; placement: Placement }) { const { route, candidate, piece, index, stackSize } = placement; // `piece` is `package:type#id`; the parts map is keyed by `type#id`. const part = pkg.parts.get(piece.split(':').slice(1).join(':')); if (!part) return null; const { x, y, rotation, z, tilt } = useStacking(route.stacking, index, stackSize); // Route anchors and stacking offsets are in mm; convert to world units so // parts land on the (world-scaled) surface. `z` raises the part along the // surface normal (world +Y); `tilt` fans it about its local Y (long) axis. const anchorX = ((candidate?.x ?? route.x ?? 0) + x) * MM_TO_WORLD; const anchorY = ((candidate?.y ?? route.y ?? 0) + y) * MM_TO_WORLD; const anchorZ = z * MM_TO_WORLD; const anchorRotation = (candidate?.rotation ?? route.rotation ?? 0) + rotation; return ( {/* The part mesh extrudes along +Z; lay it flat so its face points up. */} ); }