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.
This commit is contained in:
2026-08-10 00:29:23 +08:00
parent 8d0e393100
commit f12b40e82b
3 changed files with 9 additions and 10 deletions
+1 -1
View File
@@ -373,7 +373,7 @@ layout:
`1`. See the positioning process below. `1`. See the positioning process below.
- `tilt` — rotation in degrees applied to every shown part about the card's - `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` 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 - `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, of the `curve`. The stack ramps linearly between them across its span,
lifting it in 3D. Requires a `curve`. lifting it in 3D. Requires a `curve`.
+4 -5
View File
@@ -44,9 +44,8 @@ describe('pointAt', () => {
}); });
describe('stackingOffset', () => { describe('stackingOffset', () => {
it('returns no offset without a curve', () => { it('defaults to a 1° tilt without a curve', () => {
expect(stackingOffset(undefined, 0, 3)).toBe(NO_OFFSET); expect(stackingOffset(undefined, 0, 3)).toEqual({ x: 0, y: 0, rotation: 0, z: 0, tilt: 1 });
// A stacking strategy defaults to a 1° tilt, so it's not the identity.
expect(stackingOffset({ limit: 5 }, 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); expect(last.z).toBeCloseTo(40);
}); });
it('returns no offset without a stacking strategy', () => { it('returns no offset for an empty stack', () => {
expect(stackingOffset(undefined, 0, 3)).toBe(NO_OFFSET); expect(stackingOffset(undefined, 0, 0)).toBe(NO_OFFSET);
}); });
}); });
+4 -4
View File
@@ -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 * 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 * stack, given the route's stacking strategy. Returns `NO_OFFSET` when the
* no curve, no tilt, and no z profile, or the stack is empty. * stack is empty. Every placed part gets a default 1° tilt unless overridden.
*/ */
export function stackingOffset( export function stackingOffset(
stacking: Stacking | undefined, stacking: Stacking | undefined,
@@ -45,8 +45,8 @@ export function stackingOffset(
// `tilt` rotates each shown part about its local Y (long) axis by the same // `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 // amount. It applies even without a curve. Defaults to 1° when a stacking
// strategy is present but doesn't specify a tilt. // strategy doesn't specify a tilt.
const tilt = stacking?.tilt ?? (stacking ? 1 : 0); const tilt = stacking?.tilt ?? 1;
// The horizontal position along the curve (or a straight pile when there's // 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. // no curve), plus the normalized progress used to ramp the z height.