UNPKG

@logic-pad/core

Version:
138 lines (137 loc) 4.19 kB
import GridConnections from './gridConnections.js'; import GridZones from './gridZones.js'; import { Color } from './primitives.js'; const colorIndex = { [Color.Dark]: 2, [Color.Light]: 1, [Color.Gray]: 0, }; function compareColor(a, b) { return colorIndex[a] - colorIndex[b]; } function compareElement(a, b) { if (!a && !b) return 0; if (!a) return -1; if (!b) return 1; return a.y - b.y || a.x - b.x || compareColor(a.color, b.color); } function compareShape(a, b) { for (let i = 0; i < Math.max(a.elements.length, b.elements.length); i++) { const cmp = compareElement(a.elements[i], b.elements[i]); if (cmp !== 0) return cmp; } return 0; } export function shapeEquals(a, b) { return compareShape(a, b) === 0; } function recenterShape(shape) { let minX = Number.POSITIVE_INFINITY; let minY = Number.POSITIVE_INFINITY; let maxX = Number.NEGATIVE_INFINITY; let maxY = Number.NEGATIVE_INFINITY; for (const element of shape.elements) { minX = Math.min(minX, element.x); minY = Math.min(minY, element.y); maxX = Math.max(maxX, element.x); maxY = Math.max(maxY, element.y); } for (const element of shape.elements) { element.x -= minX; element.y -= minY; } shape.width = maxX - minX + 1; shape.height = maxY - minY + 1; if (!Number.isFinite(shape.width)) { shape.width = 0; } if (!Number.isFinite(shape.height)) { shape.height = 0; } return shape; } export function tilesToShape(tiles) { const shape = { width: Math.max(...tiles.map(row => row.length)), height: tiles.length, elements: [], }; for (let y = 0; y < tiles.length; y++) { for (let x = 0; x < tiles[y].length; x++) { const tile = tiles[y][x]; if (tile.exists && tile.color !== Color.Gray) { shape.elements.push({ x, y, color: tile.color }); } } } return recenterShape(shape); } export function positionsToShape(positions, color) { return recenterShape({ width: 0, height: 0, elements: positions.map(p => ({ x: p.x, y: p.y, color })), }); } const shapeVariants = [ { x: 'x', y: 'y' }, { x: '-y', y: 'x' }, { x: '-x', y: '-y' }, { x: 'y', y: '-x' }, { x: '-x', y: 'y' }, { x: 'x', y: '-y' }, { x: 'y', y: 'x' }, { x: '-y', y: '-x' }, ]; function mapCoord(map, x, y, width, height) { switch (map) { case 'x': return x; case 'y': return y; case '-x': return width - 1 - x; case '-y': return height - 1 - y; } } export function getShapeVariants(shape) { const variants = shapeVariants.map(({ x, y }) => ({ width: x.endsWith('x') ? shape.width : shape.height, height: y.endsWith('y') ? shape.height : shape.width, elements: shape.elements .map(element => ({ x: mapCoord(x, element.x, element.y, shape.width, shape.height), y: mapCoord(y, element.x, element.y, shape.width, shape.height), color: element.color, })) .sort(compareElement), })); const uniqueVariants = []; for (const variant of variants) { if (!uniqueVariants.some(shape => shapeEquals(shape, variant))) { uniqueVariants.push(variant); } } return uniqueVariants; } export function normalizeShape(shape) { const variants = getShapeVariants(shape); return variants.reduce((min, variant) => (compareShape(variant, min) < 0 ? variant : min), variants[0]); } export function sanitizePatternGrid(pattern, tileMapper = t => t) { return (pattern // unlock all tiles .withTiles(tiles => tiles.map(row => row.map(t => tileMapper(t.exists ? t.withFixed(false) : t.copyWith({ exists: true, color: Color.Gray, fixed: false }))))) // strip all symbols and rules .withRules([]) .withSymbols(new Map()) .withConnections(new GridConnections()) .withZones(new GridZones())); }