Tile palette with previews, click-to-place, independent rotation
Completely reworked feature placement UX: - Tile palette in sidebar shows 7 pattern types (dead-end, straight, wide curve, sharp bend, Y-split, Y-wide, crossroads) for each linear terrain (road, river, coastline) with small hex previews - Click pattern to select, click hex to place — no more edge-clicking - Q/E keys or scroll wheel to rotate selected pattern before placing - Each placed feature has independent rotation controls (arrows) and remove button in the hex inspector panel - Edge constraint enforcement still runs automatically on placement Also added: core/tile-patterns.ts with canonical pattern definitions and rotation math (accounting for symmetry). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
89
core/tile-patterns.ts
Normal file
89
core/tile-patterns.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* Canonical tile patterns for linear features.
|
||||
* Each pattern is defined by a base edge mask and a human-readable name.
|
||||
* Patterns can be rotated by 60° increments to produce all placements.
|
||||
*/
|
||||
|
||||
import type { EdgeMask } from './types.js';
|
||||
import { HexEdge } from './types.js';
|
||||
import { edgeMask, rotateMask, edgeCount } from './edge-connectivity.js';
|
||||
|
||||
export interface TilePattern {
|
||||
id: string;
|
||||
name: string;
|
||||
baseMask: EdgeMask;
|
||||
/** Number of distinct rotations (accounting for symmetry) */
|
||||
rotations: number;
|
||||
}
|
||||
|
||||
/** All canonical patterns for linear features */
|
||||
export const TILE_PATTERNS: TilePattern[] = [
|
||||
{
|
||||
id: 'dead-end',
|
||||
name: 'Dead End',
|
||||
baseMask: edgeMask(HexEdge.E),
|
||||
rotations: 6,
|
||||
},
|
||||
{
|
||||
id: 'straight',
|
||||
name: 'Straight',
|
||||
baseMask: edgeMask(HexEdge.E, HexEdge.W),
|
||||
rotations: 3, // Symmetric: E-W = same as W-E
|
||||
},
|
||||
{
|
||||
id: 'gentle-curve',
|
||||
name: 'Wide Curve',
|
||||
baseMask: edgeMask(HexEdge.E, HexEdge.SW),
|
||||
rotations: 6,
|
||||
},
|
||||
{
|
||||
id: 'sharp-bend',
|
||||
name: 'Sharp Bend',
|
||||
baseMask: edgeMask(HexEdge.E, HexEdge.SE),
|
||||
rotations: 6,
|
||||
},
|
||||
{
|
||||
id: 'y-junction',
|
||||
name: 'Y Split',
|
||||
baseMask: edgeMask(HexEdge.NE, HexEdge.SE, HexEdge.W),
|
||||
rotations: 6,
|
||||
},
|
||||
{
|
||||
id: 'y-junction-wide',
|
||||
name: 'Y Wide',
|
||||
baseMask: edgeMask(HexEdge.NE, HexEdge.SW, HexEdge.SE),
|
||||
rotations: 6,
|
||||
},
|
||||
{
|
||||
id: 'crossroads',
|
||||
name: 'Cross',
|
||||
baseMask: edgeMask(HexEdge.NE, HexEdge.E, HexEdge.SW, HexEdge.W),
|
||||
rotations: 3,
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* Get all unique rotations for a pattern.
|
||||
* Returns array of { mask, rotation } where rotation is the number of 60° steps.
|
||||
*/
|
||||
export function getPatternRotations(pattern: TilePattern): Array<{ mask: EdgeMask; rotation: number }> {
|
||||
const seen = new Set<EdgeMask>();
|
||||
const result: Array<{ mask: EdgeMask; rotation: number }> = [];
|
||||
|
||||
for (let r = 0; r < 6; r++) {
|
||||
const mask = rotateMask(pattern.baseMask, r);
|
||||
if (!seen.has(mask)) {
|
||||
seen.add(mask);
|
||||
result.push({ mask, rotation: r });
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotate a mask by N steps (each step = 60° clockwise).
|
||||
*/
|
||||
export function rotatePattern(mask: EdgeMask, steps: number): EdgeMask {
|
||||
return rotateMask(mask, steps);
|
||||
}
|
||||
Reference in New Issue
Block a user