feat: add facing orientation to parts

Replace the face/back boolean with a three-state facing (face, back,
standing) seeded from setup placements. Parts now orient about the
anchor at the resting face/edge, so back-down parts sit on the table
instead of below it and standing parts rest on their bottom edge.
This commit is contained in:
2026-08-10 11:47:58 +08:00
parent aa23e93b3f
commit d663f4afea
13 changed files with 126 additions and 36 deletions
+10 -6
View File
@@ -9,10 +9,10 @@ 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, DEG_TO_RAD } from './part.js';
import { MM_TO_WORLD, DEG_TO_RAD, facingTransform, partDimensions } from './part.js';
export function PartPlacement({ pkg, placement }: { pkg: Package; placement: Placement }) {
const { route, candidate, piece, index, stackSize, face } = placement;
const { route, candidate, piece, index, stackSize, facing } = 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;
@@ -34,12 +34,16 @@ export function PartPlacement({ pkg, placement }: { pkg: Package; placement: Pla
const anchorRotation =
((candidate?.rotation ?? route.rotation ?? 0) - rotation) * DEG_TO_RAD;
// The facing pivot is the center of the face/edge that rests on the table
// and should land at the anchor. Translate the mesh by the pivot, then apply
// the facing rotation and tilt about it, so the part sits on the table.
const { width, height, depth } = partDimensions(part);
const { pivot, xRotation } = facingTransform(facing, { height, depth });
return (
<group position={[anchorX, anchorZ, anchorY]} rotation={[0, anchorRotation, 0]}>
{/* The part mesh extrudes along +Z; lay it flat so its face points up.
A face-down part is flipped over about its local X axis. */}
<group rotation={[-Math.PI / 2, tilt * DEG_TO_RAD, 0]}>
<group rotation={!face ? [Math.PI, 0, 0] : [0, 0, 0]}>
<group position={[-pivot[0], -pivot[1], -pivot[2]]}>
<group rotation={[xRotation, tilt * DEG_TO_RAD, 0]}>
<PartView part={part} baseUrl={part.baseUrl} />
</group>
</group>