Files
tts-workshop/packages/tabletop/src/placement.tsx
T
hypercross c5d6dff12d feat(tabletop): add tilt and zStart/zEnd stacking options
Replace the per-part lift with a local Y-axis tilt that fans the stack,
and add zStart/zEnd to ramp the stack's height across the curve so it
arches in 3D. Update the poker deck and docs accordingly.
2026-08-09 23:36:42 +08:00

39 lines
1.7 KiB
TypeScript

/**
* `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 (
<group position={[anchorX, anchorZ, anchorY]} rotation={[0, anchorRotation, 0]}>
{/* The part mesh extrudes along +Z; lay it flat so its face points up. */}
<group rotation={[-Math.PI / 2, 0, tilt]}>
<PartView part={part} baseUrl={part.baseUrl} />
</group>
</group>
);
}