feat(tabletop): let candidates inherit and override route stacking
Candidates now carry their own stacking strategy, inheriting the route's when absent, so the default tilt applies consistently. Drop the now redundant explicit tilt from the poker deck.
This commit is contained in:
+1
-1
@@ -341,7 +341,7 @@ string,number,number,number
|
|||||||
|
|
||||||
The router should select only the first candidate with all params matched against its props — the fields in the candidate's CSV row (e.g. `:seat` matches the candidate's `seat` value).
|
The router should select only the first candidate with all params matched against its props — the fields in the candidate's CSV row (e.g. `:seat` matches the candidate's `seat` value).
|
||||||
|
|
||||||
When no candidates match, the whole route fails to match.
|
A candidate inherits the route's `x`, `y`, `rotation`, and `stacking`, and may override any of them with its own values. When no candidates match, the whole route fails to match.
|
||||||
|
|
||||||
### Stacking
|
### Stacking
|
||||||
|
|
||||||
|
|||||||
@@ -106,7 +106,6 @@ layout:
|
|||||||
align: center
|
align: center
|
||||||
zStart: 0
|
zStart: 0
|
||||||
#zEnd: 100
|
#zEnd: 100
|
||||||
tilt: 1
|
|
||||||
curve: M -50 -200 C 50 -150 450 -150 550 -200
|
curve: M -50 -200 C 50 -150 450 -150 550 -200
|
||||||
- route: /community/:slot
|
- route: /community/:slot
|
||||||
candidates:
|
candidates:
|
||||||
|
|||||||
@@ -84,6 +84,8 @@ export interface Candidate {
|
|||||||
x?: number;
|
x?: number;
|
||||||
y?: number;
|
y?: number;
|
||||||
rotation?: number;
|
rotation?: number;
|
||||||
|
/** Stacking strategy; overrides the route's when set, else inherits it. */
|
||||||
|
stacking?: Stacking;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Route {
|
export interface Route {
|
||||||
|
|||||||
@@ -17,7 +17,9 @@ export function PartPlacement({ pkg, placement }: { pkg: Package; placement: Pla
|
|||||||
const part = pkg.parts.get(piece.split(':').slice(1).join(':'));
|
const part = pkg.parts.get(piece.split(':').slice(1).join(':'));
|
||||||
if (!part) return null;
|
if (!part) return null;
|
||||||
|
|
||||||
const { x, y, rotation, z, tilt } = useStacking(route.stacking, index, stackSize);
|
// A candidate inherits the route's stacking unless it overrides it.
|
||||||
|
const stacking = candidate?.stacking ?? route.stacking;
|
||||||
|
const { x, y, rotation, z, tilt } = useStacking(stacking, index, stackSize);
|
||||||
|
|
||||||
// Route anchors and stacking offsets are in mm; convert to world units so
|
// 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
|
// parts land on the (world-scaled) surface. `z` raises the part along the
|
||||||
|
|||||||
@@ -98,6 +98,22 @@ describe('computeSurfacePlacements', () => {
|
|||||||
expect(placements[0]!.candidate).toEqual({ seat: '0', x: 40, y: 5, rotation: 1 });
|
expect(placements[0]!.candidate).toEqual({ seat: '0', x: 40, y: 5, rotation: 1 });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('keeps the candidate stacking alongside its anchor', () => {
|
||||||
|
const surface = makeSurface({
|
||||||
|
layout: [
|
||||||
|
{
|
||||||
|
route: '/dock/:seat',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
rotation: 0,
|
||||||
|
candidates: [{ seat: '0', x: 40, y: 5, stacking: { tilt: 2 } }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const placements = computeSurfacePlacements(surface, { '/dock/0': ['harbor:boat#fleet'] });
|
||||||
|
expect(placements[0]!.candidate).toEqual({ seat: '0', x: 40, y: 5, stacking: { tilt: 2 } });
|
||||||
|
});
|
||||||
|
|
||||||
it('keeps the same piece on two paths as distinct placements', () => {
|
it('keeps the same piece on two paths as distinct placements', () => {
|
||||||
const surface = makeSurface({
|
const surface = makeSurface({
|
||||||
layout: [
|
layout: [
|
||||||
|
|||||||
Reference in New Issue
Block a user