@logic-pad/core
Version:
138 lines (137 loc) • 4.19 kB
JavaScript
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()));
}