From f12b40e82bff51f532975c65d1eb8ca56ae4e547 Mon Sep 17 00:00:00 2001 From: hypercross Date: Mon, 10 Aug 2026 00:29:23 +0800 Subject: [PATCH] fix(tabletop): default tilt to 1 degree for all parts Apply the default 1 degree tilt even when a route has no stacking strategy, so every placed part is tilted consistently. --- docs/bgm-format.md | 2 +- packages/tabletop/src/stacking.test.ts | 9 ++++----- packages/tabletop/src/stacking.ts | 8 ++++---- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/docs/bgm-format.md b/docs/bgm-format.md index 35a9f08..5fa108a 100644 --- a/docs/bgm-format.md +++ b/docs/bgm-format.md @@ -373,7 +373,7 @@ layout: `1`. See the positioning process below. - `tilt` — rotation in degrees applied to every shown part about the card's local Y (long) axis. It applies even without a `curve`, so a bare `tilt` - rotates a straight pile. + rotates a straight pile. Defaults to `1` when not specified. - `zStart` / `zEnd` — the height (surface-normal) in mm at the start and end of the `curve`. The stack ramps linearly between them across its span, lifting it in 3D. Requires a `curve`. diff --git a/packages/tabletop/src/stacking.test.ts b/packages/tabletop/src/stacking.test.ts index bd4684a..df84983 100644 --- a/packages/tabletop/src/stacking.test.ts +++ b/packages/tabletop/src/stacking.test.ts @@ -44,9 +44,8 @@ describe('pointAt', () => { }); describe('stackingOffset', () => { - it('returns no offset without a curve', () => { - expect(stackingOffset(undefined, 0, 3)).toBe(NO_OFFSET); - // A stacking strategy defaults to a 1° tilt, so it's not the identity. + it('defaults to a 1° tilt without a curve', () => { + expect(stackingOffset(undefined, 0, 3)).toEqual({ x: 0, y: 0, rotation: 0, z: 0, tilt: 1 }); expect(stackingOffset({ limit: 5 }, 0, 3)).toEqual({ x: 0, y: 0, rotation: 0, z: 0, tilt: 1 }); }); @@ -114,7 +113,7 @@ describe('stackingOffset', () => { expect(last.z).toBeCloseTo(40); }); - it('returns no offset without a stacking strategy', () => { - expect(stackingOffset(undefined, 0, 3)).toBe(NO_OFFSET); + it('returns no offset for an empty stack', () => { + expect(stackingOffset(undefined, 0, 0)).toBe(NO_OFFSET); }); }); \ No newline at end of file diff --git a/packages/tabletop/src/stacking.ts b/packages/tabletop/src/stacking.ts index d180071..185a0af 100644 --- a/packages/tabletop/src/stacking.ts +++ b/packages/tabletop/src/stacking.ts @@ -27,8 +27,8 @@ export const NO_OFFSET: StackOffset = { x: 0, y: 0, rotation: 0, z: 0, tilt: 0 } /** * Compute the offset/rotation for the piece at `index` of a `stackSize`-piece - * stack, given the route's stacking strategy. Returns `NO_OFFSET` when there's - * no curve, no tilt, and no z profile, or the stack is empty. + * stack, given the route's stacking strategy. Returns `NO_OFFSET` when the + * stack is empty. Every placed part gets a default 1° tilt unless overridden. */ export function stackingOffset( stacking: Stacking | undefined, @@ -45,8 +45,8 @@ export function stackingOffset( // `tilt` rotates each shown part about its local Y (long) axis by the same // amount. It applies even without a curve. Defaults to 1° when a stacking - // strategy is present but doesn't specify a tilt. - const tilt = stacking?.tilt ?? (stacking ? 1 : 0); + // strategy doesn't specify a tilt. + const tilt = stacking?.tilt ?? 1; // The horizontal position along the curve (or a straight pile when there's // no curve), plus the normalized progress used to ramp the z height.