feat(tabletop): show surface bounds and lay parts flat

Add a toggleable surface plane, bounds outline, and constant-size HTML
label. Scale placement and mount anchors to world units so parts land on
the surface, and rotate parts to face up.
This commit is contained in:
2026-08-09 23:04:51 +08:00
parent abf901418b
commit 5808e15c45
6 changed files with 116 additions and 11 deletions
+9 -3
View File
@@ -9,6 +9,7 @@ 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;
@@ -18,13 +19,18 @@ export function PartPlacement({ pkg, placement }: { pkg: Package; placement: Pla
const { x, y, rotation } = useStacking(route.stacking, index, stackSize);
const anchorX = (candidate?.x ?? route.x ?? 0) + x;
const anchorY = (candidate?.y ?? route.y ?? 0) + y;
// Route anchors and stacking offsets are in mm; convert to world units so
// parts land on the (world-scaled) surface.
const anchorX = ((candidate?.x ?? route.x ?? 0) + x) * MM_TO_WORLD;
const anchorY = ((candidate?.y ?? route.y ?? 0) + y) * MM_TO_WORLD;
const anchorRotation = (candidate?.rotation ?? route.rotation ?? 0) + rotation;
return (
<group position={[anchorX, 0, anchorY]} rotation={[0, anchorRotation, 0]}>
<PartView part={part} baseUrl={part.baseUrl} />
{/* The part mesh extrudes along +Z; lay it flat so its face points up. */}
<group rotation={[-Math.PI / 2, 0, 0]}>
<PartView part={part} baseUrl={part.baseUrl} />
</group>
</group>
);
}