From 157fbf295f7ffbe474d253fe843dca6d0a146389 Mon Sep 17 00:00:00 2001 From: jonny Date: Fri, 15 Jul 2022 13:27:49 +0200 Subject: [PATCH] grid helper --- .gitignore | 3 +- .vscode/settings.json | 7 +++ grid.js | 140 ++++++++++++++++++++++++++++++++++++++++++ index.d.ts | 3 + package-lock.json | 26 ++++++++ package.json | 5 ++ sketch.js | 79 ++++++++++++++---------- 7 files changed, 230 insertions(+), 33 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 grid.js create mode 100644 index.d.ts create mode 100644 package-lock.json create mode 100644 package.json diff --git a/.gitignore b/.gitignore index 496ee2c..91dfed8 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -.DS_Store \ No newline at end of file +.DS_Store +node_modules \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..16fd9f4 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "switchSettings.current": { + "env": { + "userindex": 0 + } + } +} \ No newline at end of file diff --git a/grid.js b/grid.js new file mode 100644 index 0000000..2d007ea --- /dev/null +++ b/grid.js @@ -0,0 +1,140 @@ +/// + +/** + * @typedef {{ + * initializer?:()=>Cell + * }} TileGridOptions + */ + + +class TileGrid { + + /** + * @type {number} + */ + dimensions + + /** + * @type {Array} + */ + grid = [] + + + /** + * + * @param {number} dimensions + * @param {TileGridOptions} [options] + */ + constructor(dimensions, options) { + this.dimensions = dimensions + this.options = options + + + const initializer = this.options.initializer + if(initializer) { + this.forEachPosition(pos => { + this.grid[this.vectorToIndex(pos)] = initializer(pos) + }) + } + } + + getMapSize() { + return createVector(this.dimensions, this.dimensions) + } + /** + * + * @param {import("p5").Vector} position + */ + getCellForPosition(position) { + return this.grid[this.vectorToIndex(position)]; + } + + /** + * + * @param {((pos: import("p5").Vector) => void)} cb + */ + forEachPosition(cb) { + for(let y = 0; y < this.getMapSize().x; y++) { + for(let x = 0; x < this.getMapSize().y; x++) { + cb(createVector(y, x)); + } + } + } + /** + * @param { import("p5").Vector} position + * @param {((pos: import("p5").Vector,directionString:"up"|"down"|"left"|"right") => void)} cb + */ + forEachDirection(position, cb) { + const directions = [ + [this.up(position), "up"], + [this.down(position), "down"], + [this.left(position), "left"], + [this.right(position), "right"]] + for(const direction of directions) { + if(direction !== null) { + cb(direction[0], direction[1]); + } + } + } + /** + * @param {import("p5").Vector} vector + */ + vectorToIndex(vector) { + return vector.y * this.getMapSize().x + vector.x + } + + /** + * + * @param {import("p5").Vector} vector + */ + validPosition(vector) { + return vector.x >= 0 && vector.y >= 0 + && vector.x <= this.getMapSize().x && vector.y <= this.getMapSize().y + && vector.x % 1 === 0 && vector.y % 1 === 0 + } + + /** + * @param {import("p5").Vector} vector + * @param {number} [increments] + */ + up(vector, increments = 1) { + const newVector = vector.add(0, -1 * increments) + if(!this.validPosition(newVector)) { + return null; + } + return newVector + } + /** + * @param {import("p5").Vector} vector + * @param {number} [increments] + */ + down(vector, increments = 1) { + const newVector = vector.add(0, 1 * increments) + if(!this.validPosition(newVector)) { + return null; + } + return newVector + } + /** + * @param {import("p5").Vector} vector + * @param {number} [increments] + */ + right(vector, increments = 1) { + const newVector = vector.add(1 * increments, 0) + if(!this.validPosition(newVector)) { + return null; + } + return newVector + } + /** + * @param {import("p5").Vector} vector + * @param {number} [increments] + */ + left(vector, increments = 1) { + const newVector = vector.add(-1 * increments, 0) + if(!this.validPosition(newVector)) { + return null; + } + return newVector + } +} \ No newline at end of file diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000..7195a00 --- /dev/null +++ b/index.d.ts @@ -0,0 +1,3 @@ +import "p5/global" +import "p5/constants" +import "p5" \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..ed40692 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,26 @@ +{ + "name": "Wave-Function-Collapse", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "devDependencies": { + "@types/p5": "^1.4.2" + } + }, + "node_modules/@types/p5": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@types/p5/-/p5-1.4.2.tgz", + "integrity": "sha512-tzJ2PdmeXlX8tidbA1/pQEhs0MHVWam0K4ux5ri0GrZXhBU3QrpTpSVzNaBDuo6KheryHdH8wR82x1nPvxo42g==", + "dev": true + } + }, + "dependencies": { + "@types/p5": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@types/p5/-/p5-1.4.2.tgz", + "integrity": "sha512-tzJ2PdmeXlX8tidbA1/pQEhs0MHVWam0K4ux5ri0GrZXhBU3QrpTpSVzNaBDuo6KheryHdH8wR82x1nPvxo42g==", + "dev": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..3ef2119 --- /dev/null +++ b/package.json @@ -0,0 +1,5 @@ +{ + "devDependencies": { + "@types/p5": "^1.4.2" + } +} diff --git a/sketch.js b/sketch.js index a3696aa..d6e1c3f 100644 --- a/sketch.js +++ b/sketch.js @@ -1,7 +1,10 @@ let tiles = []; const tileImages = []; -let grid = []; +/** + * @type {TileGrid} + */ +let grid; const DIM = 25; @@ -12,14 +15,14 @@ function preload() { // } const path = 'tiles/circuit-coding-train'; - for (let i = 0; i < 13; i++) { + for(let i = 0; i < 13; i++) { tileImages[i] = loadImage(`${path}/${i}.png`); } } function removeDuplicatedTiles(tiles) { const uniqueTilesMap = {}; - for (const tile of tiles) { + for(const tile of tiles) { const key = tile.edges.join(','); // ex: "ABB,BCB,BBA,AAA" uniqueTilesMap[key] = tile; } @@ -53,14 +56,14 @@ function setup() { tiles[11] = new Tile(tileImages[11], ['BCB', 'BCB', 'BBB', 'BBB']); tiles[12] = new Tile(tileImages[12], ['BBB', 'BCB', 'BBB', 'BCB']); - for (let i = 0; i < 12; i++) { + for(let i = 0; i < 12; i++) { tiles[i].index = i; } const initialTileCount = tiles.length; - for (let i = 0; i < initialTileCount; i++) { + for(let i = 0; i < initialTileCount; i++) { let tempTiles = []; - for (let j = 0; j < 4; j++) { + for(let j = 0; j < 4; j++) { tempTiles.push(tiles[i].rotate(j)); } tempTiles = removeDuplicatedTiles(tempTiles); @@ -69,7 +72,7 @@ function setup() { console.log(tiles.length); // Generate the adjacency rules based on edges - for (let i = 0; i < tiles.length; i++) { + for(let i = 0; i < tiles.length; i++) { const tile = tiles[i]; tile.analyze(tiles); } @@ -78,21 +81,20 @@ function setup() { } function startOver() { - // Create cell for each spot on the grid - for (let i = 0; i < DIM * DIM; i++) { - grid[i] = new Cell(tiles.length); - } + grid = new TileGrid(DIM, { + initializer: () => new Cell(tiles.length) + }) } function checkValid(arr, valid) { //console.log(arr, valid); - for (let i = arr.length - 1; i >= 0; i--) { + for(let i = arr.length - 1; i >= 0; i--) { // VALID: [BLANK, RIGHT] // ARR: [BLANK, UP, RIGHT, DOWN, LEFT] // result in removing UP, DOWN, LEFT let element = arr[i]; // console.log(element, valid.includes(element)); - if (!valid.includes(element)) { + if(!valid.includes(element)) { arr.splice(i, 1); } } @@ -109,10 +111,10 @@ function draw() { const w = width / DIM; const h = height / DIM; - for (let j = 0; j < DIM; j++) { - for (let i = 0; i < DIM; i++) { + for(let j = 0; j < DIM; j++) { + for(let i = 0; i < DIM; i++) { let cell = grid[i + j * DIM]; - if (cell.collapsed) { + if(cell.collapsed) { let index = cell.options[0]; image(tiles[index].img, i * w, j * h, w, h); } else { @@ -129,7 +131,7 @@ function draw() { // console.table(grid); // console.table(gridCopy); - if (gridCopy.length == 0) { + if(gridCopy.length == 0) { return; } gridCopy.sort((a, b) => { @@ -138,66 +140,79 @@ function draw() { let len = gridCopy[0].options.length; let stopIndex = 0; - for (let i = 1; i < gridCopy.length; i++) { - if (gridCopy[i].options.length > len) { + for(let i = 1; i < gridCopy.length; i++) { + if(gridCopy[i].options.length > len) { stopIndex = i; break; } } - if (stopIndex > 0) gridCopy.splice(stopIndex); + if(stopIndex > 0) gridCopy.splice(stopIndex); const cell = random(gridCopy); cell.collapsed = true; const pick = random(cell.options); - if (pick === undefined) { + if(pick === undefined) { startOver(); return; } cell.options = [pick]; const nextGrid = []; - for (let j = 0; j < DIM; j++) { - for (let i = 0; i < DIM; i++) { + + grid.forEachPosition(pos => { + grid.forEachDirection(pos, (directedPosition, directionStr) => { + const directedCell = grid.getCellForPosition(directedPosition) + let validOptions = []; + for(let option of directedCell.options) { + let valid = tiles[option].down; + validOptions = validOptions.concat(valid); + } + checkValid(options, validOptions); + }) + }) + + for(let j = 0; j < DIM; j++) { + for(let i = 0; i < DIM; i++) { let index = i + j * DIM; - if (grid[index].collapsed) { + if(grid[index].collapsed) { nextGrid[index] = grid[index]; } else { let options = new Array(tiles.length).fill(0).map((x, i) => i); // Look up - if (j > 0) { + if(j > 0) { let up = grid[i + (j - 1) * DIM]; let validOptions = []; - for (let option of up.options) { + for(let option of up.options) { let valid = tiles[option].down; validOptions = validOptions.concat(valid); } checkValid(options, validOptions); } // Look right - if (i < DIM - 1) { + if(i < DIM - 1) { let right = grid[i + 1 + j * DIM]; let validOptions = []; - for (let option of right.options) { + for(let option of right.options) { let valid = tiles[option].left; validOptions = validOptions.concat(valid); } checkValid(options, validOptions); } // Look down - if (j < DIM - 1) { + if(j < DIM - 1) { let down = grid[i + (j + 1) * DIM]; let validOptions = []; - for (let option of down.options) { + for(let option of down.options) { let valid = tiles[option].up; validOptions = validOptions.concat(valid); } checkValid(options, validOptions); } // Look left - if (i > 0) { + if(i > 0) { let left = grid[i - 1 + j * DIM]; let validOptions = []; - for (let option of left.options) { + for(let option of left.options) { let valid = tiles[option].right; validOptions = validOptions.concat(valid); }