From b229930aa1be3075cf260d52ee7259b53e23929c Mon Sep 17 00:00:00 2001 From: Max Goodman Date: Mon, 6 Jul 2020 13:59:15 -0700 Subject: [PATCH] Add files missed in code move --- src/geometry.js | 80 ++++++++++++++++++++++++++++++++++++++ src/geometry.test.js | 91 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 171 insertions(+) create mode 100644 src/geometry.js create mode 100644 src/geometry.test.js diff --git a/src/geometry.js b/src/geometry.js new file mode 100644 index 0000000..615cc45 --- /dev/null +++ b/src/geometry.js @@ -0,0 +1,80 @@ +import isEqual from 'lodash/isEqual' + +export function boxesFromViewContentMap(width, height, viewContentMap) { + const boxes = [] + const visited = new Set() + + function isPosContent(x, y, content) { + const checkIdx = width * y + x + return ( + !visited.has(checkIdx) && isEqual(viewContentMap.get(checkIdx), content) + ) + } + + function findLargestBox(x, y) { + const idx = width * y + x + const spaces = [idx] + const content = viewContentMap.get(idx) + + let maxY + for (maxY = y + 1; maxY < height; maxY++) { + if (!isPosContent(x, maxY, content)) { + break + } + spaces.push(width * maxY + x) + } + + let cx = x + let cy = y + scan: for (cx = x + 1; cx < width; cx++) { + for (cy = y; cy < maxY; cy++) { + if (!isPosContent(cx, cy, content)) { + break scan + } + } + for (let cy = y; cy < maxY; cy++) { + spaces.push(width * cy + cx) + } + } + const w = cx - x + const h = maxY - y + spaces.sort() + return { content, x, y, w, h, spaces } + } + + for (let y = 0; y < width; y++) { + for (let x = 0; x < height; x++) { + const idx = width * y + x + if (visited.has(idx) || viewContentMap.get(idx) === undefined) { + continue + } + + const box = findLargestBox(x, y) + boxes.push(box) + for (const boxIdx of box.spaces) { + visited.add(boxIdx) + } + } + } + + return boxes +} + +export function idxToCoords(gridCount, idx) { + const x = idx % gridCount + const y = Math.floor(idx / gridCount) + return { x, y } +} + +export function idxInBox(gridCount, start, end, idx) { + let { x: startX, y: startY } = idxToCoords(gridCount, start) + let { x: endX, y: endY } = idxToCoords(gridCount, end) + const { x, y } = idxToCoords(gridCount, idx) + const lowX = Math.min(startX, endX) + const highX = Math.max(startX, endX) + const lowY = Math.min(startY, endY) + const highY = Math.max(startY, endY) + const xInBox = x >= lowX && x <= highX + const yInBox = y >= lowY && y <= highY + return xInBox && yInBox +} diff --git a/src/geometry.test.js b/src/geometry.test.js new file mode 100644 index 0000000..360651b --- /dev/null +++ b/src/geometry.test.js @@ -0,0 +1,91 @@ +import { boxesFromViewContentMap } from './geometry' + +function example([text]) { + return text + .replace(/\s/g, '') + .split('') + .map((c) => (c === '.' ? undefined : { url: c })) +} + +const box1 = example` + ab + ab +` + +const box2 = example` + aa + bb +` + +const box3 = example` + aac + aaa + dae +` + +const box4 = example` + ... + .aa + .aa +` + +const box5 = example` + ..a + ..a + .aa +` + +describe.each([ + [ + 2, + 2, + box1, + [ + { content: { url: 'a' }, x: 0, y: 0, w: 1, h: 2, spaces: [0, 2] }, + { content: { url: 'b' }, x: 1, y: 0, w: 1, h: 2, spaces: [1, 3] }, + ], + ], + [ + 2, + 2, + box2, + [ + { content: { url: 'a' }, x: 0, y: 0, w: 2, h: 1, spaces: [0, 1] }, + { content: { url: 'b' }, x: 0, y: 1, w: 2, h: 1, spaces: [2, 3] }, + ], + ], + [ + 3, + 3, + box3, + [ + { content: { url: 'a' }, x: 0, y: 0, w: 2, h: 2, spaces: [0, 1, 3, 4] }, + { content: { url: 'c' }, x: 2, y: 0, w: 1, h: 1, spaces: [2] }, + { content: { url: 'a' }, x: 2, y: 1, w: 1, h: 1, spaces: [5] }, + { content: { url: 'd' }, x: 0, y: 2, w: 1, h: 1, spaces: [6] }, + { content: { url: 'a' }, x: 1, y: 2, w: 1, h: 1, spaces: [7] }, + { content: { url: 'e' }, x: 2, y: 2, w: 1, h: 1, spaces: [8] }, + ], + ], + [ + 3, + 3, + box4, + [{ content: { url: 'a' }, x: 1, y: 1, w: 2, h: 2, spaces: [4, 5, 7, 8] }], + ], + [ + 3, + 3, + box5, + [ + { content: { url: 'a' }, x: 2, y: 0, w: 1, h: 3, spaces: [2, 5, 8] }, + { content: { url: 'a' }, x: 1, y: 2, w: 1, h: 1, spaces: [7] }, + ], + ], +])('boxesFromViewContentMap(%i, %i, %j)', (width, height, data, expected) => { + test(`returns expected ${expected.length} boxes`, () => { + const stateURLMap = new Map(data.map((v, idx) => [idx, v])) + const result = boxesFromViewContentMap(width, height, stateURLMap) + expect(result).toStrictEqual(expected) + }) +})