From 4e726de305531561981b69feed0a81101fdc1623 Mon Sep 17 00:00:00 2001 From: inyourface34456 <62214409+inyourface34456@users.noreply.github.com> Date: Thu, 16 May 2024 12:44:17 -0400 Subject: [PATCH 1/7] Update the-one-true-todey-file-of-cellpond.js yea --- the-one-true-todey-file-of-cellpond.js | 11524 +++++++++++------------ 1 file changed, 5757 insertions(+), 5767 deletions(-) diff --git a/the-one-true-todey-file-of-cellpond.js b/the-one-true-todey-file-of-cellpond.js index 756eefd..6758a20 100644 --- a/the-one-true-todey-file-of-cellpond.js +++ b/the-one-true-todey-file-of-cellpond.js @@ -1,84 +1,84 @@ -/* +/*; -ribbit +ribbit; -░██████╗██╗░░░██╗██████╗░██████╗░███████╗░█████╗░██╗░░░░░██╗░██████╗████████╗ -██╔════╝██║░░░██║██╔══██╗██╔══██╗██╔════╝██╔══██╗██║░░░░░██║██╔════╝╚══██╔══╝ -╚█████╗░██║░░░██║██████╔╝██████╔╝█████╗░░███████║██║░░░░░██║╚█████╗░░░░██║░░░ -░╚═══██╗██║░░░██║██╔══██╗██╔══██╗██╔══╝░░██╔══██║██║░░░░░██║░╚═══██╗░░░██║░░░ -██████╔╝╚██████╔╝██║░░██║██║░░██║███████╗██║░░██║███████╗██║██████╔╝░░░██║░░░ -╚═════╝░░╚═════╝░╚═╝░░╚═╝╚═╝░░╚═╝╚══════╝╚═╝░░╚═╝╚══════╝╚═╝╚═════╝░░░░╚═╝░░░ +░██████╗██╗░░░██╗██████╗░██████╗░███████╗░█████╗░██╗░░░░░██╗░██████╗████████╗; +██╔════╝██║░░░██║██╔══██╗██╔══██╗██╔════╝██╔══██╗██║░░░░░██║██╔════╝╚══██╔══╝; +╚█████╗░██║░░░██║██████╔╝██████╔╝█████╗░░███████║██║░░░░░██║╚█████╗░░░░██║░░░; +░╚═══██╗██║░░░██║██╔══██╗██╔══██╗██╔══╝░░██╔══██║██║░░░░░██║░╚═══██╗░░░██║░░░; +██████╔╝╚██████╔╝██║░░██║██║░░██║███████╗██║░░██║███████╗██║██████╔╝░░░██║░░░; +╚═════╝░░╚═════╝░╚═╝░░╚═╝╚═╝░░╚═╝╚══════╝╚═╝░░╚═╝╚══════╝╚═╝╚═════╝░░░░╚═╝░░░; -░█████╗░██╗░░░██╗████████╗░█████╗░███╗░░░███╗░█████╗░████████╗██╗░██████╗███╗░░░███╗ -██╔══██╗██║░░░██║╚══██╔══╝██╔══██╗████╗░████║██╔══██╗╚══██╔══╝██║██╔════╝████╗░████║ -███████║██║░░░██║░░░██║░░░██║░░██║██╔████╔██║███████║░░░██║░░░██║╚█████╗░██╔████╔██║ -██╔══██║██║░░░██║░░░██║░░░██║░░██║██║╚██╔╝██║██╔══██║░░░██║░░░██║░╚═══██╗██║╚██╔╝██║ -██║░░██║╚██████╔╝░░░██║░░░╚█████╔╝██║░╚═╝░██║██║░░██║░░░██║░░░██║██████╔╝██║░╚═╝░██║ -╚═╝░░╚═╝░╚═════╝░░░░╚═╝░░░░╚════╝░╚═╝░░░░░╚═╝╚═╝░░╚═╝░░░╚═╝░░░╚═╝╚═════╝░╚═╝░░░░░╚═╝ +░█████╗░██╗░░░██╗████████╗░█████╗░███╗░░░███╗░█████╗░████████╗██╗░██████╗███╗░░░███╗; +██╔══██╗██║░░░██║╚══██╔══╝██╔══██╗████╗░████║██╔══██╗╚══██╔══╝██║██╔════╝████╗░████║; +███████║██║░░░██║░░░██║░░░██║░░██║██╔████╔██║███████║░░░██║░░░██║╚█████╗░██╔████╔██║; +██╔══██║██║░░░██║░░░██║░░░██║░░██║██║╚██╔╝██║██╔══██║░░░██║░░░██║░╚═══██╗██║╚██╔╝██║; +██║░░██║╚██████╔╝░░░██║░░░╚█████╔╝██║░╚═╝░██║██║░░██║░░░██║░░░██║██████╔╝██║░╚═╝░██║; +╚═╝░░╚═╝░╚═════╝░░░░╚═╝░░░░╚════╝░╚═╝░░░░░╚═╝╚═╝░░╚═╝░░░╚═╝░░░╚═╝╚═════╝░╚═╝░░░░░╚═╝; -Welcome traveller! -Welcome to the SOURCE of the CellPond. +Welcome traveller!; +Welcome to the SOURCE of the CellPond.; -If you venture further, may tode be with you. -What you are about to discover... - ... is a single javascript file ... - ... of gargantuan size ... - ... over 8000 lines ... - ... globally scoped ... +If you venture further, may tode be with you.; +What you are about to discover...; + ... is a single javascript file ...; + ... of gargantuan size ...; + ... over 8000 lines ...; + ... globally scoped ...; - >>> There is no room for fear here! <<< + >>> There is no room for fear here! <<<; -Be brave. - Trust no comments. - Trust no names. +Be brave.; + Trust no comments.; + Trust no names.; -A simple seed ... grown into a mountain ... - CellPond is a performance ... - and by reading this you JOIN THE RITUAL ... +A simple seed ... grown into a mountain ...; + CellPond is a performance ...; + and by reading this you JOIN THE RITUAL ...; -============================================================= +=============================================================; - ... many months later + ... many months later; - the source of CellPond calls me back + the source of CellPond calls me back; - and it calls you too! + and it calls you too!; -============================================================= +=============================================================; -//------// -// FAQs // -//------// -Q: -A: it's a secret +//------//; +// FAQs //; +//------//; +Q:; +A: it's a secret; */ -var middleClicked = false +var middleClicked = false; document.addEventListener('mousedown', function(event) { if (event.button === 1) { - middleClicked = true + middleClicked = true; } -}) - -const urlParams = new URLSearchParams(window.location.search) -const NO_SECRET_MODE = urlParams.has("nosecret") -const NO_FOOLS_MODE = urlParams.has("nofools") -const UNLOCK_MODE = urlParams.has("unlock") -const SCALE = urlParams.get("scale") ?? 1 -const DPR = urlParams.get("dpr") ?? devicePixelRatio -print('DPR:', DPR) +}); + +const urlParams = new URLSearchParams(window.location.search); +const NO_SECRET_MODE = urlParams.has("nosecret"); +const NO_FOOLS_MODE = urlParams.has("nofools"); +const UNLOCK_MODE = urlParams.has("unlock"); +const SCALE = urlParams.get("scale") ?? 1; +const DPR = urlParams.get("dpr") ?? devicePixelRatio; +print('DPR:', DPR); if (NO_SECRET_MODE) { - localStorage.setItem("secretHasAlreadyBeenRevealed", "true") + localStorage.setItem("secretHasAlreadyBeenRevealed", "true"); } -const secretHasAlreadyBeenRevealed = localStorage.getItem("secretHasAlreadyBeenRevealed") +const secretHasAlreadyBeenRevealed = localStorage.getItem("secretHasAlreadyBeenRevealed"); -//========// -// COLOUR // -//========// +//========//; +// COLOUR //; +//========//; const TODEPOND_COLOURS = [ Colour.Green.splash, Colour.Red.splash, @@ -94,32 +94,32 @@ const TODEPOND_COLOURS = [ Colour.Grey.splash, Colour.Silver.splash, Colour.White.splash, -] +]; -const TODEPOND_RAINBOW_COLOURS = TODEPOND_COLOURS.slice(0, -4) +const TODEPOND_RAINBOW_COLOURS = TODEPOND_COLOURS.slice(0, -4); const getRGB = (splash) => { - const gb = splash % 100 - let b = gb % 10 - let g = gb - b - let r = splash - gb - return [r, g, b] -} + const gb = splash % 100; + let b = gb % 10; + let g = gb - b; + let r = splash - gb; + return [r, g, b]; +}; const clamp = (number, min, max) => { - if (number < min) return min - if (number > max) return max - return number -} + if (number < min) return min; + if (number > max) return max; + return number; +}; const wrap = (number, min, max) => { - const length = (max - min)+1 - if (number < min) return wrap(number + length, min, max) - if (number > max) return wrap(number - length, min, max) - return number -} + const length = (max - min)+1; + if (number < min) return wrap(number + length, min, max); + if (number > max) return wrap(number - length, min, max); + return number; +}; -let brushColourCycleIndex = 0 +let brushColourCycleIndex = 0; const brushColourCycle = [ 999, @@ -129,119 +129,119 @@ const brushColourCycle = [ Colour.Yellow.splash, Colour.Black.splash, - + Colour.Rose.splash, Colour.Cyan.splash, Colour.Orange.splash, Colour.Purple.splash, Colour.Pink.splash, - + Colour.Grey.splash, Colour.Silver.splash, -] +]; -//======// -// CELL // -//======// +//======//; +// CELL //; +//======//; const makeCell = ({x=0, y=0, width=1, height=1, colour=112} = {}) => { - - const left = x - const right = x+width - const top = y - const bottom = y+height - - const size = width * height - const centerX = left + width/2 - const centerY = top + height/2 + const left = x; + const right = x+width; + const top = y; + const bottom = y+height; - const sections = [] - const lastDraw = undefined - //const lastDrawCount = 1 - const lastDrawRepeat = 0 + const size = width * height; - const cell = {x, y, width, height, colour, left, right, top, bottom, centerX, centerY, sections, size, lastDraw, lastDrawRepeat} - return cell + const centerX = left + width/2; + const centerY = top + height/2; -} + const sections = []; + const lastDraw = undefined; + //const lastDrawCount = 1; + const lastDrawRepeat = 0; + + const cell = {x, y, width, height, colour, left, right, top, bottom, centerX, centerY, sections, size, lastDraw, lastDrawRepeat}; + return cell; + +}; -let edgeMode = 0 +let edgeMode = 0; const pickCell = (x, y) => { /*if (edgeMode === 0) { - if (x >= 1) return undefined - if (y >= 1) return undefined - if (x < 0) return undefined - if (y < 0) return undefined + if (x >= 1) return undefined; + if (y >= 1) return undefined; + if (x < 0) return undefined; + if (y < 0) return undefined; } else if (edgeMode === 1) { - while (x >= 1) x -= 1 - while (y >= 1) y -= 1 - while (x < 0) x += 1 - while (y < 0) y += 1 + while (x >= 1) x -= 1; + while (y >= 1) y -= 1; + while (x < 0) x += 1; + while (y < 0) y += 1; }*/ - if (x >= 1) return undefined - if (y >= 1) return undefined - if (x < 0) return undefined - if (y < 0) return undefined + if (x >= 1) return undefined; + if (y >= 1) return undefined; + if (x < 0) return undefined; + if (y < 0) return undefined; - const gridX = Math.floor(x * GRID_SIZE) - const gridY = Math.floor(y * GRID_SIZE) - const sectionId = gridX*GRID_SIZE + gridY - const section = state.grid[sectionId] + const gridX = Math.floor(x * GRID_SIZE); + const gridY = Math.floor(y * GRID_SIZE); + const sectionId = gridX*GRID_SIZE + gridY; + const section = state.grid[sectionId]; - if (section === undefined) return undefined + if (section === undefined) return undefined; - let i = 1 - const size = section.size - const values = section.values() + let i = 1; + const size = section.size; + const values = section.values(); for (const cell of values) { - if (i === size) return cell - i++ - if (cell.left > x) continue - if (cell.top > y) continue - if (cell.right <= x) continue - if (cell.bottom <= y) continue - return cell + if (i === size) return cell; + i++; + if (cell.left > x) continue; + if (cell.top > y) continue; + if (cell.right <= x) continue; + if (cell.bottom <= y) continue; + return cell; } - return undefined -} + return undefined; +}; const pickNeighbour = (cell, dx, dy) => { - const centerX = cell.left + cell.width/2 - const centerY = cell.top + cell.height/2 + const centerX = cell.left + cell.width/2; + const centerY = cell.top + cell.height/2; - const x = centerX + dx*cell.width - const y = centerY + dy*cell.height + const x = centerX + dx*cell.width; + const y = centerY + dy*cell.height; - const neighbour = pickCell(x, y) - return neighbour -} + const neighbour = pickCell(x, y); + return neighbour; +}; const pickRandomCell = () => { - const x = Random.Uint32 / 4294967295 - const y = Random.Uint32 / 4294967295 - const cell = pickCell(x, y) - return cell -} + const x = Random.Uint32 / 4294967295; + const y = Random.Uint32 / 4294967295; + const cell = pickCell(x, y); + return cell; +}; const pickRandomVisibleCell = () => { - - if (!state.view.visible) return undefined - if (state.view.fullyVisible) return pickRandomCell() - - const x = state.region.left + (Random.Uint32 / 4294967295) * state.region.width - const y = state.region.top + (Random.Uint32 / 4294967295) * state.region.height - const cell = pickCell(x, y) - return cell -} -//=======// -// STATE // -//=======// + if (!state.view.visible) return undefined; + if (state.view.fullyVisible) return pickRandomCell(); + + const x = state.region.left + (Random.Uint32 / 4294967295) * state.region.width; + const y = state.region.top + (Random.Uint32 / 4294967295) * state.region.height; + const cell = pickCell(x, y); + return cell; +}; + +//=======//; +// STATE //; +//=======//; const state = { grid: [], @@ -283,7 +283,7 @@ const state = { width: undefined, iheight: undefined, iwidth: undefined, - + left: undefined, right: undefined, top: undefined, @@ -339,7 +339,7 @@ const state = { }, cursor: { - previous: { + previous: { x: undefined, y: undefined, }, @@ -348,612 +348,611 @@ const state = { dragon: { behaves: [], } -} +}; -let WORLD_SIZE = undefined -let WORLD_CELL_COUNT = undefined -let WORLD_DIMENSION = undefined -let WORLD_CELL_SIZE = undefined +let WORLD_SIZE; +let WORLD_CELL_COUNT; +let WORLD_DIMENSION; +let WORLD_CELL_SIZE; const setWorldSize = (size) => { - WORLD_SIZE = size - WORLD_CELL_COUNT = 2 ** (WORLD_SIZE*2) - WORLD_DIMENSION = 2 ** WORLD_SIZE - WORLD_CELL_SIZE = 1 / WORLD_DIMENSION -} -setWorldSize(6) + WORLD_SIZE = size; + WORLD_CELL_COUNT = 2 ** (WORLD_SIZE*2); + WORLD_DIMENSION = 2 ** WORLD_SIZE; + WORLD_CELL_SIZE = 1 / WORLD_DIMENSION; +}; +setWorldSize(6); const addCell = (cell) => { - cacheCell(cell) - state.cellCount++ -} + cacheCell(cell); + state.cellCount++; +}; const deleteCell = (cell) => { - uncacheCell(cell) - cell.isDeleted = true - state.cellCount-- -} + uncacheCell(cell); + cell.isDeleted = true; + state.cellCount--; +}; const getCells = () => { - const cells = new Set() + const cells = new Set(); for (const section of state.grid) { for (const cell of section.values()) { - cells.add(cell) + cells.add(cell); } } - return cells -} + return cells; +}; -//======// -// GRID // -//======// -// The grid is basically the screen cut up into smaller sections -// It helps to speed up cell lookup because it gives us a smaller area to search through -// Note: Cells can be in multiple sections if they are big enough :) -// NOTE: GRID_SIZE MUST BE BIG ENOUGH SO THAT SECTIONS ARE SMALLER OR EQUAL TO WORLD CELLS -const GRID_SIZE = 128 +//======//; +// GRID //; +//======//; +// The grid is basically the screen cut up into smaller sections; +// It helps to speed up cell lookup because it gives us a smaller area to search through; +// Note: Cells can be in multiple sections if they are big enough :); +// NOTE: GRID_SIZE MUST BE BIG ENOUGH SO THAT SECTIONS ARE SMALLER OR EQUAL TO WORLD CELLS; +const GRID_SIZE = 128; for (let x = 0; x < GRID_SIZE; x++) { for (let y = 0; y < GRID_SIZE; y++) { - const section = new Set() - state.grid.push(section) - section.left = x / GRID_SIZE - section.top = y / GRID_SIZE - section.right = section.left + 1/GRID_SIZE - section.bottom = section.top + 1/GRID_SIZE - section.isSection = true + const section = new Set(); + state.grid.push(section); + section.left = x / GRID_SIZE; + section.top = y / GRID_SIZE; + section.right = section.left + 1/GRID_SIZE; + section.bottom = section.top + 1/GRID_SIZE; + section.isSection = true; } } const cacheCell = (cell) => { - const left = Math.floor(cell.left * GRID_SIZE) - const top = Math.floor(cell.top * GRID_SIZE) - const right = Math.ceil(cell.right * GRID_SIZE) - const bottom = Math.ceil(cell.bottom * GRID_SIZE) + const left = Math.floor(cell.left * GRID_SIZE); + const top = Math.floor(cell.top * GRID_SIZE); + const right = Math.ceil(cell.right * GRID_SIZE); + const bottom = Math.ceil(cell.bottom * GRID_SIZE); for (let x = left; x < right; x++) { for (let y = top; y < bottom; y++) { - const id = x*GRID_SIZE + y + const id = x*GRID_SIZE + y; if (state.grid[id] === undefined) { - continue + continue; } - state.grid[id].add(cell) - cell.sections.push(state.grid[id]) + state.grid[id].add(cell); + cell.sections.push(state.grid[id]); } } -} +}; const uncacheCell = (cell) => { for (const section of cell.sections) { - section.delete(cell) + section.delete(cell); } -} +}; -//=======// -// SETUP // -//=======// -// Setup World -const world = makeCell({colour: WORLD_SIZE * 111}) -addCell(world) +//=======//; +// SETUP //; +//=======//; +// Setup World; +const world = makeCell({colour: WORLD_SIZE * 111}); +addCell(world); on.load(() => { - - // Setup Show - const show = Show.start({paused: false, scale: DPR}) - const {context, canvas} = show - canvas.style["position"] = "absolute" - - //===============// - // IMAGE + SIZES // - //===============// + // Setup Show; + const show = Show.start({paused: false, scale: DPR}); + const {context, canvas} = show; + canvas.style["position"] = "absolute"; + + //===============//; + // IMAGE + SIZES //; + //===============//; const updateImageSize = () => { - state.image.baseSize = Math.min(canvas.width, canvas.height) - state.image.size = state.image.baseSize * state.camera.scale - - state.image.left = state.camera.x * state.camera.scale - state.image.top = state.camera.y * state.camera.scale - state.image.right = state.image.left + state.image.size - state.image.bottom = state.image.top + state.image.size - - state.view.left = clamp(state.image.left, 0, canvas.width) - state.view.top = clamp(state.image.top, 0, canvas.height) - state.view.right = clamp(state.image.right, 0, canvas.width) - state.view.bottom = clamp(state.image.bottom, 0, canvas.height) - - state.view.width = state.view.right - state.view.left - state.view.height = state.view.bottom - state.view.top - - state.view.visible = state.view.width > 0 && state.view.height > 0 - state.view.fullyVisible = state.view.left === state.image.left && state.view.right === state.image.right && state.view.top === state.image.top && state.view.bottom === state.image.bottom - - state.view.iwidth = Math.ceil(state.view.width) - state.view.iheight = Math.ceil(state.view.height) - - state.region.left = (state.view.left - state.image.left) / state.image.size - state.region.right = 1.0 + (state.view.right - state.image.right) / state.image.size - state.region.top = (state.view.top - state.image.top) / state.image.size - state.region.bottom = 1.0 + (state.view.bottom - state.image.bottom) / state.image.size - - state.region.width = state.region.right - state.region.left - state.region.height = state.region.bottom - state.region.top - - //state.image.data = context.getImageData(0, 0, state.image.size.iwidth, state.image.size.iheight) - - drawQueueNeedsReset = true - } + state.image.baseSize = Math.min(canvas.width, canvas.height); + state.image.size = state.image.baseSize * state.camera.scale; + + state.image.left = state.camera.x * state.camera.scale; + state.image.top = state.camera.y * state.camera.scale; + state.image.right = state.image.left + state.image.size; + state.image.bottom = state.image.top + state.image.size; + + state.view.left = clamp(state.image.left, 0, canvas.width); + state.view.top = clamp(state.image.top, 0, canvas.height); + state.view.right = clamp(state.image.right, 0, canvas.width); + state.view.bottom = clamp(state.image.bottom, 0, canvas.height); + + state.view.width = state.view.right - state.view.left; + state.view.height = state.view.bottom - state.view.top; + + state.view.visible = state.view.width > 0 && state.view.height > 0; + state.view.fullyVisible = state.view.left === state.image.left && state.view.right === state.image.right && state.view.top === state.image.top && state.view.bottom === state.image.bottom; + + state.view.iwidth = Math.ceil(state.view.width); + state.view.iheight = Math.ceil(state.view.height); + + state.region.left = (state.view.left - state.image.left) / state.image.size; + state.region.right = 1.0 + (state.view.right - state.image.right) / state.image.size; + state.region.top = (state.view.top - state.image.top) / state.image.size; + state.region.bottom = 1.0 + (state.view.bottom - state.image.bottom) / state.image.size; + + state.region.width = state.region.right - state.region.left; + state.region.height = state.region.bottom - state.region.top; + + //state.image.data = context.getImageData(0, 0, state.image.size.iwidth, state.image.size.iheight); + + drawQueueNeedsReset = true; + }; const updateImageData = () => { - state.image.data = context.getImageData(0, 0, canvas.width, canvas.height) - } + state.image.data = context.getImageData(0, 0, canvas.width, canvas.height); + }; - // Setup ImageData - context.fillStyle = Colour.Void - context.fillRect(0, 0, canvas.width, canvas.height) - updateImageSize() - updateImageData() + // Setup ImageData; + context.fillStyle = Colour.Void; + context.fillRect(0, 0, canvas.width, canvas.height); + updateImageSize(); + updateImageData(); - state.camera.x += (canvas.width - state.image.size) / 2 - state.camera.y += (canvas.height - state.image.size) / 2 + state.camera.x += (canvas.width - state.image.size) / 2; + state.camera.y += (canvas.height - state.image.size) / 2; - //======// - // DRAW // - //======// + //======//; + // DRAW //; + //======//; show.resize = () => { - context.fillStyle = Colour.Void - context.fillRect(0, 0, canvas.width, canvas.height) - updateImageSize() - updateImageData() - } + context.fillStyle = Colour.Void; + context.fillRect(0, 0, canvas.width, canvas.height); + updateImageSize(); + updateImageData(); + }; const stampScale = (scale) => { - //context.fillStyle = Colour.Void - //context.fillRect(0, 0, canvas.width, canvas.height) - //context.drawImage(canvas, 0, 0, canvas.width * scale, canvas.height * scale) + //context.fillStyle = Colour.Void; + //context.fillRect(0, 0, canvas.width, canvas.height); + //context.drawImage(canvas, 0, 0, canvas.width * scale, canvas.height * scale); /*if (scale < 1.0) { - const growthX = canvas.width - canvas.width * scale - const growthY = canvas.height - canvas.height * scale - //context.fillRect(canvas.width - growthX, 0, growthX, canvas.height) - //context.fillRect(0, canvas.height - growthY, canvas.width, growthY) + const growthX = canvas.width - canvas.width * scale; + const growthY = canvas.height - canvas.height * scale; + //context.fillRect(canvas.width - growthX, 0, growthX, canvas.height); + //context.fillRect(0, canvas.height - growthY, canvas.width, growthY); }*/ - updateImageSize() - } + updateImageSize(); + }; const drawCells = () => { - const cells = getCells() + const cells = getCells(); for (const cell of cells.values()) { - setCellColour(cell, cell.colour) + setCellColour(cell, cell.colour); } - } + }; const drawCell = (cell, override) => { - return setCellColour(cell, cell.colour, override) - } + return setCellColour(cell, cell.colour, override); + }; const isSectionVisible = (section) => { - if (section.right <= state.region.left) return false - if (section.left >= state.region.right) return false - if (section.bottom <= state.region.top) return false - if (section.top >= state.region.bottom) return false - return true - } + if (section.right <= state.region.left) return false; + if (section.left >= state.region.right) return false; + if (section.bottom <= state.region.top) return false; + if (section.top >= state.region.bottom) return false; + return true; + }; const isCellVisible = (cell) => { - if (cell.right <= state.region.left) return false - if (cell.left >= state.region.right) return false - if (cell.bottom <= state.region.top) return false - if (cell.top >= state.region.bottom) return false - return true - } + if (cell.right <= state.region.left) return false; + if (cell.left >= state.region.right) return false; + if (cell.bottom <= state.region.top) return false; + if (cell.top >= state.region.bottom) return false; + return true; + }; const queueCellDraw = (cell, colour) => { - cell.colour = colour - if (!isCellVisible(cell)) return 0 - drawQueuePriority.add(cell) - drawQueue.delete(cell) - return 0.01 - } + cell.colour = colour; + if (!isCellVisible(cell)) return 0; + drawQueuePriority.add(cell); + drawQueue.delete(cell); + return 0.01; + }; const setCellColour = (cell, colour, override = false) => { - if (cell.isDeleted) return 0 - cell.colour = colour - if (!isCellVisible(cell)) return 0 - - /* + if (cell.isDeleted) return 0; + cell.colour = colour; + if (!isCellVisible(cell)) return 0; + + /*; if (!override && cell.lastDraw === state.time) { - cell.lastDrawRepeat += state.speed.redrawRepeatPenalty - return state.speed.redrawRepeatScore * cell.lastDrawRepeat - } + cell.lastDrawRepeat += state.speed.redrawRepeatPenalty; + return state.speed.redrawRepeatScore * cell.lastDrawRepeat; + }; */ - const size = state.image.size - const imageWidth = canvas.width + const size = state.image.size; + const imageWidth = canvas.width; - const panX = state.camera.x * state.camera.scale - const panY = state.camera.y * state.camera.scale + const panX = state.camera.x * state.camera.scale; + const panY = state.camera.y * state.camera.scale; - // Position - let left = Math.round(size * cell.left + panX) - if (left > canvas.width) return 0 - if (left < 0) left = 0 + // Position ; + let left = Math.round(size * cell.left + panX); + if (left > canvas.width) return 0; + if (left < 0) left = 0; - let top = Math.round(size * cell.top + panY) - if (top > canvas.height) return 0 - if (top < 0) top = 0 + let top = Math.round(size * cell.top + panY); + if (top > canvas.height) return 0; + if (top < 0) top = 0; - let right = Math.round(size * cell.right + panX) - if (right < 0) return 0 - if (right > canvas.width) right = canvas.width + let right = Math.round(size * cell.right + panX); + if (right < 0) return 0; + if (right > canvas.width) right = canvas.width; - let bottom = Math.round(size * cell.bottom + panY) - if (bottom < 0) return 0 - if (bottom > canvas.height) bottom = canvas.height + let bottom = Math.round(size * cell.bottom + panY); + if (bottom < 0) return 0; + if (bottom > canvas.height) bottom = canvas.height; - // Colour - const splash = Colour.splash(cell.colour) - let red = splash[0] - let green = splash[1] - let blue = splash[2] + // Colour; + const splash = Colour.splash(cell.colour); + let red = splash[0]; + let green = splash[1]; + let blue = splash[2]; /*if (!NO_FOOLS_MODE) { - const average = Math.round((red + green + blue) / 3) - red = average - green = average - blue = average + const average = Math.round((red + green + blue) / 3); + red = average; + green = average; + blue = average; }*/ - // Draw - const iy = imageWidth * 4 + // Draw; + const iy = imageWidth * 4; - const width = right-left - const ix = 4 - const sx = width * ix + const width = right-left; + const ix = 4; + const sx = width * ix; - //let pixelCount = 0 - let id = (top*imageWidth + left) * 4 - const data = state.image.data.data + //let pixelCount = 0; + let id = (top*imageWidth + left) * 4; + const data = state.image.data.data; - let borderRed = Colour.Void.red - let borderGreen = Colour.Void.green - let borderBlue = Colour.Void.blue + let borderRed = Colour.Void.red; + let borderGreen = Colour.Void.green; + let borderBlue = Colour.Void.blue; if (!gridMode || width <= 3 || bottom-top <= 3) { - /* - borderRed = Colour.Void.red - borderGreen = Colour.Void.green - borderBlue = Colour.Void.blue + /*; + borderRed = Colour.Void.red; + borderGreen = Colour.Void.green; + borderBlue = Colour.Void.blue; */ - - borderRed = red - borderGreen = green - borderBlue = blue + + borderRed = red; + borderGreen = green; + borderBlue = blue; for (let y = top; y < bottom; y++) { - for (let x = left; x < right; x++) { - data[id] = red - data[id+1] = green - data[id+2] = blue - id += 4 - //pixelCount++ + for (let x = left; x < right; x++) { + data[id] = red; + data[id+1] = green; + data[id+2] = blue; + id += 4; + //pixelCount++; } - id += iy - id -= sx + id += iy; + id -= sx; } - return 1 + return 1; } - const left1 = left + 1 - const right_1 = right - 1 - const top1 = top + 1 - const bottom_1 = bottom - 1 + const left1 = left + 1; + const right_1 = right - 1; + const top1 = top + 1; + const bottom_1 = bottom - 1; - // DRAW TOP ROW - data[id] = borderRed - data[id+1] = borderGreen - data[id+2] = borderBlue - id += 4 + // DRAW TOP ROW; + data[id] = borderRed; + data[id+1] = borderGreen; + data[id+2] = borderBlue; + id += 4; for (let x = left1; x < right_1; x++) { - data[id] = borderRed - data[id+1] = borderGreen - data[id+2] = borderBlue - id += 4 - //pixelCount++ + data[id] = borderRed; + data[id+1] = borderGreen; + data[id+2] = borderBlue; + id += 4; + //pixelCount++; } - data[id] = borderRed - data[id+1] = borderGreen - data[id+2] = borderBlue - id += 4 - id -= sx - id += iy + data[id] = borderRed; + data[id+1] = borderGreen; + data[id+2] = borderBlue; + id += 4; + id -= sx; + id += iy; - // DRAW MIDDLE ROWS + // DRAW MIDDLE ROWS; for (let y = top1; y < bottom_1; y++) { - - data[id] = borderRed - data[id+1] = borderGreen - data[id+2] = borderBlue - id += 4 - - for (let x = left1; x < right_1; x++) { - data[id] = red - data[id+1] = green - data[id+2] = blue - id += 4 - //pixelCount++ + + data[id] = borderRed; + data[id+1] = borderGreen; + data[id+2] = borderBlue; + id += 4; + + for (let x = left1; x < right_1; x++) { + data[id] = red; + data[id+1] = green; + data[id+2] = blue; + id += 4; + //pixelCount++; } - data[id] = borderRed - data[id+1] = borderGreen - data[id+2] = borderBlue - id += 4 + data[id] = borderRed; + data[id+1] = borderGreen; + data[id+2] = borderBlue; + id += 4; - id -= sx - id += iy + id -= sx; + id += iy; } - // DRAW BOTTOM ROW - data[id] = borderRed - data[id+1] = borderGreen - data[id+2] = borderBlue - id += 4 + // DRAW BOTTOM ROW; + data[id] = borderRed; + data[id+1] = borderGreen; + data[id+2] = borderBlue; + id += 4; - for (let x = left1; x < right_1; x++) { - data[id] = borderRed - data[id+1] = borderGreen - data[id+2] = borderBlue - id += 4 - //pixelCount++ + for (let x = left1; x < right_1; x++) { + data[id] = borderRed; + data[id+1] = borderGreen; + data[id+2] = borderBlue; + id += 4; + //pixelCount++; } - data[id] = borderRed - data[id+1] = borderGreen - data[id+2] = borderBlue + data[id] = borderRed; + data[id+1] = borderGreen; + data[id+2] = borderBlue; - /* - cell.lastDraw = state.time - //cell.lastDrawCount = pixelCount - cell.lastDrawRepeat = 1 + /*; + cell.lastDraw = state.time; + //cell.lastDrawCount = pixelCount; + cell.lastDrawRepeat = 1; */ - return 1 + return 1; - } + }; - //========// - // CURSOR // - //========// + //========//; + // CURSOR //; + //========//; const updateCursor = () => { - updateBrush() - updatePan() + updateBrush(); + updatePan(); - const [x, y] = Mouse.position - state.cursor.previous.x = x - state.cursor.previous.y = y - - } + const [x, y] = Mouse.position; + state.cursor.previous.x = x; + state.cursor.previous.y = y; + + }; - let pencilled = false + let pencilled = false; const updateBrush = () => { - if (!state.worldBuilt) return + if (!state.worldBuilt) return; if (!Mouse.Middle) { - pencilled = false + pencilled = false; } - if (state.colourTode.hand.state !== HAND.BRUSHING && state.colourTode.hand.state !== HAND.PENCILLING) return + if (state.colourTode.hand.state !== HAND.BRUSHING && state.colourTode.hand.state !== HAND.PENCILLING) return; if (Mouse.Middle && !pencilled) { - const [x, y] = Mouse.position - brush(...getCursorView(x, y), {single: true}) - pencilled = true + const [x, y] = Mouse.position; + brush(...getCursorView(x, y), {single: true}); + pencilled = true; } - if (!Mouse.Left) return - let [x, y] = getCursorView(...Mouse.position) + if (!Mouse.Left) return; + let [x, y] = getCursorView(...Mouse.position); if (x === undefined || y === undefined) { - return + return; } - - let [px, py] = getCursorView(state.cursor.previous.x, state.cursor.previous.y) - - const size = state.brush.size * WORLD_CELL_SIZE - const dx = x - px - const dy = y - py + let [px, py] = getCursorView(state.cursor.previous.x, state.cursor.previous.y); + + const size = state.brush.size * WORLD_CELL_SIZE; - const sx = Math.sign(dx) - const sy = Math.sign(dy) + const dx = x - px; + const dy = y - py; - const ax = Math.abs(dx) - const ay = Math.abs(dy) + const sx = Math.sign(dx); + const sy = Math.sign(dy); - const biggest = Math.max(ax, ay) + const ax = Math.abs(dx); + const ay = Math.abs(dy); - let ix = 0 - let iy = 0 + const biggest = Math.max(ax, ay); + + let ix = 0; + let iy = 0; if (ax === biggest) { - iy = (WORLD_CELL_SIZE * sy) * (ay / ax) - ix = WORLD_CELL_SIZE * sx + iy = (WORLD_CELL_SIZE * sy) * (ay / ax); + ix = WORLD_CELL_SIZE * sx; } else { - ix = (WORLD_CELL_SIZE * sx) * (ax / ay) - iy = WORLD_CELL_SIZE * sy + ix = (WORLD_CELL_SIZE * sx) * (ax / ay); + iy = WORLD_CELL_SIZE * sy; } - const points = new Set() + const points = new Set(); - const length = biggest / WORLD_CELL_SIZE + const length = biggest / WORLD_CELL_SIZE; if (dx === 0 && dy === 0) { for (let dx = -size/2; dx <= size/2; dx += WORLD_CELL_SIZE) { for (let dy = -size/2; dy <= size/2; dy += WORLD_CELL_SIZE) { - points.add([x + dx, y + dy]) + points.add([x + dx, y + dy]); } } } else for (let i = 0; i <= length; i++) { - - const X = px + ix * i - const Y = py + iy * i + + const X = px + ix * i; + const Y = py + iy * i; for (let dx = -size/2; dx <= size/2; dx += WORLD_CELL_SIZE) { for (let dy = -size/2; dy <= size/2; dy += WORLD_CELL_SIZE) { - points.add([X + dx, Y + dy]) + points.add([X + dx, Y + dy]); } } } for (const point of points.values()) { - brush(point[0], point[1]) + brush(point[0], point[1]); } - - } + + }; const getCursorView = (x, y) => { - x -= state.camera.x * state.camera.scale / DPR - y -= state.camera.y * state.camera.scale / DPR + x -= state.camera.x * state.camera.scale / DPR; + y -= state.camera.y * state.camera.scale / DPR; - x /= state.image.size - y /= state.image.size + x /= state.image.size; + y /= state.image.size; - x *= DPR - y *= DPR + x *= DPR; + y *= DPR; - return [x, y] - } + return [x, y]; + }; const brush = (x, y, {single = false} = {}) => { - let cell = pickCell(x, y) - if (cell === undefined) return + let cell = pickCell(x, y); + if (cell === undefined) return; if (!single && (cell.width !== WORLD_CELL_SIZE || cell.height != WORLD_CELL_SIZE)) { - const worldCells = getWorldCellsSet(x, y) + const worldCells = getWorldCellsSet(x, y); if (worldCells !== undefined) { - const merged = mergeCells([...worldCells]) - cell = merged + const merged = mergeCells([...worldCells]); + cell = merged; } } if (typeof state.brush.colour === "number") { - cell.colour = state.brush.colour - drawCell(cell) - return + cell.colour = state.brush.colour; + drawCell(cell); + return; } - let children = [] + let children = []; if (state.brush.colour.left[0].content.isDiagram) { - children = splitCellToDiagram(cell, state.brush.colour.left[0].content) + children = splitCellToDiagram(cell, state.brush.colour.left[0].content); } else { - children = splitCellToDiagram(cell, state.brush.colour) + children = splitCellToDiagram(cell, state.brush.colour); } for (const child of children) { - drawCell(child) + drawCell(child); } - } + }; const getWorldCellsSet = (x, y) => { - const sections = getSectionsOfWorldCell(x, y) - const worldCells = getWorldCellsFromSections(sections, x, y) + const sections = getSectionsOfWorldCell(x, y); + const worldCells = getWorldCellsFromSections(sections, x, y); - return worldCells + return worldCells; - } + }; const getSectionsOfWorldCell = (x, y) => { - const snappedX = Math.floor(x*WORLD_DIMENSION) / WORLD_DIMENSION - const snappedY = Math.floor(y*WORLD_DIMENSION) / WORLD_DIMENSION + const snappedX = Math.floor(x*WORLD_DIMENSION) / WORLD_DIMENSION; + const snappedY = Math.floor(y*WORLD_DIMENSION) / WORLD_DIMENSION; - const sectionSizeScale = GRID_SIZE / WORLD_DIMENSION + const sectionSizeScale = GRID_SIZE / WORLD_DIMENSION; - const sections = new Set() + const sections = new Set(); for (let wx = 0; wx < sectionSizeScale; wx++) { - const gridX = Math.floor((snappedX + wx * WORLD_CELL_SIZE / sectionSizeScale) * GRID_SIZE) + const gridX = Math.floor((snappedX + wx * WORLD_CELL_SIZE / sectionSizeScale) * GRID_SIZE); for (let wy = 0; wy < sectionSizeScale; wy++) { - const gridY = Math.floor((snappedY + wy * WORLD_CELL_SIZE / sectionSizeScale) * GRID_SIZE) - const sectionId = gridX*GRID_SIZE + gridY - const section = state.grid[sectionId] - sections.add(section) + const gridY = Math.floor((snappedY + wy * WORLD_CELL_SIZE / sectionSizeScale) * GRID_SIZE); + const sectionId = gridX*GRID_SIZE + gridY; + const section = state.grid[sectionId]; + sections.add(section); } } - return sections - } + return sections; + }; const getWorldCellsFromSections = (sections, x, y) => { - /*const wleft = x - const wtop = y - const wright = x + 1/256 + /*const wleft = x; + const wtop = y; + const wright = x + 1/256; const wbottom = y + 1/256*/ - const worldCells = new Set() + const worldCells = new Set(); - // Check if any cells in these sections overlap with an outer section + // Check if any cells in these sections overlap with an outer section; for (const section of sections.values()) { /*if (section.left >= wleft && section.right <= wright && section.top >= wtop && section.bottom <= wbottom) { - worldCells.add(section) - continue + worldCells.add(section); + continue; }*/ for (const cell of section.values()) { - if (worldCells.has(cell)) continue + if (worldCells.has(cell)) continue; for (const cellSection of cell.sections) { - if (!sections.has(cellSection)) return undefined + if (!sections.has(cellSection)) return undefined; } - worldCells.add(cell) + worldCells.add(cell); } } - return worldCells - } + return worldCells; + }; - let dropperStartX = undefined - let dropperStartY = undefined - let dropperStartT = undefined + let dropperStartX; + let dropperStartY; + let dropperStartT; - state.brush.hoverColour = Colour.Void + state.brush.hoverColour = Colour.Void; const updatePan = () => { - const [x, y] = Mouse.position + const [x, y] = Mouse.position; if (hand.state === HAND.BRUSH || hand.state === HAND.BRUSHING || hand.state === HAND.PENCILLING) { - const cell = pickCell(...getCursorView(x, y)) - if (cell !== undefined) state.brush.hoverColour = cell.colour + const cell = pickCell(...getCursorView(x, y)); + if (cell !== undefined) state.brush.hoverColour = cell.colour; } else { - const atom = getAtom(x / CT_SCALE, y / CT_SCALE) + const atom = getAtom(x / CT_SCALE, y / CT_SCALE); if (atom !== undefined) { if (atom.isSquare || atom === squareTool) { - state.brush.hoverColour = atom.value + state.brush.hoverColour = atom.value; if (atom.joinExpanded) { - const clon = cloneDragonArray(atom.value) - clon.joins = [] - state.brush.hoverColour = clon + const clon = cloneDragonArray(atom.value); + clon.joins = []; + state.brush.hoverColour = clon; } } else if (atom.isTallRectangle) { - // TODO: what colour should rectangles set the brush? + // TODO: what colour should rectangles set the brush?; } else if (atom.isPaddle) { - state.brush.hoverColour = atom.getColour(atom) + state.brush.hoverColour = atom.getColour(atom); } else if (atom.isSlot) { - state.brush.hoverColour = atom.parent.getColour(atom.parent) + state.brush.hoverColour = atom.parent.getColour(atom.parent); } else { - state.brush.hoverColour = atom.colour.splash + state.brush.hoverColour = atom.colour.splash; } } else { - state.brush.hoverColour = Colour.Void + state.brush.hoverColour = Colour.Void; } } @@ -962,445 +961,445 @@ on.load(() => { if (dropperStartX !== undefined) { - const dropperDistance = Math.hypot(x - dropperStartX, y - dropperStartY) - const dropperTime = Date.now() - dropperStartT + const dropperDistance = Math.hypot(x - dropperStartX, y - dropperStartY); + const dropperTime = Date.now() - dropperStartT; if (dropperTime < 100 || dropperDistance <= 0) { - + if (state.brush.hoverColour === Colour.Void) { - brushColourCycleIndex++ + brushColourCycleIndex++; if (brushColourCycleIndex >= brushColourCycle.length) { - brushColourCycleIndex = 0 + brushColourCycleIndex = 0; } - setBrushColour(brushColourCycle[brushColourCycleIndex]) + setBrushColour(brushColourCycle[brushColourCycleIndex]); } else { - setBrushColour(state.brush.hoverColour) + setBrushColour(state.brush.hoverColour); } - squareTool.toolbarNeedsColourUpdate = true + squareTool.toolbarNeedsColourUpdate = true; } - drawQueueNeedsReset = true + drawQueueNeedsReset = true; } - dropperStartX = undefined - dropperStartY = undefined - return + dropperStartX = undefined; + dropperStartY = undefined; + return; } - drawQueueNeedsReset = true - + drawQueueNeedsReset = true; + if (dropperStartX === undefined) { - dropperStartX = x - dropperStartY = y - dropperStartT = Date.now() - dropperMovement = 0 + dropperStartX = x; + dropperStartY = y; + dropperStartT = Date.now(); + dropperMovement = 0; } if (hand.state === HAND.FREE || hand.state == HAND.VOIDING || hand.state === HAND.BRUSH || hand.state === HAND.BRUSHING || hand.state === HAND.PENCILLING) { - const {x: px, y: py} = state.cursor.previous - if (px === undefined || py === undefined) return - if (x === undefined || y === undefined) return - const [dx, dy] = [x - px, y - py] - state.camera.x += dx / state.camera.scale - state.camera.y += dy / state.camera.scale - updateImageSize() + const {x: px, y: py} = state.cursor.previous; + if (px === undefined || py === undefined) return; + if (x === undefined || y === undefined) return; + const [dx, dy] = [x - px, y - py]; + state.camera.x += dx / state.camera.scale; + state.camera.y += dy / state.camera.scale; + updateImageSize(); } - } + }; - const ZOOM = 0.02 - let CT_SCALE = DPR * SCALE + const ZOOM = 0.02; + let CT_SCALE = DPR * SCALE; on.wheel((e) => { - e.preventDefault() + e.preventDefault(); - let dy = e.deltaY / 100 + let dy = e.deltaY / 100; if (e.altKey) { - PADDLE.scroll -= 50 * dy - positionPaddles() + PADDLE.scroll -= 50 * dy; + positionPaddles(); } else if (e.ctrlKey || e.metaKey) { - CT_SCALE -= dy * 0.1 - const allAtoms = getAllAtoms() + CT_SCALE -= dy * 0.1; + const allAtoms = getAllAtoms(); for (const atom of allAtoms) { - atom.needsColoursUpdate = true + atom.needsColoursUpdate = true; } - squareTool.toolbarNeedsColourUpdate = true + squareTool.toolbarNeedsColourUpdate = true; } - + else if (e.shiftKey) { - if (dy === 0) dy = e.deltaX / 100 - state.brush.size -= Math.sign(dy) - if (state.brush.size < 0) state.brush.size = 0 + if (dy === 0) dy = e.deltaX / 100; + state.brush.size -= Math.sign(dy); + if (state.brush.size < 0) state.brush.size = 0; } else { - doZoom(dy, ...Mouse.position) + doZoom(dy, ...Mouse.position); } - - }, {passive: false}) + + }, {passive: false}); on.keydown(e => { - if (e.key === "Alt") e.preventDefault() - }, {passive: false}) + if (e.key === "Alt") e.preventDefault(); + }, {passive: false}); on.keydown(e => { if (e.key === "f") { - state.camera.x = 1920 * 1/3 - state.camera.y = 30 - state.camera.scale = 0.95 - updateImageSize() + state.camera.x = 1920 * 1/3; + state.camera.y = 30; + state.camera.scale = 0.95; + updateImageSize(); } - }) + }); const getNeatScale = (underScale) => { - return underScale - } + return underScale; + }; const doZoom = (dy, centerX, centerY) => { - centerX *= DPR - centerY *= DPR + centerX *= DPR; + centerY *= DPR; - const sign = -Math.sign(dy) - const dd = Math.abs(dy) + const sign = -Math.sign(dy); + const dd = Math.abs(dy); for (let i = 0; i < dd; i++) { - const zoom = ZOOM * state.camera.underScale + const zoom = ZOOM * state.camera.underScale; - const szoom = zoom * sign - state.camera.underScale += szoom + const szoom = zoom * sign; + state.camera.underScale += szoom; - const oldScale = state.camera.scale - const newScale = getNeatScale(state.camera.underScale) - state.camera.scale = newScale - const scale = newScale / oldScale + const oldScale = state.camera.scale; + const newScale = getNeatScale(state.camera.underScale); + state.camera.scale = newScale; + const scale = newScale / oldScale; - state.camera.x += (1-scale) * centerX/newScale - state.camera.y += (1-scale) * centerY/newScale + state.camera.x += (1-scale) * centerX/newScale; + state.camera.y += (1-scale) * centerY/newScale; } - //const newScale = state.camera.scale - //const scale = newScale / oldScale - //stampScale(scale) + //const newScale = state.camera.scale; + //const scale = newScale / oldScale; + //stampScale(scale); - updateImageSize() - } + updateImageSize(); + }; on.contextmenu((e) => { - e.preventDefault() - }) + e.preventDefault(); + }); - //==========// - // KEYBOARD // - //==========// + //==========//; + // KEYBOARD //; + //==========//; on.keydown(e => { - const keydown = KEYDOWN[e.key] - if (keydown !== undefined) keydown(e) - }) - - const KEYDOWN = {} - KEYDOWN.e = () => state.camera.mscaleTarget += state.camera.mscaleTargetControl - KEYDOWN.q = () => state.camera.mscaleTarget -= state.camera.mscaleTargetControl - - KEYDOWN.w = () => state.camera.dyTarget += state.camera.dsControl + const keydown = KEYDOWN[e.key]; + if (keydown !== undefined) keydown(e); + }); + + const KEYDOWN = {}; + KEYDOWN.e = () => state.camera.mscaleTarget += state.camera.mscaleTargetControl; + KEYDOWN.q = () => state.camera.mscaleTarget -= state.camera.mscaleTargetControl; + + KEYDOWN.w = () => state.camera.dyTarget += state.camera.dsControl; KEYDOWN.s = (e) => { - if ((e.ctrlKey || e.metaKey)) return - state.camera.dyTarget -= state.camera.dsControl - } - KEYDOWN.a = () => state.camera.dxTarget += state.camera.dsControl - KEYDOWN['d'] = () => state.camera.dxTarget -= state.camera.dsControl - - KEYDOWN[0] = () => setWorldSize(0) - KEYDOWN[1] = () => setWorldSize(1) - KEYDOWN[2] = () => setWorldSize(2) - KEYDOWN[3] = () => setWorldSize(3) - KEYDOWN[4] = () => setWorldSize(4) - KEYDOWN[5] = () => setWorldSize(5) - KEYDOWN[6] = () => setWorldSize(6) - KEYDOWN[7] = () => setWorldSize(7) - KEYDOWN[8] = () => setWorldSize(8) - KEYDOWN[9] = () => setWorldSize(9) + if ((e.ctrlKey || e.metaKey)) return; + state.camera.dyTarget -= state.camera.dsControl; + }; + KEYDOWN.a = () => state.camera.dxTarget += state.camera.dsControl; + KEYDOWN['d'] = () => state.camera.dxTarget -= state.camera.dsControl; + + KEYDOWN[0] = () => setWorldSize(0); + KEYDOWN[1] = () => setWorldSize(1); + KEYDOWN[2] = () => setWorldSize(2); + KEYDOWN[3] = () => setWorldSize(3); + KEYDOWN[4] = () => setWorldSize(4); + KEYDOWN[5] = () => setWorldSize(5); + KEYDOWN[6] = () => setWorldSize(6); + KEYDOWN[7] = () => setWorldSize(7); + KEYDOWN[8] = () => setWorldSize(8); + KEYDOWN[9] = () => setWorldSize(9); KEYDOWN.r = () => { - state.camera.mscaleTarget = 1.0 - state.camera.dxTarget = 0.0 - state.camera.dyTarget = 0.0 - } + state.camera.mscaleTarget = 1.0; + state.camera.dxTarget = 0.0; + state.camera.dyTarget = 0.0; + }; - KEYDOWN["="] = () => edgeMode = 1 - KEYDOWN["-"] = () => edgeMode = 0 - KEYDOWN["o"] = () => edgeMode = edgeMode === 0 ? 1 : 0 + KEYDOWN["="] = () => edgeMode = 1; + KEYDOWN["-"] = () => edgeMode = 0; + KEYDOWN["o"] = () => edgeMode = edgeMode === 0 ? 1 : 0; - gridMode = true + gridMode = true; KEYDOWN["g"] = () => { - gridMode = !gridMode - drawQueueNeedsReset = true - } + gridMode = !gridMode; + drawQueueNeedsReset = true; + }; - //========// - // CAMERA // - //========// + //========//; + // CAMERA //; + //========//; const updateCamera = () => { if (state.camera.mscale !== state.camera.mscaleTarget) { - const dd = state.camera.mscaleTarget - state.camera.mscale - state.camera.mscale += dd * state.camera.mscaleTargetSpeed + const dd = state.camera.mscaleTarget - state.camera.mscale; + state.camera.mscale += dd * state.camera.mscaleTargetSpeed; - const sign = Math.sign(dd) - const snap = state.camera.mscaleTarget * state.camera.mscaleTargetControl * state.camera.mscaleTargetSpeed - if (sign === 1 && state.camera.mscale > state.camera.mscaleTarget - snap) state.camera.mscale = state.camera.mscaleTarget - if (sign === -1 && state.camera.mscale < state.camera.mscaleTarget + snap) state.camera.mscale = state.camera.mscaleTarget + const sign = Math.sign(dd); + const snap = state.camera.mscaleTarget * state.camera.mscaleTargetControl * state.camera.mscaleTargetSpeed; + if (sign === 1 && state.camera.mscale > state.camera.mscaleTarget - snap) state.camera.mscale = state.camera.mscaleTarget; + if (sign === -1 && state.camera.mscale < state.camera.mscaleTarget + snap) state.camera.mscale = state.camera.mscaleTarget; } if (state.camera.dx !== state.camera.dxTarget) { - const dd = state.camera.dxTarget - state.camera.dx - state.camera.dx += dd * state.camera.dsTargetSpeed + const dd = state.camera.dxTarget - state.camera.dx; + state.camera.dx += dd * state.camera.dsTargetSpeed; - const sign = Math.sign(dd) - const snap = state.camera.dxTarget * state.camera.dsControl * state.camera.dsTargetSpeed - if (sign === 1 && state.camera.dx > state.camera.dxTarget - snap) state.camera.dx = state.camera.dxTarget - if (sign === -1 && state.camera.dx < state.camera.dxTarget + snap) state.camera.dx = state.camera.dxTarget + const sign = Math.sign(dd); + const snap = state.camera.dxTarget * state.camera.dsControl * state.camera.dsTargetSpeed; + if (sign === 1 && state.camera.dx > state.camera.dxTarget - snap) state.camera.dx = state.camera.dxTarget; + if (sign === -1 && state.camera.dx < state.camera.dxTarget + snap) state.camera.dx = state.camera.dxTarget; } if (state.camera.dy !== state.camera.dyTarget) { - const dd = state.camera.dyTarget - state.camera.dy - state.camera.dy += dd * state.camera.dsTargetSpeed + const dd = state.camera.dyTarget - state.camera.dy; + state.camera.dy += dd * state.camera.dsTargetSpeed; - const sign = Math.sign(dd) - const snap = state.camera.dyTarget * state.camera.dsControl * state.camera.dsTargetSpeed - if (sign === 1 && state.camera.dy > state.camera.dyTarget - snap) state.camera.dy = state.camera.dyTarget - if (sign === -1 && state.camera.dy < state.camera.dyTarget + snap) state.camera.dy = state.camera.dyTarget + const sign = Math.sign(dd); + const snap = state.camera.dyTarget * state.camera.dsControl * state.camera.dsTargetSpeed; + if (sign === 1 && state.camera.dy > state.camera.dyTarget - snap) state.camera.dy = state.camera.dyTarget; + if (sign === -1 && state.camera.dy < state.camera.dyTarget + snap) state.camera.dy = state.camera.dyTarget; } if (state.camera.dx !== 0.0 || state.camera.dy !== 0.0) { - state.camera.x += state.camera.dx - state.camera.y += state.camera.dy - updateImageSize() - if (hand.state.camerapan) hand.state.camerapan() + state.camera.x += state.camera.dx; + state.camera.y += state.camera.dy; + updateImageSize(); + if (hand.state.camerapan) hand.state.camerapan(); } if (state.camera.mscale !== 1.0) { - //addAllCellsToDrawQueue() + //addAllCellsToDrawQueue(); - const oldScale = state.camera.scale - state.camera.scale *= state.camera.mscale - const newScale = state.camera.scale - const scale = newScale / oldScale + const oldScale = state.camera.scale; + state.camera.scale *= state.camera.mscale; + const newScale = state.camera.scale; + const scale = newScale / oldScale; - let centerX = Mouse.position[0] - let centerY = Mouse.position[1] + let centerX = Mouse.position[0]; + let centerY = Mouse.position[1]; - if (centerX === undefined) centerX = canvas.width/2 - if (centerY === undefined) centerY = canvas.height/2 + if (centerX === undefined) centerX = canvas.width/2; + if (centerY === undefined) centerY = canvas.height/2; - state.camera.x += (1-scale) * centerX/newScale - state.camera.y += (1-scale) * centerY/newScale + state.camera.x += (1-scale) * centerX/newScale; + state.camera.y += (1-scale) * centerY/newScale; - updateImageSize() + updateImageSize(); } - } + }; - //======// - // TICK // - //======// - drawCells() + //======//; + // TICK //; + //======//; + drawCells(); show.tick = () => { - - updateHand() - updateCursor() - updateCamera() - + + updateHand(); + updateCursor(); + updateCamera(); + if (drawQueueNeedsReset) { - addAllCellsTodrawQueue() - drawQueueNeedsReset = false + addAllCellsTodrawQueue(); + drawQueueNeedsReset = false; } - if (!show.paused) fireRandomSpotEvents() - else fireRandomSpotDrawEvents() + if (!show.paused) fireRandomSpotEvents(); + else fireRandomSpotDrawEvents(); - context.putImageData(state.image.data, 0, 0) - //context.filter = "grayscale(100%)" - //context.drawImage(context.canvas, 0, 0, context.canvas.width, context.canvas.height) + context.putImageData(state.image.data, 0, 0); + //context.filter = "grayscale(100%)"; + //context.drawImage(context.canvas, 0, 0, context.canvas.width, context.canvas.height); - // Draw void - context.clearRect(0, 0, state.view.left, state.view.bottom) - context.clearRect(state.view.left, 0, canvas.width, state.view.top) - context.clearRect(state.view.right, state.view.top, canvas.width, canvas.height) - context.clearRect(0, state.view.bottom, canvas.width, canvas.height) + // Draw void; + context.clearRect(0, 0, state.view.left, state.view.bottom); + context.clearRect(state.view.left, 0, canvas.width, state.view.top); + context.clearRect(state.view.right, state.view.top, canvas.width, canvas.height); + context.clearRect(0, state.view.bottom, canvas.width, canvas.height); - state.time++ - if (state.time > state.maxTime) state.time = 0 + state.time++; + if (state.time > state.maxTime) state.time = 0; - } + }; + + const drawQueue = new Set(); + const drawQueuePriority = new Set(); + drawQueueNeedsReset = false; - const drawQueue = new Set() - const drawQueuePriority = new Set() - drawQueueNeedsReset = false - const shuffleArray = (array) => { for (let i = array.length - 1; i > 0; i--) { - const r = Random.Uint32 % (i+1) - ;[array[i], array[r]] = [array[r], array[i]] + const r = Random.Uint32 % (i+1); +[array[i], array[r]] = [array[r], array[i]]; } - return array - } + return array; + }; const addAllCellsTodrawQueue = () => { - drawQueue.clear() + drawQueue.clear(); for (const section of shuffleArray([...state.grid])) { - if (!isSectionVisible(section)) continue + if (!isSectionVisible(section)) continue; for (const cell of section.values()) { - //if (!isCellVisible(cell)) continue - drawQueue.add(cell) + //if (!isCellVisible(cell)) continue; + drawQueue.add(cell); } } - } + }; const fireRandomSpotEvents = () => { - let count = state.speed.dynamic? state.speed.aer * state.cellCount : state.speed.count - count = Math.min(count, state.cellCount) - count *= state.worldBuilt? 1 : 0.1 - const redrawCount = count * state.speed.redraw - let redraw = true - if (!state.worldBuilt) redraw = false - let drawnCount = 0 + let count = state.speed.dynamic? state.speed.aer * state.cellCount : state.speed.count; + count = Math.min(count, state.cellCount); + count *= state.worldBuilt? 1 : 0.1; + const redrawCount = count * state.speed.redraw; + let redraw = true; + if (!state.worldBuilt) redraw = false; + let drawnCount = 0; for (let i = 0; i < count; i++) { - const cell = pickRandomCell() - - if (redraw && drawnCount >= redrawCount) redraw = false - const drawn = fireCellEvent(cell, redraw) - drawnCount += drawn + const cell = pickRandomCell(); + + if (redraw && drawnCount >= redrawCount) redraw = false; + const drawn = fireCellEvent(cell, redraw); + drawnCount += drawn; } for (const cell of drawQueuePriority) { - drawnCount += drawCell(cell) - drawQueuePriority.delete(cell) - if (drawnCount >= redrawCount) break + drawnCount += drawCell(cell); + drawQueuePriority.delete(cell); + if (drawnCount >= redrawCount) break; } for (const cell of drawQueue) { - drawnCount += drawCell(cell) - drawQueue.delete(cell) - if (drawnCount >= redrawCount) break + drawnCount += drawCell(cell); + drawQueue.delete(cell); + if (drawnCount >= redrawCount) break; } - /* + /*; for (const cell of drawQueue) { - drawnCount += drawCell(cell) - drawQueue.shift(cell) - if (drawnCount >= redrawCount) return - } + drawnCount += drawCell(cell); + drawQueue.shift(cell); + if (drawnCount >= redrawCount) return; + }; */ - /* + /*; for (const cell of drawQueue) { - drawnCount += drawCell(cell) - drawQueue.shift() - if (drawnCount >= redrawCount) return - } + drawnCount += drawCell(cell); + drawQueue.shift(); + if (drawnCount >= redrawCount) return; + }; */ - /*if (!state.view.visible) return + /*if (!state.view.visible) return; while (drawnCount < redrawCount) { - const cell = pickRandomVisibleCell() - drawnCount += drawCell(cell) + const cell = pickRandomVisibleCell(); + drawnCount += drawCell(cell); }*/ - } + }; const fireRandomSpotDrawEvents = () => { - if (!state.view.visible) return - const count = state.speed.dynamic? state.speed.aer * state.cellCount : state.speed.count - let redrawCount = count * state.speed.redraw - if (!state.worldBuilt) redrawCount = 1 + if (!state.view.visible) return; + const count = state.speed.dynamic? state.speed.aer * state.cellCount : state.speed.count; + let redrawCount = count * state.speed.redraw; + if (!state.worldBuilt) redrawCount = 1; - let drawnCount = 0 + let drawnCount = 0; for (const cell of drawQueue) { - drawnCount += drawCell(cell) - drawQueue.delete(cell) - if (drawnCount >= redrawCount) break + drawnCount += drawCell(cell); + drawQueue.delete(cell); + if (drawnCount >= redrawCount) break; } /*for (let i = 0; i < redrawCount; i++) { - const cell = pickRandomVisibleCell() - drawnCount += drawCell(cell) + const cell = pickRandomVisibleCell(); + drawnCount += drawCell(cell); }*/ - } + }; - // this function is currently full of debug code - // Returns the number of cells it drew + // this function is currently full of debug code; + // Returns the number of cells it drew; const fireCellEvent = (cell, redraw) => { - if (BUILD_WORLD(cell, redraw) !== undefined) return 1 + if (BUILD_WORLD(cell, redraw) !== undefined) return 1; - state.dragon.behaves.shuffle() + state.dragon.behaves.shuffle(); for (const behave of state.dragon.behaves) { - const result = behave(cell, redraw) - if (result === undefined) continue - //if (result === 0) continue - return result + const result = behave(cell, redraw); + if (result === undefined) continue; + //if (result === 0) continue; + return result; } - return 0 + return 0; - } - - //===============// - // SPLIT + MERGE // - //===============// + }; + + //===============//; + // SPLIT + MERGE //; + //===============//; const splitCell = (cell, width, height) => { - const childWidth = cell.width / width - const childHeight = cell.height / height + const childWidth = cell.width / width; + const childHeight = cell.height / height; - const children = [] + const children = []; + + const xStart = cell.x; + const yStart = cell.y; + const dx = childWidth; + const dy = childHeight; - const xStart = cell.x - const yStart = cell.y - const dx = childWidth - const dy = childHeight - for (let ix = 0; ix < width; ix++) { - const x = xStart + dx*ix + const x = xStart + dx*ix; for (let iy = 0; iy < height; iy++) { - const y = yStart + dy*iy - const child = makeCell({x, y, width: childWidth, height: childHeight, colour: cell.colour, lastDraw: cell.lastDraw}) - children.push(child) + const y = yStart + dy*iy; + const child = makeCell({x, y, width: childWidth, height: childHeight, colour: cell.colour, lastDraw: cell.lastDraw}); + children.push(child); } } - deleteCell(cell) + deleteCell(cell); for (const child of children) { - addCell(child) + addCell(child); } - return children - } - + return children; + }; + const splitCellToDiagram = (cell, diagram) => { - const [diagramWidth, diagramHeight] = getDiagramDimensions(diagram) - const widthScale = cell.width / diagramWidth - const heightScale = cell.height / diagramHeight + const [diagramWidth, diagramHeight] = getDiagramDimensions(diagram); + const widthScale = cell.width / diagramWidth; + const heightScale = cell.height / diagramHeight; - const children = [] + const children = []; for (const diagramCell of diagram.left) { - const colours = getSplashesArrayFromArray(diagramCell.content, {source: cell.colour}) - const colour = colours[Random.Uint32 % colours.length] + const colours = getSplashesArrayFromArray(diagramCell.content, {source: cell.colour}); + const colour = colours[Random.Uint32 % colours.length]; const child = makeCell({ x: cell.x + diagramCell.x * widthScale, @@ -1408,44 +1407,44 @@ on.load(() => { width: diagramCell.width * widthScale, height: diagramCell.height * heightScale, colour: colour, - }) + }); - children.push(child) + children.push(child); } - deleteCell(cell) + deleteCell(cell); for (const child of children) { - addCell(child) + addCell(child); } - return children + return children; - } + }; - // Warning: bugs will happen if you try to merge cells that don't align or aren't next to each other + // Warning: bugs will happen if you try to merge cells that don't align or aren't next to each other; const mergeCells = (cells) => { - - let left = 1 - let top = 1 - let right = 0 - let bottom = 0 + + let left = 1; + let top = 1; + let right = 0; + let bottom = 0; for (const cell of cells) { /*if (cell.isSection) { for (const c of cell.values()) { - if (c.left < left) left = c.left - if (c.top < top) top = c.top - if (c.right > right) right = c.right - if (c.bottom > bottom) bottom = c.bottom - deleteCell(c) - } - continue + if (c.left < left) left = c.left; + if (c.top < top) top = c.top; + if (c.right > right) right = c.right; + if (c.bottom > bottom) bottom = c.bottom; + deleteCell(c); + }; + continue; }*/ - if (cell.left < left) left = cell.left - if (cell.top < top) top = cell.top - if (cell.right > right) right = cell.right - if (cell.bottom > bottom) bottom = cell.bottom - deleteCell(cell) + if (cell.left < left) left = cell.left; + if (cell.top < top) top = cell.top; + if (cell.right > right) right = cell.right; + if (cell.bottom > bottom) bottom = cell.bottom; + deleteCell(cell); } const cell = makeCell({ @@ -1455,449 +1454,448 @@ on.load(() => { height: bottom-top, colour: cells[0].colour, lastDraw: cells[0].lastDraw, - }) + }); - addCell(cell) + addCell(cell); - return cell + return cell; - } + }; - // NOTE: this checks if the cells fit together in ANY POSSIBLE WAY - // it might not be the arrangement of cells that you are interested in + // NOTE: this checks if the cells fit together in ANY POSSIBLE WAY; + // it might not be the arrangement of cells that you are interested in; const fits = (cells) => { - const [head, ...tail] = cells - - const connections = [head] - let failureCount = 0 - - let i = 0 + const [head, ...tail] = cells; + + const connections = [head]; + let failureCount = 0; + + let i = 0; while (connections.length <= cells.length) { - const cell = tail[i] - const connection = connections.find(connection => isFit(cell, connection)) + const cell = tail[i]; + const connection = connections.find(connection => isFit(cell, connection)); if (!connection) { - failureCount++ - if (failureCount === (cells.length - connections.length)) return false + failureCount++; + if (failureCount === (cells.length - connections.length)) return false; } else { - failureCount = 0 - connections.push(cell) + failureCount = 0; + connections.push(cell); } - i++ - if (i >= tail.length) i = 0 + i++; + if (i >= tail.length) i = 0; } - return true + return true; - } + }; - // Only checks if cells are the same size + // Only checks if cells are the same size; const aligns = (cells) => { - - const [head, ...tail] = cells - const {width, height} = head - const isAligned = tail.every(cell => cell.width === width && cell.height === height) - return isAligned - } + const [head, ...tail] = cells; + const {width, height} = head; + const isAligned = tail.every(cell => cell.width === width && cell.height === height); + return isAligned; + + }; const isFit = (cell, connection) => { if (cell.height === connection.height && cell.top === connection.top) { - if (cell.left === connection.right) return true - if (cell.right === connection.left) return true + if (cell.left === connection.right) return true; + if (cell.right === connection.left) return true; } - + if (cell.width === connection.width && cell.right === connection.right) { - if (cell.top === connection.bottom) return true - if (cell.bottom === connection.top) return true + if (cell.top === connection.bottom) return true; + if (cell.bottom === connection.top) return true; } - return - } + return; + }; - //=========// - // ELEMENT // - //=========// - // Behave functions must return how many cells got drawn + //=========//; + // ELEMENT //; + //=========//; + // Behave functions must return how many cells got drawn; const BUILD_WORLD = (cell, redraw) => { - if (state.worldBuilt) return undefined + if (state.worldBuilt) return undefined; if (state.cellCount >= WORLD_CELL_COUNT) { - state.worldBuilt = true - return undefined + state.worldBuilt = true; + return undefined; } if (cell.colour < 111) { - return 0 + return 0; } - cell.colour -= 111 - const width = 2 - const height = 2 - const children = splitCell(cell, width, height) + cell.colour -= 111; + const width = 2; + const height = 2; + const children = splitCell(cell, width, height); for (const child of children) { - drawCell(child) + drawCell(child); } - return 1 - } + return 1; + }; - //=================// - // DRAGON - NUMBER // - //=================// + //=================//; + // DRAGON - NUMBER //; + //=================//; const makeNumber = ({values, channel = 0, variable, add, subtract} = {}) => { - let numberValues = undefined - + let numberValues; + if (variable !== undefined) { - // placeholder for places in the codebase that don't specify a source! - numberValues = [true, true, true, true, true, true, true, true, true, true] - //numberValues = [false, false, false, false, false, false, false, false, false, false] + // placeholder for places in the codebase that don't specify a source!; + numberValues = [true, true, true, true, true, true, true, true, true, true]; + //numberValues = [false, false, false, false, false, false, false, false, false, false]; } - - else numberValues = values - return {values: numberValues, variable, channel, add, subtract} - } + + else numberValues = values; + return {values: numberValues, variable, channel, add, subtract}; + }; const cloneDragonNumber = (number) => { - const values = [...number.values] - const variable = number.variable - const channel = number.channel - const add = number.add === undefined? undefined : cloneDragonNumber(number.add) - const subtract = number.subtract === undefined? undefined : cloneDragonNumber(number.subtract) - const clone = makeNumber({values, variable, channel, add, subtract}) - return clone - } + const values = [...number.values]; + const variable = number.variable; + const channel = number.channel; + const add = number.add === undefined? undefined : cloneDragonNumber(number.add); + const subtract = number.subtract === undefined? undefined : cloneDragonNumber(number.subtract); + const clone = makeNumber({values, variable, channel, add, subtract}); + return clone; + }; const makeValuesFromInt = (int) => { - const values = [false, false, false, false, false, false, false, false, false, false] - values[int] = true - return values - } + const values = [false, false, false, false, false, false, false, false, false, false]; + values[int] = true; + return values; + }; const makeNumberFromInt = (int) => { - const values = makeValuesFromInt(int) - return makeNumber({values}) - } - - const VARIABLE_EVALUATOR = {} - + const values = makeValuesFromInt(int); + return makeNumber({values}); + }; + + const VARIABLE_EVALUATOR = {}; + VARIABLE_EVALUATOR.red = (number, {source} = {}) => { if (source === undefined) { - return [true, true, true, true, true, true, true, true, true, true] + return [true, true, true, true, true, true, true, true, true, true]; } - const [r, g, b] = getRGB(source) - const values = makeValuesFromInt(r / 100) - return values - } - + const [r, g, b] = getRGB(source); + const values = makeValuesFromInt(r / 100); + return values; + }; + VARIABLE_EVALUATOR.green = (number, {source} = {}) => { if (source === undefined) { - return [true, true, true, true, true, true, true, true, true, true] + return [true, true, true, true, true, true, true, true, true, true]; } - const [r, g, b] = getRGB(source) - const values = makeValuesFromInt(g / 10) - return values - } - + const [r, g, b] = getRGB(source); + const values = makeValuesFromInt(g / 10); + return values; + }; + VARIABLE_EVALUATOR.blue = (number, {source} = {}) => { if (source === undefined) { - return [true, true, true, true, true, true, true, true, true, true] + return [true, true, true, true, true, true, true, true, true, true]; } - const [r, g, b] = getRGB(source) - const values = makeValuesFromInt(b) - return values - } + const [r, g, b] = getRGB(source); + const values = makeValuesFromInt(b); + return values; + }; const evaluateNumber = (number, args = {}) => { if (number.variable === undefined) { - return number.values + return number.values; } - let results = VARIABLE_EVALUATOR[number.variable](number, args) - const isHue = number.variable === "red" + let results = VARIABLE_EVALUATOR[number.variable](number, args); + const isHue = number.variable === "red"; if (number.add !== undefined) { - results = addChannelToResults(results, number.add, {source: args.source, multiplier: 1, isHue}) + results = addChannelToResults(results, number.add, {source: args.source, multiplier: 1, isHue}); } if (number.subtract !== undefined) { - results = addChannelToResults(results, number.subtract, {source: args.source, multiplier: -1, isHue}) + results = addChannelToResults(results, number.subtract, {source: args.source, multiplier: -1, isHue}); } - - return results - } + return results; + + }; - //================// - // DRAGON - ARRAY // - //================// - // Channels[3] - what dragon numbers are in each colour channel (or undefined for a partial array) - // Stamp - what shape of stamp the channel has (or undefined for no stamp) + //================//; + // DRAGON - ARRAY //; + //================//; + // Channels[3] - what dragon numbers are in each colour channel (or undefined for a partial array); + // Stamp - what shape of stamp the channel has (or undefined for no stamp); const makeArray = ({channels, stamp, joins = []} = {}) => { - if (channels === undefined) channels = [undefined, undefined, undefined] - return {channels, stamp, joins} - } + if (channels === undefined) channels = [undefined, undefined, undefined]; + return {channels, stamp, joins}; + }; makeArrayFromSplash = (splash) => { - let [r, g, b] = getRGB(splash) - r /= 100 - g /= 10 - const redValues = [false, false, false, false, false, false, false, false, false, false] - const greenValues = [false, false, false, false, false, false, false, false, false, false] - const blueValues = [false, false, false, false, false, false, false, false, false, false] - redValues[r] = true - greenValues[g] = true - blueValues[b] = true - const red = makeNumber({values: redValues, channel: 0}) - const green = makeNumber({values: greenValues, channel: 1}) - const blue = makeNumber({values: blueValues, channel: 2}) - - const array = makeArray({channels: [red, green, blue]}) - return array - } - + let [r, g, b] = getRGB(splash); + r /= 100; + g /= 10; + const redValues = [false, false, false, false, false, false, false, false, false, false]; + const greenValues = [false, false, false, false, false, false, false, false, false, false]; + const blueValues = [false, false, false, false, false, false, false, false, false, false]; + redValues[r] = true; + greenValues[g] = true; + blueValues[b] = true; + const red = makeNumber({values: redValues, channel: 0}); + const green = makeNumber({values: greenValues, channel: 1}); + const blue = makeNumber({values: blueValues, channel: 2}); + + const array = makeArray({channels: [red, green, blue]}); + return array; + }; + const getSplashesSetFromArray = (array, args) => { - const splashesArray = getSplashesArrayFromArray(array, args) + const splashesArray = getSplashesArrayFromArray(array, args); + + const splashes = new Set(splashesArray); + return splashes; + }; - const splashes = new Set(splashesArray) - return splashes - } - const getSplashesArrayFromArray = (array, args = {}) => { - const splashes = [] + const splashes = []; for (const join of array.joins) { - const joinSplashes = getSplashesArrayFromArray(join) - splashes.push(...joinSplashes) + const joinSplashes = getSplashesArrayFromArray(join); + splashes.push(...joinSplashes); } - if (array.isDiagram) { - splashes.push(900) // backup in case of a bug in my code - shows red for error(!?) - return splashes + splashes.push(900); // backup in case of a bug in my code - shows red for error(!?); + return splashes; } - //if (array.channels === undefined) print(array) - let [reds, greens, blues] = array.channels + //if (array.channels === undefined) print(array); + let [reds, greens, blues] = array.channels; - if (reds === undefined) reds = makeNumber({channel: 0, variable: "red"}) - if (greens === undefined) greens = makeNumber({channel: 1, variable: "green"}) - if (blues === undefined) blues = makeNumber({channel: 2, variable: "blue"}) + if (reds === undefined) reds = makeNumber({channel: 0, variable: "red"}); + if (greens === undefined) greens = makeNumber({channel: 1, variable: "green"}); + if (blues === undefined) blues = makeNumber({channel: 2, variable: "blue"}); - const rvalues = evaluateNumber(reds, args) - const gvalues = evaluateNumber(greens, args) - const bvalues = evaluateNumber(blues, args) + const rvalues = evaluateNumber(reds, args); + const gvalues = evaluateNumber(greens, args); + const bvalues = evaluateNumber(blues, args); for (let r = 0; r < rvalues.length; r++) { - const red = rvalues[r] - if (!red) continue + const red = rvalues[r]; + if (!red) continue; for (let g = 0; g < gvalues.length; g++) { - const green = gvalues[g] - if (!green) continue + const green = gvalues[g]; + if (!green) continue; for (let b = 0; b < bvalues.length; b++) { - const blue = bvalues[b] - if (!blue) continue - const splash = r*100 + g*10 + b*1 - splashes.push(splash) + const blue = bvalues[b]; + if (!blue) continue; + const splash = r*100 + g*10 + b*1; + splashes.push(splash); } } } - return splashes - } + return splashes; + }; const fillEmptyChannels = (array) => { for (let i = 0; i < 3; i++) { - if (array.channels[i] !== undefined) continue + if (array.channels[i] !== undefined) continue; array.channels[i] = makeNumber({ channel: i, variable: CHANNEL_VARIABLES[i], - }) + }); } - } + }; const isDragonArrayDynamic = (array) => { for (const channel of array.channels) { - if (channel === undefined) return false - if (channel.variable !== undefined) return true + if (channel === undefined) return false; + if (channel.variable !== undefined) return true; } - return false - } + return false; + }; const cloneDragonArray = (array) => { if (array === undefined) { - const red = makeNumber({values: [true, true, true, true, true, true, true, true, true, true], channel: 0}) - const green = makeNumber({values: [true, true, true, true, true, true, true, true, true, true], channel: 1}) - const blue = makeNumber({values: [true, true, true, true, true, true, true, true, true, true], channel: 2}) - const leftClone = makeArray({channels: [red, green, blue]}) - return leftClone + const red = makeNumber({values: [true, true, true, true, true, true, true, true, true, true], channel: 0}); + const green = makeNumber({values: [true, true, true, true, true, true, true, true, true, true], channel: 1}); + const blue = makeNumber({values: [true, true, true, true, true, true, true, true, true, true], channel: 2}); + const leftClone = makeArray({channels: [red, green, blue]}); + return leftClone; } if (array.isDiagram) { - return cloneDiagram(array) + return cloneDiagram(array); } - let red = undefined - let green = undefined - let blue = undefined + let red; + let green; + let blue; - const stamp = array.stamp + const stamp = array.stamp; if (array.channels[0] !== undefined && array.channels[0] !== null) { - red = cloneDragonNumber(array.channels[0]) + red = cloneDragonNumber(array.channels[0]); } - + if (array.channels[1] !== undefined && array.channels[1] !== null) { - green = cloneDragonNumber(array.channels[1]) + green = cloneDragonNumber(array.channels[1]); } - + if (array.channels[2] !== undefined && array.channels[2] !== null) { - blue = cloneDragonNumber(array.channels[2]) + blue = cloneDragonNumber(array.channels[2]); } - const joins = [] + const joins = []; for (const join of array.joins) { - const joinClone = cloneDragonArray(join) - joins.push(joinClone) + const joinClone = cloneDragonArray(join); + joins.push(joinClone); } - const clone = makeArray({stamp, channels: [red, green, blue], joins}) - return clone - } + const clone = makeArray({stamp, channels: [red, green, blue], joins}); + return clone; + }; - //==================// - // DRAGON - DIAGRAM // - //==================// - // Note: these functions don't check for safety at all - // for example, you can make an invalid diagram by having the left and right sides not match - // or you can make an invalid side by giving it two cells in the same place + //==================//; + // DRAGON - DIAGRAM //; + //==================//; + // Note: these functions don't check for safety at all; + // for example, you can make an invalid diagram by having the left and right sides not match; + // or you can make an invalid side by giving it two cells in the same place; - // Right can be undefined to represent a single-sided diagram + // Right can be undefined to represent a single-sided diagram; makeDiagram = ({left = [], right, joins = []} = {}) => { - return {left, right, isDiagram: true, joins} - } + return {left, right, isDiagram: true, joins}; + }; const cloneDiagram = (diagram) => { - const clone = makeDiagram() + const clone = makeDiagram(); for (const sideName of ["left", "right"]) { - if (diagram[sideName] === undefined) continue - const side = [] + if (diagram[sideName] === undefined) continue; + const side = []; for (const cell of diagram[sideName]) { - const cloneCell = cloneDiagramCell(cell) - side.push(cloneCell) + const cloneCell = cloneDiagramCell(cell); + side.push(cloneCell); } - clone[sideName] = side + clone[sideName] = side; } - return clone - } + return clone; + }; - // Content can be a dragon-array or another dragon-diagram + // Content can be a dragon-array or another dragon-diagram; makeDiagramCell = ({x = 0, y = 0, width = 1, height = 1, content = makeArray(), instruction = DRAGON_INSTRUCTION.recolour, splitX = 1, splitY = 1} = {}) => { - return {x, y, width, height, content, instruction, splitX, splitY} - } + return {x, y, width, height, content, instruction, splitX, splitY}; + }; const cloneDiagramCell = (cell) => { - const content = cloneDragonArray(cell.content) - return {...cell, content} - } + const content = cloneDragonArray(cell.content); + return {...cell, content}; + }; - // Make the entire diagram fit within 1x1 + // Make the entire diagram fit within 1x1; const normaliseDiagram = (diagram) => { - const [width, height] = getDiagramDimensions(diagram) + const [width, height] = getDiagramDimensions(diagram); for (const sideName of ["left", "right"]) { - const side = diagram[sideName] - if (side === undefined) continue + const side = diagram[sideName]; + if (side === undefined) continue; for (const diagramCell of side) { - diagramCell.width /= width - diagramCell.height /= height - diagramCell.x /= width - diagramCell.y /= height + diagramCell.width /= width; + diagramCell.height /= height; + diagramCell.x /= width; + diagramCell.y /= height; } } - return diagram - } + return diagram; + }; - // Make the SMALLEST cell of a diagram be 1x1 - // Note: doesn't really work if the diagram contains another diagram + // Make the SMALLEST cell of a diagram be 1x1; + // Note: doesn't really work if the diagram contains another diagram; const makeMaximisedDiagram = (diagram) => { - let smallestWidth = Infinity - let smallestHeight = Infinity + let smallestWidth = Infinity; + let smallestHeight = Infinity; for (const sideName of ["left", "right"]) { - const side = diagram[sideName] - if (side === undefined) continue + const side = diagram[sideName]; + if (side === undefined) continue; for (const diagramCell of side) { if (diagramCell.width < smallestWidth) { - smallestWidth = diagramCell.width + smallestWidth = diagramCell.width; } if (diagramCell.height < smallestHeight) { - smallestHeight = diagramCell.height + smallestHeight = diagramCell.height; } } } - const maximisedDiagram = makeDiagram({joins: diagram.joins}) + const maximisedDiagram = makeDiagram({joins: diagram.joins}); for (const sideName of ["left", "right"]) { - const side = diagram[sideName] - if (side === undefined) continue + const side = diagram[sideName]; + if (side === undefined) continue; if (maximisedDiagram[sideName] === undefined) { - maximisedDiagram[sideName] = [] + maximisedDiagram[sideName] = []; } for (const diagramCell of side) { - const maximisedDiagramCell = cloneDiagramCell(diagramCell) - maximisedDiagramCell.width /= smallestWidth - maximisedDiagramCell.height /= smallestHeight - maximisedDiagramCell.x /= smallestWidth - maximisedDiagramCell.y /= smallestHeight - maximisedDiagram[sideName].push(maximisedDiagramCell) + const maximisedDiagramCell = cloneDiagramCell(diagramCell); + maximisedDiagramCell.width /= smallestWidth; + maximisedDiagramCell.height /= smallestHeight; + maximisedDiagramCell.x /= smallestWidth; + maximisedDiagramCell.y /= smallestHeight; + maximisedDiagram[sideName].push(maximisedDiagramCell); } } - return maximisedDiagram - } + return maximisedDiagram; + }; const getDiagramDimensions = (diagram) => { - let left = Infinity - let right = -Infinity - let top = Infinity - let bottom = -Infinity + let left = Infinity; + let right = -Infinity; + let top = Infinity; + let bottom = -Infinity; for (const cell of diagram.left) { - const cleft = cell.x - const cright = cell.x + cell.width - const ctop = cell.y - const cbottom = cell.y + cell.height + const cleft = cell.x; + const cright = cell.x + cell.width; + const ctop = cell.y; + const cbottom = cell.y + cell.height; - if (cleft < left) left = cleft - if (ctop < top) top = ctop - if (cright > right) right = cright - if (cbottom > bottom) bottom = cbottom + if (cleft < left) left = cleft; + if (ctop < top) top = ctop; + if (cright > right) right = cright; + if (cbottom > bottom) bottom = cbottom; } - const width = right - left - const height = bottom - top + const width = right - left; + const height = bottom - top; - return [width, height] - } + return [width, height]; + }; - //===============// - // DRAGON - RULE // - //===============// + //===============//; + // DRAGON - RULE //; + //===============//; makeRule = ({steps = [], transformations = DRAGON_TRANSFORMATIONS.NONE, locked = true, chance} = {}) => { - return {steps, transformations, locked, chance} - } + return {steps, transformations, locked, chance}; + }; - //==========================// - // DRAGON - TRANSFORMATIONS // - //==========================// + //==========================//; + // DRAGON - TRANSFORMATIONS //; + //==========================//; DRAGON_TRANSFORMATIONS = { NONE: [ (x, y, w, h, W, H) => [x, y], @@ -1927,100 +1925,100 @@ on.load(() => { (x, y, w, h, W, H) => [-y-h+H, x], (x, y, w, h, W, H) => [-x-w+W, -y-h+H], (x, y, w, h, W, H) => [ y, -x-w+W], - + (x, y, w, h, W, H) => [-x-w+W, y], (x, y, w, h, W, H) => [-y-h+H, -x-w+W], (x, y, w, h, W, H) => [ x, -y-h+H], (x, y, w, h, W, H) => [ y, x], ] - } + }; const getTransformedRule = (rule, transformation, isTranslation) => { - // TODO: SHOULD THIS ACTUALLY BE THE MAX SIZE OF ALL STEPS COMBINED???? NOT SURE NEEDS TESTING - const [ruleWidth, ruleHeight] = getDiagramDimensions(rule.steps[0]) + // TODO: SHOULD THIS ACTUALLY BE THE MAX SIZE OF ALL STEPS COMBINED???? NOT SURE NEEDS TESTING; + const [ruleWidth, ruleHeight] = getDiagramDimensions(rule.steps[0]); - const steps = rule.steps.map(step => getTransformedDiagram(step, transformation, isTranslation, ruleWidth, ruleHeight)) + const steps = rule.steps.map(step => getTransformedDiagram(step, transformation, isTranslation, ruleWidth, ruleHeight)); - const transformedRule = makeRule({steps, transformations: rule.transformations, locked: rule.locked, chance: rule.chance}) - return transformedRule - } + const transformedRule = makeRule({steps, transformations: rule.transformations, locked: rule.locked, chance: rule.chance}); + return transformedRule; + }; const getTransformedDiagram = (diagram, transformation, isTranslation, ruleWidth, ruleHeight) => { - const {left, right} = diagram - const [diagramWidth, diagramHeight] = [ruleWidth, ruleHeight] + const {left, right} = diagram; + const [diagramWidth, diagramHeight] = [ruleWidth, ruleHeight]; - const transformedLeft = [] - const transformedRight = right === undefined? undefined : [] + const transformedLeft = []; + const transformedRight = right === undefined? undefined : []; for (let i = 0; i < left.length; i++) { - const leftCell = left[i] - const transformedLeftCell = getTransformedCell(leftCell, transformation, diagramWidth, diagramHeight, isTranslation) - transformedLeft.push(transformedLeftCell) + const leftCell = left[i]; + const transformedLeftCell = getTransformedCell(leftCell, transformation, diagramWidth, diagramHeight, isTranslation); + transformedLeft.push(transformedLeftCell); } for (let i = 0; i < right.length; i++) { - const rightCell = right[i] - const transformedRightCell = getTransformedCell(rightCell, transformation, diagramWidth, diagramHeight, isTranslation) - transformedRight.push(transformedRightCell) + const rightCell = right[i]; + const transformedRightCell = getTransformedCell(rightCell, transformation, diagramWidth, diagramHeight, isTranslation); + transformedRight.push(transformedRightCell); } - // Re-order the additional instruction from splitting - so that they are all in order from left-to-right, top-to-bottom - // This is because the engine passes cells to the instruction in that order - // (it does this specific static order for performance reasons - rather than dynamically finding the cells in any order you want) + // Re-order the additional instruction from splitting - so that they are all in order from left-to-right, top-to-bottom; + // This is because the engine passes cells to the instruction in that order; + // (it does this specific static order for performance reasons - rather than dynamically finding the cells in any order you want); for (let i = 0; i < transformedRight.length; i++) { - const diagramCell = transformedRight[i] - if (diagramCell.instruction.type !== "SPLIT") continue - const additionalInstructionCount = diagramCell.splitX * diagramCell.splitY - const additionalInstructions = transformedRight.slice(i+1, i+1+additionalInstructionCount) - const orderedAdditionalInstructions = getOrderedCellAtoms(additionalInstructions) - transformedRight.splice(i+1, additionalInstructionCount, ...orderedAdditionalInstructions) + const diagramCell = transformedRight[i]; + if (diagramCell.instruction.type !== "SPLIT") continue; + const additionalInstructionCount = diagramCell.splitX * diagramCell.splitY; + const additionalInstructions = transformedRight.slice(i+1, i+1+additionalInstructionCount); + const orderedAdditionalInstructions = getOrderedCellAtoms(additionalInstructions); + transformedRight.splice(i+1, additionalInstructionCount, ...orderedAdditionalInstructions); } - const transformedDiagram = makeDiagram({left: transformedLeft, right: transformedRight}) - return transformedDiagram - } + const transformedDiagram = makeDiagram({left: transformedLeft, right: transformedRight}); + return transformedDiagram; + }; const getTransformedCell = (cell, transformation, diagramWidth, diagramHeight, isTranslation = false) => { - let [x, y, width, height] = transformation(cell.x, cell.y, cell.width, cell.height, diagramWidth, diagramHeight) - - let {splitX, splitY} = cell + let [x, y, width, height] = transformation(cell.x, cell.y, cell.width, cell.height, diagramWidth, diagramHeight); - // Detect rotation, and rotate splitX and splitY if need be + let {splitX, splitY} = cell; + + // Detect rotation, and rotate splitX and splitY if need be; if (!isTranslation) { - const [testSplitX, testSplitY] = transformation(cell.splitX, cell.splitY, 1, 1, 1, 1).map(n => Math.abs(n)) - //print(transformation.toString(), splitX, splitY, "to", testSplitX, testSplitY) - splitX = testSplitX - splitY = testSplitY - - const [testWidth, testHeight] = transformation(cell.width, cell.height, 1, 1, 1, 1).map(n => Math.abs(n)) - //print(transformation.toString(), splitX, splitY, "to", testSplitX, testSplitY) - width = testWidth - height = testHeight - - } - - if (x === undefined) x = cell.x - if (y === undefined) y = cell.y - if (width === undefined) width = cell.width - if (height === undefined) height = cell.height - - // leftover from when content could be a sub-diagram - //const content = cell.content.isDiagram? getTransformedDiagram(cell.content, transformation) : cell.content - const content = cell.content - - return makeDiagramCell({x, y, splitX, splitY, width, height, content, instruction: cell.instruction}) - } + const [testSplitX, testSplitY] = transformation(cell.splitX, cell.splitY, 1, 1, 1, 1).map(n => Math.abs(n)); + //print(transformation.toString(), splitX, splitY, "to", testSplitX, testSplitY); + splitX = testSplitX; + splitY = testSplitY; + + const [testWidth, testHeight] = transformation(cell.width, cell.height, 1, 1, 1, 1).map(n => Math.abs(n)); + //print(transformation.toString(), splitX, splitY, "to", testSplitX, testSplitY); + width = testWidth; + height = testHeight; + + } + + if (x === undefined) x = cell.x; + if (y === undefined) y = cell.y; + if (width === undefined) width = cell.width; + if (height === undefined) height = cell.height; - //=================// - // DRAGON - BEHAVE // - //=================// - // From a rule, register 'behave' functions that get used to implement the rules in the engine - // Note: This function doesn't check for safety - // eg: If it is a locked-in rule or not - // Or if the left side matches the shape of the right side + // leftover from when content could be a sub-diagram; + //const content = cell.content.isDiagram? getTransformedDiagram(cell.content, transformation) : cell.content; + const content = cell.content; + + return makeDiagramCell({x, y, splitX, splitY, width, height, content, instruction: cell.instruction}); + }; + + //=================//; + // DRAGON - BEHAVE //; + //=================//; + // From a rule, register 'behave' functions that get used to implement the rules in the engine; + // Note: This function doesn't check for safety; + // eg: If it is a locked-in rule or not; + // Or if the left side matches the shape of the right side; const chanceAmounts = [ 0.000001, 0.00001, @@ -2029,506 +2027,504 @@ on.load(() => { 0.01, 0.1, 1.0, - ] + ]; registerRule = (rule) => { - // Apply Symmetry! - const transformedRules = [] + // Apply Symmetry!; + const transformedRules = []; for (const transformation of rule.transformations) { - const transformedRule = getTransformedRule(rule, transformation) - transformedRules.push(transformedRule) + const transformedRule = getTransformedRule(rule, transformation); + transformedRules.push(transformedRule); } - // Get Redundant Rules! - const redundantRules = [] + // Get Redundant Rules!; + const redundantRules = []; for (const transformedRule of transformedRules) { - const redundantTransformedRules = getRedundantRules(transformedRule) - redundantRules.push(...redundantTransformedRules) + const redundantTransformedRules = getRedundantRules(transformedRule); + redundantRules.push(...redundantTransformedRules); } - // Make behave functions!!! - const behaveFunctions = [] + // Make behave functions!!!; + const behaveFunctions = []; for (const redundantRule of redundantRules) { - const behaveFunction = makeBehaveFunction(redundantRule) - behaveFunctions.push(behaveFunction) - state.dragon.behaves.push(behaveFunction) + const behaveFunction = makeBehaveFunction(redundantRule); + behaveFunctions.push(behaveFunction); + state.dragon.behaves.push(behaveFunction); } - return {redundantRules, transformedRules, behaveFunctions} + return {redundantRules, transformedRules, behaveFunctions}; - } + }; const unregisterRegistry = ({behaveFunctions}) => { - state.dragon.behaves = state.dragon.behaves.filter(behaveFunction => !behaveFunctions.includes(behaveFunction)) - } + state.dragon.behaves = state.dragon.behaves.filter(behaveFunction => !behaveFunctions.includes(behaveFunction)); + }; - // For one rule, we could take its 'origin' as any of the cells in the first step - // This function gets all those redundant variations + // For one rule, we could take its 'origin' as any of the cells in the first step; + // This function gets all those redundant variations; const getRedundantRules = (rule) => { - - const redundantRules = [] - const [head] = rule.steps + + const redundantRules = []; + const [head] = rule.steps; for (const cell of head.left) { const transformation = (x, y, width = 1, height = 1) => { - const newWidth = width / cell.width - const newHeight = height / cell.height + const newWidth = width / cell.width; + const newHeight = height / cell.height; - const newX = (x - cell.x) * 1/cell.width - const newY = (y - cell.y) * 1/cell.height + const newX = (x - cell.x) * 1/cell.width; + const newY = (y - cell.y) * 1/cell.height; - return [newX, newY, newWidth, newHeight] + return [newX, newY, newWidth, newHeight]; - } - const redundantRule = getTransformedRule(rule, transformation, true) - redundantRules.push(redundantRule) + }; + const redundantRule = getTransformedRule(rule, transformation, true); + redundantRules.push(redundantRule); } - return redundantRules - - } + return redundantRules; + + }; const getStampNamesOfStep = (step) => { - const stampNames = new Set() - const sides = [step.left, step.right] + const stampNames = new Set(); + const sides = [step.left, step.right]; for (const side of sides) { for (const diagramCell of side) { - const stamp = diagramCell.content.stamp - if (stamp === undefined) continue - stampNames.add(stamp) + const stamp = diagramCell.content.stamp; + if (stamp === undefined) continue; + stampNames.add(stamp); } } - return [...stampNames.values()] - } + return [...stampNames.values()]; + }; const makeBehaveFunction = (rule) => { - const stepFunctions = [] + const stepFunctions = []; for (const step of rule.steps) { - const stampNames = getStampNamesOfStep(step) - const conditionFunction = makeConditionFunction(step, stampNames, rule.chance) - const resultFunction = makeResultFunction(step, stampNames) + const stampNames = getStampNamesOfStep(step); + const conditionFunction = makeConditionFunction(step, stampNames, rule.chance); + const resultFunction = makeResultFunction(step, stampNames); const stepFunction = (origin, redraw) => { - const [neighbours, stamps] = conditionFunction(origin) - if (neighbours === undefined) return - return resultFunction(neighbours, redraw, stamps) - } + const [neighbours, stamps] = conditionFunction(origin); + if (neighbours === undefined) return; + return resultFunction(neighbours, redraw, stamps); + }; - stepFunctions.push(stepFunction) + stepFunctions.push(stepFunction); } const behaveFunction = (origin, redraw) => { - let count = 1 + let count = 1; for (const stepFunction of stepFunctions) { - const drawn = stepFunction(origin, redraw) - if (drawn !== undefined) return drawn + const drawn = stepFunction(origin, redraw); + if (drawn !== undefined) return drawn; } - return undefined - } + return undefined; + }; - return behaveFunction + return behaveFunction; - } + }; const makeConditionFunction = (diagram, stampNames, chance = 6) => { - const conditions = [] + const conditions = []; for (const cell of diagram.left) { - const splashes = getSplashesSetFromArray(cell.content) + const splashes = getSplashesSetFromArray(cell.content); const condition = (origin) => { - - const width = cell.width * origin.width - const height = cell.height * origin.height - let x = origin.x + cell.x*origin.width - let y = origin.y + cell.y*origin.height + const width = cell.width * origin.width; + const height = cell.height * origin.height; + + let x = origin.x + cell.x*origin.width; + let y = origin.y + cell.y*origin.height; if (edgeMode === 1) { - while (x >= 1) x -= 1 - while (y >= 1) y -= 1 - while (x < 0) x += 1 - while (y < 0) y += 1 + while (x >= 1) x -= 1; + while (y >= 1) y -= 1; + while (x < 0) x += 1; + while (y < 0) y += 1; } - const centerX = x + width/2 - const centerY = y + height/2 + const centerX = x + width/2; + const centerY = y + height/2; - const neighbour = pickCell(centerX, centerY) + const neighbour = pickCell(centerX, centerY); - if (neighbour === undefined) return [undefined, undefined] - if (neighbour.left !== x) return [undefined, undefined] - if (neighbour.top !== y) return [undefined, undefined] - if (neighbour.width !== width) return [undefined, undefined] - if (neighbour.height !== height) return [undefined, undefined] - if (!splashes.has(neighbour.colour)) return [undefined, undefined] - - return [neighbour, cell.content.stamp] - } + if (neighbour === undefined) return [undefined, undefined]; + if (neighbour.left !== x) return [undefined, undefined]; + if (neighbour.top !== y) return [undefined, undefined]; + if (neighbour.width !== width) return [undefined, undefined]; + if (neighbour.height !== height) return [undefined, undefined]; + if (!splashes.has(neighbour.colour)) return [undefined, undefined]; - conditions.push(condition) + return [neighbour, cell.content.stamp]; + }; + + conditions.push(condition); } - const chanceAmount = Math.round(10 ** (3-chance/2)) - const chanceCondition = () => oneIn(chanceAmount) + const chanceAmount = Math.round(10 ** (3-chance/2)); + const chanceCondition = () => oneIn(chanceAmount); const conditionFunction = (origin) => { - if (chance !== undefined && !chanceCondition()) return [undefined, undefined] + if (chance !== undefined && !chanceCondition()) return [undefined, undefined]; - const neighbours = [] - const stamps = {} + const neighbours = []; + const stamps = {}; for (const stamp of stampNames) { - stamps[stamp] = [] + stamps[stamp] = []; } for (const condition of conditions) { - const [neighbour, stamp] = condition(origin) - if (neighbour === undefined) return [undefined, undefined] + const [neighbour, stamp] = condition(origin); + if (neighbour === undefined) return [undefined, undefined]; if (stamp !== undefined) { - stamps[stamp].push(neighbour.colour) + stamps[stamp].push(neighbour.colour); } - neighbours.push(neighbour) + neighbours.push(neighbour); } - return [neighbours, stamps] - } + return [neighbours, stamps]; + }; - return conditionFunction - } + return conditionFunction; + }; - // TODO: also support Merging (with some funky backend syntax if needed) - // TODO: also support Splitting (with some funky backend syntax if needed) - // this funky syntax could include dummy cells on the right + // TODO: also support Merging (with some funky backend syntax if needed); + // TODO: also support Splitting (with some funky backend syntax if needed); + // this funky syntax could include dummy cells on the right; const makeResultFunction = (diagram, stampNames) => { - const results = [] + const results = []; for (const cell of diagram.right) { - const result = cell.instruction(cell) - results.push(result) + const result = cell.instruction(cell); + results.push(result); } const refillAllStampRemainers = (remainers, stamps, stampNames) => { for (const stampName of stampNames) { - refillStampRemainer(remainers, stamps, stampName) + refillStampRemainer(remainers, stamps, stampName); } - } + }; const refillStampRemainer = (remainers, stamps, stampName) => { - remainers[stampName] = [...stamps[stampName]] - } + remainers[stampName] = [...stamps[stampName]]; + }; return (neighbours, redraw, stamps) => { - let drawn = 0 - let neighbourId = 0 - let skip = 0 - const bonusTargets = [] + let drawn = 0; + let neighbourId = 0; + let skip = 0; + const bonusTargets = []; - const stampRemainers = {} - refillAllStampRemainers(stampRemainers, stamps, stampNames) + const stampRemainers = {}; + refillAllStampRemainers(stampRemainers, stamps, stampNames); for (const instruction of results) { - const target = bonusTargets.length > 0? bonusTargets.pop() : neighbours[neighbourId] + const target = bonusTargets.length > 0? bonusTargets.pop() : neighbours[neighbourId]; - const result = instruction(target, redraw, neighbours, neighbourId, stampRemainers) + const result = instruction(target, redraw, neighbours, neighbourId, stampRemainers); if (result.stampNameTakenFrom !== undefined) { if (stampRemainers[result.stampNameTakenFrom].length === 0) { - refillStampRemainer(stampRemainers, stamps, result.stampNameTakenFrom) + refillStampRemainer(stampRemainers, stamps, result.stampNameTakenFrom); } } - const {drawn: resultDrawn, bonusTargets: resultBonusTargets, skip: resultSkip} = result - if (resultSkip !== undefined) skip += resultSkip - drawn += resultDrawn + const {drawn: resultDrawn, bonusTargets: resultBonusTargets, skip: resultSkip} = result; + if (resultSkip !== undefined) skip += resultSkip; + drawn += resultDrawn; if (resultBonusTargets !== undefined) { - bonusTargets.push(...resultBonusTargets) + bonusTargets.push(...resultBonusTargets); } - + if (bonusTargets.length === 0) { - neighbourId++ + neighbourId++; if (skip > 0) { - neighbourId++ - skip-- + neighbourId++; + skip--; } } } - return drawn - } - - return undefined - } + return drawn; + }; + }; const isDiagramCellFullyInside = (cell, target) => { - const cleft = cell.x - const ctop = cell.top - const cright = cell.x + cell.width - const cbottom = cell.top + cell.height + const cleft = cell.x; + const ctop = cell.top; + const cright = cell.x + cell.width; + const cbottom = cell.top + cell.height; - const tleft = target.x - const ttop = target.top - const tright = target.x + target.width - const tbottom = target.top + target.height + const tleft = target.x; + const ttop = target.top; + const tright = target.x + target.width; + const tbottom = target.top + target.height; - if (cleft < tleft) return false - if (ctop < ttop) return false - if (cbottom > tbottom) return false - if (cright > tright) return false + if (cleft < tleft) return false; + if (ctop < ttop) return false; + if (cbottom > tbottom) return false; + if (cright > tright) return false; - return true + return true; - } + }; - //======================// - // DRAGON - INSTRUCTION // - //======================// - // These are the different types of instructions available for the right-hand-side of rules - // Default is recolour - DRAGON_INSTRUCTION = {} + //======================//; + // DRAGON - INSTRUCTION //; + //======================//; + // These are the different types of instructions available for the right-hand-side of rules; + // Default is recolour; + DRAGON_INSTRUCTION = {}; DRAGON_INSTRUCTION.nothing = (cell) => () => { - return {drawn: 0} - } - DRAGON_INSTRUCTION.nothing.type = "NOTHING" + return {drawn: 0}; + }; + DRAGON_INSTRUCTION.nothing.type = "NOTHING"; DRAGON_INSTRUCTION.recolour = (cell) => { - fillEmptyChannels(cell.content) + fillEmptyChannels(cell.content); - const splashes = getSplashesArrayFromArray(cell.content) - const isDynamic = isDragonArrayDynamic(cell.content) + const splashes = getSplashesArrayFromArray(cell.content); + const isDynamic = isDragonArrayDynamic(cell.content); const instruction = (target, redraw, neighbours, neighbourId, stamps) => { - let colour = undefined - let source = target.colour - let stampNameTakenFrom = undefined + let colour; + let source = target.colour; + let stampNameTakenFrom; if (cell.content.stamp === undefined) { - colour = splashes[Random.Uint32 % splashes.length] + colour = splashes[Random.Uint32 % splashes.length]; } else { - const stampChoices = stamps[cell.content.stamp] + const stampChoices = stamps[cell.content.stamp]; if (stampChoices.length === 0) { - colour = splashes[Random.Uint32 % splashes.length] + colour = splashes[Random.Uint32 % splashes.length]; } else { - const stampId = Random.Uint32 % stampChoices.length - source = stampChoices[stampId] - colour = source - stampChoices.splice(stampId, 1) - stampNameTakenFrom = cell.content.stamp + const stampId = Random.Uint32 % stampChoices.length; + source = stampChoices[stampId]; + colour = source; + stampChoices.splice(stampId, 1); + stampNameTakenFrom = cell.content.stamp; } } if (isDynamic) { - const parts = getRGB(colour) + const parts = getRGB(colour); for (let i = 0; i < cell.content.channels.length; i++) { - const channel = cell.content.channels[i] - if (channel.variable === undefined) continue - let results = VARIABLE_EVALUATOR[channel.variable](channel, {source}) - const isHue = channel.variable === "red" + const channel = cell.content.channels[i]; + if (channel.variable === undefined) continue; + let results = VARIABLE_EVALUATOR[channel.variable](channel, {source}); + const isHue = channel.variable === "red"; if (channel.add !== undefined) { - results = addChannelToResults(results, channel.add, {source, multiplier: 1, isHue}) + results = addChannelToResults(results, channel.add, {source, multiplier: 1, isHue}); } if (channel.subtract !== undefined) { - results = addChannelToResults(results, channel.subtract, {source, multiplier: -1, isHue}) + results = addChannelToResults(results, channel.subtract, {source, multiplier: -1, isHue}); } - const choices = results.map((v, i) => v === true? i : false).filter(v => v !== false) - const newPart = choices[Random.Uint8 % choices.length] - colour -= parts[i] - const m = 10 ** (2-i) - colour += newPart * m + const choices = results.map((v, i) => v === true? i : false).filter(v => v !== false); + const newPart = choices[Random.Uint8 % choices.length]; + colour -= parts[i]; + const m = 10 ** (2-i); + colour += newPart * m; } } - let drawn = 0 - if (redraw) drawn += queueCellDraw(target, colour, true) - else target.colour = colour + let drawn = 0; + if (redraw) drawn += queueCellDraw(target, colour, true); + else target.colour = colour; - return {drawn, stampNameTakenFrom} - } + return {drawn, stampNameTakenFrom}; + }; - return instruction - } - DRAGON_INSTRUCTION.recolour.type = "RECOLOUR" + return instruction; + }; + DRAGON_INSTRUCTION.recolour.type = "RECOLOUR"; const addChannelToResults = (augendResults, addend, {source, multiplier = 1, isHue = false}) => { - let addendResults = addend.values + let addendResults = addend.values; if (addend.variable !== undefined) { - addendResults = VARIABLE_EVALUATOR[addend.variable](addend, {source}) + addendResults = VARIABLE_EVALUATOR[addend.variable](addend, {source}); } - const addendChoices = addendResults.map((v, i) => v === true? i : false).filter(v => v !== false) - const augendChoices = augendResults.map((v, i) => v === true? i : false).filter(v => v !== false) - - const choices = new Set() + const addendChoices = addendResults.map((v, i) => v === true? i : false).filter(v => v !== false); + const augendChoices = augendResults.map((v, i) => v === true? i : false).filter(v => v !== false); + + const choices = new Set(); for (const augendChoice of augendChoices) { for (const addendChoice of addendChoices) { - const choice = isHue? clamp(augendChoice + addendChoice*multiplier, 0, 9) : clamp(augendChoice + addendChoice*multiplier, 0, 9) - choices.add(choice) + const choice = isHue? clamp(augendChoice + addendChoice*multiplier, 0, 9) : clamp(augendChoice + addendChoice*multiplier, 0, 9); + choices.add(choice); } } - let results = [false, false, false, false, false, false, false, false, false, false] + let results = [false, false, false, false, false, false, false, false, false, false]; for (const choice of choices) { - results[choice] = true + results[choice] = true; } if (addend.add !== undefined) { - results = addChannelToResults(results, addend.add, {source, multiplier: 1, isHue}) + results = addChannelToResults(results, addend.add, {source, multiplier: 1, isHue}); } if (addend.subtract !== undefined) { - results = addChannelToResults(results, addend.add, {source, multiplier: -1, isHue}) + results = addChannelToResults(results, addend.add, {source, multiplier: -1, isHue}); } - - return results - - } - // A SPLIT REQUIRES THE CORRECT NUMBER OF RECOLOUR COMMANDS AFTER IT - // IF YOU DON'T, IT WILL GO WRONG + return results; + + }; + + // A SPLIT REQUIRES THE CORRECT NUMBER OF RECOLOUR COMMANDS AFTER IT; + // IF YOU DON'T, IT WILL GO WRONG; DRAGON_INSTRUCTION.split = (cell) => { - //const splashes = getSplashesArrayFromArray(cell.content) + //const splashes = getSplashesArrayFromArray(cell.content); const instruction = (target, redraw, neighbours, neighbourId, stamps) => { - - const children = splitCell(target, cell.splitX, cell.splitY) - //const [head, ...tail] = children - /*const colour = splashes[Random.Uint32 % splashes.length] - let drawn = 0 - if (redraw) drawn += setCellColour(head, colour, true) + const children = splitCell(target, cell.splitX, cell.splitY); + //const [head, ...tail] = children; + + /*const colour = splashes[Random.Uint32 % splashes.length]; + let drawn = 0; + if (redraw) drawn += setCellColour(head, colour, true); else head.colour = colour*/ - return {drawn: 0, bonusTargets: children.reverse()} + return {drawn: 0, bonusTargets: children.reverse()}; - } - return instruction + }; + return instruction; - } - DRAGON_INSTRUCTION.split.type = "SPLIT" + }; + DRAGON_INSTRUCTION.split.type = "SPLIT"; DRAGON_INSTRUCTION.merge = (cell) => { - //const splashes = getSplashesArrayFromArray(cell.content) - - const childCount = Math.abs(cell.splitX) * Math.abs(cell.splitY) + //const splashes = getSplashesArrayFromArray(cell.content); + + const childCount = Math.abs(cell.splitX) * Math.abs(cell.splitY); const instruction = (target, redraw, neighbours, neighbourId, stamps) => { - const children = neighbours.slice(neighbourId, neighbourId+childCount) - const merged = mergeCells(children) + const children = neighbours.slice(neighbourId, neighbourId+childCount); + const merged = mergeCells(children); - /*const colour = splashes[Random.Uint32 % splashes.length] - let drawn = 0 - if (redraw) drawn += setCellColour(merged, colour) + /*const colour = splashes[Random.Uint32 % splashes.length]; + let drawn = 0; + if (redraw) drawn += setCellColour(merged, colour); else merged.colour = colour*/ - return {drawn: 0, skip: childCount-1, bonusTargets: [merged]} + return {drawn: 0, skip: childCount-1, bonusTargets: [merged]}; - } + }; - return instruction + return instruction; - } - DRAGON_INSTRUCTION.merge.type = "MERGE" + }; + DRAGON_INSTRUCTION.merge.type = "MERGE"; - //=================// - // DRAGON - ORIGIN // - //=================// - // The origin is the cell at (0,0) of the first step of a rule - // It is the cell/colour that triggers the rule + //=================//; + // DRAGON - ORIGIN //; + //=================//; + // The origin is the cell at (0,0) of the first step of a rule; + // It is the cell/colour that triggers the rule ; const getOriginOfRule = (rule) => { - const head = rule.steps[0] - return getOriginOfDiagram(head) - } + const head = rule.steps[0]; + return getOriginOfDiagram(head); + }; const getOriginOfDiagram = (diagram) => { for (const cell of diagram.left) { - if (cell.x === 0 && cell.y === 0) return cell + if (cell.x === 0 && cell.y === 0) return cell; } - } + }; - //================// - // DRAGON - DEBUG // - //================// + //================//; + // DRAGON - DEBUG //; + //================//; debugRegistry = (registry, {transforms = true, redundants = true} = {}) => { - const {redundantRules, transformedRules} = registry - print("") - print("=================================================================") + const {redundantRules, transformedRules} = registry; + print(""); + print("================================================================="); if (transforms) { - print("") - print("TRANSFORMED RULES") + print(""); + print("TRANSFORMED RULES"); for (const rule of transformedRules) { - debugRule(rule) + debugRule(rule); } } if (redundants) { - print("") - print("REDUNDANT RULES") + print(""); + print("REDUNDANT RULES"); for (const rule of redundantRules) { - debugRule(rule) + debugRule(rule); } } - } + }; debugRule = (rule) => { for (const step of rule.steps) { - print("") - print("=== LEFT ===") + print(""); + print("=== LEFT ==="); for (const cell of step.left) { - debugDiagramCell(cell, {read: true}) + debugDiagramCell(cell, {read: true}); } - print("=== RIGHT ===") + print("=== RIGHT ==="); for (const cell of step.right) { - debugDiagramCell(cell) + debugDiagramCell(cell); } } - } + }; debugDiagramCell = (cell, {read = false} = {}) => { if (read) { - print("CHECK", "at", cell.x, cell.y, "with size", cell.width, cell.height, "for", getSplashesArrayFromArray(cell.content)) + print("CHECK", "at", cell.x, cell.y, "with size", cell.width, cell.height, "for", getSplashesArrayFromArray(cell.content)); } else { if (cell.instruction.type === "NOTHING") { - print(cell.instruction.type, "at", cell.x, cell.y, "with size", cell.width, cell.height) + print(cell.instruction.type, "at", cell.x, cell.y, "with size", cell.width, cell.height); } else if (cell.instruction.type === "RECOLOUR") { - print(cell.instruction.type, "at", cell.x, cell.y, "with size", cell.width, cell.height, "to", getSplashesArrayFromArray(cell.content)) + print(cell.instruction.type, "at", cell.x, cell.y, "with size", cell.width, cell.height, "to", getSplashesArrayFromArray(cell.content)); } else { - print(cell.instruction.type, cell.splitX, cell.splitY, "at", cell.x, cell.y, "with size", cell.width, cell.height) + print(cell.instruction.type, cell.splitX, cell.splitY, "at", cell.x, cell.y, "with size", cell.width, cell.height); } } - } + }; - const GREY = makeArrayFromSplash(Colour.Grey.splash) - const BLACK = makeArrayFromSplash(Colour.Black.splash) - const CYAN = makeArrayFromSplash(Colour.Cyan.splash) - const BLUE = makeArrayFromSplash(Colour.Blue.splash) - const YELLOW = makeArrayFromSplash(Colour.Yellow.splash) - const PURPLE = makeArrayFromSplash(Colour.Cyan.splash - 111) - const RED = makeArrayFromSplash(Colour.Red.splash) - let [RED_R, RED_G, RED_B] = getRGB(Colour.Red.splash) - RED_R /= 100 - RED_G /= 10 - /*BLACK.channels[0].values[RED_R] = true - BLACK.channels[1].values[RED_G] = true + const GREY = makeArrayFromSplash(Colour.Grey.splash); + const BLACK = makeArrayFromSplash(Colour.Black.splash); + const CYAN = makeArrayFromSplash(Colour.Cyan.splash); + const BLUE = makeArrayFromSplash(Colour.Blue.splash); + const YELLOW = makeArrayFromSplash(Colour.Yellow.splash); + const PURPLE = makeArrayFromSplash(Colour.Cyan.splash - 111); + const RED = makeArrayFromSplash(Colour.Red.splash); + let [RED_R, RED_G, RED_B] = getRGB(Colour.Red.splash); + RED_R /= 100; + RED_G /= 10; + /*BLACK.channels[0].values[RED_R] = true; + BLACK.channels[1].values[RED_G] = true; BLACK.channels[2].values[RED_B] = true*/ const ROCK_FALL_DIAGRAM = makeDiagram({ @@ -2540,8 +2536,8 @@ on.load(() => { makeDiagramCell({x: 0, y: 0, content: BLACK}), makeDiagramCell({x: 0, y: 1, content: GREY}), ], - }) - + }); + const SAND_FALL_DIAGRAM = makeDiagram({ left: [ makeDiagramCell({x: 0, y: 0, content: YELLOW}), @@ -2551,8 +2547,8 @@ on.load(() => { makeDiagramCell({x: 0, y: 0, content: BLACK}), makeDiagramCell({x: 0, y: 1, content: YELLOW}), ], - }) - + }); + const SAND_SLIDE_DIAGRAM = makeDiagram({ left: [ makeDiagramCell({x: 0, y: 0, content: YELLOW}), @@ -2562,14 +2558,14 @@ on.load(() => { makeDiagramCell({x: 0, y: 0, content: BLACK}), makeDiagramCell({x: 1, y: 1, content: YELLOW}), ], - }) + }); const WATER_RIGHT = makeDiagram({ left: [ makeDiagramCell({x: 0, y: 0, content: CYAN}), makeDiagramCell({x: 1, y: 0, content: BLUE}), ], - }) + }); const WATER_RIGHT_FALL = makeDiagram({ left: [ @@ -2580,7 +2576,7 @@ on.load(() => { makeDiagramCell({x: 0, y: 0, content: BLACK}), makeDiagramCell({x: 0, y: 1, content: BLUE}), ], - }) + }); const WATER_RIGHT_FLIP = makeDiagram({ left: [ @@ -2591,7 +2587,7 @@ on.load(() => { makeDiagramCell({x: 0, y: 0, content: CYAN}), makeDiagramCell({x: 1, y: 0, content: PURPLE}), ], - }) + }); const WATER_RIGHT_SLIP = makeDiagram({ left: [ @@ -2601,7 +2597,7 @@ on.load(() => { makeDiagramCell({x: 0, y: 0, width: 0.5, content: PURPLE, instruction: DRAGON_INSTRUCTION.split, splitX: 2, splitY: 1}), makeDiagramCell({x: 0.5, y: 0, width: 0.5, content: CYAN}), ], - }) + }); const WATER_RIGHT_UNSLIP = makeDiagram({ left: [ @@ -2613,7 +2609,7 @@ on.load(() => { makeDiagramCell({x: 0, y: 0, width: 2, content: BLUE, instruction: DRAGON_INSTRUCTION.merge, splitX: 2, splitY: 1}), makeDiagramCell({x: 0, y: 1, width: 2, content: BLACK}), ], - }) + }); const WATER_RIGHT_UNSLIPP = makeDiagram({ left: [ @@ -2623,7 +2619,7 @@ on.load(() => { right: [ makeDiagramCell({x: 0, y: 0, width: 2, content: BLUE, instruction: DRAGON_INSTRUCTION.merge, splitX: 2, splitY: 1}), ], - }) + }); const WATER_RIGHT_UNSLIPC = makeDiagram({ left: [ @@ -2633,7 +2629,7 @@ on.load(() => { right: [ makeDiagramCell({x: 0, y: 0, width: 2, content: BLUE, instruction: DRAGON_INSTRUCTION.merge, splitX: 2, splitY: 1}), ], - }) + }); const WATER_RIGHT_SLIDE = makeDiagram({ left: [ @@ -2646,7 +2642,7 @@ on.load(() => { makeDiagramCell({x: 2, y: 0, width: 1, content: PURPLE, instruction: DRAGON_INSTRUCTION.split, splitX: 2, splitY: 1}), makeDiagramCell({x: 3, y: 0, width: 1, content: CYAN}), ], - }) + }); const WATER_RIGHT_FALL_BLUE = makeDiagram({ left: [ @@ -2659,7 +2655,7 @@ on.load(() => { makeDiagramCell({x: 0, y: 1, width: 0.5, content: BLUE, instruction: DRAGON_INSTRUCTION.split, splitX: 2, splitY: 1}), makeDiagramCell({x: 0.5, y: 1, width: 0.5, content: BLUE}), ], - }) + }); const WATER_RIGHT_FALL_CYAN = makeDiagram({ left: [ @@ -2672,8 +2668,8 @@ on.load(() => { makeDiagramCell({x: 0, y: 1, width: 0.5, content: CYAN, instruction: DRAGON_INSTRUCTION.split, splitX: 2, splitY: 1}), makeDiagramCell({x: 0.5, y: 1, width: 0.5, content: CYAN}), ], - }) - + }); + const WATER_RIGHT_SPIN = makeDiagram({ left: [ @@ -2684,8 +2680,8 @@ on.load(() => { makeDiagramCell({x: 0, y: 0, content: CYAN}), makeDiagramCell({x: 1, y: 0, content: BLUE}), ], - }) - + }); + const WATER_RIGHT_SPAWN_DIAGRAM = makeDiagram({ left: [ makeDiagramCell({x: 0, y: 0, content: PURPLE}), @@ -2694,8 +2690,8 @@ on.load(() => { makeDiagramCell({x: 0, y: 0, width: 0.5, content: BLUE, instruction: DRAGON_INSTRUCTION.split, splitX: 2, splitY: 1}), makeDiagramCell({x: 0.5, y: 0, width: 0.5, content: CYAN, instruction: DRAGON_INSTRUCTION.recolour}), ], - }) - + }); + const WATER_RIGHT_RESPAWN_BLUE = makeDiagram({ left: [ makeDiagramCell({x: 0, y: 0, width: 1, content: BLUE}), @@ -2705,8 +2701,8 @@ on.load(() => { makeDiagramCell({x: 0, y: 0, width: 1, content: BLUE}), makeDiagramCell({x: 1, y: 0, width: 1, content: CYAN}), ], - }) - + }); + const WATER_RIGHT_RESPAWN_CYAN = makeDiagram({ left: [ makeDiagramCell({x: 0, y: 0, width: 1, content: CYAN}), @@ -2716,8 +2712,8 @@ on.load(() => { makeDiagramCell({x: 0, y: 0, width: 1, content: BLUE}), makeDiagramCell({x: 1, y: 0, width: 1, content: CYAN}), ], - }) - + }); + const WATER_DARK_FALL = makeDiagram({ left: [ makeDiagramCell({x: 0, y: 0, width: 1, content: GREY}), @@ -2727,8 +2723,8 @@ on.load(() => { makeDiagramCell({x: 0, y: 0, width: 1, content: BLACK}), makeDiagramCell({x: 0, y: 1, width: 1, content: GREY}), ], - }) - + }); + const WATER_DARK_SLIP = makeDiagram({ left: [ makeDiagramCell({x: 0, y: 0, width: 1, content: GREY}), @@ -2738,32 +2734,32 @@ on.load(() => { makeDiagramCell({x: 0, y: 0, width: 1, content: BLACK}), makeDiagramCell({x: 1, y: 0, width: 1, content: GREY}), ], - }) - - //registerRule(makeRule({steps: [ROCK_FALL_DIAGRAM], transformations: DRAGON_TRANSFORMATIONS.NONE})) - /*registerRule(makeRule({steps: [SAND_FALL_DIAGRAM], transformations: DRAGON_TRANSFORMATIONS.NONE})) - registerRule(makeRule({steps: [SAND_SLIDE_DIAGRAM], transformations: DRAGON_TRANSFORMATIONS.X})) + }); + + //registerRule(makeRule({steps: [ROCK_FALL_DIAGRAM], transformations: DRAGON_TRANSFORMATIONS.NONE})); + /*registerRule(makeRule({steps: [SAND_FALL_DIAGRAM], transformations: DRAGON_TRANSFORMATIONS.NONE})); + registerRule(makeRule({steps: [SAND_SLIDE_DIAGRAM], transformations: DRAGON_TRANSFORMATIONS.X})); - registerRule(makeRule({steps: [WATER_DARK_FALL], transformations: DRAGON_TRANSFORMATIONS.NONE})) + registerRule(makeRule({steps: [WATER_DARK_FALL], transformations: DRAGON_TRANSFORMATIONS.NONE})); registerRule(makeRule({steps: [WATER_DARK_SLIP], transformations: DRAGON_TRANSFORMATIONS.X}))*/ - //registerRule(makeRule({steps: [WATER_RIGHT_SPAWN_DIAGRAM], transformations: DRAGON_TRANSFORMATIONS.X})) - //registerRule(makeRule({steps: [WATER_RIGHT_FALL], transformations: DRAGON_TRANSFORMATIONS.X})) - //registerRule(makeRule({steps: [WATER_RIGHT_SLIP], transformations: DRAGON_TRANSFORMATIONS.X})) - //registerRule(makeRule({steps: [WATER_RIGHT_SLIDE, WATER_RIGHT_FLIP], transformations: DRAGON_TRANSFORMATIONS.X})) - //registerRule(makeRule({steps: [WATER_RIGHT_UNSLIP], transformations: DRAGON_TRANSFORMATIONS.X})) - //registerRule(makeRule({steps: [WATER_RIGHT_UNSLIPP], transformations: DRAGON_TRANSFORMATIONS.NONE})) - //registerRule(makeRule({steps: [WATER_RIGHT_UNSLIPC], transformations: DRAGON_TRANSFORMATIONS.NONE})) - //registerRule(makeRule({steps: [], transformations: DRAGON_TRANSFORMATIONS.X})) - //registerRule(makeRule({steps: [WATER_RIGHT_FALL_CYAN], transformations: DRAGON_TRANSFORMATIONS.NONE})) - //registerRule(makeRule({steps: [WATER_RIGHT_SLIDE_DIAGRAM, WATER_RIGHT_SPIN], transformations: DRAGON_TRANSFORMATIONS.X})) + //registerRule(makeRule({steps: [WATER_RIGHT_SPAWN_DIAGRAM], transformations: DRAGON_TRANSFORMATIONS.X})); + //registerRule(makeRule({steps: [WATER_RIGHT_FALL], transformations: DRAGON_TRANSFORMATIONS.X})); + //registerRule(makeRule({steps: [WATER_RIGHT_SLIP], transformations: DRAGON_TRANSFORMATIONS.X})); + //registerRule(makeRule({steps: [WATER_RIGHT_SLIDE, WATER_RIGHT_FLIP], transformations: DRAGON_TRANSFORMATIONS.X})); + //registerRule(makeRule({steps: [WATER_RIGHT_UNSLIP], transformations: DRAGON_TRANSFORMATIONS.X})); + //registerRule(makeRule({steps: [WATER_RIGHT_UNSLIPP], transformations: DRAGON_TRANSFORMATIONS.NONE})); + //registerRule(makeRule({steps: [WATER_RIGHT_UNSLIPC], transformations: DRAGON_TRANSFORMATIONS.NONE})); + //registerRule(makeRule({steps: [], transformations: DRAGON_TRANSFORMATIONS.X})); + //registerRule(makeRule({steps: [WATER_RIGHT_FALL_CYAN], transformations: DRAGON_TRANSFORMATIONS.NONE})); + //registerRule(makeRule({steps: [WATER_RIGHT_SLIDE_DIAGRAM, WATER_RIGHT_SPIN], transformations: DRAGON_TRANSFORMATIONS.X})); - //registerRule(makeRule({steps: [], transformations: DRAGON_TRANSFORMATIONS.X})) - //registerRule(makeRule({steps: [WATER_RIGHT_SPIN], transformations: DRAGON_TRANSFORMATIONS.X})) + //registerRule(makeRule({steps: [], transformations: DRAGON_TRANSFORMATIONS.X})); + //registerRule(makeRule({steps: [WATER_RIGHT_SPIN], transformations: DRAGON_TRANSFORMATIONS.X})); - /* + /*; -registerRule( +registerRule(; makeRule({ transformations: DRAGON_TRANSFORMATIONS.R, steps: [ @@ -2776,12 +2772,12 @@ registerRule( makeDiagramCell({x: 0, y: 0, content: makeArrayFromSplash(000)}), makeDiagramCell({x: 1, y: 0, content: makeArrayFromSplash(999)}), ], - }) + }); ], }), -) +); -registerRule( +registerRule(; makeRule({ transformations: DRAGON_TRANSFORMATIONS.X, steps: [ @@ -2793,20 +2789,20 @@ registerRule( makeDiagramCell({x: 0, y: 0, width: 0.5, splitX: 2, splitY: 1, content: makeArrayFromSplash(Colour.Blue.splash), instruction: DRAGON_INSTRUCTION.split}), makeDiagramCell({x: 0.5, y: 0, width: 0.5, content: makeArrayFromSplash(Colour.Red.splash), instruction: DRAGON_INSTRUCTION.recolour}), ], - }) + }); ], }), -) +); */ - const RAINBOW = makeArray() - RAINBOW.channels = [makeNumber(), makeNumber(), makeNumber()] + const RAINBOW = makeArray(); + RAINBOW.channels = [makeNumber(), makeNumber(), makeNumber()]; for (let c = 0; c < 3; c++) { - const channel = RAINBOW.channels[c] + const channel = RAINBOW.channels[c]; for (let i = 0; i < 10; i++) { - if (c === 0 & i > 0) continue - channel.values[i] = true + if (c === 0 & i > 0) continue; + channel.values[i] = true; } } @@ -2814,27 +2810,26 @@ registerRule( left: [ makeDiagramCell({content: RAINBOW}) ] - }) + }); - const RAINBOW2 = makeArray() - RAINBOW2.channels = [makeNumber(), makeNumber(), makeNumber()] + const RAINBOW2 = makeArray(); + RAINBOW2.channels = [makeNumber(), makeNumber(), makeNumber()]; for (let c = 0; c < 3; c++) { - const channel = RAINBOW2.channels[c] + const channel = RAINBOW2.channels[c]; for (let i = 0; i < 10; i++) { - if (c === 1 & i > 0) continue - //if (c === 2 & i > 0) continue - channel.values[i] = true + if (c === 1 & i > 0) continue; + //if (c === 2 & i > 0) continue; + channel.values[i] = true; } } - + RAINBOW_DIAGRAM_2 = makeDiagram({ left: [ makeDiagramCell({content: RAINBOW2}) ] - }) + }); + - - const WATER_SPAWN_DIAGRAM_CYAN = makeDiagram({ left: [ makeDiagramCell({x: 0, y: 0, content: PURPLE}), @@ -2842,8 +2837,8 @@ registerRule( right: [ makeDiagramCell({x: 0, y: 0, content: CYAN}), ], - }) - + }); + const WATER_SPAWN_DIAGRAM_BLUE = makeDiagram({ left: [ makeDiagramCell({x: 0, y: 0, content: PURPLE}), @@ -2851,8 +2846,8 @@ registerRule( right: [ makeDiagramCell({x: 0, y: 0, content: BLUE}), ], - }) - + }); + const WATER_FALL_CYAN = makeDiagram({ left: [ makeDiagramCell({x: 0, y: 0, content: CYAN}), @@ -2862,8 +2857,8 @@ registerRule( makeDiagramCell({x: 0, y: 0, content: BLACK}), makeDiagramCell({x: 0, y: 1, content: CYAN}), ], - }) - + }); + const WATER_FALL_BLUE = makeDiagram({ left: [ makeDiagramCell({x: 0, y: 0, content: BLUE}), @@ -2873,7 +2868,7 @@ registerRule( makeDiagramCell({x: 0, y: 0, content: BLACK}), makeDiagramCell({x: 0, y: 1, content: BLUE}), ], - }) + }); const WATER_SLIDE_BLUE = makeDiagram({ left: [ @@ -2884,7 +2879,7 @@ registerRule( makeDiagramCell({x: 0, y: 0, content: BLACK}), makeDiagramCell({x: 1, y: 0, content: BLUE}), ], - }) + }); const WATER_SLIDE_CYAN = makeDiagram({ left: [ @@ -2895,7 +2890,7 @@ registerRule( makeDiagramCell({x: 0, y: 0, content: BLACK}), makeDiagramCell({x: -1, y: 0, content: CYAN}), ], - }) + }); const WATER_SWAP_BLUE = makeDiagram({ left: [ @@ -2904,7 +2899,7 @@ registerRule( right: [ makeDiagramCell({x: 0, y: 0, content: CYAN}), ], - }) + }); const WATER_SWAP_CYAN = makeDiagram({ left: [ @@ -2913,28 +2908,28 @@ registerRule( right: [ makeDiagramCell({x: 0, y: 0, content: BLUE}), ], - }) + }); - /*registerRule(makeRule({steps: [WATER_SPAWN_DIAGRAM_CYAN]})) - registerRule(makeRule({steps: [WATER_SPAWN_DIAGRAM_BLUE]})) + /*registerRule(makeRule({steps: [WATER_SPAWN_DIAGRAM_CYAN]})); + registerRule(makeRule({steps: [WATER_SPAWN_DIAGRAM_BLUE]})); - registerRule(makeRule({steps: [WATER_FALL_BLUE]})) - registerRule(makeRule({steps: [WATER_FALL_CYAN]})) + registerRule(makeRule({steps: [WATER_FALL_BLUE]})); + registerRule(makeRule({steps: [WATER_FALL_CYAN]})); - registerRule(makeRule({steps: [WATER_SLIDE_BLUE, WATER_SWAP_BLUE]})) + registerRule(makeRule({steps: [WATER_SLIDE_BLUE, WATER_SWAP_BLUE]})); registerRule(makeRule({steps: [WATER_SLIDE_CYAN, WATER_SWAP_CYAN]}))*/ - - //state.brush.colour = RAINBOW_DIAGRAM_2 - - /*state.brush.colour = WATER_RIGHT - state.brush.colour = Colour.Purple.splash - state.brush.colour = Colour.Blue.splash + + //state.brush.colour = RAINBOW_DIAGRAM_2; + + /*state.brush.colour = WATER_RIGHT; + state.brush.colour = Colour.Purple.splash; + state.brush.colour = Colour.Blue.splash; state.brush.colour = Colour.Yellow.splash*/ - //====================// - // COLOURTODE - STATE // - //====================// + //====================//; + // COLOURTODE - STATE //; + //====================//; state.colourTode = { atoms: [], hand: { @@ -2946,728 +2941,728 @@ registerRule( velocityMemory: 5, previous: {x: 0, y: 0}, }, - } - const hand = state.colourTode.hand - - //====================// - // COLOURTODE - SETUP // - //====================// - const colourTodeCanvas = document.createElement("canvas") - const colourTodeContext = colourTodeCanvas.getContext("2d") - - //colourTodeCanvas.style["image-rendering"] = "pixelated" - colourTodeCanvas.style["position"] = "absolute" - colourTodeCanvas.style["top"] = "0px" - - document.body.append(colourTodeCanvas) + }; + const hand = state.colourTode.hand; + + //====================//; + // COLOURTODE - SETUP //; + //====================//; + const colourTodeCanvas = document.createElement("canvas"); + const colourTodeContext = colourTodeCanvas.getContext("2d"); + + //colourTodeCanvas.style["image-rendering"] = "pixelated"; + colourTodeCanvas.style["position"] = "absolute"; + colourTodeCanvas.style["top"] = "0px"; + + document.body.append(colourTodeCanvas); on.resize(() => { - colourTodeCanvas.width = innerWidth * DPR - colourTodeCanvas.height = innerHeight * DPR - colourTodeCanvas.style["width"] = innerWidth - colourTodeCanvas.style["height"] = innerHeight - }) + colourTodeCanvas.width = innerWidth * DPR; + colourTodeCanvas.height = innerHeight * DPR; + colourTodeCanvas.style["width"] = innerWidth; + colourTodeCanvas.style["height"] = innerHeight; + }); - trigger("resize") + trigger("resize"); - //===================// - // COLOURTODE - TICK // - //===================// + //===================//; + // COLOURTODE - TICK //; + //===================//; const colourTodeTick = () => { - colourTodeUpdate() - colourTodeDraw() - requestAnimationFrame(colourTodeTick) - } + colourTodeUpdate(); + colourTodeDraw(); + requestAnimationFrame(colourTodeTick); + }; const updateHand = () => { if (hand.velocityHistory.length >= hand.velocityMemory) { - hand.velocityHistory.shift() + hand.velocityHistory.shift(); } if (Mouse.position !== undefined && Mouse.position[0] !== undefined && hand.previous.x !== undefined) { - const [x, y] = Mouse.position.map(n => n / CT_SCALE) - const dx = (x - hand.previous.x) * DPR - const dy = (y - hand.previous.y) * DPR - const velocity = {x: dx, y: dy} - hand.velocityHistory.push(velocity) - const sum = hand.velocityHistory.reduce((a, b) => ({x: a.x+b.x, y: a.y+b.y}), {x:0, y:0}) - const average = {x: sum.x / hand.velocityHistory.length, y: sum.y / hand.velocityHistory.length} - hand.velocity.x = average.x - hand.velocity.y = average.y - hand.previous.x = x - hand.previous.y = y + const [x, y] = Mouse.position.map(n => n / CT_SCALE); + const dx = (x - hand.previous.x) * DPR; + const dy = (y - hand.previous.y) * DPR; + const velocity = {x: dx, y: dy}; + hand.velocityHistory.push(velocity); + const sum = hand.velocityHistory.reduce((a, b) => ({x: a.x+b.x, y: a.y+b.y}), {x:0, y:0}); + const average = {x: sum.x / hand.velocityHistory.length, y: sum.y / hand.velocityHistory.length}; + hand.velocity.x = average.x; + hand.velocity.y = average.y; + hand.previous.x = x; + hand.previous.y = y; } - } - - const COLOURTODE_FRICTION = 0.9 + }; + + const COLOURTODE_FRICTION = 0.9; const colourTodeUpdate = () => { for (const atom of state.colourTode.atoms) { - updateAtom(atom) + updateAtom(atom); } - } + }; const updateAtom = (atom, checkOffscreen = true) => { for (const child of atom.children) { - updateAtom(child, false) + updateAtom(child, false); } - // HIGHLIGHT + // HIGHLIGHT; if (atom.hover !== undefined) { - updateAtomHighlight(atom) + updateAtomHighlight(atom); } - atom.update(atom) + atom.update(atom); - // MOVEMENT - if (hand.content === atom) return - if (atom.dx === 0 && atom.dy === 0) return + // MOVEMENT; + if (hand.content === atom) return; + if (atom.dx === 0 && atom.dy === 0) return; - atom.x += atom.dx - atom.y += atom.dy + atom.x += atom.dx; + atom.y += atom.dy; - atom.x = clamp(atom.x, atom.minX, atom.maxX) - atom.y = clamp(atom.y, atom.minY, atom.maxY) + atom.x = clamp(atom.x, atom.minX, atom.maxX); + atom.y = clamp(atom.y, atom.minY, atom.maxY); - atom.dx *= COLOURTODE_FRICTION - atom.dy *= COLOURTODE_FRICTION + atom.dx *= COLOURTODE_FRICTION; + atom.dy *= COLOURTODE_FRICTION; if (checkOffscreen && isAtomOffscreen(atom)) { - deleteAtom(atom) - return + deleteAtom(atom); + return; } - const [mx, my] = Mouse.position.map(n => n / CT_SCALE) - if (hand.state.atommove) hand.state.atommove(atom, mx, my) - } + const [mx, my] = Mouse.position.map(n => n / CT_SCALE); + if (hand.state.atommove) hand.state.atommove(atom, mx, my); + }; const updateAtomHighlight = (atom) => { - // Remove the previous highlight - atom.highlightedAtom = undefined + // Remove the previous highlight; + atom.highlightedAtom = undefined; + + // Only highlight if I'm being dragged; + if (hand.content !== atom) return; + if (hand.state !== HAND.DRAGGING) return; - // Only highlight if I'm being dragged - if (hand.content !== atom) return - if (hand.state !== HAND.DRAGGING) return - if (atom.highlight !== undefined) { - deleteChild(atom, atom.highlight) - atom.highlight = undefined + deleteChild(atom, atom.highlight); + atom.highlight = undefined; } - const highlightedAtom = atom.hover(atom) + const highlightedAtom = atom.hover(atom); - // Create the highlight - if (highlightedAtom === undefined) return + // Create the highlight; + if (highlightedAtom === undefined) return; if (atom.highlight === undefined) { - const highlight = createChild(atom, HIGHLIGHT, {bottom: true}) - highlight.hasBorder = true - highlight.colour = Colour.Grey - const {x, y} = getAtomPosition(highlightedAtom) - highlight.x = x - highlight.y = y - highlight.width = highlightedAtom.width - highlight.height = highlightedAtom.height - atom.highlight = highlight - } - - atom.highlightedAtom = highlightedAtom - } + const highlight = createChild(atom, HIGHLIGHT, {bottom: true}); + highlight.hasBorder = true; + highlight.colour = Colour.Grey; + const {x, y} = getAtomPosition(highlightedAtom); + highlight.x = x; + highlight.y = y; + highlight.width = highlightedAtom.width; + highlight.height = highlightedAtom.height; + atom.highlight = highlight; + } + + atom.highlightedAtom = highlightedAtom; + }; const colourTodeDraw = () => { - colourTodeContext.clearRect(0, 0, colourTodeCanvas.width, colourTodeCanvas.height) + colourTodeContext.clearRect(0, 0, colourTodeCanvas.width, colourTodeCanvas.height); /*if (!NO_FOOLS_MODE) { - colourTodeContext.filter = "grayscale(100%)" + colourTodeContext.filter = "grayscale(100%)"; }*/ - colourTodeContext.scale(CT_SCALE, CT_SCALE) + colourTodeContext.scale(CT_SCALE, CT_SCALE); for (const atom of state.colourTode.atoms) { - drawAtom(atom) + drawAtom(atom); } - colourTodeContext.scale(1/CT_SCALE, 1/CT_SCALE) - } + colourTodeContext.scale(1/CT_SCALE, 1/CT_SCALE); + }; - requestAnimationFrame(colourTodeTick) + requestAnimationFrame(colourTodeTick); - //===================// - // COLOURTODE - HAND // - //===================// - const DRAG_PITY = 15 - const DRAG_PITY_TIME = 100 - const DRAG_UNPITY_SPEED = 10 + //===================//; + // COLOURTODE - HAND //; + //===================//; + const DRAG_PITY = 15; + const DRAG_PITY_TIME = 100; + const DRAG_UNPITY_SPEED = 10; - const HAND = {} - HAND_RELEASE = 0.5 + const HAND = {}; + HAND_RELEASE = 0.5; HAND.FREE = { cursor: "auto", mousemove: (e) => { - const x = e.clientX / CT_SCALE - const y = e.clientY / CT_SCALE - const atom = getAtom(x, y) + const x = e.clientX / CT_SCALE; + const y = e.clientY / CT_SCALE; + const atom = getAtom(x, y); if (atom !== undefined) { if (atom.grabbable) { if (!Mouse.Left) { - if (atom.cursor !== undefined) changeHandState(HAND.HOVER, atom.cursor(atom, HAND.HOVER)) - else if (atom.dragOnly) changeHandState(HAND.HOVER, "move") - else changeHandState(HAND.HOVER) + if (atom.cursor !== undefined) changeHandState(HAND.HOVER, atom.cursor(atom, HAND.HOVER)); + else if (atom.dragOnly) changeHandState(HAND.HOVER, "move"); + else changeHandState(HAND.HOVER); } else { if (atom.grabbable && atom.draggable) { - grabAtom(atom, x, y) - hand.pityStartX = e.clientX - hand.pityStartY = e.clientY - hand.pityStartT = Date.now() - changeHandState(HAND.TOUCHING) - //hand.content = hand.content.drag(hand.content, x, y) - HAND.TOUCHING.mousemove(e) + grabAtom(atom, x, y); + hand.pityStartX = e.clientX; + hand.pityStartY = e.clientY; + hand.pityStartT = Date.now(); + changeHandState(HAND.TOUCHING); + //hand.content = hand.content.drag(hand.content, x, y); + HAND.TOUCHING.mousemove(e); } } } - return + return; } - let [mx, my] = Mouse.position - mx *= DPR - my *= DPR + let [mx, my] = Mouse.position; + mx *= DPR; + my *= DPR; if (mx < state.view.left || mx > state.view.right || my < state.view.top || my > state.view.bottom) { - return + return; } - if (Mouse.Left) changeHandState(HAND.BRUSHING) - else if (Mouse.Middle) changeHandState(HAND.PENCILLING) - else changeHandState(HAND.BRUSH) + if (Mouse.Left) changeHandState(HAND.BRUSHING); + else if (Mouse.Middle) changeHandState(HAND.PENCILLING); + else changeHandState(HAND.BRUSH); }, mousedown: (e) => { - if (!state.worldBuilt) return - hand.voidingStart = [e.clientX, e.clientY] - changeHandState(HAND.VOIDING) + if (!state.worldBuilt) return; + hand.voidingStart = [e.clientX, e.clientY]; + changeHandState(HAND.VOIDING); }, atommove: (atom, mx, my) => { - if (!atom.grabbable) return - if (!isAtomOverlapping(atom, mx, my)) return + if (!atom.grabbable) return; + if (!isAtomOverlapping(atom, mx, my)) return; if (Mouse.Left) { - grabAtom(atom, mx, my) - changeHandState(HAND.DRAGGING) - hand.content = hand.content.drag(hand.content, mx, my) - return + grabAtom(atom, mx, my); + changeHandState(HAND.DRAGGING); + hand.content = hand.content.drag(hand.content, mx, my); + return; } - if (atom.cursor !== undefined) changeHandState(HAND.HOVER, atom.cursor(atom, HAND.HOVER)) - else if (atom.dragOnly) changeHandState(HAND.HOVER, "move") - else changeHandState(HAND.HOVER) + if (atom.cursor !== undefined) changeHandState(HAND.HOVER, atom.cursor(atom, HAND.HOVER)); + else if (atom.dragOnly) changeHandState(HAND.HOVER, "move"); + else changeHandState(HAND.HOVER); }, camerapan: () => { - let [x, y] = Mouse.position - x *= DPR - y *= DPR + let [x, y] = Mouse.position; + x *= DPR; + y *= DPR; if (x >= state.view.left && x <= state.view.right && y >= state.view.top && y <= state.view.bottom) { - changeHandState(HAND.BRUSH) - return + changeHandState(HAND.BRUSH); + return; } }, - } + }; - let voidingType = true + let voidingType = true; HAND.VOIDING = { cursor: "auto", mousemove: (e) => { - const start = hand.voidingStart - const [sx, sy] = start - const displacement = [e.clientX - sx, e.clientY - sy] - const distance = Math.hypot(...displacement) + const start = hand.voidingStart; + const [sx, sy] = start; + const displacement = [e.clientX - sx, e.clientY - sy]; + const distance = Math.hypot(...displacement); if (distance > 10) { - changeHandState(HAND.FREE) - HAND.FREE.mousemove(e) + changeHandState(HAND.FREE); + HAND.FREE.mousemove(e); } }, mouseup: (e) => { - const oldWorldSize = WORLD_SIZE - setWorldSize(0) + const oldWorldSize = WORLD_SIZE; + setWorldSize(0); if (voidingType) { - brush(0.5, 0.5) + brush(0.5, 0.5); } else { - const oldBrushColour = state.brush.colour - state.brush.colour = oldWorldSize * 111 - brush(0.5, 0.5) - state.brush.colour = oldBrushColour - state.worldBuilt = false - show.paused = false - canvas.style["background-color"] = Colour.Void - } - voidingType = !voidingType - setWorldSize(oldWorldSize) - changeHandState(HAND.FREE) + const oldBrushColour = state.brush.colour; + state.brush.colour = oldWorldSize * 111; + brush(0.5, 0.5); + state.brush.colour = oldBrushColour; + state.worldBuilt = false; + show.paused = false; + canvas.style["background-color"] = Colour.Void; + } + voidingType = !voidingType; + setWorldSize(oldWorldSize); + changeHandState(HAND.FREE); }, - } + }; HAND.BRUSH = { cursor: "crosshair", mousemove: (e) => { - const x = e.clientX / CT_SCALE - const y = e.clientY / CT_SCALE - const atom = getAtom(x, y) + const x = e.clientX / CT_SCALE; + const y = e.clientY / CT_SCALE; + const atom = getAtom(x, y); if (atom !== undefined) { if (atom.grabbable) { - if (atom.cursor !== undefined) changeHandState(HAND.HOVER, atom.cursor(atom, HAND.HOVER)) - else if (atom.dragOnly) changeHandState(HAND.HOVER, "move") - else changeHandState(HAND.HOVER) + if (atom.cursor !== undefined) changeHandState(HAND.HOVER, atom.cursor(atom, HAND.HOVER)); + else if (atom.dragOnly) changeHandState(HAND.HOVER, "move"); + else changeHandState(HAND.HOVER); } - else changeHandState(HAND.FREE) - return + else changeHandState(HAND.FREE); + return; } - const mx = e.clientX * DPR - const my = e.clientY * DPR + const mx = e.clientX * DPR; + const my = e.clientY * DPR; if (mx >= state.view.left && mx <= state.view.right && my >= state.view.top && my <= state.view.bottom) { - return + return; } - changeHandState(HAND.FREE) + changeHandState(HAND.FREE); }, mousedown: (e) => { - changeHandState(HAND.BRUSHING) + changeHandState(HAND.BRUSHING); }, middlemousedown: (e) => { - changeHandState(HAND.PENCILLING) + changeHandState(HAND.PENCILLING); }, atommove: (atom, mx, my) => { - if (!isAtomOverlapping(atom, mx, my)) return - if (atom.grabbable) changeHandState(HAND.HOVER) - else changeHandState(HAND.FREE) + if (!isAtomOverlapping(atom, mx, my)) return; + if (atom.grabbable) changeHandState(HAND.HOVER); + else changeHandState(HAND.FREE); }, camerapan: () => { - let [x, y] = Mouse.position - x *= DPR - y *= DPR + let [x, y] = Mouse.position; + x *= DPR; + y *= DPR; if (x >= state.view.left && x <= state.view.right && y >= state.view.top && y <= state.view.bottom) { - return + return; } - changeHandState(HAND.FREE) + changeHandState(HAND.FREE); }, - } + }; HAND.BRUSHING = { cursor: "crosshair", mousemove: (e) => { - const x = e.clientX * DPR - const y = e.clientY * DPR + const x = e.clientX * DPR; + const y = e.clientY * DPR; if (x >= state.view.left && x <= state.view.right && y >= state.view.top && y <= state.view.bottom) { - return + return; } - changeHandState(HAND.FREE) + changeHandState(HAND.FREE); }, mouseup: (e) => { - changeHandState(HAND.BRUSH) + changeHandState(HAND.BRUSH); }, camerapan: () => { - let [mx, my] = Mouse.position - mx *= DPR - my *= DPR + let [mx, my] = Mouse.position; + mx *= DPR; + my *= DPR; if (mx >= state.view.left && mx <= state.view.right && my >= state.view.top && my <= state.view.bottom) { - return + return; } - const [x, y] = Mouse.position.map(n => n / CT_SCALE) - const atom = getAtom(x, y) + const [x, y] = Mouse.position.map(n => n / CT_SCALE); + const atom = getAtom(x, y); if (atom !== undefined) { if (atom.grabbable) { - if (atom.cursor !== undefined) changeHandState(HAND.HOVER, atom.cursor(atom, HAND.HOVER)) - else if (atom.dragOnly) changeHandState(HAND.HOVER, "move") - else changeHandState(HAND.HOVER) + if (atom.cursor !== undefined) changeHandState(HAND.HOVER, atom.cursor(atom, HAND.HOVER)); + else if (atom.dragOnly) changeHandState(HAND.HOVER, "move"); + else changeHandState(HAND.HOVER); } - else changeHandState(HAND.FREE) - return - } - changeHandState(HAND.FREE) + else changeHandState(HAND.FREE); + return; + } + changeHandState(HAND.FREE); }, - } + }; HAND.PENCILLING = { cursor: "crosshair", mousemove: HAND.BRUSHING.mousemove, middlemouseup: HAND.BRUSHING.mouseup, camerapan: HAND.BRUSHING.camerapan, - } + }; HAND.HOVER = { cursor: "pointer", - + mousedown: (e) => { - const atom = getAtom(e.clientX / CT_SCALE, e.clientY / CT_SCALE) - if (atom === undefined) return - if (!atom.grabbable) return - grabAtom(atom, e.clientX / CT_SCALE, e.clientY / CT_SCALE) - + const atom = getAtom(e.clientX / CT_SCALE, e.clientY / CT_SCALE); + if (atom === undefined) return; + if (!atom.grabbable) return; + grabAtom(atom, e.clientX / CT_SCALE, e.clientY / CT_SCALE); + if (atom.dragOnly) { - - hand.pityStartX = e.clientX - hand.pityStartY = e.clientY - hand.pityStartT = Date.now() - hand.hasStartedDragging = false - hand.touchButton = 0 - changeHandState(HAND.TOUCHING, "move") + + hand.pityStartX = e.clientX; + hand.pityStartY = e.clientY; + hand.pityStartT = Date.now(); + hand.hasStartedDragging = false; + hand.touchButton = 0; + changeHandState(HAND.TOUCHING, "move"); } else { - hand.pityStartX = e.clientX - hand.pityStartY = e.clientY - hand.pityStartT = Date.now() - hand.hasStartedDragging = false - hand.touchButton = 0 - changeHandState(HAND.TOUCHING) + hand.pityStartX = e.clientX; + hand.pityStartY = e.clientY; + hand.pityStartT = Date.now(); + hand.hasStartedDragging = false; + hand.touchButton = 0; + changeHandState(HAND.TOUCHING); } }, mousemove: (e) => { - const x = e.clientX / CT_SCALE - const y = e.clientY / CT_SCALE - const atom = getAtom(x, y) + const x = e.clientX / CT_SCALE; + const y = e.clientY / CT_SCALE; + const atom = getAtom(x, y); if (atom !== undefined) { if (atom.grabbable) { - if (atom.cursor !== undefined) changeHandState(HAND.HOVER, atom.cursor(atom, HAND.HOVER)) - else if (atom.dragOnly) changeHandState(HAND.HOVER, "move") - else changeHandState(HAND.HOVER) + if (atom.cursor !== undefined) changeHandState(HAND.HOVER, atom.cursor(atom, HAND.HOVER)); + else if (atom.dragOnly) changeHandState(HAND.HOVER, "move"); + else changeHandState(HAND.HOVER); } - else changeHandState(HAND.FREE) - return + else changeHandState(HAND.FREE); + return; } - const mx = e.clientX - const my = e.clientY + const mx = e.clientX; + const my = e.clientY; if (mx >= state.view.left && mx <= state.view.right && my >= state.view.top && my <= state.view.bottom) { - changeHandState(HAND.BRUSH) - return + changeHandState(HAND.BRUSH); + return; } - changeHandState(HAND.FREE) + changeHandState(HAND.FREE); }, atommove: (atom, x, y) => { - if (isAtomOverlapping(atom, x, y)) return - const newAtom = getAtom(x, y) + if (isAtomOverlapping(atom, x, y)) return; + const newAtom = getAtom(x, y); if (newAtom !== undefined) { - return + return; } - let [mx, my] = Mouse.position - mx *= DPR - my *= DPR + let [mx, my] = Mouse.position; + mx *= DPR; + my *= DPR; if (mx >= state.view.left && mx <= state.view.right && my >= state.view.top && my <= state.view.bottom) { - changeHandState(HAND.BRUSH) - return + changeHandState(HAND.BRUSH); + return; } - changeHandState(HAND.FREE) + changeHandState(HAND.FREE); }, rightmousedown: (e) => { - const atom = getAtom(e.clientX / CT_SCALE, e.clientY / CT_SCALE) - if (atom === undefined) return - if (!atom.grabbable) return - grabAtom(atom, e.clientX / CT_SCALE, e.clientY / CT_SCALE) - + const atom = getAtom(e.clientX / CT_SCALE, e.clientY / CT_SCALE); + if (atom === undefined) return; + if (!atom.grabbable) return; + grabAtom(atom, e.clientX / CT_SCALE, e.clientY / CT_SCALE); + if (atom.dragOnly) { - - hand.pityStartX = e.clientX - hand.pityStartY = e.clientY - hand.pityStartT = Date.now() - hand.hasStartedDragging = false - hand.touchButton = 2 - changeHandState(HAND.TOUCHING, "move") + + hand.pityStartX = e.clientX; + hand.pityStartY = e.clientY; + hand.pityStartT = Date.now(); + hand.hasStartedDragging = false; + hand.touchButton = 2; + changeHandState(HAND.TOUCHING, "move"); } else { - hand.pityStartX = e.clientX - hand.pityStartY = e.clientY - hand.pityStartT = Date.now() - hand.hasStartedDragging = false - hand.touchButton = 2 - changeHandState(HAND.TOUCHING) + hand.pityStartX = e.clientX; + hand.pityStartY = e.clientY; + hand.pityStartT = Date.now(); + hand.hasStartedDragging = false; + hand.touchButton = 2; + changeHandState(HAND.TOUCHING); } } - } + }; const dampen = (n, noReally) => { - if (noReally) return n * 0.6 - return n - } + if (noReally) return n * 0.6; + return n; + }; HAND.TOUCHING = { cursor: "pointer", mousemove: (e) => { - if (e.movementX === 0 && e.movementY === 0) return - if (hand.touchButton === 2 && !hand.content.rightDraggable) return + if (e.movementX === 0 && e.movementY === 0) return; + if (hand.touchButton === 2 && !hand.content.rightDraggable) return; - const distanceFromPityStart = Math.hypot(e.clientX - hand.pityStartX, e.clientY - hand.pityStartY) - const pity = DRAG_PITY + const distanceFromPityStart = Math.hypot(e.clientX - hand.pityStartX, e.clientY - hand.pityStartY); + const pity = DRAG_PITY; - const dx = e.clientX - hand.pityStartX - const dy = e.clientY - hand.pityStartY - - if (!hand.content.dragLockX) hand.content.x = (hand.pityStartX + dampen(dx, hand.content.attached && !hand.content.noDampen)) / CT_SCALE * DPR + hand.offset.x - if (!hand.content.dragLockY) hand.content.y = (hand.pityStartY + dampen(dy, hand.content.attached && !hand.content.noDampen)) / CT_SCALE * DPR + hand.offset.y + const dx = e.clientX - hand.pityStartX; + const dy = e.clientY - hand.pityStartY; - hand.content.x = clamp(hand.content.x, hand.content.minX, hand.content.maxX) - hand.content.y = clamp(hand.content.y, hand.content.minY, hand.content.maxY) + if (!hand.content.dragLockX) hand.content.x = (hand.pityStartX + dampen(dx, hand.content.attached && !hand.content.noDampen)) / CT_SCALE * DPR + hand.offset.x; + if (!hand.content.dragLockY) hand.content.y = (hand.pityStartY + dampen(dy, hand.content.attached && !hand.content.noDampen)) / CT_SCALE * DPR + hand.offset.y; + + hand.content.x = clamp(hand.content.x, hand.content.minX, hand.content.maxX); + hand.content.y = clamp(hand.content.y, hand.content.minY, hand.content.maxY); if (distanceFromPityStart < pity) { - return + return; } - const timeSincePityStart = Date.now() - hand.pityStartT + const timeSincePityStart = Date.now() - hand.pityStartT; if (timeSincePityStart < DRAG_PITY_TIME) { - const handSpeed = Math.hypot(hand.velocity.x, hand.velocity.y) - if (handSpeed <= DRAG_UNPITY_SPEED) return + const handSpeed = Math.hypot(hand.velocity.x, hand.velocity.y); + if (handSpeed <= DRAG_UNPITY_SPEED) return; } - if (!hand.content.dragLockX) hand.content.x = hand.pityStartX / CT_SCALE * DPR + hand.offset.x - if (!hand.content.dragLockY) hand.content.y = hand.pityStartY / CT_SCALE * DPR + hand.offset.y + if (!hand.content.dragLockX) hand.content.x = hand.pityStartX / CT_SCALE * DPR + hand.offset.x; + if (!hand.content.dragLockY) hand.content.y = hand.pityStartY / CT_SCALE * DPR + hand.offset.y; - hand.content.x = clamp(hand.content.x, hand.content.minX, hand.content.maxX) - hand.content.y = clamp(hand.content.y, hand.content.minY, hand.content.maxY) + hand.content.x = clamp(hand.content.x, hand.content.minX, hand.content.maxX); + hand.content.y = clamp(hand.content.y, hand.content.minY, hand.content.maxY); - const x = e.clientX / CT_SCALE - const y = e.clientY / CT_SCALE + const x = e.clientX / CT_SCALE; + const y = e.clientY / CT_SCALE; if (hand.touchButton === 0 && hand.content.draggable) { - changeHandState(HAND.DRAGGING) + changeHandState(HAND.DRAGGING); + + const attached = hand.content.attached && !hand.content.dragOnly && !hand.content.noDampen; - const attached = hand.content.attached && !hand.content.dragOnly && !hand.content.noDampen + hand.content = hand.content.drag(hand.content, x, y); - hand.content = hand.content.drag(hand.content, x, y) - - if (!hand.content.dragLockX) hand.content.x = (hand.pityStartX + dampen(dx, attached)) / CT_SCALE + hand.offset.x - if (!hand.content.dragLockY) hand.content.y = (hand.pityStartY + dampen(dy, attached)) / CT_SCALE + hand.offset.y + if (!hand.content.dragLockX) hand.content.x = (hand.pityStartX + dampen(dx, attached)) / CT_SCALE + hand.offset.x; + if (!hand.content.dragLockY) hand.content.y = (hand.pityStartY + dampen(dy, attached)) / CT_SCALE + hand.offset.y; - hand.content.x = clamp(hand.content.x, hand.content.minX, hand.content.maxX) - hand.content.y = clamp(hand.content.y, hand.content.minY, hand.content.maxY) + hand.content.x = clamp(hand.content.x, hand.content.minX, hand.content.maxX); + hand.content.y = clamp(hand.content.y, hand.content.minY, hand.content.maxY); - /*hand.offset.x = hand.content.x - e.clientX / CT_SCALE + /*hand.offset.x = hand.content.x - e.clientX / CT_SCALE; hand.offset.y = hand.content.y - e.clientY / CT_SCALE*/ - HAND.DRAGGING.mousemove(e) - return + HAND.DRAGGING.mousemove(e); + return; } else if (hand.touchButton === 2 && hand.content.rightDraggable) { - changeHandState(HAND.DRAGGING) + changeHandState(HAND.DRAGGING); - const attached = hand.content.attached && !hand.content.dragOnly && !hand.content.noDampen + const attached = hand.content.attached && !hand.content.dragOnly && !hand.content.noDampen; - hand.content = hand.content.rightDrag(hand.content, x, y) - - if (!hand.content.dragLockX) hand.content.x = (hand.pityStartX + dampen(dx, attached)) / CT_SCALE + hand.offset.x - if (!hand.content.dragLockY) hand.content.y = (hand.pityStartY + dampen(dy, attached)) / CT_SCALE + hand.offset.y + hand.content = hand.content.rightDrag(hand.content, x, y); - hand.content.x = clamp(hand.content.x, hand.content.minX, hand.content.maxX) - hand.content.y = clamp(hand.content.y, hand.content.minY, hand.content.maxY) + if (!hand.content.dragLockX) hand.content.x = (hand.pityStartX + dampen(dx, attached)) / CT_SCALE + hand.offset.x; + if (!hand.content.dragLockY) hand.content.y = (hand.pityStartY + dampen(dy, attached)) / CT_SCALE + hand.offset.y; - /*hand.offset.x = hand.content.x - e.clientX / CT_SCALE + hand.content.x = clamp(hand.content.x, hand.content.minX, hand.content.maxX); + hand.content.y = clamp(hand.content.y, hand.content.minY, hand.content.maxY); + + /*hand.offset.x = hand.content.x - e.clientX / CT_SCALE; hand.offset.y = hand.content.y - e.clientY / CT_SCALE*/ - HAND.DRAGGING.mousemove(e) - return + HAND.DRAGGING.mousemove(e); + return; } - const atom = getAtom(x, y) + const atom = getAtom(x, y); if (atom !== undefined) { if (atom.grabbable) { - if (atom.cursor !== undefined) changeHandState(HAND.HOVER, atom.cursor(atom, HAND.HOVER)) - else if (atom.dragOnly) changeHandState(HAND.HOVER, "move") - else changeHandState(HAND.HOVER) + if (atom.cursor !== undefined) changeHandState(HAND.HOVER, atom.cursor(atom, HAND.HOVER)); + else if (atom.dragOnly) changeHandState(HAND.HOVER, "move"); + else changeHandState(HAND.HOVER); } - else changeHandState(HAND.FREE) - return + else changeHandState(HAND.FREE); + return; } - const mx = e.clientX * DPR - const my = e.clientY * DPR + const mx = e.clientX * DPR; + const my = e.clientY * DPR; if (mx >= state.view.left && mx <= state.view.right && my >= state.view.top && my <= state.view.bottom) { - changeHandState(HAND.BRUSH) - return + changeHandState(HAND.BRUSH); + return; } - changeHandState(HAND.FREE) + changeHandState(HAND.FREE); }, mouseup: (e) => { - if (hand.touchButton !== 0) return - hand.clickContent.click(hand.clickContent) - hand.clickContent.dx = 0 - hand.clickContent.dy = 0 - hand.clickContent = undefined + if (hand.touchButton !== 0) return; + hand.clickContent.click(hand.clickContent); + hand.clickContent.dx = 0; + hand.clickContent.dy = 0; + hand.clickContent = undefined; if (hand.content.attached) { - hand.content.x = hand.pityStartX / CT_SCALE * DPR + hand.offset.x - hand.content.y = hand.pityStartY / CT_SCALE * DPR + hand.offset.y + hand.content.x = hand.pityStartX / CT_SCALE * DPR + hand.offset.x; + hand.content.y = hand.pityStartY / CT_SCALE * DPR + hand.offset.y; } - hand.content.dx = 0 - hand.content.dy = 0 - hand.content = undefined + hand.content.dx = 0; + hand.content.dy = 0; + hand.content = undefined; - const x = e.clientX / CT_SCALE - const y = e.clientY / CT_SCALE - const atom = getAtom(x, y) + const x = e.clientX / CT_SCALE; + const y = e.clientY / CT_SCALE; + const atom = getAtom(x, y); if (atom !== undefined) { - if (atom.cursor !== undefined) changeHandState(HAND.HOVER, atom.cursor(atom, HAND.HOVER)) - else if (atom.dragOnly) changeHandState(HAND.HOVER, "move") - else changeHandState(HAND.HOVER) + if (atom.cursor !== undefined) changeHandState(HAND.HOVER, atom.cursor(atom, HAND.HOVER)); + else if (atom.dragOnly) changeHandState(HAND.HOVER, "move"); + else changeHandState(HAND.HOVER); } - else changeHandState(HAND.FREE) + else changeHandState(HAND.FREE); }, rightmouseup: (e) => { - if (hand.touchButton !== 2) return - hand.clickContent.rightClick(hand.clickContent) - hand.clickContent.dx = 0 - hand.clickContent.dy = 0 - hand.clickContent = undefined + if (hand.touchButton !== 2) return; + hand.clickContent.rightClick(hand.clickContent); + hand.clickContent.dx = 0; + hand.clickContent.dy = 0; + hand.clickContent = undefined; if (hand.content.attached) { - hand.content.x = hand.pityStartX / CT_SCALE * DPR + hand.offset.x - hand.content.y = hand.pityStartY / CT_SCALE * DPR + hand.offset.y + hand.content.x = hand.pityStartX / CT_SCALE * DPR + hand.offset.x; + hand.content.y = hand.pityStartY / CT_SCALE * DPR + hand.offset.y; } - hand.content.dx = 0 - hand.content.dy = 0 - hand.content = undefined + hand.content.dx = 0; + hand.content.dy = 0; + hand.content = undefined; - const x = e.clientX / CT_SCALE - const y = e.clientY / CT_SCALE - const atom = getAtom(x, y) + const x = e.clientX / CT_SCALE; + const y = e.clientY / CT_SCALE; + const atom = getAtom(x, y); if (atom !== undefined) { - if (atom.cursor !== undefined) changeHandState(HAND.HOVER, atom.cursor(atom, HAND.HOVER)) - else if (atom.dragOnly) changeHandState(HAND.HOVER, "move") - else changeHandState(HAND.HOVER) + if (atom.cursor !== undefined) changeHandState(HAND.HOVER, atom.cursor(atom, HAND.HOVER)); + else if (atom.dragOnly) changeHandState(HAND.HOVER, "move"); + else changeHandState(HAND.HOVER); } - else changeHandState(HAND.FREE) + else changeHandState(HAND.FREE); } - } + }; HAND.DRAGGING = { cursor: "move", mousemove: (e) => { if (!hand.hasStartedDragging) { - hand.hasStartedDragging = true - hand.content = hand.content.drag(hand.content, e.clientX / CT_SCALE * DPR, e.clientY / CT_SCALE * DPR) + hand.hasStartedDragging = true; + hand.content = hand.content.drag(hand.content, e.clientX / CT_SCALE * DPR, e.clientY / CT_SCALE * DPR); } - const oldX = hand.content.x - const oldY = hand.content.y + const oldX = hand.content.x; + const oldY = hand.content.y; - if (!hand.content.dragLockX) hand.content.x = e.clientX / CT_SCALE * DPR + hand.offset.x - if (!hand.content.dragLockY) hand.content.y = e.clientY / CT_SCALE * DPR + hand.offset.y + if (!hand.content.dragLockX) hand.content.x = e.clientX / CT_SCALE * DPR + hand.offset.x; + if (!hand.content.dragLockY) hand.content.y = e.clientY / CT_SCALE * DPR + hand.offset.y; - hand.content.x = clamp(hand.content.x, hand.content.minX, hand.content.maxX) - hand.content.y = clamp(hand.content.y, hand.content.minY, hand.content.maxY) + hand.content.x = clamp(hand.content.x, hand.content.minX, hand.content.maxX); + hand.content.y = clamp(hand.content.y, hand.content.minY, hand.content.maxY); - const dx = hand.content.x - oldX - const dy = hand.content.y - oldY - hand.content.move(hand.content, dx, dy) + const dx = hand.content.x - oldX; + const dy = hand.content.y - oldY; + hand.content.move(hand.content, dx, dy); - //hand.content.dx = hand.velocity.x - //hand.content.dy = hand.velocity.y + //hand.content.dx = hand.velocity.x; + //hand.content.dy = hand.velocity.y; }, mouseup: (e) => { - if (hand.touchButton !== 0) return - hand.hasStartedDragging = true - if (!hand.content.dragLockX) hand.content.dx = hand.velocity.x * HAND_RELEASE - if (!hand.content.dragLockY) hand.content.dy = hand.velocity.y * HAND_RELEASE - hand.content.drop(hand.content) + if (hand.touchButton !== 0) return; + hand.hasStartedDragging = true; + if (!hand.content.dragLockX) hand.content.dx = hand.velocity.x * HAND_RELEASE; + if (!hand.content.dragLockY) hand.content.dy = hand.velocity.y * HAND_RELEASE; + hand.content.drop(hand.content); if (hand.content.highlightedAtom !== undefined) { - hand.content.place(hand.content, hand.content.highlightedAtom) + hand.content.place(hand.content, hand.content.highlightedAtom); if (hand.content.highlight !== undefined) { - deleteChild(hand.content, hand.content.highlight) - hand.content.highlight = undefined + deleteChild(hand.content, hand.content.highlight); + hand.content.highlight = undefined; } } - hand.content = undefined - const x = e.clientX / CT_SCALE - const y = e.clientY / CT_SCALE - const atom = getAtom(x, y) + hand.content = undefined; + const x = e.clientX / CT_SCALE; + const y = e.clientY / CT_SCALE; + const atom = getAtom(x, y); if (atom !== undefined) { if (atom.grabbable) { - if (atom.cursor !== undefined) changeHandState(HAND.HOVER, atom.cursor(atom, HAND.HOVER)) - else if (atom.dragOnly) changeHandState(HAND.HOVER, "move") - else changeHandState(HAND.HOVER) + if (atom.cursor !== undefined) changeHandState(HAND.HOVER, atom.cursor(atom, HAND.HOVER)); + else if (atom.dragOnly) changeHandState(HAND.HOVER, "move"); + else changeHandState(HAND.HOVER); } - else changeHandState(HAND.FREE) - return + else changeHandState(HAND.FREE); + return; } - else changeHandState(HAND.FREE) - return + else changeHandState(HAND.FREE); + return; }, rightmouseup: (e) => { - if (hand.touchButton !== 2) return - hand.hasStartedDragging = true - if (!hand.content.dragLockX) hand.content.dx = hand.velocity.x * HAND_RELEASE - if (!hand.content.dragLockY) hand.content.dy = hand.velocity.y * HAND_RELEASE - hand.content.drop(hand.content) + if (hand.touchButton !== 2) return; + hand.hasStartedDragging = true; + if (!hand.content.dragLockX) hand.content.dx = hand.velocity.x * HAND_RELEASE; + if (!hand.content.dragLockY) hand.content.dy = hand.velocity.y * HAND_RELEASE; + hand.content.drop(hand.content); if (hand.content.highlightedAtom !== undefined) { - hand.content.place(hand.content, hand.content.highlightedAtom) + hand.content.place(hand.content, hand.content.highlightedAtom); if (hand.content.highlight !== undefined) { - deleteChild(hand.content, hand.content.highlight) - hand.content.highlight = undefined + deleteChild(hand.content, hand.content.highlight); + hand.content.highlight = undefined; } } - hand.content = undefined - const x = e.clientX / CT_SCALE - const y = e.clientY / CT_SCALE - const atom = getAtom(x, y) + hand.content = undefined; + const x = e.clientX / CT_SCALE; + const y = e.clientY / CT_SCALE; + const atom = getAtom(x, y); if (atom !== undefined) { if (atom.grabbable) { - if (atom.cursor !== undefined) changeHandState(HAND.HOVER, atom.cursor(atom, HAND.HOVER)) - else if (atom.dragOnly) changeHandState(HAND.HOVER, "move") - else changeHandState(HAND.HOVER) + if (atom.cursor !== undefined) changeHandState(HAND.HOVER, atom.cursor(atom, HAND.HOVER)); + else if (atom.dragOnly) changeHandState(HAND.HOVER, "move"); + else changeHandState(HAND.HOVER); } - else changeHandState(HAND.FREE) - return + else changeHandState(HAND.FREE); + return; } - else changeHandState(HAND.FREE) - return + else changeHandState(HAND.FREE); + return; } - } + }; const changeHandState = (state, cursor = state.cursor) => { if (hand.content !== undefined && hand.content.cursor !== undefined) { - cursor = hand.content.cursor(hand.content, state) + cursor = hand.content.cursor(hand.content, state); } - colourTodeCanvas.style["cursor"] = cursor - hand.state = state - } + colourTodeCanvas.style["cursor"] = cursor; + hand.state = state; + }; - on.mousemove(e => hand.state.mousemove? hand.state.mousemove(e) : undefined) + on.mousemove(e => hand.state.mousemove? hand.state.mousemove(e) : undefined); on.mousedown(e => { - if (e.button === 0) if (hand.state.mousedown) hand.state.mousedown(e) - if (e.button === 1) if (hand.state.middlemousedown) hand.state.middlemousedown(e) - if (e.button === 2) if (hand.state.rightmousedown) hand.state.rightmousedown(e) - }) + if (e.button === 0) if (hand.state.mousedown) hand.state.mousedown(e); + if (e.button === 1) if (hand.state.middlemousedown) hand.state.middlemousedown(e); + if (e.button === 2) if (hand.state.rightmousedown) hand.state.rightmousedown(e); + }); on.mouseup(e => { - if (e.button === 0) if (hand.state.mouseup) hand.state.mouseup(e) - if (e.button === 1) if (hand.state.middlemouseup) hand.state.middlemouseup(e) - if (e.button === 2) if (hand.state.rightmouseup) hand.state.rightmouseup(e) - - }) + if (e.button === 0) if (hand.state.mouseup) hand.state.mouseup(e); + if (e.button === 1) if (hand.state.middlemouseup) hand.state.middlemouseup(e); + if (e.button === 2) if (hand.state.rightmouseup) hand.state.rightmouseup(e); + + }); - hand.state = HAND.FREE + hand.state = HAND.FREE; - //===================// - // COLOURTODE - ATOM // - //===================// + //===================//; + // COLOURTODE - ATOM //; + //===================//; const COLOURTODE_BASE_PARENT = { x: 0, y: 0, grab: (atom, x, y, child = atom) => child, touch: (atom, child = atom) => child, - } + }; const makeAtom = ({ grabbable = true, draggable = true, - click = () => {}, // Fires when you mouseup a click on the atom + click = () => {}, // Fires when you mouseup a click on the atom; rightClick = () => {}, - drag = (a) => a, // Fires when you start dragging the atom + drag = (a) => a, // Fires when you start dragging the atom; rightDrag = (a) => a, - move = () => {}, // Fires when you start or continue dragging the atom //TODO: change this to be whenever the atom moves for any reason - drop = () => {}, // Fires when you let go of the atom after a drag + move = () => {}, // Fires when you start or continue dragging the atom //TODO: change this to be whenever the atom moves for any reason; + drop = () => {}, // Fires when you let go of the atom after a drag; draw = () => {}, update = () => {}, offscreen = () => false, overlaps = () => false, - grab = (a) => a, // Fires when you start a clock on the atom - returns atom that gets dragged - touch = (a) => a, // Fires when you start a click on the atom - returns atom that handles the click - highlighter = false, // If true, enables the hover and place events // I dont think its needed lollll, i think it does it either way>??? - hover = () => {}, // Fires whenever you are dragging the atom - returns what atom should get highlighted (if any) (the returned atom gets auto-highlighted unless you manually set the 'highlight' property in this function) - place = () => {}, // Fires whenever you drop the atom onto a highlighted atom + grab = (a) => a, // Fires when you start a clock on the atom - returns atom that gets dragged; + touch = (a) => a, // Fires when you start a click on the atom - returns atom that handles the click; + highlighter = false, // If true, enables the hover and place events // I dont think its needed lollll, i think it does it either way>???; + hover = () => {}, // Fires whenever you are dragging the atom - returns what atom should get highlighted (if any) (the returned atom gets auto-highlighted unless you manually set the 'highlight' property in this function); + place = () => {}, // Fires whenever you drop the atom onto a highlighted atom; x = 0, y = 0, dx = 0, @@ -3686,679 +3681,678 @@ registerRule( hasInner = true, ...properties } = {}, ...args) => { - const atom = {highlighter, place, hover, hasInner, move, drop, maxX, minX, maxY, minY, update, construct, draggable, width, height, touch, parent, children, draw, grabbable, click, drag, overlaps, offscreen, grab, x, y, dx, dy, size, colour, rightClick, rightDrag, ...properties} - atom.construct(atom, ...args) - return atom - } + const atom = {highlighter, place, hover, hasInner, move, drop, maxX, minX, maxY, minY, update, construct, draggable, width, height, touch, parent, children, draw, grabbable, click, drag, overlaps, offscreen, grab, x, y, dx, dy, size, colour, rightClick, rightDrag, ...properties}; + atom.construct(atom, ...args); + return atom; + }; const getAtom = (x, y) => { - x *= DPR - y *= DPR + x *= DPR; + y *= DPR; for (let i = state.colourTode.atoms.length-1; i >= 0; i--) { - const atom = state.colourTode.atoms[i] - if (atom.justVisual) continue - const result = isAtomOverlapping(atom, x, y) - if (result !== undefined) return result + const atom = state.colourTode.atoms[i]; + if (atom.justVisual) continue; + const result = isAtomOverlapping(atom, x, y); + if (result !== undefined) return result; } - } + }; const drawAtom = (atom) => { for (const child of atom.children) { - if (child.behindParent) drawAtom(child) + if (child.behindParent) drawAtom(child); } - if (atom.behindChildren) atom.draw(atom) + if (atom.behindChildren) atom.draw(atom); for (const child of atom.children) { - if (!child.behindParent) drawAtom(child) + if (!child.behindParent) drawAtom(child); } - if (!atom.behindChildren) atom.draw(atom) - } + if (!atom.behindChildren) atom.draw(atom); + }; const deleteAtom = (atom) => { - const id = state.colourTode.atoms.indexOf(atom) - state.colourTode.atoms.splice(id, 1) - } + const id = state.colourTode.atoms.indexOf(atom); + state.colourTode.atoms.splice(id, 1); + }; const registerAtom = (atom) => { - state.colourTode.atoms.push(atom) - } + state.colourTode.atoms.push(atom); + }; - // including children + // including children; const isAtomOffscreen = (atom) => { for (const child of atom.children) { - if (!isAtomOffscreen(child)) return false + if (!isAtomOffscreen(child)) return false; } - return atom.offscreen(atom) - } + return atom.offscreen(atom); + }; - // including children + // including children; const isAtomOverlapping = (atom, x, y) => { - - if (!atom.behindChildren && atom.overlaps(atom, x, y)) return atom - + + if (!atom.behindChildren && atom.overlaps(atom, x, y)) return atom; + for (let i = atom.children.length-1; i >= 0; i--) { - const child = atom.children[i] - if (child.behindParent) continue - const result = isAtomOverlapping(child, x, y) - if (result) return result - } - - if (atom.behindChildren && atom.overlaps(atom, x, y)) return atom - + const child = atom.children[i]; + if (child.behindParent) continue; + const result = isAtomOverlapping(child, x, y); + if (result) return result; + } + + if (atom.behindChildren && atom.overlaps(atom, x, y)) return atom; + for (let i = atom.children.length-1; i >= 0; i--) { - const child = atom.children[i] - if (!child.behindParent) continue - const result = isAtomOverlapping(child, x, y) - if (result) return result + const child = atom.children[i]; + if (!child.behindParent) continue; + const result = isAtomOverlapping(child, x, y); + if (result) return result; } - } + }; const grabAtom = (atom, x, y) => { - let previousTouched = atom - let touched = atom.touch(atom) + let previousTouched = atom; + let touched = atom.touch(atom); if (touched !== previousTouched) { - const newTouched = touched.touch(touched, x, y, previousTouched) - previousTouched = touched - touched = newTouched + const newTouched = touched.touch(touched, x, y, previousTouched); + previousTouched = touched; + touched = newTouched; } - hand.clickContent = touched + hand.clickContent = touched; - - let previousGrabbed = atom - let grabbed = atom.grab(atom, x, y) + let previousGrabbed = atom; + let grabbed = atom.grab(atom, x, y); - if (grabbed === undefined) return + if (grabbed === undefined) return; if (grabbed !== previousGrabbed) { - const newGrabbed = grabbed.grab(grabbed, x, y, previousGrabbed) - previousGrabbed = grabbed - grabbed = newGrabbed + const newGrabbed = grabbed.grab(grabbed, x, y, previousGrabbed); + previousGrabbed = grabbed; + grabbed = newGrabbed; } - hand.content = grabbed - const {x: grabbedX, y: grabbedY} = getAtomPosition(grabbed, {forceAbsolute: true}) - hand.offset.x = grabbedX - x * DPR - hand.offset.y = grabbedY - y * DPR - grabbed.dx = 0 - grabbed.dy = 0 + hand.content = grabbed; + const {x: grabbedX, y: grabbedY} = getAtomPosition(grabbed, {forceAbsolute: true}); + hand.offset.x = grabbedX - x * DPR; + hand.offset.y = grabbedY - y * DPR; + grabbed.dx = 0; + grabbed.dy = 0; - if (atom.stayAtBack) bringAtomToBack(grabbed) - else bringAtomToFront(grabbed) + if (atom.stayAtBack) bringAtomToBack(grabbed); + else bringAtomToFront(grabbed); - return grabbed - } + return grabbed; + }; const bringAtomToFront = (grabbed) => { - // If atom isn't a child, bring it to the top level + // If atom isn't a child, bring it to the top level; if (grabbed.parent === COLOURTODE_BASE_PARENT) { - deleteAtom(grabbed) - registerAtom(grabbed) + deleteAtom(grabbed); + registerAtom(grabbed); } else { - const childId = grabbed.parent.children.indexOf(grabbed) - grabbed.parent.children.splice(childId, 1) - grabbed.parent.children.push(grabbed) - if (grabbed.parent.stayAtBack) bringAtomToBack(grabbed.parent) - else bringAtomToFront(grabbed.parent) + const childId = grabbed.parent.children.indexOf(grabbed); + grabbed.parent.children.splice(childId, 1); + grabbed.parent.children.push(grabbed); + if (grabbed.parent.stayAtBack) bringAtomToBack(grabbed.parent); + else bringAtomToFront(grabbed.parent); } - } + }; const bringAtomToBack = (grabbed) => { if (grabbed.parent === COLOURTODE_BASE_PARENT) { - const id = state.colourTode.atoms.indexOf(grabbed) - state.colourTode.atoms.splice(id, 1) - state.colourTode.atoms.unshift(grabbed) + const id = state.colourTode.atoms.indexOf(grabbed); + state.colourTode.atoms.splice(id, 1); + state.colourTode.atoms.unshift(grabbed); } else { - const childId = grabbed.parent.children.indexOf(grabbed) - grabbed.parent.children.splice(childId, 1) - grabbed.parent.children.unshift(grabbed) - if (grabbed.parent.stayAtBack) bringAtomToBack(grabbed.parent) - else bringAtomToFront(grabbed.parent) + const childId = grabbed.parent.children.indexOf(grabbed); + grabbed.parent.children.splice(childId, 1); + grabbed.parent.children.unshift(grabbed); + if (grabbed.parent.stayAtBack) bringAtomToBack(grabbed.parent); + else bringAtomToFront(grabbed.parent); } - } + }; const getAtomPosition = (atom, {forceAbsolute = false} = {}) => { - const {x, y} = atom - if (forceAbsolute) return {x, y} - if (atom.parent === undefined) return {x, y} - if (atom.hasAbsolutePosition) return {x, y} - const {x: px, y: py} = getAtomPosition(atom.parent) - return {x: x+px, y: y+py} - } + const {x, y} = atom; + if (forceAbsolute) return {x, y}; + if (atom.parent === undefined) return {x, y}; + if (atom.hasAbsolutePosition) return {x, y}; + const {x: px, y: py} = getAtomPosition(atom.parent); + return {x: x+px, y: y+py}; + }; - //=======================// - // COLOURTODE - CHILDREN // - //=======================// + //=======================//; + // COLOURTODE - CHILDREN //; + //=======================//; const createChild = (parent, element, {bottom = false} = {}) => { - const child = makeAtom(element) - if (!bottom) parent.children.push(child) - else parent.children.unshift(child) - child.parent = parent - return child - } - + const child = makeAtom(element); + if (!bottom) parent.children.push(child); + else parent.children.unshift(child); + child.parent = parent; + return child; + }; + const deleteChild = (parent, child, {quiet = false} = {}) => { - const id = parent.children.indexOf(child) + const id = parent.children.indexOf(child); if (id === -1) { - if (quiet) return - else throw new Error(`Can't delete child of atom because I can't find it!`) + if (quiet) return; + else throw new Error(`Can't delete child of atom because I can't find it!`); } - parent.children.splice(id, 1) - child.parent = COLOURTODE_BASE_PARENT - } - + parent.children.splice(id, 1); + child.parent = COLOURTODE_BASE_PARENT; + }; + const giveChild = (parent, atom) => { if (atom === undefined) { - throw new Error(`Can't give child because child is undefined`) + throw new Error(`Can't give child because child is undefined`); } if (parent === undefined) { - throw new Error(`Can't give child because parent is undefined`) - } - deleteAtom(atom) - if (atom.stayAtBack || atom.behindOtherChildren) parent.children.unshift(atom) - else parent.children.push(atom) - atom.parent = parent - } + throw new Error(`Can't give child because parent is undefined`); + } + deleteAtom(atom); + if (atom.stayAtBack || atom.behindOtherChildren) parent.children.unshift(atom); + else parent.children.push(atom); + atom.parent = parent; + }; const freeChild = (parent, child) => { if (hand.content === child) { - const {x, y} = getAtomPosition(parent) - hand.offset.x += x - hand.offset.y += y + const {x, y} = getAtomPosition(parent); + hand.offset.x += x; + hand.offset.y += y; } - deleteChild(parent, child) - registerAtom(child) - } + deleteChild(parent, child); + registerAtom(child); + }; - //======================// - // COLOURTODE - ELEMENT // - //======================// - const COLOUR_CYCLE_SPEED = 5 - const COLOUR_CYCLE_LENGTH = 30 - const BORDER_THICKNESS = 3 + //======================//; + // COLOURTODE - ELEMENT //; + //======================//; + const COLOUR_CYCLE_SPEED = 5; + const COLOUR_CYCLE_LENGTH = 30; + const BORDER_THICKNESS = 3; const getColourCycleLength = (atom) => { - let length = Math.max(COLOUR_CYCLE_LENGTH / atom.colours.length, COLOUR_CYCLE_SPEED) + let length = Math.max(COLOUR_CYCLE_LENGTH / atom.colours.length, COLOUR_CYCLE_SPEED); /*if (atom.joins !== undefined && atom.joins.length > 0) { - length *= 3 + length *= 3; }*/ - //if (atom.joins !== undefined && atom.joins.length > 0 && atom.joinExpanded !== false) return Infinity - return length - } + //if (atom.joins !== undefined && atom.joins.length > 0 && atom.joinExpanded !== false) return Infinity; + return length; + }; - // prepare border colours - borderColours = PREBUILT_BORDER_COLOURS + // prepare border colours; + borderColours = PREBUILT_BORDER_COLOURS; /*for (let i = 0; i < 1000; i++) { - const colour = Colour.splash(i) - let borderColour = undefined - //let borderColour = Colour.add(colour, {lightness: -20}) - const darkness = 70 - colour.lightness - borderColour = Colour.add(colour, {lightness: -20}) - borderColours.push(borderColour) + const colour = Colour.splash(i); + let borderColour = undefined; + //let borderColour = Colour.add(colour, {lightness: -20}); + const darkness = 70 - colour.lightness; + borderColour = Colour.add(colour, {lightness: -20}); + borderColours.push(borderColour); }*/ - const toolBorderColours = borderColours.clone - toolBorderColours[999] = Colour.splash(999) + const toolBorderColours = borderColours.clone; + toolBorderColours[999] = Colour.splash(999); + + /*const toolBorderColours = []; - /*const toolBorderColours = [] - for (let i = 0; i < 1000; i++) { - const colour = Colour.splash(i) - let borderColour = Colour.add(colour, {lightness: -20}) + const colour = Colour.splash(i); + let borderColour = Colour.add(colour, {lightness: -20}); if (colour.lightness <= 35) { - borderColour = Colour.add(colour, {lightness: 15}) - } - toolBorderColours.push(borderColour) - } + borderColour = Colour.add(colour, {lightness: 15}); + }; + toolBorderColours.push(borderColour); + }; toolBorderColours[000] = Colour.Grey*/ const COLOURTODE_RECTANGLE = { draw: (atom) => { - let {x, y} = getAtomPosition(atom) + let {x, y} = getAtomPosition(atom); - let X = Math.round(x) - let Y = Math.round(y) - let W = Math.round(atom.width) - let H = Math.round(atom.height) - let R = Math.round(atom.width/2) + let X = Math.round(x); + let Y = Math.round(y); + let W = Math.round(atom.width); + let H = Math.round(atom.height); + let R = Math.round(atom.width/2); if (atom.hasBorder) { if (atom.hasInner) { - let border = BORDER_THICKNESS + let border = BORDER_THICKNESS; if (atom.borderColour === undefined) { - colourTodeContext.fillStyle = Colour.splash(atom.colour.splash) + colourTodeContext.fillStyle = Colour.splash(atom.colour.splash); if (atom.isTool) { - colourTodeContext.fillStyle = Colour.splash(atom.colour.splash) - border *= 1.5 + colourTodeContext.fillStyle = Colour.splash(atom.colour.splash); + border *= 1.5; } else if (atom.width === atom.height) { - border *= 1.5 + border *= 1.5; } } else { - colourTodeContext.fillStyle = atom.borderColour + colourTodeContext.fillStyle = atom.borderColour; } - colourTodeContext.beginPath() - colourTodeContext.rect(X, Y, W, H) + colourTodeContext.beginPath(); + colourTodeContext.rect(X, Y, W, H); if (atom.stamp !== undefined) { - colourTodeContext.arc(X+R, Y+R, Math.round((PADDLE_HANDLE.size - OPTION_MARGIN/2)/2), 0, 2*Math.PI) + colourTodeContext.arc(X+R, Y+R, Math.round((PADDLE_HANDLE.size - OPTION_MARGIN/2)/2), 0, 2*Math.PI); } if (atom.isGradient) { - colourTodeContext.putImageData(atom.gradient, X * CT_SCALE, Y * CT_SCALE) + colourTodeContext.putImageData(atom.gradient, X * CT_SCALE, Y * CT_SCALE); } else { - colourTodeContext.fill("evenodd") + colourTodeContext.fill("evenodd"); - colourTodeContext.beginPath() - colourTodeContext.fillStyle = atom.colour - colourTodeContext.rect(X+border, Y+border, W-border*2, H-border*2) + colourTodeContext.beginPath(); + colourTodeContext.fillStyle = atom.colour; + colourTodeContext.rect(X+border, Y+border, W-border*2, H-border*2); if (atom.stamp !== undefined) { - colourTodeContext.arc(X+R, Y+R, Math.round((PADDLE_HANDLE.size - OPTION_MARGIN/2)/2)+border, 0, 2*Math.PI) + colourTodeContext.arc(X+R, Y+R, Math.round((PADDLE_HANDLE.size - OPTION_MARGIN/2)/2)+border, 0, 2*Math.PI); } - colourTodeContext.fill("evenodd") + colourTodeContext.fill("evenodd"); } - } + }; else { if (atom.borderColour === undefined) { - colourTodeContext.strokeStyle = borderColours[atom.colour.splash] - } + colourTodeContext.strokeStyle = borderColours[atom.colour.splash]; + }; else { - colourTodeContext.strokeStyle = atom.borderColour - } + colourTodeContext.strokeStyle = atom.borderColour; + }; - X = Math.round(x + 0.5) - 0.5 - Y = Math.round(y + 0.5) - 0.5 + X = Math.round(x + 0.5) - 0.5; + Y = Math.round(y + 0.5) - 0.5; - colourTodeContext.lineWidth = BORDER_THICKNESS - colourTodeContext.strokeRect(X, Y, W, H) - } - } + colourTodeContext.lineWidth = BORDER_THICKNESS; + colourTodeContext.strokeRect(X, Y, W, H); + }; + }; else { - colourTodeContext.fillStyle = atom.colour - colourTodeContext.fillRect(X, Y, W, H) - } + colourTodeContext.fillStyle = atom.colour; + colourTodeContext.fillRect(X, Y, W, H); + }; }, offscreen: (atom) => { - const {x, y} = getAtomPosition(atom) - const left = x - const right = x + atom.width - const top = y - const bottom = y + atom.height - if (right < 0) return true - if (bottom < 0) return true - if (left > canvas.width) return true - if (top > canvas.height) return true - return false + const {x, y} = getAtomPosition(atom); + const left = x; + const right = x + atom.width; + const top = y; + const bottom = y + atom.height; + if (right < 0) return true; + if (bottom < 0) return true; + if (left > canvas.width) return true; + if (top > canvas.height) return true; + return false; }, overlaps: (atom, mx, my) => { - const {x, y} = getAtomPosition(atom) - let border = BORDER_THICKNESS + const {x, y} = getAtomPosition(atom); + let border = BORDER_THICKNESS; if (atom.isTool || atom.isSquare || atom.isTallRectangle) { - border *= 1.5 - } - const left = x - const right = x + atom.width - const top = y - const bottom = y + atom.height - - if (mx < left) return false - if (my < top) return false - if (mx > right) return false - if (my > bottom) return false - - return true + border *= 1.5; + }; + const left = x; + const right = x + atom.width; + const top = y; + const bottom = y + atom.height; + + if (mx < left) return false; + if (my < top) return false; + if (mx > right) return false; + if (my > bottom) return false; + + return true; }, - } + }; const CIRCLE = { draw: (atom) => { - const {x, y} = getAtomPosition(atom) + const {x, y} = getAtomPosition(atom); - const X = x + atom.width/2 - const Y = y + atom.height/2 - let R = (atom.width/2) + const X = x + atom.width/2; + const Y = y + atom.height/2; + let R = (atom.width/2); if (atom.hasBorder) { if (atom.isTool) { - atom.borderColour = toolBorderColours[atom.colour.splash] - } - colourTodeContext.fillStyle = atom.borderColour !== undefined? atom.borderColour : Colour.Void - colourTodeContext.beginPath() - colourTodeContext.arc(X, Y, R, 0, 2*Math.PI) - colourTodeContext.fill() - let borderScale = atom.borderScale !== undefined? atom.borderScale : 1.0 - R = (atom.width/2 - BORDER_THICKNESS*1.5 * borderScale) - } - - colourTodeContext.fillStyle = atom.colour - colourTodeContext.beginPath() - colourTodeContext.arc(X, Y, R, 0, 2*Math.PI) - colourTodeContext.fill() + atom.borderColour = toolBorderColours[atom.colour.splash]; + }; + colourTodeContext.fillStyle = atom.borderColour !== undefined? atom.borderColour : Colour.Void; + colourTodeContext.beginPath(); + colourTodeContext.arc(X, Y, R, 0, 2*Math.PI); + colourTodeContext.fill(); + let borderScale = atom.borderScale !== undefined? atom.borderScale : 1.0; + R = (atom.width/2 - BORDER_THICKNESS*1.5 * borderScale); + }; + + colourTodeContext.fillStyle = atom.colour; + colourTodeContext.beginPath(); + colourTodeContext.arc(X, Y, R, 0, 2*Math.PI); + colourTodeContext.fill(); }, offscreen: COLOURTODE_RECTANGLE.offscreen, overlaps: COLOURTODE_RECTANGLE.overlaps, - - } + + }; const isCellAtomSpotFilled = (paddle, [sx, sy], slotted = false) => { for (let cellAtom of paddle.cellAtoms) { - if (slotted) cellAtom = cellAtom.slot - const {x, y} = getAtomPosition(cellAtom) + if (slotted) cellAtom = cellAtom.slot; + const {x, y} = getAtomPosition(cellAtom); if (x === sx && y === sy) { - //if (cellAtom.isLeftSlot) continue - return true - } - } - return false - } + //if (cellAtom.isLeftSlot) continue; + return true; + }; + }; + return false; + }; const isCellAtomSlotFree = (paddle, [sx, sy], slotted = false) => { for (let cellAtom of paddle.cellAtoms) { - if (slotted) cellAtom = cellAtom.slot - const {x, y} = getAtomPosition(cellAtom) + if (slotted) cellAtom = cellAtom.slot; + const {x, y} = getAtomPosition(cellAtom); if (x === sx && y === sy) { - if (cellAtom.isLeftSlot || cellAtom.isSlot) return true - } - } - return false - } + if (cellAtom.isLeftSlot || cellAtom.isSlot) return true; + }; + }; + return false; + }; const setBrushColour = (value) => { if (typeof value === "number") { - state.brush.colour = value - squareTool.toolbarNeedsColourUpdate = true - squareTool.value = makeArrayFromSplash(value) + state.brush.colour = value; + squareTool.toolbarNeedsColourUpdate = true; + squareTool.value = makeArrayFromSplash(value); } else { - const diagramCell = makeDiagramCell({content: value}) - state.brush.colour = makeDiagram({left: [diagramCell]}) - squareTool.value = diagramCell.content - squareTool.toolbarNeedsColourUpdate = true - } - } + const diagramCell = makeDiagramCell({content: value}); + state.brush.colour = makeDiagram({left: [diagramCell]}); + squareTool.value = diagramCell.content; + squareTool.toolbarNeedsColourUpdate = true; + }; + }; - // Ctrl+F: sqdef + // Ctrl+F: sqdef; const COLOURTODE_SQUARE = { isSquare: true, hasBorder: true, draw: (atom) => { - if (atom.value.isDiagram) return - else COLOURTODE_RECTANGLE.draw(atom) + if (atom.value.isDiagram) return; + else COLOURTODE_RECTANGLE.draw(atom); }, overlaps: COLOURTODE_RECTANGLE.overlaps, offscreen: COLOURTODE_RECTANGLE.offscreen, touch: (atom) => { - setBrushColour(atom.value) - return atom + setBrushColour(atom.value); + return atom; }, click: (atom) => { if (atom.joins.length > 0) { if (atom.parent === COLOURTODE_BASE_PARENT || !atom.parent.isPaddle) { if (atom.joinExpanded) { - atom.joinUnepxand(atom) + atom.joinUnepxand(atom); } else { - atom.joinExpand(atom) - } - } - } + atom.joinExpand(atom); + }; + }; + }; else if (atom.value.isDiagram) { - } + }; else if (!atom.expanded) { if (atom.parent === COLOURTODE_BASE_PARENT || !atom.parent.isPaddle) { - atom.expand(atom) - } + atom.expand(atom); + }; - } + }; else { - atom.unexpand(atom) - } + atom.unexpand(atom); + }; - setBrushColour(atom.value) + setBrushColour(atom.value); }, expand: (atom) => { - atom.expanded = true - atom.createPicker(atom) + atom.expanded = true; + atom.createPicker(atom); if (atom.value.channels.some(v => v === undefined)) { - // unlockMenuTool("hexagon") - // unlockMenuTool("wide_rectangle") - unlockMenuTool("triangle") - } + // unlockMenuTool("hexagon"); + // unlockMenuTool("wide_rectangle"); + unlockMenuTool("triangle"); + }; }, unexpand: (atom) => { - atom.expanded = false - atom.redExpanded = atom.red && atom.red.expanded - atom.greenExpanded = atom.green && atom.green.expanded - atom.blueExpanded = atom.blue && atom.blue.expanded - atom.deletePicker(atom) + atom.expanded = false; + atom.redExpanded = atom.red && atom.red.expanded; + atom.greenExpanded = atom.green && atom.green.expanded; + atom.blueExpanded = atom.blue && atom.blue.expanded; + atom.deletePicker(atom); }, createPicker: (atom) => { - const pickerHandle = createChild(atom, SYMMETRY_HANDLE) - pickerHandle.width += OPTION_MARGIN - atom.pickerHandle = pickerHandle - atom.pickerHandle.behindParent = true - - const pickerPad = createChild(atom, COLOURTODE_PICKER_PAD) - atom.pickerPad = pickerPad + const pickerHandle = createChild(atom, SYMMETRY_HANDLE); + pickerHandle.width += OPTION_MARGIN; + atom.pickerHandle = pickerHandle; + atom.pickerHandle.behindParent = true; + + const pickerPad = createChild(atom, COLOURTODE_PICKER_PAD); + atom.pickerPad = pickerPad; if (atom.value.channels[2] !== undefined) { if (atom.value.channels[2].variable === undefined) { - const blue = createChild(atom, COLOURTODE_PICKER_CHANNEL) - blue.channelSlot = "blue" //note: a colour doesn't necessarily have to be in its own channel slot - blue.x += COLOURTODE_PICKER_PAD_MARGIN + 3 * (COLOURTODE_SQUARE.size + COLOURTODE_PICKER_PAD_MARGIN) - blue.value = atom.value.channels[2] - blue.needsColoursUpdate = true - //blue.grab = () => atom - atom.blue = blue - blue.deletedOptions = atom.deletedBlueOptions - if (atom.blueExpanded) atom.blue.click(atom.blue) - atom.blue.attached = true + const blue = createChild(atom, COLOURTODE_PICKER_CHANNEL); + blue.channelSlot = "blue" //note: a colour doesn't necessarily have to be in its own channel slot; + blue.x += COLOURTODE_PICKER_PAD_MARGIN + 3 * (COLOURTODE_SQUARE.size + COLOURTODE_PICKER_PAD_MARGIN); + blue.value = atom.value.channels[2]; + blue.needsColoursUpdate = true; + //blue.grab = () => atom; + atom.blue = blue; + blue.deletedOptions = atom.deletedBlueOptions; + if (atom.blueExpanded) atom.blue.click(atom.blue); + atom.blue.attached = true; } else { - // alert('no') - const hexagon = atom.variableAtoms[2] - hexagon.behindOtherChildren = false - registerAtom(hexagon) - giveChild(atom, hexagon) - hexagon.variable = "blue" - hexagon.x = (COLOURTODE_PICKER_PAD_MARGIN + COLOURTODE_SQUARE.size)*3 + (COLOURTODE_SQUARE.size + COLOURTODE_PICKER_PAD_MARGIN)/2 - hexagon.width/3 - hexagon.y = atom.height/2 - hexagon.height/2 - hexagon.attached = true - - atom.blue = hexagon - } - } + // alert('no'); + const hexagon = atom.variableAtoms[2]; + hexagon.behindOtherChildren = false; + registerAtom(hexagon); + giveChild(atom, hexagon); + hexagon.variable = "blue"; + hexagon.x = (COLOURTODE_PICKER_PAD_MARGIN + COLOURTODE_SQUARE.size)*3 + (COLOURTODE_SQUARE.size + COLOURTODE_PICKER_PAD_MARGIN)/2 - hexagon.width/3; + hexagon.y = atom.height/2 - hexagon.height/2; + hexagon.attached = true; + + atom.blue = hexagon; + }; + }; if (atom.value.channels[1] !== undefined) { if (atom.value.channels[1].variable === undefined) { - const green = createChild(atom, COLOURTODE_PICKER_CHANNEL) - green.channelSlot = "green" //note: a colour doesn't necessarily have to be in its own channel slot - green.x += COLOURTODE_PICKER_PAD_MARGIN + 2 * (COLOURTODE_SQUARE.size + COLOURTODE_PICKER_PAD_MARGIN) - green.value = atom.value.channels[1] - green.needsColoursUpdate = true - //green.grab = () => atom - atom.green = green - green.deletedOptions = atom.deletedGreenOptions - if (atom.greenExpanded) atom.green.click(atom.green) - atom.green.attached = true + const green = createChild(atom, COLOURTODE_PICKER_CHANNEL); + green.channelSlot = "green" //note: a colour doesn't necessarily have to be in its own channel slot; + green.x += COLOURTODE_PICKER_PAD_MARGIN + 2 * (COLOURTODE_SQUARE.size + COLOURTODE_PICKER_PAD_MARGIN); + green.value = atom.value.channels[1]; + green.needsColoursUpdate = true; + //green.grab = () => atom; + atom.green = green; + green.deletedOptions = atom.deletedGreenOptions; + if (atom.greenExpanded) atom.green.click(atom.green); + atom.green.attached = true; } else { - // alert('noo') - const hexagon = atom.variableAtoms[1] - hexagon.behindOtherChildren = false - registerAtom(hexagon) - giveChild(atom, hexagon) - hexagon.variable = "green" - hexagon.x = (COLOURTODE_PICKER_PAD_MARGIN + COLOURTODE_SQUARE.size)*2 + (COLOURTODE_SQUARE.size + COLOURTODE_PICKER_PAD_MARGIN)/2 - hexagon.width/3 - hexagon.y = atom.height/2 - hexagon.height/2 - hexagon.attached = true - - atom.green = hexagon - } - } + // alert('noo'); + const hexagon = atom.variableAtoms[1]; + hexagon.behindOtherChildren = false; + registerAtom(hexagon); + giveChild(atom, hexagon); + hexagon.variable = "green"; + hexagon.x = (COLOURTODE_PICKER_PAD_MARGIN + COLOURTODE_SQUARE.size)*2 + (COLOURTODE_SQUARE.size + COLOURTODE_PICKER_PAD_MARGIN)/2 - hexagon.width/3; + hexagon.y = atom.height/2 - hexagon.height/2; + hexagon.attached = true; + + atom.green = hexagon; + }; + }; if (atom.value.channels[0] !== undefined) { if (atom.value.channels[0].variable === undefined) { - const red = createChild(atom, COLOURTODE_PICKER_CHANNEL) - red.channelSlot = "red" //note: a colour doesn't necessarily have to be in its own channel slot - red.x += COLOURTODE_PICKER_PAD_MARGIN + COLOURTODE_SQUARE.size + COLOURTODE_PICKER_PAD_MARGIN - red.value = atom.value.channels[0] - red.needsColoursUpdate = true - //red.grab = () => atom - atom.red = red - red.deletedOptions = atom.deletedRedOptions - if (atom.redExpanded) atom.red.click(atom.red) - atom.red.attached = true + const red = createChild(atom, COLOURTODE_PICKER_CHANNEL); + red.channelSlot = "red" //note: a colour doesn't necessarily have to be in its own channel slot; + red.x += COLOURTODE_PICKER_PAD_MARGIN + COLOURTODE_SQUARE.size + COLOURTODE_PICKER_PAD_MARGIN; + red.value = atom.value.channels[0]; + red.needsColoursUpdate = true; + //red.grab = () => atom; + atom.red = red; + red.deletedOptions = atom.deletedRedOptions; + if (atom.redExpanded) atom.red.click(atom.red); + atom.red.attached = true; } else { - const triangle = atom.variableAtoms[0] - triangle.behindOtherChildren = false - registerAtom(triangle) - giveChild(atom, triangle) - triangle.x = (COLOURTODE_PICKER_PAD_MARGIN + COLOURTODE_SQUARE.size) + (COLOURTODE_SQUARE.size + COLOURTODE_PICKER_PAD_MARGIN)/2 - triangle.width/3 - triangle.y = atom.height/2 - triangle.height/2 - triangle.attached = true - - atom.red = triangle - } - } + const triangle = atom.variableAtoms[0]; + triangle.behindOtherChildren = false; + registerAtom(triangle); + giveChild(atom, triangle); + triangle.x = (COLOURTODE_PICKER_PAD_MARGIN + COLOURTODE_SQUARE.size) + (COLOURTODE_SQUARE.size + COLOURTODE_PICKER_PAD_MARGIN)/2 - triangle.width/3; + triangle.y = atom.height/2 - triangle.height/2; + triangle.attached = true; + + atom.red = triangle; + }; + }; }, deletePicker: (atom) => { - deleteChild(atom, atom.pickerPad) - deleteChild(atom, atom.pickerHandle) + deleteChild(atom, atom.pickerPad); + deleteChild(atom, atom.pickerHandle); if (atom.red) { - atom.deletedRedOptions = atom.red.options - deleteChild(atom, atom.red) - } + atom.deletedRedOptions = atom.red.options; + deleteChild(atom, atom.red); + }; if (atom.green) { - atom.deletedGreenOptions = atom.green.options - deleteChild(atom, atom.green) - } + atom.deletedGreenOptions = atom.green.options; + deleteChild(atom, atom.green); + }; if (atom.blue) { - atom.deletedBlueOptions = atom.blue.options - deleteChild(atom, atom.blue) - } + atom.deletedBlueOptions = atom.blue.options; + deleteChild(atom, atom.blue); + }; }, receiveNumber: (atom, number, channel = number.channel, {expanded, numberAtom} = {}) => { - - atom.redExpanded = atom.red && atom.red.expanded - atom.greenExpanded = atom.green && atom.green.expanded - atom.blueExpanded = atom.blue && atom.blue.expanded - + + atom.redExpanded = atom.red && atom.red.expanded; + atom.greenExpanded = atom.green && atom.green.expanded; + atom.blueExpanded = atom.blue && atom.blue.expanded; + if (atom.variableAtoms === undefined) { - atom.variableAtoms = [undefined, undefined, undefined] - } + atom.variableAtoms = [undefined, undefined, undefined]; + }; if (number !== undefined && number.variable !== undefined) { - atom.variableAtoms[channel] = numberAtom + atom.variableAtoms[channel] = numberAtom; } else { - atom.variableAtoms[channel] = undefined - } + atom.variableAtoms[channel] = undefined; + }; if (expanded !== undefined) { - const channelName = CHANNEL_NAMES[channel] - atom[`${channelName}Expanded`] = expanded - } + const channelName = CHANNEL_NAMES[channel]; + atom[`${channelName}Expanded`] = expanded; + }; - atom.value.channels[channel] = number + atom.value.channels[channel] = number; - atom.deletePicker(atom) - atom.createPicker(atom) - atom.needsColoursUpdate = true - atom.colourTicker = Infinity + atom.deletePicker(atom); + atom.createPicker(atom); + atom.needsColoursUpdate = true; + atom.colourTicker = Infinity; - /*const diagramCell = makeDiagramCell({content: atom.value}) - state.brush.colour = makeDiagram({left: [diagramCell]})*/ + /*const diagramCell = makeDiagramCell({content: atom.value}); + state.brush.colour = makeDiagram({left: [diagramCell]})*/; if (atom.parent !== COLOURTODE_BASE_PARENT) { - const paddle = atom.parent - updatePaddleRule(paddle) - } + const paddle = atom.parent; + updatePaddleRule(paddle); + }; - const brushDiagramCell = makeDiagramCell({content: atom.value}) - state.brush.colour = makeDiagram({left: [brushDiagramCell]}) + const brushDiagramCell = makeDiagramCell({content: atom.value}); + state.brush.colour = makeDiagram({left: [brushDiagramCell]}); - squareTool.toolbarNeedsColourUpdate = true - triangleTool.toolbarNeedsColourUpdate = true - circleTool.toolbarNeedsColourUpdate = true - // wideRectangleTool.toolbarNeedsColourUpdate = true - tallRectangleTool.toolbarNeedsColourUpdate = true + squareTool.toolbarNeedsColourUpdate = true; + triangleTool.toolbarNeedsColourUpdate = true; + circleTool.toolbarNeedsColourUpdate = true; + // wideRectangleTool.toolbarNeedsColourUpdate = true; + tallRectangleTool.toolbarNeedsColourUpdate = true; }, construct: (atom) => { - atom.needsColoursUpdate = true - /*const r = Random.Uint8 % 10 - const g = Random.Uint8 % 10 - const b = Random.Uint8 % 10*/ - /*const r = Random.Uint8 % 10 - const g = Random.Uint8 % 10 - const b = Random.Uint8 % 10*/ - //atom.value = makeArrayFromSplash(r*100 + g*10 + b) - //atom.value = makeArrayFromSplash(555) - //const splash = TODEPOND_COLOURS[Random.Uint8 % TODEPOND_COLOURS.length] + atom.needsColoursUpdate = true; + /*const r = Random.Uint8 % 10; + const g = Random.Uint8 % 10; + const b = Random.Uint8 % 10*/; + /*const r = Random.Uint8 % 10; + const g = Random.Uint8 % 10; + const b = Random.Uint8 % 10*/; + //atom.value = makeArrayFromSplash(r*100 + g*10 + b); + //atom.value = makeArrayFromSplash(555); + //const splash = TODEPOND_COLOURS[Random.Uint8 % TODEPOND_COLOURS.length]; if (typeof state.brush.colour === "number") { - atom.value = makeArrayFromSplash(state.brush.colour) + atom.value = makeArrayFromSplash(state.brush.colour); } else { - atom.value = cloneDragonArray(state.brush.colour.left[0].content) - } - - atom.colourId = 0 - atom.dcolourId = 1 - atom.colourTicker = Infinity - atom.joins = [] - atom.joinColourIds = [] - atom.variableAtoms = [] + atom.value = cloneDragonArray(state.brush.colour.left[0].content); + }; + + atom.colourId = 0; + atom.dcolourId = 1; + atom.colourTicker = Infinity; + atom.joins = []; + atom.joinColourIds = []; + atom.variableAtoms = []; - atom.gradient = new ImageData(atom.width * CT_SCALE, atom.height * CT_SCALE) - atom.headGradient = new ImageData(atom.width * CT_SCALE, atom.height * CT_SCALE) + atom.gradient = new ImageData(atom.width * CT_SCALE, atom.height * CT_SCALE); + atom.headGradient = new ImageData(atom.width * CT_SCALE, atom.height * CT_SCALE); }, updateGradient: (atom) => { - const valueClone = cloneDragonArray(atom.value) - valueClone.joins = [] - atom.colours = getSplashesArrayFromArray(valueClone) + const valueClone = cloneDragonArray(atom.value); + valueClone.joins = []; + atom.colours = getSplashesArrayFromArray(valueClone); - // Create pixel values for gradient - atom.isGradient = true + // Create pixel values for gradient; + atom.isGradient = true; if (atom.joins.length > 0 && !atom.joinExpanded) { - const joinGradients = [] + const joinGradients = []; for (const join of atom.joins) { - join.updateGradient(join) - joinGradients.push(join.gradient) - } + join.updateGradient(join); + joinGradients.push(join.gradient); + }; atom.headGradient = getGradientImageFromColours({ colours: atom.colours, width: atom.width * CT_SCALE, height: atom.height * CT_SCALE, stamp: atom.value.stamp, gradient: atom.headGradient, - }) + }); - const gradients = [atom.headGradient, ...joinGradients] + const gradients = [atom.headGradient, ...joinGradients]; atom.gradient = getMergedGradient({ gradients, width: atom.width * CT_SCALE, height: atom.height * CT_SCALE, stamp: atom.value.stamp, mergedGradient: atom.gradient, - }) - + }); + } else { atom.gradient = getGradientImageFromColours({ colours: atom.colours, @@ -4366,392 +4360,392 @@ registerRule( height: atom.height * CT_SCALE, gradient: atom.gradient, stamp: atom.value.stamp, - }) - } + }); + }; }, - - // Ctrl+F: sqwww + + // Ctrl+F: sqwww; update: (atom) => { - + if (atom.value.isDiagram) { if (atom.multiAtoms === undefined || atom.multiAtoms.length === 0) { - atom.multiAtoms = [] - const diagram = atom.value - const [diagramWidth, diagramHeight] = getDiagramDimensions(diagram) - const cellAtomWidth = atom.width / diagramWidth - const cellAtomHeight = atom.height / diagramHeight + atom.multiAtoms = []; + const diagram = atom.value; + const [diagramWidth, diagramHeight] = getDiagramDimensions(diagram); + const cellAtomWidth = atom.width / diagramWidth; + const cellAtomHeight = atom.height / diagramHeight; for (const diagramCell of diagram.left) { - const multiAtom = createChild(atom, COLOURTODE_SQUARE) - multiAtom.x = diagramCell.x * cellAtomWidth - multiAtom.y = diagramCell.y * cellAtomHeight - multiAtom.width = diagramCell.width * cellAtomWidth - multiAtom.height = diagramCell.height * cellAtomHeight - multiAtom.value = diagramCell.content - atom.multiAtoms.push(multiAtom) - } - } + const multiAtom = createChild(atom, COLOURTODE_SQUARE); + multiAtom.x = diagramCell.x * cellAtomWidth; + multiAtom.y = diagramCell.y * cellAtomHeight; + multiAtom.width = diagramCell.width * cellAtomWidth; + multiAtom.height = diagramCell.height * cellAtomHeight; + multiAtom.value = diagramCell.content; + atom.multiAtoms.push(multiAtom); + }; + }; } else { if (atom.needsColoursUpdate) { - atom.updateGradient(atom) - atom.needsColoursUpdate = false - } - } + atom.updateGradient(atom); + atom.needsColoursUpdate = false; + }; + }; - const {x, y} = getAtomPosition(atom) + const {x, y} = getAtomPosition(atom); - atom.highlightedAtom = undefined + atom.highlightedAtom = undefined; if (hand.content === atom && hand.state === HAND.DRAGGING) { - const left = x - const top = y - const right = x + atom.width - const bottom = y + atom.height + const left = x; + const top = y; + const right = x + atom.width; + const bottom = y + atom.height; if (atom.highlight !== undefined) { - deleteChild(atom, atom.highlight) - atom.highlight = undefined - } - + deleteChild(atom, atom.highlight); + atom.highlight = undefined; + }; + if (atom.highlightedAtom === undefined) { - const atoms = getAllBaseAtoms() + const atoms = getAllBaseAtoms(); for (let other of atoms) { - if (other === atom) continue - if (!other.isSquare) continue + if (other === atom) continue; + if (!other.isSquare) continue; if (other.joins.length > 0 && other.joinExpanded) { - other = other.pickerPad - } - - const {x: ox, y: oy} = getAtomPosition(other) - const oleft = ox - const oright = ox + other.width - const otop = oy - const obottom = oy + other.height - - if (left > oright) continue - if (right < oleft) continue - if (bottom < otop) continue - if (top > obottom) continue + other = other.pickerPad; + }; + + const {x: ox, y: oy} = getAtomPosition(other); + const oleft = ox; + const oright = ox + other.width; + const otop = oy; + const obottom = oy + other.height; + + if (left > oright) continue; + if (right < oleft) continue; + if (bottom < otop) continue; + if (top > obottom) continue; if (other.isPicker) { - atom.highlightedAtom = other.parent + atom.highlightedAtom = other.parent; } else { - if (other.parent !== COLOURTODE_BASE_PARENT) continue - atom.highlightedAtom = other - } + if (other.parent !== COLOURTODE_BASE_PARENT) continue; + atom.highlightedAtom = other; + }; - atom.highlight = createChild(atom, HIGHLIGHT, {bottom: true}) - atom.highlight.hasBorder = true - atom.highlight.hasInner = false - atom.highlight.width = other.width - atom.highlight.height = other.height - atom.highlight.x = ox - atom.highlight.y = oy + atom.highlight = createChild(atom, HIGHLIGHT, {bottom: true}); + atom.highlight.hasBorder = true; + atom.highlight.hasInner = false; + atom.highlight.width = other.width; + atom.highlight.height = other.height; + atom.highlight.x = ox; + atom.highlight.y = oy; - break + break; - } - } + }; + }; if (atom.highlightedAtom === undefined) for (const paddle of paddles) { - if (!paddle.expanded) continue + if (!paddle.expanded) continue; - const {x: px, y: py} = getAtomPosition(paddle) - const pleft = px - const pright = px + paddle.width - const ptop = py - const pbottom = py + paddle.height + const {x: px, y: py} = getAtomPosition(paddle); + const pleft = px; + const pright = px + paddle.width; + const ptop = py; + const pbottom = py + paddle.height; - if (left > pright) continue - if (right < pleft) continue - if (top > pbottom) continue - if (bottom < ptop) continue + if (left > pright) continue; + if (right < pleft) continue; + if (top > pbottom) continue; + if (bottom < ptop) continue; if (paddle.cellAtoms.length === 0) { - const {x: dummyLeftX, y: dummyLeftY} = getAtomPosition(paddle.dummyLeft) - const {x: dummyRightX, y: dummyRightY} = getAtomPosition(paddle.dummyRight) + const {x: dummyLeftX, y: dummyLeftY} = getAtomPosition(paddle.dummyLeft); + const {x: dummyRightX, y: dummyRightY} = getAtomPosition(paddle.dummyRight); if (paddle.rightTriangle === undefined) { - atom.highlight = createChild(atom, HIGHLIGHT, {bottom: true}) - atom.highlight.hasBorder = true - atom.highlight.colour = Colour.Grey - atom.highlight.x = dummyLeftX - atom.highlight.y = dummyLeftY - atom.highlight.width = paddle.dummyLeft.width - atom.highlight.height = paddle.dummyLeft.height - atom.highlightedSide = "left" - - atom.highlightedAtom = paddle + atom.highlight = createChild(atom, HIGHLIGHT, {bottom: true}); + atom.highlight.hasBorder = true; + atom.highlight.colour = Colour.Grey; + atom.highlight.x = dummyLeftX; + atom.highlight.y = dummyLeftY; + atom.highlight.width = paddle.dummyLeft.width; + atom.highlight.height = paddle.dummyLeft.height; + atom.highlightedSide = "left"; + + atom.highlightedAtom = paddle; } else if (left > pleft + paddle.rightTriangle.x) { - atom.highlight = createChild(atom, HIGHLIGHT, {bottom: true}) - atom.highlight.hasBorder = true - atom.highlight.colour = Colour.Grey - atom.highlight.x = dummyRightX - atom.highlight.y = dummyRightY - atom.highlight.width = paddle.dummyRight.width - atom.highlight.height = paddle.dummyRight.height - atom.highlightedSide = "right" - atom.highlightedAtom = paddle + atom.highlight = createChild(atom, HIGHLIGHT, {bottom: true}); + atom.highlight.hasBorder = true; + atom.highlight.colour = Colour.Grey; + atom.highlight.x = dummyRightX; + atom.highlight.y = dummyRightY; + atom.highlight.width = paddle.dummyRight.width; + atom.highlight.height = paddle.dummyRight.height; + atom.highlightedSide = "right"; + atom.highlightedAtom = paddle; } else { - atom.highlight = createChild(atom, HIGHLIGHT, {bottom: true}) - atom.highlight.hasBorder = true - atom.highlight.colour = Colour.Grey - atom.highlight.x = dummyLeftX - atom.highlight.y = dummyLeftY - atom.highlight.width = paddle.dummyLeft.width - atom.highlight.height = paddle.dummyLeft.height - atom.highlightedSide = "left" - atom.highlightedAtom = paddle - } - break - - } + atom.highlight = createChild(atom, HIGHLIGHT, {bottom: true}); + atom.highlight.hasBorder = true; + atom.highlight.colour = Colour.Grey; + atom.highlight.x = dummyLeftX; + atom.highlight.y = dummyLeftY; + atom.highlight.width = paddle.dummyLeft.width; + atom.highlight.height = paddle.dummyLeft.height; + atom.highlightedSide = "left"; + atom.highlightedAtom = paddle; + }; + break; + + }; else if (paddle.rightTriangle !== undefined && left > pleft + paddle.rightTriangle.x) { - let winningDistance = Infinity - let winningSide = undefined - let winningCellAtom = undefined + let winningDistance = Infinity; + let winningSide = undefined; + let winningCellAtom = undefined; for (const catom of paddle.cellAtoms) { - const cellAtom = catom.slot - const {x: cx, y: cy} = getAtomPosition(cellAtom) - const cleft = cx - const cright = cx + cellAtom.width - const ctop = cy - const cbottom = cy + cellAtom.height - - const spotCenter = [cleft, ctop] - const spotLeft = [cleft - cellAtom.width, ctop] - const spotAbove = [cleft, ctop - cellAtom.height] - const spotRight = [cright, ctop] - const spotBelow = [cleft, cbottom] - - const dspotCenter = Math.hypot(x - spotCenter[0], y - spotCenter[1]) + const cellAtom = catom.slot; + const {x: cx, y: cy} = getAtomPosition(cellAtom); + const cleft = cx; + const cright = cx + cellAtom.width; + const ctop = cy; + const cbottom = cy + cellAtom.height; + + const spotCenter = [cleft, ctop]; + const spotLeft = [cleft - cellAtom.width, ctop]; + const spotAbove = [cleft, ctop - cellAtom.height]; + const spotRight = [cright, ctop]; + const spotBelow = [cleft, cbottom]; + + const dspotCenter = Math.hypot(x - spotCenter[0], y - spotCenter[1]); if (catom.slotted === undefined && isCellAtomSlotFree(paddle, spotCenter, true) && dspotCenter < winningDistance) { - winningDistance = dspotCenter - winningCellAtom = cellAtom - winningSide = "slot" - } + winningDistance = dspotCenter; + winningCellAtom = cellAtom; + winningSide = "slot"; + }; - const dspotLeft = Math.hypot(x - spotLeft[0], y - spotLeft[1]) + const dspotLeft = Math.hypot(x - spotLeft[0], y - spotLeft[1]); if (!isCellAtomSpotFilled(paddle, spotLeft, true) && dspotLeft < winningDistance) { - winningDistance = dspotLeft - winningCellAtom = cellAtom - winningSide = "left" - } + winningDistance = dspotLeft; + winningCellAtom = cellAtom; + winningSide = "left"; + }; - const dspotAbove = Math.hypot(x - spotAbove[0], y - spotAbove[1]) + const dspotAbove = Math.hypot(x - spotAbove[0], y - spotAbove[1]); if (!isCellAtomSpotFilled(paddle, spotAbove, true) && dspotAbove < winningDistance) { - winningDistance = dspotAbove - winningCellAtom = cellAtom - winningSide = "above" - } + winningDistance = dspotAbove; + winningCellAtom = cellAtom; + winningSide = "above"; + }; - const dspotRight = Math.hypot(x - spotRight[0], y - spotRight[1]) + const dspotRight = Math.hypot(x - spotRight[0], y - spotRight[1]); if (!isCellAtomSpotFilled(paddle, spotRight, true) && dspotRight < winningDistance) { - winningDistance = dspotRight - winningCellAtom = cellAtom - winningSide = "right" - } + winningDistance = dspotRight; + winningCellAtom = cellAtom; + winningSide = "right"; + }; - const dspotBelow = Math.hypot(x - spotBelow[0], y - spotBelow[1]) + const dspotBelow = Math.hypot(x - spotBelow[0], y - spotBelow[1]); if (!isCellAtomSpotFilled(paddle, spotBelow, true) && dspotBelow < winningDistance) { - winningDistance = dspotBelow - winningCellAtom = cellAtom - winningSide = "below" - } - } + winningDistance = dspotBelow; + winningCellAtom = cellAtom; + winningSide = "below"; + }; + }; - const {x: cx, y: cy} = getAtomPosition(winningCellAtom) + const {x: cx, y: cy} = getAtomPosition(winningCellAtom); - atom.highlight = createChild(atom, HIGHLIGHT, {bottom: true}) + atom.highlight = createChild(atom, HIGHLIGHT, {bottom: true}); if (winningSide === "left" || winningSide === "right") { - atom.highlight.width = HIGHLIGHT_THICKNESS - atom.highlight.height = winningCellAtom.height - } + atom.highlight.width = HIGHLIGHT_THICKNESS; + atom.highlight.height = winningCellAtom.height; + }; else if (winningSide === "above" || winningSide === "below") { - atom.highlight.width = winningCellAtom.width - atom.highlight.height = HIGHLIGHT_THICKNESS - } + atom.highlight.width = winningCellAtom.width; + atom.highlight.height = HIGHLIGHT_THICKNESS; + }; if (winningSide === "left") { - atom.highlight.x = cx - HIGHLIGHT_THICKNESS/2 - atom.highlight.y = cy - } + atom.highlight.x = cx - HIGHLIGHT_THICKNESS/2; + atom.highlight.y = cy; + }; else if (winningSide === "right") { - atom.highlight.x = cx - HIGHLIGHT_THICKNESS/2 + winningCellAtom.width - atom.highlight.y = cy - } + atom.highlight.x = cx - HIGHLIGHT_THICKNESS/2 + winningCellAtom.width; + atom.highlight.y = cy; + }; else if (winningSide === "above") { - atom.highlight.x = cx - atom.highlight.y = cy - HIGHLIGHT_THICKNESS/2 - } + atom.highlight.x = cx; + atom.highlight.y = cy - HIGHLIGHT_THICKNESS/2; + }; else if (winningSide === "below") { - atom.highlight.x = cx - atom.highlight.y = cy - HIGHLIGHT_THICKNESS/2 + winningCellAtom.height - } + atom.highlight.x = cx; + atom.highlight.y = cy - HIGHLIGHT_THICKNESS/2 + winningCellAtom.height; + }; if (winningSide === "slot") { - atom.highlight.width = COLOURTODE_SQUARE.size - atom.highlight.height = COLOURTODE_SQUARE.size - const {x: cx, y: cy} = getAtomPosition(winningCellAtom) - atom.highlight.x = cx - atom.highlight.y = cy - atom.highlight.hasBorder = true - atom.highlight.colour = Colour.Grey - } + atom.highlight.width = COLOURTODE_SQUARE.size; + atom.highlight.height = COLOURTODE_SQUARE.size; + const {x: cx, y: cy} = getAtomPosition(winningCellAtom); + atom.highlight.x = cx; + atom.highlight.y = cy; + atom.highlight.hasBorder = true; + atom.highlight.colour = Colour.Grey; + }; - atom.highlightedAtom = winningCellAtom - atom.highlightedSide = winningSide + atom.highlightedAtom = winningCellAtom; + atom.highlightedSide = winningSide; - break + break; - } + }; - /* + /*; else if (paddle.rightTriangle !== undefined && left > pleft + paddle.rightTriangle.x) { - let winningDistance = Infinity - let winningSlot = undefined + let winningDistance = Infinity; + let winningSlot = undefined; for (const cellAtom of paddle.cellAtoms) { - if (cellAtom.slotted !== undefined) continue - const {x: cx, y: cy} = getAtomPosition(cellAtom.slot) - - const distance = Math.hypot(x - cx, y - cy) + if (cellAtom.slotted !== undefined) continue; + const {x: cx, y: cy} = getAtomPosition(cellAtom.slot); + + const distance = Math.hypot(x - cx, y - cy); if (distance < winningDistance) { - winningDistance = distance - winningSlot = cellAtom.slot - } + winningDistance = distance; + winningSlot = cellAtom.slot; + }; - } + }; - if (winningSlot === undefined) break + if (winningSlot === undefined) break; - const {x: cx, y: cy} = getAtomPosition(winningSlot) - atom.highlight = createChild(atom, HIGHLIGHT, {bottom: true}) - atom.highlight.x = cx - atom.highlight.y = cy - atom.highlight.hasBorder = true - atom.highlight.colour = Colour.Grey - atom.highlightedAtom = winningSlot + const {x: cx, y: cy} = getAtomPosition(winningSlot); + atom.highlight = createChild(atom, HIGHLIGHT, {bottom: true}); + atom.highlight.x = cx; + atom.highlight.y = cy; + atom.highlight.hasBorder = true; + atom.highlight.colour = Colour.Grey; + atom.highlightedAtom = winningSlot; + + break; + }; + */; - break - } - */ - else { - let winningDistance = Infinity - let winningSide = undefined - let winningCellAtom = undefined + let winningDistance = Infinity; + let winningSide = undefined; + let winningCellAtom = undefined; for (const cellAtom of paddle.cellAtoms) { - const {x: cx, y: cy} = getAtomPosition(cellAtom) - const cleft = cx - const cright = cx + cellAtom.width - const ctop = cy - const cbottom = cy + cellAtom.height - - const spotCenter = [cleft, ctop] - const spotLeft = [cleft - cellAtom.width, ctop] - const spotAbove = [cleft, ctop - cellAtom.height] - const spotRight = [cright, ctop] - const spotBelow = [cleft, cbottom] - - const dspotCenter = Math.hypot(x - spotCenter[0], y - spotCenter[1]) + const {x: cx, y: cy} = getAtomPosition(cellAtom); + const cleft = cx; + const cright = cx + cellAtom.width; + const ctop = cy; + const cbottom = cy + cellAtom.height; + + const spotCenter = [cleft, ctop]; + const spotLeft = [cleft - cellAtom.width, ctop]; + const spotAbove = [cleft, ctop - cellAtom.height]; + const spotRight = [cright, ctop]; + const spotBelow = [cleft, cbottom]; + + const dspotCenter = Math.hypot(x - spotCenter[0], y - spotCenter[1]); if (isCellAtomSlotFree(paddle, spotCenter) && dspotCenter < winningDistance) { - winningDistance = dspotCenter - winningCellAtom = cellAtom - winningSide = "slot" - } + winningDistance = dspotCenter; + winningCellAtom = cellAtom; + winningSide = "slot"; + }; - const dspotLeft = Math.hypot(x - spotLeft[0], y - spotLeft[1]) + const dspotLeft = Math.hypot(x - spotLeft[0], y - spotLeft[1]); if (!isCellAtomSpotFilled(paddle, spotLeft) && dspotLeft < winningDistance) { - winningDistance = dspotLeft - winningCellAtom = cellAtom - winningSide = "left" - } + winningDistance = dspotLeft; + winningCellAtom = cellAtom; + winningSide = "left"; + }; - const dspotAbove = Math.hypot(x - spotAbove[0], y - spotAbove[1]) + const dspotAbove = Math.hypot(x - spotAbove[0], y - spotAbove[1]); if (!isCellAtomSpotFilled(paddle, spotAbove) && dspotAbove < winningDistance) { - winningDistance = dspotAbove - winningCellAtom = cellAtom - winningSide = "above" - } + winningDistance = dspotAbove; + winningCellAtom = cellAtom; + winningSide = "above"; + }; - const dspotRight = Math.hypot(x - spotRight[0], y - spotRight[1]) + const dspotRight = Math.hypot(x - spotRight[0], y - spotRight[1]); if (!isCellAtomSpotFilled(paddle, spotRight) && dspotRight < winningDistance) { - winningDistance = dspotRight - winningCellAtom = cellAtom - winningSide = "right" - } + winningDistance = dspotRight; + winningCellAtom = cellAtom; + winningSide = "right"; + }; - const dspotBelow = Math.hypot(x - spotBelow[0], y - spotBelow[1]) + const dspotBelow = Math.hypot(x - spotBelow[0], y - spotBelow[1]); if (!isCellAtomSpotFilled(paddle, spotBelow) && dspotBelow < winningDistance) { - winningDistance = dspotBelow - winningCellAtom = cellAtom - winningSide = "below" - } - } + winningDistance = dspotBelow; + winningCellAtom = cellAtom; + winningSide = "below"; + }; + }; - const {x: cx, y: cy} = getAtomPosition(winningCellAtom) + const {x: cx, y: cy} = getAtomPosition(winningCellAtom); - atom.highlight = createChild(atom, HIGHLIGHT, {bottom: true}) + atom.highlight = createChild(atom, HIGHLIGHT, {bottom: true}); if (winningSide === "left" || winningSide === "right") { - atom.highlight.width = HIGHLIGHT_THICKNESS - atom.highlight.height = winningCellAtom.height - } + atom.highlight.width = HIGHLIGHT_THICKNESS; + atom.highlight.height = winningCellAtom.height; + }; else if (winningSide === "above" || winningSide === "below") { - atom.highlight.width = winningCellAtom.width - atom.highlight.height = HIGHLIGHT_THICKNESS - } + atom.highlight.width = winningCellAtom.width; + atom.highlight.height = HIGHLIGHT_THICKNESS; + }; if (winningSide === "left") { - atom.highlight.x = cx - HIGHLIGHT_THICKNESS/2 - atom.highlight.y = cy - } + atom.highlight.x = cx - HIGHLIGHT_THICKNESS/2; + atom.highlight.y = cy; + }; else if (winningSide === "right") { - atom.highlight.x = cx - HIGHLIGHT_THICKNESS/2 + winningCellAtom.width - atom.highlight.y = cy - } + atom.highlight.x = cx - HIGHLIGHT_THICKNESS/2 + winningCellAtom.width; + atom.highlight.y = cy; + }; else if (winningSide === "above") { - atom.highlight.x = cx - atom.highlight.y = cy - HIGHLIGHT_THICKNESS/2 - } + atom.highlight.x = cx; + atom.highlight.y = cy - HIGHLIGHT_THICKNESS/2; + }; else if (winningSide === "below") { - atom.highlight.x = cx - atom.highlight.y = cy - HIGHLIGHT_THICKNESS/2 + winningCellAtom.height - } + atom.highlight.x = cx; + atom.highlight.y = cy - HIGHLIGHT_THICKNESS/2 + winningCellAtom.height; + }; if (winningSide === "slot") { - atom.highlight.width = COLOURTODE_SQUARE.size - atom.highlight.height = COLOURTODE_SQUARE.size - const {x: cx, y: cy} = getAtomPosition(winningCellAtom) - atom.highlight.x = cx - atom.highlight.y = cy - atom.highlight.hasBorder = true - atom.highlight.colour = Colour.Grey - } + atom.highlight.width = COLOURTODE_SQUARE.size; + atom.highlight.height = COLOURTODE_SQUARE.size; + const {x: cx, y: cy} = getAtomPosition(winningCellAtom); + atom.highlight.x = cx; + atom.highlight.y = cy; + atom.highlight.hasBorder = true; + atom.highlight.colour = Colour.Grey; + }; - atom.highlightedAtom = winningCellAtom - atom.highlightedSide = winningSide + atom.highlightedAtom = winningCellAtom; + atom.highlightedSide = winningSide; - break + break; - } + }; - } + }; - } + }; if (atom.highlightedAtom === undefined && atom.highlight !== undefined) { - deleteChild(atom, atom.highlight) - atom.highlight = undefined - } + deleteChild(atom, atom.highlight); + atom.highlight = undefined; + }; }, @@ -4759,362 +4753,362 @@ registerRule( if (atom.highlight !== undefined) { if (atom.highlightedAtom.isPaddle) { - const paddle = atom.highlightedAtom - atom.attached = true + const paddle = atom.highlightedAtom; + atom.attached = true; if (atom.highlightedSide === "right") { - const dummy = createChild(paddle, SLOT, {bottom: true}) - dummy.x = PADDLE.width/2 - atom.width/2 - dummy.y = PADDLE.height/2 - atom.height/2 - dummy.isLeftSlot = true - dummy.isSlot = false - paddle.cellAtoms.push(dummy) + const dummy = createChild(paddle, SLOT, {bottom: true}); + dummy.x = PADDLE.width/2 - atom.width/2; + dummy.y = PADDLE.height/2 - atom.height/2; + dummy.isLeftSlot = true; + dummy.isSlot = false; + paddle.cellAtoms.push(dummy); - dummy.slotted = atom - atom.cellAtom = dummy - atom.x = atom.highlightedAtom.x - atom.y = atom.highlightedAtom.y - atom.slottee = true - giveChild(paddle, atom) + dummy.slotted = atom; + atom.cellAtom = dummy; + atom.x = atom.highlightedAtom.x; + atom.y = atom.highlightedAtom.y; + atom.slottee = true; + giveChild(paddle, atom); } else { - paddle.cellAtoms.push(atom) - atom.x = atom.highlightedAtom.x - atom.y = atom.highlightedAtom.y - atom.dx = 0 - atom.dy = 0 - giveChild(paddle, atom) - } - - updatePaddleSize(paddle) - } + paddle.cellAtoms.push(atom); + atom.x = atom.highlightedAtom.x; + atom.y = atom.highlightedAtom.y; + atom.dx = 0; + atom.dy = 0; + giveChild(paddle, atom); + }; + + updatePaddleSize(paddle); + }; else if (atom.highlightedAtom.isSlot && atom.highlightedSide === "slot") { - const slot = atom.highlightedAtom - const paddle = slot.parent - atom.attached = true - giveChild(paddle, atom) - atom.x = slot.x - atom.y = slot.y - atom.dx = 0 - atom.dy = 0 - slot.cellAtom.slotted = atom - atom.cellAtom = slot.cellAtom - atom.slottee = true - - updatePaddleSize(slot.parent) - } + const slot = atom.highlightedAtom; + const paddle = slot.parent; + atom.attached = true; + giveChild(paddle, atom); + atom.x = slot.x; + atom.y = slot.y; + atom.dx = 0; + atom.dy = 0; + slot.cellAtom.slotted = atom; + atom.cellAtom = slot.cellAtom; + atom.slottee = true; + + updatePaddleSize(slot.parent); + }; else if (atom.highlightedAtom.isLeftSlot && atom.highlightedSide === "slot") { - const slot = atom.highlightedAtom - const paddle = slot.parent - const id = paddle.cellAtoms.indexOf(slot) - paddle.cellAtoms.splice(id, 1) - atom.x = slot.x - atom.y = slot.y - - atom.attached = true - paddle.cellAtoms.push(atom) - atom.slotted = slot.slotted - atom.slot = slot.slot + const slot = atom.highlightedAtom; + const paddle = slot.parent; + const id = paddle.cellAtoms.indexOf(slot); + paddle.cellAtoms.splice(id, 1); + atom.x = slot.x; + atom.y = slot.y; + + atom.attached = true; + paddle.cellAtoms.push(atom); + atom.slotted = slot.slotted; + atom.slot = slot.slot; if (slot.slotted !== undefined) { - slot.slotted.cellAtom = atom - } - giveChild(paddle, atom) - updatePaddleRule(paddle) - deleteChild(paddle, slot) + slot.slotted.cellAtom = atom; + }; + giveChild(paddle, atom); + updatePaddleRule(paddle); + deleteChild(paddle, slot); - } + }; else if (atom.highlightedAtom.isSlot && atom.highlightedSide !== "slot") { - const slot = atom.highlightedAtom - const paddle = slot.parent - atom.attached = true - giveChild(paddle, atom) - - const dummy = createChild(paddle, SLOT, {bottom: true}) - dummy.isLeftSlot = true - //giveChild(paddle, dummy) - paddle.cellAtoms.push(dummy) - dummy.isSlot = false - dummy.slotted = atom - dummy.slotted.cellAtom = dummy - dummy.slot = slot - atom.slotted = undefined + const slot = atom.highlightedAtom; + const paddle = slot.parent; + atom.attached = true; + giveChild(paddle, atom); + + const dummy = createChild(paddle, SLOT, {bottom: true}); + dummy.isLeftSlot = true; + //giveChild(paddle, dummy); + paddle.cellAtoms.push(dummy); + dummy.isSlot = false; + dummy.slotted = atom; + dummy.slotted.cellAtom = dummy; + dummy.slot = slot; + atom.slotted = undefined; if (atom.expanded) { - atom.unexpand(atom) - } + atom.unexpand(atom); + }; if (atom.highlightedSide === "left") { - atom.x = slot.x - atom.width - atom.y = slot.y + atom.x = slot.x - atom.width; + atom.y = slot.y; } else if (atom.highlightedSide === "right") { - atom.x = slot.x + slot.width - atom.y = slot.y + atom.x = slot.x + slot.width; + atom.y = slot.y; } else if (atom.highlightedSide === "above") { - atom.x = slot.x - atom.y = slot.y - atom.height + atom.x = slot.x; + atom.y = slot.y - atom.height; } else if (atom.highlightedSide === "below") { - atom.x = slot.x - atom.y = slot.y + slot.height - } - - dummy.x = atom.x - paddle.offset - dummy.y = atom.y - - atom.cellAtom = dummy - atom.slottee = true - atom.dx = 0 - atom.dy = 0 - updatePaddleSize(paddle) - } + atom.x = slot.x; + atom.y = slot.y + slot.height; + }; + + dummy.x = atom.x - paddle.offset; + dummy.y = atom.y; + + atom.cellAtom = dummy; + atom.slottee = true; + atom.dx = 0; + atom.dy = 0; + updatePaddleSize(paddle); + }; else if ((atom.highlightedAtom.isLeftSlot || atom.highlightedAtom.isSquare) && atom.highlightedAtom.parent.isPaddle) { - const square = atom.highlightedAtom - const paddle = square.parent - atom.attached = true - giveChild(paddle, atom) - paddle.cellAtoms.push(atom) + const square = atom.highlightedAtom; + const paddle = square.parent; + atom.attached = true; + giveChild(paddle, atom); + paddle.cellAtoms.push(atom); if (atom.expanded) { - atom.unexpand(atom) - } + atom.unexpand(atom); + }; if (atom.highlightedSide === "left") { - atom.x = square.x - atom.width - atom.y = square.y + atom.x = square.x - atom.width; + atom.y = square.y; } else if (atom.highlightedSide === "right") { - atom.x = square.x + square.width - atom.y = square.y + atom.x = square.x + square.width; + atom.y = square.y; } else if (atom.highlightedSide === "above") { - atom.x = square.x - atom.y = square.y - atom.height + atom.x = square.x; + atom.y = square.y - atom.height; } else if (atom.highlightedSide === "below") { - atom.x = square.x - atom.y = square.y + square.height - } + atom.x = square.x; + atom.y = square.y + square.height; + }; if (paddle.rightTriangle !== undefined && atom.slotted !== undefined) { - registerAtom(atom.slotted) - giveChild(paddle, atom.slotted) - } + registerAtom(atom.slotted); + giveChild(paddle, atom.slotted); + }; - atom.dx = 0 - atom.dy = 0 - updatePaddleSize(paddle) + atom.dx = 0; + atom.dy = 0; + updatePaddleSize(paddle); - } + }; else { - const joinee = atom.highlightedAtom - const joiner = atom + const joinee = atom.highlightedAtom; + const joiner = atom; if (joinee.expanded) { - joinee.unexpand(joinee) - } + joinee.unexpand(joinee); + }; if (joiner.expanded) { - joiner.unexpand(joiner) - } - + joiner.unexpand(joiner); + }; + if (joinee.joinExpanded) { - joinee.joinUnepxand(joinee) - } + joinee.joinUnepxand(joinee); + }; - joinee.joins.push(joiner) - deleteAtom(joiner) - - joinee.joinExpand(joinee) - - - joinee.value.joins.push(joiner.value) - joinee.needsColoursUpdate = true - joinee.colourTicker = Infinity - - setBrushColour(joinee.value) - - } - - if (atom.expanded) { - atom.unexpand(atom) - } - - if (atom.joinExpanded) { - atom.joinUnepxand(atom) - } + joinee.joins.push(joiner); + deleteAtom(joiner); - } - }, + joinee.joinExpand(joinee); - joinExpand: (atom) => { - atom.joinExpanded = true - - const pickerPad = createChild(atom, COLOURTODE_PICKER_PAD) - atom.pickerPad = pickerPad - pickerPad.width = atom.width + OPTION_MARGIN*2 - pickerPad.x = -OPTION_MARGIN - pickerPad.height = (atom.joins.length) * (atom.height + OPTION_MARGIN) + OPTION_MARGIN - pickerPad.y = atom.height + OPTION_MARGIN - pickerPad.touch = (atom) => atom - pickerPad.grab = (atom) => atom.parent - pickerPad.dragOnly = true - - const pickerHandle = createChild(atom, COLOURTODE_PICKER_PAD) - atom.pickerHandle = pickerHandle - pickerHandle.width = SYMMETRY_HANDLE.height - pickerHandle.x = atom.width/2 - pickerHandle.width/2 - pickerHandle.height = SYMMETRY_HANDLE.width - pickerHandle.y = atom.height - pickerHandle.touch = (atom) => atom - pickerHandle.grab = (atom) => atom.parent - pickerHandle.dragOnly = true - for (let i = 0; i < atom.joins.length; i++) { - const joiner = atom.joins[i] - registerAtom(joiner) - giveChild(atom, joiner) - joiner.x = 0 - joiner.y = (i+1) * (atom.height + OPTION_MARGIN) + OPTION_MARGIN - joiner.dx = 0 - joiner.dy = 0 - joiner.isJoiner = true - joiner.touch = (atom) => atom.parent - //joiner.grab = (atom) => atom.parent - } - - atom.needsColoursUpdate = true - atom.colourTicker = Infinity + joinee.value.joins.push(joiner.value); + joinee.needsColoursUpdate = true; + joinee.colourTicker = Infinity; + + setBrushColour(joinee.value); + + }; + + if (atom.expanded) { + atom.unexpand(atom); + }; + + if (atom.joinExpanded) { + atom.joinUnepxand(atom); + }; + + }; + }, + + joinExpand: (atom) => { + atom.joinExpanded = true; + + const pickerPad = createChild(atom, COLOURTODE_PICKER_PAD); + atom.pickerPad = pickerPad; + pickerPad.width = atom.width + OPTION_MARGIN*2; + pickerPad.x = -OPTION_MARGIN; + pickerPad.height = (atom.joins.length) * (atom.height + OPTION_MARGIN) + OPTION_MARGIN; + pickerPad.y = atom.height + OPTION_MARGIN; + pickerPad.touch = (atom) => atom; + pickerPad.grab = (atom) => atom.parent; + pickerPad.dragOnly = true; + + const pickerHandle = createChild(atom, COLOURTODE_PICKER_PAD); + atom.pickerHandle = pickerHandle; + pickerHandle.width = SYMMETRY_HANDLE.height; + pickerHandle.x = atom.width/2 - pickerHandle.width/2; + pickerHandle.height = SYMMETRY_HANDLE.width; + pickerHandle.y = atom.height; + pickerHandle.touch = (atom) => atom; + pickerHandle.grab = (atom) => atom.parent; + pickerHandle.dragOnly = true; + + for (let i = 0; i < atom.joins.length; i++) { + const joiner = atom.joins[i]; + registerAtom(joiner); + giveChild(atom, joiner); + joiner.x = 0; + joiner.y = (i+1) * (atom.height + OPTION_MARGIN) + OPTION_MARGIN; + joiner.dx = 0; + joiner.dy = 0; + joiner.isJoiner = true; + joiner.touch = (atom) => atom.parent; + //joiner.grab = (atom) => atom.parent; + }; + + atom.needsColoursUpdate = true; + atom.colourTicker = Infinity; if (atom.multiAtoms !== undefined) { for (const multiAtom of atom.multiAtoms) { - bringAtomToFront(multiAtom) - } - } + bringAtomToFront(multiAtom); + }; + }; + + atom.attached = false; - atom.attached = false - }, joinUnepxand: (atom) => { - atom.joinExpanded = false - deleteChild(atom, atom.pickerPad) - deleteChild(atom, atom.pickerHandle) + atom.joinExpanded = false; + deleteChild(atom, atom.pickerPad); + deleteChild(atom, atom.pickerHandle); for (let i = 0; i < atom.joins.length; i++) { - const joiner = atom.joins[i] - deleteChild(atom, joiner) - } - - atom.needsColoursUpdate = true - atom.colourTicker = Infinity + const joiner = atom.joins[i]; + deleteChild(atom, joiner); + }; + + atom.needsColoursUpdate = true; + atom.colourTicker = Infinity; }, - // ONLY USE .value NOT ANYTHING ELSE + // ONLY USE .value NOT ANYTHING ELSE; clone: (atom) => { - const newAtom = makeSquareFromValue(atom.value) - - const {x, y} = getAtomPosition(atom) - newAtom.x = x - newAtom.y = y + const newAtom = makeSquareFromValue(atom.value); + + const {x, y} = getAtomPosition(atom); + newAtom.x = x; + newAtom.y = y; - return newAtom + return newAtom; }, rightDraggable: true, rightDrag: (atom) => { - const newAtom = atom.clone(atom) + const newAtom = atom.clone(atom); - hand.offset.x -= atom.x - newAtom.x - hand.offset.y -= atom.y - newAtom.y + hand.offset.x -= atom.x - newAtom.x; + hand.offset.y -= atom.y - newAtom.y; - registerAtom(newAtom) - setBrushColour(newAtom.value) + registerAtom(newAtom); + setBrushColour(newAtom.value); - return newAtom + return newAtom; }, - // Ctrl+f: sqdra + // Ctrl+f: sqdra; drag: (atom) => { if (atom.joins.length > 0 && atom.joinExpanded) { - return atom - } + return atom; + }; if (atom.isJoiner) { - const id = atom.parent.joins.indexOf(atom) - atom.parent.joins.splice(id, 1) - atom.parent.value.joins.splice(id, 1) - atom.parent.joinUnepxand(atom.parent) + const id = atom.parent.joins.indexOf(atom); + atom.parent.joins.splice(id, 1); + atom.parent.value.joins.splice(id, 1); + atom.parent.joinUnepxand(atom.parent); if (atom.parent.joins.length > 0) { - atom.parent.joinExpand(atom.parent) - } - freeChild(atom.parent, atom) - atom.isJoiner = false - atom.touch = COLOURTODE_SQUARE.touch - } + atom.parent.joinExpand(atom.parent); + }; + freeChild(atom.parent, atom); + atom.isJoiner = false; + atom.touch = COLOURTODE_SQUARE.touch; + }; if (atom.attached) { - const paddle = atom.parent + const paddle = atom.parent; if (atom.slottee) { - atom.attached = false - atom.slottee = false - freeChild(paddle, atom) - //atom.cellAtom.slot.colour = Colour.Black - atom.cellAtom.slotted = undefined + atom.attached = false; + atom.slottee = false; + freeChild(paddle, atom); + //atom.cellAtom.slot.colour = Colour.Black; + atom.cellAtom.slotted = undefined; if (atom.cellAtom.isLeftSlot) { - deleteChild(paddle, atom.cellAtom) - const id = paddle.cellAtoms.indexOf(atom.cellAtom) - paddle.cellAtoms.splice(id, 1) - } - atom.cellAtom = undefined - updatePaddleSize(paddle) - return atom - } - - const {x, y} = atom - atom.attached = false - freeChild(paddle, atom) - - const id = paddle.cellAtoms.indexOf(atom) - paddle.cellAtoms.splice(id, 1) - - atom.slot = undefined + deleteChild(paddle, atom.cellAtom); + const id = paddle.cellAtoms.indexOf(atom.cellAtom); + paddle.cellAtoms.splice(id, 1); + }; + atom.cellAtom = undefined; + updatePaddleSize(paddle); + return atom; + }; + + const {x, y} = atom; + atom.attached = false; + freeChild(paddle, atom); + + const id = paddle.cellAtoms.indexOf(atom); + paddle.cellAtoms.splice(id, 1); + + atom.slot = undefined; if (paddle.rightTriangle !== undefined && atom.slotted !== undefined) { - const dummy = createChild(paddle, SLOT, {bottom: true}) - dummy.x = x - dummy.y = y - dummy.isLeftSlot = true - //giveChild(paddle, dummy) - paddle.cellAtoms.push(dummy) - dummy.isSlot = false - dummy.slotted = atom.slotted - dummy.slotted.cellAtom = dummy - atom.slotted = undefined - } - //atom.slotted = undefined - updatePaddleSize(paddle) + const dummy = createChild(paddle, SLOT, {bottom: true}); + dummy.x = x; + dummy.y = y; + dummy.isLeftSlot = true; + //giveChild(paddle, dummy); + paddle.cellAtoms.push(dummy); + dummy.isSlot = false; + dummy.slotted = atom.slotted; + dummy.slotted.cellAtom = dummy; + atom.slotted = undefined; + }; + //atom.slotted = undefined; + updatePaddleSize(paddle); - } + }; - return atom + return atom; }, size: 40, expanded: false, - } + }; const getWarpedGradientPoints = (width, height) => { - - const maxWidth = 1.0 - const maxHeight = 1.0 - - /* - const max = Math.max(width, height) - const maxWidth = max / width - const maxHeight = max / height - */ - - const midWidth = maxWidth/2 - const midHeight = maxHeight/2 + + const maxWidth = 1.0; + const maxHeight = 1.0; + + /*; + const max = Math.max(width, height); + const maxWidth = max / width; + const maxHeight = max / height; + */; + + const midWidth = maxWidth/2; + const midHeight = maxHeight/2; @@ -5122,531 +5116,531 @@ registerRule( [maxWidth, 0.0], [maxWidth, midHeight], [maxWidth, maxHeight], [midWidth, 0.0], [midWidth, midHeight], [midWidth, maxHeight], [0.0, 0.0], [0.0, midHeight], [0.0, maxHeight], - ] - } + ]; + }; const getDistancesFromGradientPoints = (x, y, points) => { - const distances = [] + const distances = []; for (const [px, py] of points) { - const displacement = [px-x, py-y] - const distance = Math.hypot(...displacement) - distances.push(distance) - } - return distances - } + const displacement = [px-x, py-y]; + const distance = Math.hypot(...displacement); + distances.push(distance); + }; + return distances; + }; const getGradientPointScoresFromDistances = (distances) => { - const scores = [] + const scores = []; for (const distance of distances) { - //if (distance === min) scores.push(distance**2) - //else scores.push(0.0) - scores.push(distance**2) - } - return scores - } + //if (distance === min) scores.push(distance**2); + //else scores.push(0.0); + scores.push(distance**2); + }; + return scores; + }; const lerp = (distance, line) => { - const [a, b] = line - const [ax, ay] = a - const [bx, by] = b - - const x = ax + (bx - ax) * distance - const y = ay + (by - ay) * distance - - const point = [x, y] - return point - - } + const [a, b] = line; + const [ax, ay] = a; + const [bx, by] = b; + + const x = ax + (bx - ax) * distance; + const y = ay + (by - ay) * distance; + + const point = [x, y]; + return point; + + }; const getMergedGradient = ({gradients, width, height, mergedGradient = new ImageData(width, height), stamp}) => { - - ;[width, height] = [width, height].map(dimension => Math.round(dimension)) - const newLength = width * height * 4 + +[width, height] = [width, height].map(dimension => Math.round(dimension)); + const newLength = width * height * 4; if (mergedGradient.data.length !== newLength) { - mergedGradient = new ImageData(width, height) - } + mergedGradient = new ImageData(width, height); + }; - const count = gradients.length - const step = 2*Math.PI / count - let offset = -step/2 - Math.PI/2 - if (count === 2) offset -= Math.PI/4 + const count = gradients.length; + const step = 2*Math.PI / count; + let offset = -step/2 - Math.PI/2; + if (count === 2) offset -= Math.PI/4; const limits = gradients.map((gradient, i) => { - let angle = i*step+step - /*while (angle < 0) angle += 2*Math.PI - while (angle > 2*Math.PI) angle -= 2*Math.PI*/ - return angle - }) - - let i = 0 + let angle = i*step+step; + /*while (angle < 0) angle += 2*Math.PI; + while (angle > 2*Math.PI) angle -= 2*Math.PI*/; + return angle; + }); + + let i = 0; for (let y = 0; y < height; y++) { for (let x = 0; x < width; x++) { - const dx = x - width/2 - const dy = y - height/2 - let angle = Math.atan2(dy, dx) - offset - while (angle < 0) angle += 2*Math.PI - while (angle > 2*Math.PI) angle -= 2*Math.PI - let id = 0 - - let blend = false - let blendScore = 0 + const dx = x - width/2; + const dy = y - height/2; + let angle = Math.atan2(dy, dx) - offset; + while (angle < 0) angle += 2*Math.PI; + while (angle > 2*Math.PI) angle -= 2*Math.PI; + let id = 0; + + let blend = false; + let blendScore = 0; while (angle > limits[id]) { - id++ + id++; if (id >= gradients.length) { - id = 0 - break - } - } - - const diff = limits[id] - angle - const prevId = (id-1 < 0)? limits.length-1 : id-1 - const prevLimit = limits[prevId] - const prefDiff = prevLimit - angle - const nextId = (id+1 >= limits.length)? 0 : id+1 - let blendId = undefined - - const pity = 0.05 + id = 0; + break; + }; + }; + + const diff = limits[id] - angle ; + const prevId = (id-1 < 0)? limits.length-1 : id-1; + const prevLimit = limits[prevId]; + const prefDiff = prevLimit - angle; + const nextId = (id+1 >= limits.length)? 0 : id+1; + let blendId = undefined; + + const pity = 0.05; if (Math.abs(prefDiff) < pity) { - blend = true - blendScore = (-prefDiff) / pity / 2 + 0.5 - blendId = prevId + blend = true; + blendScore = (-prefDiff) / pity / 2 + 0.5; + blendId = prevId; } else if (Math.abs(diff) < pity) { - blend = true - blendScore = (diff) / pity / 2 + 0.5 - blendId = nextId + blend = true; + blendScore = (diff) / pity / 2 + 0.5; + blendId = nextId; } else if (angle < pity) { - blend = true - blendScore = angle / pity / 2 + 0.5 - blendId = prevId - } + blend = true; + blendScore = angle / pity / 2 + 0.5; + blendId = prevId; + }; if (blend) { - mergedGradient.data[i] = (gradients[id].data[i]*(blendScore) + gradients[blendId].data[i]*((1-blendScore))) - mergedGradient.data[i+1] = (gradients[id].data[i+1]*(blendScore) + gradients[blendId].data[i+1]*((1-blendScore))) - mergedGradient.data[i+2] = (gradients[id].data[i+2]*(blendScore) + gradients[blendId].data[i+2]*((1-blendScore))) - mergedGradient.data[i+3] = (gradients[id].data[i+3]*(blendScore) + gradients[blendId].data[i+3]*((1-blendScore))) + mergedGradient.data[i] = (gradients[id].data[i]*(blendScore) + gradients[blendId].data[i]*((1-blendScore))); + mergedGradient.data[i+1] = (gradients[id].data[i+1]*(blendScore) + gradients[blendId].data[i+1]*((1-blendScore))); + mergedGradient.data[i+2] = (gradients[id].data[i+2]*(blendScore) + gradients[blendId].data[i+2]*((1-blendScore))); + mergedGradient.data[i+3] = (gradients[id].data[i+3]*(blendScore) + gradients[blendId].data[i+3]*((1-blendScore))); } else { - mergedGradient.data[i] = gradients[id].data[i] - mergedGradient.data[i+1] = gradients[id].data[i+1] - mergedGradient.data[i+2] = gradients[id].data[i+2] - mergedGradient.data[i+3] = gradients[id].data[i+3] - } - - i += 4 - } - } + mergedGradient.data[i] = gradients[id].data[i]; + mergedGradient.data[i+1] = gradients[id].data[i+1]; + mergedGradient.data[i+2] = gradients[id].data[i+2]; + mergedGradient.data[i+3] = gradients[id].data[i+3]; + }; - return mergedGradient - } + i += 4; + }; + }; + + return mergedGradient; + }; const getGradientImageFromColours = ({colours, width, height, gradient = new ImageData(width, height), stamp}) => { - - ;[width, height] = [width, height].map(dimension => Math.round(dimension)) - //const size = Math.max(width, height) - //width = height = size - const newLength = width * height * 4 + +[width, height] = [width, height].map(dimension => Math.round(dimension)); + //const size = Math.max(width, height); + //width = height = size; + const newLength = width * height * 4; if (gradient.data.length !== newLength) { - gradient = new ImageData(width, height) - } - let minRed = Infinity - let maxRed = -Infinity - let minGreen = Infinity - let maxGreen = -Infinity - let minBlue = Infinity - let maxBlue = -Infinity + gradient = new ImageData(width, height); + }; + let minRed = Infinity; + let maxRed = -Infinity; + let minGreen = Infinity; + let maxGreen = -Infinity; + let minBlue = Infinity; + let maxBlue = -Infinity; for (const colour of colours) { - const [r, g, b] = getRGB(colour) - if (r < minRed) minRed = r - if (r > maxRed) maxRed = r - if (g < minGreen) minGreen = g - if (g > maxGreen) maxGreen = g - if (b < minBlue) minBlue = b - if (b > maxBlue) maxBlue = b - } + const [r, g, b] = getRGB(colour); + if (r < minRed) minRed = r; + if (r > maxRed) maxRed = r; + if (g < minGreen) minGreen = g; + if (g > maxGreen) maxGreen = g; + if (b < minBlue) minBlue = b; + if (b > maxBlue) maxBlue = b; + }; const makeGradientColour = (red, green, blue) => { - //return Colour.splash(maxRed + maxGreen + maxBlue) - return Colour.splash(((red === 1? maxRed : minRed) + (green === 1? maxGreen : minGreen) + (blue === 1? maxBlue : minBlue))) - } + //return Colour.splash(maxRed + maxGreen + maxBlue); + return Colour.splash(((red === 1? maxRed : minRed) + (green === 1? maxGreen : minGreen) + (blue === 1? maxBlue : minBlue))); + }; const gradientColours = [ makeGradientColour(0, 0, 1), makeGradientColour(0, 0, 1), makeGradientColour(0, 0, 1), - + makeGradientColour(0, 1, 1), makeGradientColour(1, 0, 0), makeGradientColour(1, 0, 0), - + makeGradientColour(0, 1, 0), makeGradientColour(0, 1, 0), makeGradientColour(1, 0, 0), - - /* + + /*; makeGradientColour(0, 1, 0), makeGradientColour(0, 1, 1), makeGradientColour(0, 0, 1), - + makeGradientColour(1, 1, 0), makeGradientColour(1, 1, 1), makeGradientColour(1, 0, 1), - + makeGradientColour(1, 0, 0), makeGradientColour(0, 0, 0), makeGradientColour(0, 0, 1), - */ + */; - ] + ]; - const points = getWarpedGradientPoints(width, height) - let i = 0 + const points = getWarpedGradientPoints(width, height); + let i = 0; for (let y = 0; y < height; y++) { for (let x = 0; x < width; x++) { - const distances = getDistancesFromGradientPoints(x / width, y / height, points) - const scores = getGradientPointScoresFromDistances(distances) - const sumValues = [0, 0, 0] - const sumScore = scores.reduce((a, b) => a + b) + const distances = getDistancesFromGradientPoints(x / width, y / height, points); + const scores = getGradientPointScoresFromDistances(distances); + const sumValues = [0, 0, 0]; + const sumScore = scores.reduce((a, b) => a + b); for (let j = 0; j < 9; j++) { - const score = scores[j] - const colour = gradientColours[j] - ;[0, 1, 2].forEach(channel => sumValues[channel] += score * colour[channel]) - } - const values = sumValues.map(value => value / sumScore) + const score = scores[j]; + const colour = gradientColours[j]; +[0, 1, 2].forEach(channel => sumValues[channel] += score * colour[channel]); + }; + const values = sumValues.map(value => value / sumScore); if (stamp === "circle" && x >= width/4 && x < width*3/4 && y >= height/4 && y < height*3/4) { - /* - gradient.data[i] = values[0] / 3*2 - gradient.data[i+1] = values[1] / 3*2 - gradient.data[i+2] = values[2] / 3*2 - */ - gradient.data[i+3] = 0 + /*; + gradient.data[i] = values[0] / 3*2; + gradient.data[i+1] = values[1] / 3*2; + gradient.data[i+2] = values[2] / 3*2; + */; + gradient.data[i+3] = 0; } else { - gradient.data[i] = values[0] - gradient.data[i+1] = values[1] - gradient.data[i+2] = values[2] - gradient.data[i+3] = 255 - } - - i += 4 - if (i >= gradient.data.length) break - } - } - return gradient - } + gradient.data[i] = values[0]; + gradient.data[i+1] = values[1]; + gradient.data[i+2] = values[2]; + gradient.data[i+3] = 255; + }; + + i += 4; + if (i >= gradient.data.length) break; + }; + }; + return gradient; + }; const TRIANGLE_RIGHT = { size: COLOURTODE_SQUARE.size, - width: COLOURTODE_SQUARE.size * Math.sqrt(3)/2, //the only reason width is set is for the menu spacing + width: COLOURTODE_SQUARE.size * Math.sqrt(3)/2, //the only reason width is set is for the menu spacing; draw: (atom) => { - const {x, y} = getAtomPosition(atom) + const {x, y} = getAtomPosition(atom); - let size = atom.size - if (atom.isTool) size -= BORDER_THICKNESS*2.5 - if (!atom.isTool) size -= 2 + let size = atom.size; + if (atom.isTool) size -= BORDER_THICKNESS*2.5; + if (!atom.isTool) size -= 2 ; - const height = size - const width = size * Math.sqrt(3)/2 + const height = size; + const width = size * Math.sqrt(3)/2; - const left = x - const right = left + width - let top = y + 1 - if (atom.isTool) top += BORDER_THICKNESS*1.25 - const bottom = top + height - const middleY = top + height/2 + const left = x; + const right = left + width; + let top = y + 1; + if (atom.isTool) top += BORDER_THICKNESS*1.25; + const bottom = top + height; + const middleY = top + height/2; - colourTodeContext.fillStyle = atom.colour - const path = new Path2D() + colourTodeContext.fillStyle = atom.colour; + const path = new Path2D(); - path.moveTo(left, top) - path.lineTo(right, middleY) - path.lineTo(left, bottom) - path.closePath() - colourTodeContext.fillStyle = atom.colour - colourTodeContext.fill(path) + path.moveTo(left, top); + path.lineTo(right, middleY); + path.lineTo(left, bottom); + path.closePath(); + colourTodeContext.fillStyle = atom.colour; + colourTodeContext.fill(path); if (atom.hasBorder) { - colourTodeContext.lineWidth = BORDER_THICKNESS*1.5 - colourTodeContext.strokeStyle = atom.borderColour + colourTodeContext.lineWidth = BORDER_THICKNESS*1.5; + colourTodeContext.strokeStyle = atom.borderColour; if (atom.isTool) { - //colourTodeContext.lineWidth = BORDER_THICKNESS*1.0 - colourTodeContext.strokeStyle = toolBorderColours[atom.colour.splash] - } - colourTodeContext.stroke(path) - } + //colourTodeContext.lineWidth = BORDER_THICKNESS*1.0; + colourTodeContext.strokeStyle = toolBorderColours[atom.colour.splash]; + }; + colourTodeContext.stroke(path); + }; }, overlaps: (atom, x, y) => { - - const {x: ax, y: ay} = getAtomPosition(atom) - const height = atom.size - const width = atom.size * Math.sqrt(3)/2 - - const left = ax - const right = left + width - const top = ay - const bottom = top + height + const {x: ax, y: ay} = getAtomPosition(atom); + + const height = atom.size; + const width = atom.size * Math.sqrt(3)/2; - if (x < left) return false - if (y < top) return false - if (x > right) return false - if (y > bottom) return false + const left = ax; + const right = left + width; + const top = ay; + const bottom = top + height; - return true + if (x < left) return false; + if (y < top) return false; + if (x > right) return false; + if (y > bottom) return false; + + return true; }, offscreen: (atom) => { - const {x, y} = getAtomPosition(atom) + const {x, y} = getAtomPosition(atom); + + const height = atom.size; + const width = atom.size * Math.sqrt(3)/2; - const height = atom.size - const width = atom.size * Math.sqrt(3)/2 - - const left = x - const right = left + width - const top = y - const bottom = top + height + const left = x; + const right = left + width; + const top = y; + const bottom = top + height; - if (right < 0) return true - if (bottom < 0) return true - if (left > canvas.width) return true - if (top > canvas.height) return true - return false + if (right < 0) return true; + if (bottom < 0) return true; + if (left > canvas.width) return true; + if (top > canvas.height) return true; + return false; }, - } + }; const TRIANGLE_UP = { size: COLOURTODE_SQUARE.size, draw: (atom) => { - const {x, y} = getAtomPosition(atom) + const {x, y} = getAtomPosition(atom); - const width = atom.size - const height = atom.size * Math.sqrt(3)/2 - const diff = atom.size - height + const width = atom.size; + const height = atom.size * Math.sqrt(3)/2; + const diff = atom.size - height; - const left = x - const right = left + width - const top = y + diff/2 - const bottom = top + height - const middleX = left + width/2 + const left = x; + const right = left + width; + const top = y + diff/2; + const bottom = top + height; + const middleX = left + width/2; - colourTodeContext.fillStyle = atom.colour - const path = new Path2D() + colourTodeContext.fillStyle = atom.colour; + const path = new Path2D(); - path.moveTo(left, bottom) - path.lineTo(middleX, top) - path.lineTo(right, bottom) - path.closePath() - colourTodeContext.fillStyle = atom.colour - colourTodeContext.fill(path) + path.moveTo(left, bottom); + path.lineTo(middleX, top); + path.lineTo(right, bottom); + path.closePath(); + colourTodeContext.fillStyle = atom.colour; + colourTodeContext.fill(path); if (atom.hasBorder) { - colourTodeContext.lineWidth = BORDER_THICKNESS*1.5 - colourTodeContext.strokeStyle = atom.borderColour - colourTodeContext.stroke(path) - } + colourTodeContext.lineWidth = BORDER_THICKNESS*1.5; + colourTodeContext.strokeStyle = atom.borderColour; + colourTodeContext.stroke(path); + }; }, overlaps: (atom, x, y) => { - - const {x: ax, y: ay} = getAtomPosition(atom) - const width = atom.size - const height = atom.size * Math.sqrt(3)/2 - - const left = ax - const right = left + width - const top = ay - const bottom = top + height + const {x: ax, y: ay} = getAtomPosition(atom); + + const width = atom.size; + const height = atom.size * Math.sqrt(3)/2; - if (x < left) return false - if (y < top) return false - if (x > right) return false - if (y > bottom) return false + const left = ax; + const right = left + width; + const top = ay; + const bottom = top + height; - return true + if (x < left) return false; + if (y < top) return false; + if (x > right) return false; + if (y > bottom) return false; + + return true; }, offscreen: (atom) => { - const {x, y} = getAtomPosition(atom) + const {x, y} = getAtomPosition(atom); + + const width = atom.size; + const height = atom.size * Math.sqrt(3)/2; - const width = atom.size - const height = atom.size * Math.sqrt(3)/2 - - const left = x - const right = left + width - const top = y - const bottom = top + height + const left = x; + const right = left + width; + const top = y; + const bottom = top + height; - if (right < 0) return true - if (bottom < 0) return true - if (left > canvas.width) return true - if (top > canvas.height) return true - return false + if (right < 0) return true; + if (bottom < 0) return true; + if (left > canvas.width) return true; + if (top > canvas.height) return true; + return false; }, - } + }; const TRIANGLE_DOWN = { size: COLOURTODE_SQUARE.size, draw: (atom) => { - const {x, y} = getAtomPosition(atom) + const {x, y} = getAtomPosition(atom); - const width = atom.size - const height = atom.size * Math.sqrt(3)/2 - const diff = atom.size - height + const width = atom.size; + const height = atom.size * Math.sqrt(3)/2; + const diff = atom.size - height; - const left = x - const right = left + width - const top = y + diff/2 - const bottom = top + height - const middleX = left + width/2 + const left = x; + const right = left + width; + const top = y + diff/2; + const bottom = top + height; + const middleX = left + width/2; - colourTodeContext.fillStyle = atom.colour - const path = new Path2D() + colourTodeContext.fillStyle = atom.colour; + const path = new Path2D(); - path.moveTo(left, top) - path.lineTo(middleX, bottom) - path.lineTo(right, top) - path.closePath() - colourTodeContext.fillStyle = atom.colour - colourTodeContext.fill(path) + path.moveTo(left, top); + path.lineTo(middleX, bottom); + path.lineTo(right, top); + path.closePath(); + colourTodeContext.fillStyle = atom.colour; + colourTodeContext.fill(path); if (atom.hasBorder) { - colourTodeContext.lineWidth = BORDER_THICKNESS*1.5 - colourTodeContext.strokeStyle = atom.borderColour - colourTodeContext.stroke(path) - } + colourTodeContext.lineWidth = BORDER_THICKNESS*1.5; + colourTodeContext.strokeStyle = atom.borderColour; + colourTodeContext.stroke(path); + }; }, overlaps: (atom, x, y) => { - - const {x: ax, y: ay} = getAtomPosition(atom) - const width = atom.size - const height = atom.size * Math.sqrt(3)/2 - - const left = ax - const right = left + width - const top = ay - const bottom = top + height + const {x: ax, y: ay} = getAtomPosition(atom); + + const width = atom.size; + const height = atom.size * Math.sqrt(3)/2; - if (x < left) return false - if (y < top) return false - if (x > right) return false - if (y > bottom) return false + const left = ax; + const right = left + width; + const top = ay; + const bottom = top + height; - return true + if (x < left) return false; + if (y < top) return false; + if (x > right) return false; + if (y > bottom) return false; + + return true; }, offscreen: (atom) => { - const {x, y} = getAtomPosition(atom) + const {x, y} = getAtomPosition(atom); + + const width = atom.size; + const height = atom.size * Math.sqrt(3)/2; - const width = atom.size - const height = atom.size * Math.sqrt(3)/2 - - const left = x - const right = left + width - const top = y - const bottom = top + height + const left = x; + const right = left + width; + const top = y; + const bottom = top + height; - if (right < 0) return true - if (bottom < 0) return true - if (left > canvas.width) return true - if (top > canvas.height) return true - return false + if (right < 0) return true; + if (bottom < 0) return true; + if (left > canvas.width) return true; + if (top > canvas.height) return true; + return false; }, - } + }; const TRIANGLE_LEFT = { size: COLOURTODE_SQUARE.size, - width: COLOURTODE_SQUARE.size * Math.sqrt(3)/2, //the only reason width is set is for the menu spacing + width: COLOURTODE_SQUARE.size * Math.sqrt(3)/2, //the only reason width is set is for the menu spacing; draw: (atom) => { - const {x, y} = getAtomPosition(atom) - - let size = atom.size - if (atom.isTool) size -= BORDER_THICKNESS*2.5 - if (!atom.isTool) size -= 2 - - const height = size - const width = size * Math.sqrt(3)/2 - - const left = x - const right = left + width - let top = y + 1 - if (atom.isTool) top += BORDER_THICKNESS*1.25 - const bottom = top + height - const middleY = top + height/2 - - colourTodeContext.fillStyle = atom.colour - const path = new Path2D() - - path.moveTo(right, top) - path.lineTo(left, middleY) - path.lineTo(right, bottom) - path.closePath() - colourTodeContext.fillStyle = atom.colour - colourTodeContext.fill(path) + const {x, y} = getAtomPosition(atom); + + let size = atom.size; + if (atom.isTool) size -= BORDER_THICKNESS*2.5; + if (!atom.isTool) size -= 2 ; + + const height = size; + const width = size * Math.sqrt(3)/2; + + const left = x; + const right = left + width; + let top = y + 1; + if (atom.isTool) top += BORDER_THICKNESS*1.25; + const bottom = top + height; + const middleY = top + height/2; + + colourTodeContext.fillStyle = atom.colour; + const path = new Path2D(); + + path.moveTo(right, top); + path.lineTo(left, middleY); + path.lineTo(right, bottom); + path.closePath(); + colourTodeContext.fillStyle = atom.colour; + colourTodeContext.fill(path); if (atom.hasBorder) { - colourTodeContext.lineWidth = BORDER_THICKNESS*1.5 - colourTodeContext.strokeStyle = atom.borderColour + colourTodeContext.lineWidth = BORDER_THICKNESS*1.5; + colourTodeContext.strokeStyle = atom.borderColour; if (atom.isTool) { - //colourTodeContext.lineWidth = BORDER_THICKNESS*1.0 - colourTodeContext.strokeStyle = toolBorderColours[atom.colour.splash] - } - colourTodeContext.stroke(path) - } + //colourTodeContext.lineWidth = BORDER_THICKNESS*1.0; + colourTodeContext.strokeStyle = toolBorderColours[atom.colour.splash]; + }; + colourTodeContext.stroke(path); + }; }, overlaps: (atom, x, y) => { - - const {x: ax, y: ay} = getAtomPosition(atom) - const height = atom.size - const width = atom.size * Math.sqrt(3)/2 - - const left = ax - const right = left + width - const top = ay - const bottom = top + height + const {x: ax, y: ay} = getAtomPosition(atom); + + const height = atom.size; + const width = atom.size * Math.sqrt(3)/2; - if (x < left) return false - if (y < top) return false - if (x > right) return false - if (y > bottom) return false + const left = ax; + const right = left + width; + const top = ay; + const bottom = top + height; - return true + if (x < left) return false; + if (y < top) return false; + if (x > right) return false; + if (y > bottom) return false; + + return true; }, offscreen: (atom) => { - const {x, y} = getAtomPosition(atom) + const {x, y} = getAtomPosition(atom); + + const height = atom.size; + const width = atom.size * Math.sqrt(3)/2; - const height = atom.size - const width = atom.size * Math.sqrt(3)/2 - - const left = x - const right = left + width - const top = y - const bottom = top + height + const left = x; + const right = left + width; + const top = y; + const bottom = top + height; - if (right < 0) return true - if (bottom < 0) return true - if (left > canvas.width) return true - if (top > canvas.height) return true - return false + if (right < 0) return true; + if (bottom < 0) return true; + if (left > canvas.width) return true; + if (top > canvas.height) return true; + return false; }, - } + }; - // Ctrl+F: trdef + // Ctrl+F: trdef; const COLOURTODE_TRIANGLE = { behindOtherChildren: true, expanded: false, draw: (atom) => { - if (atom.direction === "right") TRIANGLE_RIGHT.draw(atom) - else if (atom.direction === "down") TRIANGLE_DOWN.draw(atom) - else if (atom.direction === "up") TRIANGLE_UP.draw(atom) - else if (atom.direction === "left") TRIANGLE_LEFT.draw(atom) - else TRIANGLE_RIGHT.draw(atom) + if (atom.direction === "right") TRIANGLE_RIGHT.draw(atom); + else if (atom.direction === "down") TRIANGLE_DOWN.draw(atom); + else if (atom.direction === "up") TRIANGLE_UP.draw(atom); + else if (atom.direction === "left") TRIANGLE_LEFT.draw(atom); + else TRIANGLE_RIGHT.draw(atom); }, colour: Colour.splash(999), overlaps: TRIANGLE_RIGHT.overlaps, @@ -5655,344 +5649,344 @@ registerRule( width: TRIANGLE_RIGHT.width, direction: "right", click: (atom) => { - + if (atom.parent.isPaddle) { - atom.parent.pinhole.locked = !atom.parent.pinhole.locked - updatePaddleRule(atom.parent) - return - } + atom.parent.pinhole.locked = !atom.parent.pinhole.locked; + updatePaddleRule(atom.parent); + return; + }; if (atom.expanded) { - atom.unexpand(atom) - } + atom.unexpand(atom); + }; else { - atom.expand(atom) - } + atom.expand(atom); + }; }, expand: (atom) => { - atom.pad = createChild(atom, TRIANGLE_PAD) - atom.handle = createChild(atom, TRIANGLE_HANDLE) - atom.expanded = true + atom.pad = createChild(atom, TRIANGLE_PAD); + atom.handle = createChild(atom, TRIANGLE_HANDLE); + atom.expanded = true; - atom.upPick = createChild(atom, TRIANGLE_PICK_UP) - atom.downPick = createChild(atom, TRIANGLE_PICK_DOWN) - - if (atom.direction === "up") atom.upPick.value = true - // if (atom.direction === "right") atom.rightPick.value = true - if (atom.direction === "down") atom.downPick.value = true + atom.upPick = createChild(atom, TRIANGLE_PICK_UP); + atom.downPick = createChild(atom, TRIANGLE_PICK_DOWN); + + if (atom.direction === "up") atom.upPick.value = true; + // if (atom.direction === "right") atom.rightPick.value = true; + if (atom.direction === "down") atom.downPick.value = true; }, unexpand: (atom) => { - deleteChild(atom, atom.pad) - deleteChild(atom, atom.handle) - deleteChild(atom, atom.upPick) - // deleteChild(atom, atom.rightPick) - deleteChild(atom, atom.downPick) - atom.expanded = false + deleteChild(atom, atom.pad); + deleteChild(atom, atom.handle); + deleteChild(atom, atom.upPick); + // deleteChild(atom, atom.rightPick); + deleteChild(atom, atom.downPick); + atom.expanded = false; }, highlighter: true, - // Returns what atom to highlight when being hovered over stuff + // Returns what atom to highlight when being hovered over stuff; hover: (atom) => { - atom.highlightedSlot = undefined + atom.highlightedSlot = undefined; - // FIND A PADDLE!??? + // FIND A PADDLE!???; if (atom.direction === "right") { - // Get my bounds - const {x, y} = getAtomPosition(atom) - const left = x - const top = y - const right = x + atom.width - const bottom = y + atom.height + // Get my bounds; + const {x, y} = getAtomPosition(atom); + const left = x; + const top = y; + const right = x + atom.width; + const bottom = y + atom.height; for (const paddle of paddles) { - // Don't pick hidden or filled paddles - if (!paddle.expanded) continue - if (paddle.pinhole.locked) continue - if (paddle.rightTriangle !== undefined) continue - - // Get paddle bounds - const {x: px, y: py} = getAtomPosition(paddle) - const pleft = px - const pright = px + paddle.width - const ptop = py - const pbottom = py + paddle.height - - // Check if I am hovering over the paddle - if (left > pright) continue - if (right < pleft) continue - if (top > pbottom) continue - if (bottom < ptop) continue - - // Return the highlight and the highlighted atom (the paddle) - return paddle - } - } - - // FIND A SQUARE TO STAMP???? + // Don't pick hidden or filled paddles; + if (!paddle.expanded) continue; + if (paddle.pinhole.locked) continue; + if (paddle.rightTriangle !== undefined) continue; + + // Get paddle bounds; + const {x: px, y: py} = getAtomPosition(paddle); + const pleft = px; + const pright = px + paddle.width; + const ptop = py; + const pbottom = py + paddle.height; + + // Check if I am hovering over the paddle; + if (left > pright) continue; + if (right < pleft) continue; + if (top > pbottom) continue; + if (bottom < ptop) continue; + + // Return the highlight and the highlighted atom (the paddle); + return paddle; + }; + }; + + // FIND A SQUARE TO STAMP????; if (true) { - - const {x, y} = getAtomPosition(atom) - const left = x - const top = y - const right = x + atom.width - const bottom = y + atom.height - - const others = getAllBaseAtoms() + + const {x, y} = getAtomPosition(atom); + const left = x; + const top = y; + const right = x + atom.width; + const bottom = y + atom.height; + + const others = getAllBaseAtoms(); for (const other of others) { - if (!other.isSquare) continue - - const {x: px, y: py} = getAtomPosition(other) - const pleft = px - const pright = px + other.width - const ptop = py - const pbottom = py + other.height - - if (left > pright) continue - if (right < pleft) continue - if (top > pbottom) continue - if (bottom < ptop) continue - - return other - } + if (!other.isSquare) continue; + + const {x: px, y: py} = getAtomPosition(other); + const pleft = px; + const pright = px + other.width; + const ptop = py; + const pbottom = py + other.height; - // FIND A CHANNEL???? - let winningDistance = Infinity - let winningSquare = undefined - let winningSlot = undefined + if (left > pright) continue; + if (right < pleft) continue; + if (top > pbottom) continue; + if (bottom < ptop) continue; - const atoms = getAllBaseAtoms() + return other; + }; + + // FIND A CHANNEL????; + let winningDistance = Infinity; + let winningSquare = undefined; + let winningSlot = undefined; + + const atoms = getAllBaseAtoms(); for (const other of atoms) { - if (other === atom) continue - if (!other.isSquare) continue - if (!other.expanded) continue + if (other === atom) continue; + if (!other.isSquare) continue; + if (!other.expanded) continue; - const {x: px, y: py} = getAtomPosition(other.pickerPad) - const pleft = px - const pright = px + other.pickerPad.width - const ptop = py - const pbottom = py + other.pickerPad.height + const {x: px, y: py} = getAtomPosition(other.pickerPad); + const pleft = px; + const pright = px + other.pickerPad.width; + const ptop = py; + const pbottom = py + other.pickerPad.height; - if (left > pright) continue - if (right < pleft) continue - if (bottom < ptop) continue - if (top > pbottom) continue + if (left > pright) continue; + if (right < pleft) continue; + if (bottom < ptop) continue; + if (top > pbottom) continue; - const slots = ["red", "green", "blue"].filter(slot => other[slot] === undefined) - if (slots.length === 0) continue - const {x: ax, y: ay} = getAtomPosition(other) + const slots = ["red", "green", "blue"].filter(slot => other[slot] === undefined); + if (slots.length === 0) continue; + const {x: ax, y: ay} = getAtomPosition(other); for (const slot of slots) { - const slotId = CHANNEL_IDS[slot] - const sx = ax + other.size + OPTION_MARGIN*2 + slotId*(COLOURTODE_SQUARE.size + COLOURTODE_PICKER_PAD_MARGIN) - const sy = ay + OPTION_MARGIN - const distance = Math.hypot(x - sx, y - sy) + const slotId = CHANNEL_IDS[slot]; + const sx = ax + other.size + OPTION_MARGIN*2 + slotId*(COLOURTODE_SQUARE.size + COLOURTODE_PICKER_PAD_MARGIN); + const sy = ay + OPTION_MARGIN; + const distance = Math.hypot(x - sx, y - sy); if (distance < winningDistance) { - winningDistance = distance - winningSlot = slot - winningSquare = other - } - } + winningDistance = distance; + winningSlot = slot; + winningSquare = other; + }; + }; if (winningSquare !== undefined) { - const {x: ax, y: ay} = getAtomPosition(winningSquare) - const slotId = CHANNEL_IDS[winningSlot] + const {x: ax, y: ay} = getAtomPosition(winningSquare); + const slotId = CHANNEL_IDS[winningSlot]; if (atom.highlight !== undefined) { - deleteChild(atom, atom.highlight) - atom.highlight = undefined - } + deleteChild(atom, atom.highlight); + atom.highlight = undefined; + }; - atom.highlight = createChild(atom, HIGHLIGHT, {bottom: true}) - atom.highlight.hasBorder = true - atom.highlight.x = ax + winningSquare.size + OPTION_MARGIN + slotId*(OPTION_MARGIN+winningSquare.size) - atom.highlight.y = ay - atom.highlight.width = OPTION_MARGIN*2+winningSquare.size - atom.highlightedAtom = winningSquare - atom.highlightedSlot = winningSlot - } - } + atom.highlight = createChild(atom, HIGHLIGHT, {bottom: true}); + atom.highlight.hasBorder = true; + atom.highlight.x = ax + winningSquare.size + OPTION_MARGIN + slotId*(OPTION_MARGIN+winningSquare.size); + atom.highlight.y = ay; + atom.highlight.width = OPTION_MARGIN*2+winningSquare.size; + atom.highlightedAtom = winningSquare; + atom.highlightedSlot = winningSlot; + }; + }; - return winningSquare + return winningSquare; - // return - } + // return; + }; - return undefined + return undefined; }, updateValue: (atom) => { if (atom.direction === "up" || atom.direction === "down") { - atom.variable = atom.highlightedSlot + atom.variable = atom.highlightedSlot; } else if (atom.direction === "left") { - if (atom.highlightedSlot === "red") atom.variable = "blue" - else if (atom.highlightedSlot === "green") atom.variable = "red" - else if (atom.highlightedSlot === "blue") atom.variable = "green" + if (atom.highlightedSlot === "red") atom.variable = "blue"; + else if (atom.highlightedSlot === "green") atom.variable = "red"; + else if (atom.highlightedSlot === "blue") atom.variable = "green"; } else if (atom.direction === "right") { - if (atom.highlightedSlot === "red") atom.variable = "green" - else if (atom.highlightedSlot === "green") atom.variable = "blue" - else if (atom.highlightedSlot === "blue") atom.variable = "red" - } - const add = atom.direction === "up" ? makeNumberFromInt(1) : undefined - const subtract = atom.direction === "down" ? makeNumberFromInt(1) : undefined - const value = makeNumber({channel: atom.channelId, variable: atom.variable, add, subtract}) - atom.value = value + if (atom.highlightedSlot === "red") atom.variable = "green"; + else if (atom.highlightedSlot === "green") atom.variable = "blue"; + else if (atom.highlightedSlot === "blue") atom.variable = "red"; + }; + const add = atom.direction === "up" ? makeNumberFromInt(1) : undefined; + const subtract = atom.direction === "down" ? makeNumberFromInt(1) : undefined; + const value = makeNumber({channel: atom.channelId, variable: atom.variable, add, subtract}); + atom.value = value; }, place: (atom, receiver) => { if (receiver.isSquare && atom.highlightedSlot !== undefined) { - atom.channelId = CHANNEL_IDS[atom.highlightedSlot] - atom.updateValue(atom) - - const square = receiver - square.receiveNumber(square, atom.value, atom.channelId, {expanded: atom.expanded, numberAtom: atom}) - deleteAtom(atom) - atom.dx = 0 - atom.dy = 0 - return - } + atom.channelId = CHANNEL_IDS[atom.highlightedSlot]; + atom.updateValue(atom); + + const square = receiver; + square.receiveNumber(square, atom.value, atom.channelId, {expanded: atom.expanded, numberAtom: atom}); + deleteAtom(atom); + atom.dx = 0; + atom.dy = 0; + return; + }; if (receiver.isSquare) { - const square = receiver + const square = receiver; if (square.stamp === undefined) { - square.stamp = "circle" - square.value.stamp = "circle" - square.needsColoursUpdate = true + square.stamp = "circle"; + square.value.stamp = "circle"; + square.needsColoursUpdate = true; } else { - square.stamp = undefined - square.value.stamp = undefined - square.needsColoursUpdate = true - } + square.stamp = undefined; + square.value.stamp = undefined; + square.needsColoursUpdate = true; + }; - const diagramCell = makeDiagramCell({content: square.value}) - state.brush.colour = makeDiagram({left: [diagramCell]}) + const diagramCell = makeDiagramCell({content: square.value}); + state.brush.colour = makeDiagram({left: [diagramCell]}); - squareTool.toolbarNeedsColourUpdate = true - circleTool.toolbarNeedsColourUpdate = true - triangleTool.toolbarNeedsColourUpdate = true - // wideRectangleTool.toolbarNeedsColourUpdate = true - tallRectangleTool.toolbarNeedsColourUpdate = true + squareTool.toolbarNeedsColourUpdate = true; + circleTool.toolbarNeedsColourUpdate = true; + triangleTool.toolbarNeedsColourUpdate = true; + // wideRectangleTool.toolbarNeedsColourUpdate = true; + tallRectangleTool.toolbarNeedsColourUpdate = true; if (square.parent.isPaddle) { - updatePaddleRule(square.parent) - } - return - } - + updatePaddleRule(square.parent); + }; + return; + }; + if (receiver.isPaddle) { - const paddle = receiver - giveChild(paddle, atom) - paddle.rightTriangle = atom - atom.x = PADDLE.width/2 - atom.width/2 - atom.y = PADDLE.height/2 - atom.height/2 - atom.dx = 0 - atom.dy = 0 + const paddle = receiver; + giveChild(paddle, atom); + paddle.rightTriangle = atom; + atom.x = PADDLE.width/2 - atom.width/2; + atom.y = PADDLE.height/2 - atom.height/2; + atom.dx = 0; + atom.dy = 0; - atom.hasBorder = false - paddle.pinhole.locked = atom.colour === Colour.splash(999) + atom.hasBorder = false; + paddle.pinhole.locked = atom.colour === Colour.splash(999); for (const cellAtom of paddle.cellAtoms) { if (cellAtom.slotted !== undefined) { - registerAtom(cellAtom.slotted) - giveChild(paddle, cellAtom.slotted) - } - } + registerAtom(cellAtom.slotted); + giveChild(paddle, cellAtom.slotted); + }; + }; - updatePaddleSize(paddle) + updatePaddleSize(paddle); if (atom.expanded) { - atom.unexpand(atom) - } + atom.unexpand(atom); + }; - atom.attached = true + atom.attached = true; - unlockMenuTool("circle") - } + unlockMenuTool("circle"); + }; }, rightDraggable: true, rightDrag: (atom) => { - const clone = makeAtom(COLOURTODE_TRIANGLE) - clone.direction = atom.direction - const {x, y} = getAtomPosition(atom) - hand.offset.x -= atom.x - x - hand.offset.y -= atom.y - y - clone.x = x - clone.y = y - registerAtom(clone) - return clone + const clone = makeAtom(COLOURTODE_TRIANGLE); + clone.direction = atom.direction; + const {x, y} = getAtomPosition(atom); + hand.offset.x -= atom.x - x; + hand.offset.y -= atom.y - y; + clone.x = x; + clone.y = y; + registerAtom(clone); + return clone; }, drag: (atom) => { if (atom.parent.isSquare) { - const square = atom.parent - atom.attached = false - square[atom.highlightedSlot] = undefined - freeChild(square, atom) - square.receiveNumber(square, undefined, atom.channelId) - return atom - } - - if (!atom.parent.isPaddle) return atom - const paddle = atom.parent + const square = atom.parent; + atom.attached = false; + square[atom.highlightedSlot] = undefined; + freeChild(square, atom); + square.receiveNumber(square, undefined, atom.channelId); + return atom; + }; + + if (!atom.parent.isPaddle) return atom; + const paddle = atom.parent; // if (false && paddle.pinhole.locked) { - // const clone = makeAtom(COLOURTODE_TRIANGLE) - // clone.direction = atom.direction - // const {x, y} = getAtomPosition(atom) - // hand.offset.x -= atom.x - x - // hand.offset.y -= atom.y - y - // clone.x = x - // clone.y = y - // registerAtom(clone) - // return clone - // } - - atom.attached = false - freeChild(paddle, atom) - paddle.rightTriangle = undefined + // const clone = makeAtom(COLOURTODE_TRIANGLE); + // clone.direction = atom.direction; + // const {x, y} = getAtomPosition(atom); + // hand.offset.x -= atom.x - x; + // hand.offset.y -= atom.y - y; + // clone.x = x; + // clone.y = y; + // registerAtom(clone); + // return clone; + // }; + + atom.attached = false; + freeChild(paddle, atom); + paddle.rightTriangle = undefined; for (const cellAtom of paddle.cellAtoms) { if (cellAtom.slotted !== undefined) { - const {x, y} = getAtomPosition(cellAtom.slotted) - freeChild(paddle, cellAtom.slotted) - cellAtom.slotted.cellAtom = undefined - cellAtom.slotted.attached = false - cellAtom.slotted.x = x - cellAtom.slotted.y = y - cellAtom.slotted.slottee = false - //deleteAtom(cellAtom.slotted) - cellAtom.slotted = undefined - } - } + const {x, y} = getAtomPosition(cellAtom.slotted); + freeChild(paddle, cellAtom.slotted); + cellAtom.slotted.cellAtom = undefined; + cellAtom.slotted.attached = false; + cellAtom.slotted.x = x; + cellAtom.slotted.y = y; + cellAtom.slotted.slottee = false; + //deleteAtom(cellAtom.slotted); + cellAtom.slotted = undefined; + }; + }; if (atom.colour !== Colour.splash(999)) { - atom.hasBorder = true - atom.borderColour = Colour.Grey - } + atom.hasBorder = true; + atom.borderColour = Colour.Grey; + }; - paddle.pinhole.locked = false + paddle.pinhole.locked = false; - updatePaddleSize(paddle) - return atom + updatePaddleSize(paddle); + return atom; }, - } + }; - const OPTION_MARGIN = 10 - const CHANNEL_HEIGHT = COLOURTODE_SQUARE.size - OPTION_MARGIN*2 - const OPTION_SPACING = CHANNEL_HEIGHT + OPTION_MARGIN + const OPTION_MARGIN = 10; + const CHANNEL_HEIGHT = COLOURTODE_SQUARE.size - OPTION_MARGIN*2; + const OPTION_SPACING = CHANNEL_HEIGHT + OPTION_MARGIN; - const COLOURTODE_PICKER_PAD_MARGIN = OPTION_MARGIN + const COLOURTODE_PICKER_PAD_MARGIN = OPTION_MARGIN; const COLOURTODE_PICKER_PAD = { draw: COLOURTODE_RECTANGLE.draw, overlaps: COLOURTODE_RECTANGLE.overlaps, @@ -6005,23 +5999,23 @@ registerRule( x: COLOURTODE_SQUARE.size + COLOURTODE_PICKER_PAD_MARGIN, dragOnly: true, isPicker: true, - } + }; const CHANNEL_IDS = { red: 0, green: 1, blue: 2, - } + }; const CHANNEL_NAMES = [ "red", "green", "blue", - ] + ]; - // Ctrl+F: redef + // Ctrl+F: redef; const COLOURTODE_PICKER_CHANNEL = { - + //behindChildren: true, hasBorder: true, draw: COLOURTODE_RECTANGLE.draw, @@ -6030,369 +6024,368 @@ registerRule( width: COLOURTODE_SQUARE.size, y: (COLOURTODE_SQUARE.size - CHANNEL_HEIGHT)/2, height: CHANNEL_HEIGHT, - + grab: (atom) => { - //if (atom.parent.isSquare) return atom.parent - return atom + //if (atom.parent.isSquare) return atom.parent; + return atom; }, rightDraggable: true, rightDrag: (atom) => { - const clone = makeAtom(COLOURTODE_PICKER_CHANNEL) - registerAtom(clone) - const {x, y} = getAtomPosition(atom) - hand.offset.x -= atom.x - x - hand.offset.y -= atom.y - y - clone.value = cloneDragonNumber(atom.value) + const clone = makeAtom(COLOURTODE_PICKER_CHANNEL); + registerAtom(clone); + const {x, y} = getAtomPosition(atom); + hand.offset.x -= atom.x - x; + hand.offset.y -= atom.y - y; + clone.value = cloneDragonNumber(atom.value); if (atom.expanded) { - clone.createOptions(clone) - clone.expanded = true - } - return clone + clone.createOptions(clone); + clone.expanded = true; + }; + return clone; }, drag: (atom) => { if (atom.parent.isSquare) { - const square = atom.parent - square[atom.channelSlot] = undefined - const channelId = CHANNEL_IDS[atom.channelSlot] - square.receiveNumber(square, undefined, channelId) - freeChild(square, atom) - atom.channelSlot = CHANNEL_NAMES[atom.value.channel] - atom.updateColours(atom) - atom.attached = false - - - atom.needsColoursUpdate = true - atom.colourTicker = Infinity - - // unlockMenuTool("wide_rectangle") - } + const square = atom.parent; + square[atom.channelSlot] = undefined; + const channelId = CHANNEL_IDS[atom.channelSlot]; + square.receiveNumber(square, undefined, channelId); + freeChild(square, atom); + atom.channelSlot = CHANNEL_NAMES[atom.value.channel]; + atom.updateColours(atom); + atom.attached = false; + + atom.needsColoursUpdate = true; + atom.colourTicker = Infinity; + + // unlockMenuTool("wide_rectangle"); + }; else if (atom.parent.isTallRectangle) { - const diamond = atom.parent - freeChild(diamond, atom) - diamond.operationAtoms[atom.highlightedSlot] = undefined - const operationName = atom.highlightedSlot === "padTop"? "add" : "subtract" - diamond.value[operationName] = undefined - atom.attached = false + const diamond = atom.parent; + freeChild(diamond, atom); + diamond.operationAtoms[atom.highlightedSlot] = undefined; + const operationName = atom.highlightedSlot === "padTop"? "add" : "subtract"; + diamond.value[operationName] = undefined; + atom.attached = false; if (diamond.expanded) { - diamond.unexpand(diamond) - diamond.expand(diamond) + diamond.unexpand(diamond); + diamond.expand(diamond); } else { - const handle = atom.highlightedSlot === "padTop"? "handleTop" : "handleBottom" - deleteChild(diamond, diamond[handle], {quiet: true}) - deleteChild(diamond, diamond[atom.highlightedSlot], {quiet: true}) - diamond.expand(diamond) - diamond.unexpand(diamond) - } - } + const handle = atom.highlightedSlot === "padTop"? "handleTop" : "handleBottom"; + deleteChild(diamond, diamond[handle], {quiet: true}); + deleteChild(diamond, diamond[atom.highlightedSlot], {quiet: true}); + diamond.expand(diamond); + diamond.unexpand(diamond); + }; + }; else if (atom.parent.isPaddle) { - const paddle = atom.parent - paddle.chance = undefined - freeChild(paddle, atom) - updatePaddleSize(paddle) - } + const paddle = atom.parent; + paddle.chance = undefined; + freeChild(paddle, atom); + updatePaddleSize(paddle); + }; - return atom + return atom; }, construct: (atom) => { - const values = [false, false, false, false, false, false, false, false, false, true] - const channel = Random.Uint8 % 3 - atom.value = makeNumber({values, channel}) - atom.needsColoursUpdate = true - atom.colourId = 0 - atom.dcolourId = 1 - atom.colourTicker = Infinity + const values = [false, false, false, false, false, false, false, false, false, true]; + const channel = Random.Uint8 % 3; + atom.value = makeNumber({values, channel}); + atom.needsColoursUpdate = true; + atom.colourId = 0; + atom.dcolourId = 1; + atom.colourTicker = Infinity; - atom.selectionBack = createChild(atom, COLOURTODE_CHANNEL_SELECTION_SIDE) + atom.selectionBack = createChild(atom, COLOURTODE_CHANNEL_SELECTION_SIDE); - const selectionTop = createChild(atom, COLOURTODE_CHANNEL_SELECTION_END) - atom.selectionTop = selectionTop - atom.selectionTop.isTop = true - selectionTop.dragOnly = false + const selectionTop = createChild(atom, COLOURTODE_CHANNEL_SELECTION_END); + atom.selectionTop = selectionTop; + atom.selectionTop.isTop = true; + selectionTop.dragOnly = false; - const selectionBottom = createChild(atom, COLOURTODE_CHANNEL_SELECTION_END) - atom.selectionBottom = selectionBottom - atom.selectionBottom.isTop = false - selectionBottom.dragOnly = false + const selectionBottom = createChild(atom, COLOURTODE_CHANNEL_SELECTION_END); + atom.selectionBottom = selectionBottom; + atom.selectionBottom.isTop = false; + selectionBottom.dragOnly = false; - atom.positionSelection(atom) + atom.positionSelection(atom); }, positionSelection: (atom, start, end, top, bottom) => { - + if (!atom.expanded) { - atom.selectionTop.y = -atom.selectionTop.height - //atom.selectionTop.x = -atom.selectionTop.height - - atom.selectionBottom.y = atom.height - //atom.selectionBottom.x = -atom.selectionBottom.height + atom.selectionTop.y = -atom.selectionTop.height; + //atom.selectionTop.x = -atom.selectionTop.height; + + atom.selectionBottom.y = atom.height; + //atom.selectionBottom.x = -atom.selectionBottom.height; + + }; - } - else { - //const optionSpacing = (atom.height + (COLOURTODE_SQUARE.size - CHANNEL_HEIGHT)/2) - const optionSpacing = OPTION_SPACING + //const optionSpacing = (atom.height + (COLOURTODE_SQUARE.size - CHANNEL_HEIGHT)/2); + const optionSpacing = OPTION_SPACING; - atom.selectionTop.y = end - atom.selectionTop.height - atom.selectionBottom.y = start + optionSpacing - atom.selectionBottom.height + atom.selectionTop.y = end - atom.selectionTop.height; + atom.selectionBottom.y = start + optionSpacing - atom.selectionBottom.height; - atom.selectionTop.minY = top - atom.selectionTop.height - atom.selectionTop.maxY = atom.selectionBottom.y - optionSpacing + atom.selectionTop.minY = top - atom.selectionTop.height; + atom.selectionTop.maxY = atom.selectionBottom.y - optionSpacing; - atom.selectionBottom.minY = atom.selectionTop.y + optionSpacing - atom.selectionBottom.maxY = bottom - atom.selectionBottom.height + optionSpacing - } + atom.selectionBottom.minY = atom.selectionTop.y + optionSpacing; + atom.selectionBottom.maxY = bottom - atom.selectionBottom.height + optionSpacing; + }; - atom.positionSelectionBack(atom) + atom.positionSelectionBack(atom); - // bring selectors to front! - const selectionTopId = atom.children.indexOf(atom.selectionTop) - atom.children.splice(selectionTopId, 1) - atom.children.push(atom.selectionTop) + // bring selectors to front!; + const selectionTopId = atom.children.indexOf(atom.selectionTop); + atom.children.splice(selectionTopId, 1); + atom.children.push(atom.selectionTop); - const selectionBottomId = atom.children.indexOf(atom.selectionBottom) - atom.children.splice(selectionBottomId, 1) - atom.children.push(atom.selectionBottom) + const selectionBottomId = atom.children.indexOf(atom.selectionBottom); + atom.children.splice(selectionBottomId, 1); + atom.children.push(atom.selectionBottom); if (atom.parent.isTallRectangle) { - const operationName = atom.highlightedSlot === "padTop"? "add" : "subtract" - atom.parent.value[operationName] = atom.value - } + const operationName = atom.highlightedSlot === "padTop"? "add" : "subtract"; + atom.parent.value[operationName] = atom.value; + }; }, positionSelectionBack: (atom) => { - atom.selectionBack.x = -COLOURTODE_CHANNEL_SELECTION_END.height - atom.selectionBack.y = atom.selectionTop.y - atom.selectionBack.height = atom.selectionBottom.y - atom.selectionTop.y + atom.selectionTop.height - atom.selectionBack.width = atom.width + COLOURTODE_CHANNEL_SELECTION_END.height*2 + atom.selectionBack.x = -COLOURTODE_CHANNEL_SELECTION_END.height; + atom.selectionBack.y = atom.selectionTop.y; + atom.selectionBack.height = atom.selectionBottom.y - atom.selectionTop.y + atom.selectionTop.height; + atom.selectionBack.width = atom.width + COLOURTODE_CHANNEL_SELECTION_END.height*2; }, update: (atom) => { if (atom.expanded) { if (atom.needsColoursUpdate) { - atom.needsColoursUpdate = false - atom.isGradient = true + atom.needsColoursUpdate = false; + atom.isGradient = true; atom.gradient = getGradientImageFromColours({ colours: atom.colours, width: atom.width * CT_SCALE, height: atom.height * CT_SCALE, - gradient: atom.gradient - }) - atom.updateColours(atom) - } - } + gradient: atom.gradient; + }); + atom.updateColours(atom); + }; + }; if (!atom.expanded && atom.needsColoursUpdate) { - atom.needsColoursUpdate = false - + atom.needsColoursUpdate = false; + if (atom.parent.isSquare) { - - const channels = [] + + const channels = []; for (let i = 0; i < 3; i++) { if (i === atom.value.channel) { - channels[i] = atom.value - } + channels[i] = atom.value; + }; else { - const values = [true, false, false, false, false, false, false, false, false, false] - channels[i] = makeNumber({values, channel: i}) - } - } - - const array = makeArray({channels}) - atom.colours = getSplashesArrayFromArray(array) + const values = [true, false, false, false, false, false, false, false, false, false]; + channels[i] = makeNumber({values, channel: i}); + }; + }; - } + const array = makeArray({channels}); + atom.colours = getSplashesArrayFromArray(array); + + }; else { - let array = undefined + let array = undefined; for (let i = 0; i < 10; i++) { - const v = atom.value.values[i] - if (v === false) continue - const join = makeArrayFromSplash(`${i}${i}${i}`) + const v = atom.value.values[i]; + if (v === false) continue; + const join = makeArrayFromSplash(`${i}${i}${i}`); if (array === undefined) { - array = join + array = join; } else { - array.joins.push(join) - } - } + array.joins.push(join); + }; + }; - atom.colours = getSplashesArrayFromArray(array) - } + atom.colours = getSplashesArrayFromArray(array); + }; - atom.isGradient = true + atom.isGradient = true; atom.gradient = getGradientImageFromColours({ colours: atom.colours, width: atom.width * CT_SCALE, height: atom.height * CT_SCALE, - gradient: atom.gradient - }) - } + gradient: atom.gradient; + }); + }; - atom.highlightedAtom = undefined + atom.highlightedAtom = undefined; if (hand.content === atom && hand.state === HAND.DRAGGING) { - const {x, y} = getAtomPosition(atom) - let left = x - let top = y - let right = x + atom.width - let bottom = y + atom.height + const {x, y} = getAtomPosition(atom); + let left = x; + let top = y; + let right = x + atom.width; + let bottom = y + atom.height; if (atom.highlight !== undefined) { - deleteChild(atom, atom.highlight) - atom.highlight = undefined - } + deleteChild(atom, atom.highlight); + atom.highlight = undefined; + }; - let winningDistance = Infinity - let winningSquare = undefined - let winningSlot = undefined + let winningDistance = Infinity; + let winningSquare = undefined; + let winningSlot = undefined; - const atoms = getAllBaseAtoms() + const atoms = getAllBaseAtoms(); for (const square of atoms) { - const other = square + const other = square; if (other.isTallRectangle) { - if (!other.expanded) continue - const slotNames = ["padTop", "padBottom"] + if (!other.expanded) continue; + const slotNames = ["padTop", "padBottom"]; for (const slotName of slotNames) { - - let endAtom = other - + + let endAtom = other; + while (endAtom.isTallRectangle && endAtom.operationAtoms[slotName] !== undefined) { - endAtom = endAtom.operationAtoms[slotName] - } - - if (!endAtom.isTallRectangle) continue - if (!endAtom.expanded) continue - - const slot = endAtom[slotName] - const {x: px, y: py} = getAtomPosition(slot) - const pleft = px - const pright = px + slot.width - const ptop = py - const pbottom = py + slot.height - - if (left > pright) continue - if (right < pleft) continue - if (bottom < ptop) continue - if (top > pbottom) continue - - atom.highlightedSlot = slotName - atom.highlightedAtom = slot - break - - } + endAtom = endAtom.operationAtoms[slotName]; + }; + + if (!endAtom.isTallRectangle) continue; + if (!endAtom.expanded) continue; + + const slot = endAtom[slotName]; + const {x: px, y: py} = getAtomPosition(slot); + const pleft = px; + const pright = px + slot.width; + const ptop = py; + const pbottom = py + slot.height; + + if (left > pright) continue; + if (right < pleft) continue; + if (bottom < ptop) continue; + if (top > pbottom) continue; + + atom.highlightedSlot = slotName; + atom.highlightedAtom = slot; + break; + + }; } else { - if (!square.isSquare) continue - if (!square.expanded) continue + if (!square.isSquare) continue; + if (!square.expanded) continue; + + const {x: px, y: py} = getAtomPosition(square.pickerPad); - const {x: px, y: py} = getAtomPosition(square.pickerPad) + const pleft = px; + const pright = px + square.pickerPad.width; + const ptop = py; + const pbottom = py + square.pickerPad.height; - const pleft = px - const pright = px + square.pickerPad.width - const ptop = py - const pbottom = py + square.pickerPad.height + if (left > pright) continue; + if (right < pleft) continue; + if (bottom < ptop) continue; + if (top > pbottom) continue; - if (left > pright) continue - if (right < pleft) continue - if (bottom < ptop) continue - if (top > pbottom) continue + const slots = ["red", "green", "blue"].filter(slot => square[slot] === undefined); + if (slots.length === 0) continue; - const slots = ["red", "green", "blue"].filter(slot => square[slot] === undefined) - if (slots.length === 0) continue - - const {x: ax, y: ay} = getAtomPosition(square) + const {x: ax, y: ay} = getAtomPosition(square); for (const slot of slots) { - const slotId = CHANNEL_IDS[slot] - const sx = ax + square.size + OPTION_MARGIN*2 + slotId*(COLOURTODE_SQUARE.size + COLOURTODE_PICKER_PAD_MARGIN) - const sy = ay + OPTION_MARGIN - const distance = Math.hypot(x - sx, y - sy) + const slotId = CHANNEL_IDS[slot]; + const sx = ax + square.size + OPTION_MARGIN*2 + slotId*(COLOURTODE_SQUARE.size + COLOURTODE_PICKER_PAD_MARGIN); + const sy = ay + OPTION_MARGIN; + const distance = Math.hypot(x - sx, y - sy); if (distance < winningDistance) { - winningDistance = distance - winningSquare = square - winningSlot = slot - } - } - } + winningDistance = distance; + winningSquare = square; + winningSlot = slot; + }; + }; + }; - } + }; if (winningSquare !== undefined) { - const {x: ax, y: ay} = getAtomPosition(winningSquare) - const slotId = CHANNEL_IDS[winningSlot] + const {x: ax, y: ay} = getAtomPosition(winningSquare); + const slotId = CHANNEL_IDS[winningSlot]; - atom.highlight = createChild(atom, HIGHLIGHT, {bottom: true}) - atom.highlight.hasBorder = true - atom.highlight.x = ax + winningSquare.size + OPTION_MARGIN + slotId*(OPTION_MARGIN+winningSquare.size) - atom.highlight.y = ay - atom.highlight.width = OPTION_MARGIN*2+winningSquare.size - atom.highlightedAtom = winningSquare - atom.highlightedSlot = winningSlot + atom.highlight = createChild(atom, HIGHLIGHT, {bottom: true}); + atom.highlight.hasBorder = true; + atom.highlight.x = ax + winningSquare.size + OPTION_MARGIN + slotId*(OPTION_MARGIN+winningSquare.size); + atom.highlight.y = ay; + atom.highlight.width = OPTION_MARGIN*2+winningSquare.size; + atom.highlightedAtom = winningSquare; + atom.highlightedSlot = winningSlot; } else if (atom.highlightedAtom) { - const {x: ax, y: ay} = getAtomPosition(atom.highlightedAtom) - - atom.highlight = createChild(atom, HIGHLIGHT, {bottom: true}) - atom.highlight.hasBorder = true - atom.highlight.x = ax - atom.highlight.y = ay - atom.highlight.width = atom.highlightedAtom.width - atom.highlight.height = atom.highlightedAtom.height + const {x: ax, y: ay} = getAtomPosition(atom.highlightedAtom); + + atom.highlight = createChild(atom, HIGHLIGHT, {bottom: true}); + atom.highlight.hasBorder = true; + atom.highlight.x = ax; + atom.highlight.y = ay; + atom.highlight.width = atom.highlightedAtom.width; + atom.highlight.height = atom.highlightedAtom.height; } else { - /* - left -= OPTION_MARGIN - right += OPTION_MARGIN - top -= OPTION_MARGIN - bottom += OPTION_MARGIN - + /*; + left -= OPTION_MARGIN; + right += OPTION_MARGIN; + top -= OPTION_MARGIN; + bottom += OPTION_MARGIN; + for (const paddle of paddles) { - const {x: px, y: py} = getAtomPosition(paddle) - const pright = px + paddle.width - const ptop = py - const pbottom = py + paddle.height + const {x: px, y: py} = getAtomPosition(paddle); + const pright = px + paddle.width; + const ptop = py; + const pbottom = py + paddle.height; if (paddle.chance === undefined && paddle.expanded && left <= pright && right >= pright && ((top < pbottom && top > ptop) || (bottom > ptop && bottom < pbottom))) { if (atom.highlightPaddle !== undefined) { - deleteChild(atom, atom.highlightPaddle) - } - - atom.highlightPaddle = createChild(atom, HIGHLIGHT, {bottom: true}) - atom.highlightPaddle.width = HIGHLIGHT_THICKNESS - atom.highlightPaddle.height = paddle.height - atom.highlightPaddle.y = ptop - atom.highlightPaddle.x = pright - HIGHLIGHT_THICKNESS/2 - atom.highlightedPaddle = paddle - return - } - } - */ - } + deleteChild(atom, atom.highlightPaddle); + }; + + atom.highlightPaddle = createChild(atom, HIGHLIGHT, {bottom: true}); + atom.highlightPaddle.width = HIGHLIGHT_THICKNESS; + atom.highlightPaddle.height = paddle.height; + atom.highlightPaddle.y = ptop; + atom.highlightPaddle.x = pright - HIGHLIGHT_THICKNESS/2; + atom.highlightedPaddle = paddle; + return; + }; + }; + */; + }; + + }; - } - if (atom.highlightedAtom === undefined && atom.highlight !== undefined) { - deleteChild(atom, atom.highlight) - atom.highlight = undefined - } - - /* + deleteChild(atom, atom.highlight); + atom.highlight = undefined; + }; + + /*; if (atom.highlightPaddle !== undefined) { - deleteChild(atom, atom.highlightPaddle) - atom.highlightPaddle = undefined - atom.highlightedPaddle = undefined - } - */ + deleteChild(atom, atom.highlightPaddle); + atom.highlightPaddle = undefined; + atom.highlightedPaddle = undefined; + }; + */; }, @@ -6400,237 +6393,236 @@ registerRule( if (atom.highlight !== undefined) { if (atom.highlightedAtom.isSquare) { - const square = atom.highlightedAtom - const slotId = CHANNEL_IDS[atom.highlightedSlot] - atom.value.channel = slotId - /*giveChild(square, atom) - atom.dx = 0 - atom.dy = 0 - square[atom.highlightedSlot] = atom - atom.y = OPTION_MARGIN - atom.x = square.size + OPTION_MARGIN*2 + slotId*(OPTION_MARGIN*square.size)*/ - square.receiveNumber(square, atom.value, slotId, {expanded: atom.expanded}) - deleteAtom(atom) + const square = atom.highlightedAtom; + const slotId = CHANNEL_IDS[atom.highlightedSlot]; + atom.value.channel = slotId; + /*giveChild(square, atom); + atom.dx = 0; + atom.dy = 0; + square[atom.highlightedSlot] = atom; + atom.y = OPTION_MARGIN; + atom.x = square.size + OPTION_MARGIN*2 + slotId*(OPTION_MARGIN*square.size)*/; + square.receiveNumber(square, atom.value, slotId, {expanded: atom.expanded}); + deleteAtom(atom); } else { - const diamond = atom.highlightedAtom.parent - //diamond.unexpand(diamond) - - const operationName = atom.highlightedSlot === "padTop"? "add" : "subtract" - diamond.value[operationName] = atom.value - diamond.operationAtoms[atom.highlightedSlot] = atom - atom.x = atom.highlightedAtom.x + OPTION_MARGIN - atom.y = atom.highlightedAtom.y + atom.highlightedAtom.height/2 - atom.height/2 - atom.dx = 0 - atom.dy = 0 - giveChild(diamond, atom) - //diamond.expand(diamond) - - atom.attached = true - - } + const diamond = atom.highlightedAtom.parent; + //diamond.unexpand(diamond); + + const operationName = atom.highlightedSlot === "padTop"? "add" : "subtract"; + diamond.value[operationName] = atom.value; + diamond.operationAtoms[atom.highlightedSlot] = atom; + atom.x = atom.highlightedAtom.x + OPTION_MARGIN; + atom.y = atom.highlightedAtom.y + atom.highlightedAtom.height/2 - atom.height/2; + atom.dx = 0; + atom.dy = 0; + giveChild(diamond, atom); + //diamond.expand(diamond); + + atom.attached = true; + + }; } else if (atom.highlightPaddle !== undefined) { - const paddle = atom.highlightedPaddle - atom.attached = true - giveChild(paddle, atom) - - paddle.chance = atom - updatePaddleSize(paddle) - - atom.dx = 0 - atom.dy = 0 - } - - // unlockMenuTool("hexagon") - //unlockMenuTool("tall_rectangle") - unlockMenuTool("triangle") + const paddle = atom.highlightedPaddle; + atom.attached = true; + giveChild(paddle, atom); + + paddle.chance = atom; + updatePaddleSize(paddle); + + atom.dx = 0; + atom.dy = 0; + }; + + // unlockMenuTool("hexagon"); + //unlockMenuTool("tall_rectangle"); + unlockMenuTool("triangle"); }, click: (atom) => { if (!atom.expanded) { - atom.expanded = true - atom.colourId = 0 - atom.colourTicker = Infinity - atom.needsColoursUpdate = true - atom.createOptions(atom) - } + atom.expanded = true; + atom.colourId = 0; + atom.colourTicker = Infinity; + atom.needsColoursUpdate = true; + atom.createOptions(atom); + }; else { - atom.expanded = false - atom.deleteOptions(atom) - atom.needsColoursUpdate = true - } + atom.expanded = false; + atom.deleteOptions(atom); + atom.needsColoursUpdate = true; + }; }, deleteOptions: (atom) => { if (atom.options !== undefined) { - atom.deletedOptions = atom.options - } + atom.deletedOptions = atom.options; + }; for (const option of atom.options) { - if (atom !== option) deleteChild(atom, option) - } - atom.needsColoursUpdate = true - atom.colourTicker = Infinity - atom.positionSelection(atom) + if (atom !== option) deleteChild(atom, option); + }; + atom.needsColoursUpdate = true; + atom.colourTicker = Infinity; + atom.positionSelection(atom); }, updateColours: (atom) => { - let parentR = undefined - let parentG = undefined - let parentB = undefined + let parentR = undefined; + let parentG = undefined; + let parentB = undefined; if (atom.parent.isSquare) { - let redNumber = atom.parent.value.channels[0] - let greenNumber = atom.parent.value.channels[1] - let blueNumber = atom.parent.value.channels[2] + let redNumber = atom.parent.value.channels[0]; + let greenNumber = atom.parent.value.channels[1]; + let blueNumber = atom.parent.value.channels[2]; - if (redNumber === undefined) redNumber = makeNumber({channel: 0, values: [true, false, false, false, false, false, false, false, false, false]}) - if (greenNumber === undefined) greenNumber = makeNumber({channel: 1, values: [true, false, false, false, false, false, false, false, false, false]}) - if (blueNumber === undefined) blueNumber = makeNumber({channel: 2, values: [true, false, false, false, false, false, false, false, false, false]}) + if (redNumber === undefined) redNumber = makeNumber({channel: 0, values: [true, false, false, false, false, false, false, false, false, false]}); + if (greenNumber === undefined) greenNumber = makeNumber({channel: 1, values: [true, false, false, false, false, false, false, false, false, false]}); + if (blueNumber === undefined) blueNumber = makeNumber({channel: 2, values: [true, false, false, false, false, false, false, false, false, false]}); - parentR = makeNumber({values: [...redNumber.values], channel: redNumber.channel}) - parentG = makeNumber({values: [...greenNumber.values], channel: greenNumber.channel}) - parentB = makeNumber({values: [...blueNumber.values], channel: blueNumber.channel}) - } + parentR = makeNumber({values: [...redNumber.values], channel: redNumber.channel}); + parentG = makeNumber({values: [...greenNumber.values], channel: greenNumber.channel}); + parentB = makeNumber({values: [...blueNumber.values], channel: blueNumber.channel}); + }; else { - const values = [false, false, false, false, false, false, false, false, false, false] - parentR = makeNumber({values: [...values], channel: 0}) - parentG = makeNumber({values: [...values], channel: 1}) - parentB = makeNumber({values: [...values], channel: 2}) - } + const values = [false, false, false, false, false, false, false, false, false, false]; + parentR = makeNumber({values: [...values], channel: 0}); + parentG = makeNumber({values: [...values], channel: 1}); + parentB = makeNumber({values: [...values], channel: 2}); + }; - const parentChannels = [parentR, parentG, parentB] - const mainParentChannel = parentChannels[CHANNEL_IDS[atom.channelSlot]] - if (mainParentChannel !== undefined) mainParentChannel.values = [false, false, false, false, false, false, false, false, false, false] + const parentChannels = [parentR, parentG, parentB]; + const mainParentChannel = parentChannels[CHANNEL_IDS[atom.channelSlot]]; + if (mainParentChannel !== undefined) mainParentChannel.values = [false, false, false, false, false, false, false, false, false, false]; if (atom.options !== undefined && atom.options.length > 0) { for (let i = 0; i < 10; i++) { - const option = atom.options[i] - + const option = atom.options[i]; + if (atom.parent.isSquare) { - mainParentChannel.values[9-i] = true - if (i > 0) mainParentChannel.values[9-i+1] = false + mainParentChannel.values[9-i] = true; + if (i > 0) mainParentChannel.values[9-i+1] = false; } else { for (const c of parentChannels) { - c.values[9-i] = true - if (i > 0) c.values[9-i+1] = false - } - } + c.values[9-i] = true; + if (i > 0) c.values[9-i+1] = false; + }; + }; - const baseArray = makeArray({channels: parentChannels}) + const baseArray = makeArray({channels: parentChannels}); - const colours = getSplashesArrayFromArray(baseArray) + const colours = getSplashesArrayFromArray(baseArray); - option.colours = colours - option.colourTicker = Infinity + option.colours = colours; + option.colourTicker = Infinity; if (option !== atom) { - option.needsColoursUpdateCountdown = i - option.needsColoursUpdate = false - //option.needsColoursUpdate = true - //option.updateColours(option) - } - } - } + option.needsColoursUpdateCountdown = i; + option.needsColoursUpdate = false; + //option.needsColoursUpdate = true; + //option.updateColours(option); + }; + }; + }; }, getCenterId: (atom) => { - let startId = undefined - let endId = undefined - + let startId = undefined; + let endId = undefined; + for (let i = 0; i < atom.value.values.length; i++) { - const value = atom.value.values[i] + const value = atom.value.values[i]; if (value) { - if (startId === undefined) startId = i - endId = i - } - } - return Math.round((endId + startId) / 2) + if (startId === undefined) startId = i; + endId = i; + }; + }; + return Math.round((endId + startId) / 2); }, getStartAndEndId: (atom) => { - let startId = undefined - let endId = undefined - + let startId = undefined; + let endId = undefined; + for (let i = 0; i < atom.value.values.length; i++) { - const value = atom.value.values[i] + const value = atom.value.values[i]; if (value) { - if (startId === undefined) startId = i - endId = i - } - } - return [startId, endId] + if (startId === undefined) startId = i; + endId = i; + }; + }; + return [startId, endId]; }, createOptions: (atom) => { - const oldOptions = atom.parent.isSquare ? atom.deletedOptions : undefined - atom.options = [] + const oldOptions = atom.parent.isSquare ? atom.deletedOptions : undefined; + atom.options = []; + + let startId = undefined; + let endId = undefined; - let startId = undefined - let endId = undefined - for (let i = 0; i < atom.value.values.length; i++) { - const value = atom.value.values[i] + const value = atom.value.values[i]; if (value) { - if (startId === undefined) startId = i - endId = i - } - } - - if (startId === undefined) throw new Error("[ColourTode] Number cannot be NOTHING. Please let @TodePond know if you see this error!") - //const centerOptionId = 9 - Math.floor((endId + startId) / 2) - const centerOptionId = atom.getCenterId(atom) - - //const optionSpacing = (atom.height + (COLOURTODE_SQUARE.size - CHANNEL_HEIGHT)/2) - const optionSpacing = OPTION_SPACING - let top = (centerOptionId - 9) * optionSpacing - let bottom = centerOptionId*optionSpacing - - - const start = top + (9-startId) * optionSpacing - const end = top + (9-endId) * optionSpacing + if (startId === undefined) startId = i; + endId = i; + }; + }; + + if (startId === undefined) throw new Error("[ColourTode] Number cannot be NOTHING. Please let @TodePond know if you see this error!"); + //const centerOptionId = 9 - Math.floor((endId + startId) / 2); + const centerOptionId = atom.getCenterId(atom); + + //const optionSpacing = (atom.height + (COLOURTODE_SQUARE.size - CHANNEL_HEIGHT)/2); + const optionSpacing = OPTION_SPACING; + let top = (centerOptionId - 9) * optionSpacing; + let bottom = centerOptionId*optionSpacing; + + const start = top + (9-startId) * optionSpacing; + const end = top + (9-endId) * optionSpacing; for (let i = 0; i < 10; i++) { if (centerOptionId === 9-i) { if (oldOptions !== undefined) { - //atom.isGradient = true - //atom.gradient = oldOptions[i].gradient - } - atom.options.push(atom) - continue - } + //atom.isGradient = true; + //atom.gradient = oldOptions[i].gradient; + }; + atom.options.push(atom); + continue; + }; + + const pityTop = i !== 9 - endId + 1; + const pityBottom = i !== 9 - startId - 1; + const option = createChild(atom, {...COLOURTODE_PICKER_CHANNEL_OPTION, pityTop, pityBottom}); - const pityTop = i !== 9 - endId + 1 - const pityBottom = i !== 9 - startId - 1 - const option = createChild(atom, {...COLOURTODE_PICKER_CHANNEL_OPTION, pityTop, pityBottom}) - if (oldOptions !== undefined) { - option.isGradient = true - option.gradient = oldOptions[i].gradient - } + option.isGradient = true; + option.gradient = oldOptions[i].gradient; + }; - option.y = top + i * optionSpacing - option.value = 9 - i - //option.colourTicker = Infinity - //option.needsColoursUpdate = true - option.needsColoursUpdateCountdown = i - option.needsColoursUpdate = true - //option.updateColours(option) - atom.options.push(option) - } + option.y = top + i * optionSpacing; + option.value = 9 - i; + //option.colourTicker = Infinity; + //option.needsColoursUpdate = true; + option.needsColoursUpdateCountdown = i; + option.needsColoursUpdate = true; + //option.updateColours(option); + atom.options.push(option); + }; - atom.positionSelection(atom, start, end, top, bottom) - - atom.updateColours(atom) - } - } + atom.positionSelection(atom, start, end, top, bottom); + + atom.updateColours(atom); + }; + }; - // Ctrl+F: exdef - const MAGIC_NUMBER = 0.8660254 - const MINUS_MAGIC_NUMBER = (1 - MAGIC_NUMBER) + // Ctrl+F: exdef; + const MAGIC_NUMBER = 0.8660254; + const MINUS_MAGIC_NUMBER = (1 - MAGIC_NUMBER); const COLOURTODE_HEXAGON = { colour: Colour.Black, hasBorder: true, @@ -6640,8 +6632,8 @@ registerRule( overlaps: COLOURTODE_RECTANGLE.overlaps, offscreen: COLOURTODE_RECTANGLE.offscreen, draw: (atom) => { - const {x, y} = getAtomPosition(atom) - const {width, height} = atom + const {x, y} = getAtomPosition(atom); + const {width, height} = atom; let points = [ [x + width*MINUS_MAGIC_NUMBER*2, y], [x + width*(MAGIC_NUMBER-MINUS_MAGIC_NUMBER), y], @@ -6649,105 +6641,105 @@ registerRule( [x + width*(MAGIC_NUMBER-MINUS_MAGIC_NUMBER), y + height*MAGIC_NUMBER], [x + width*MINUS_MAGIC_NUMBER*2, y + height*MAGIC_NUMBER], [x, y + height*MAGIC_NUMBER/2], - ] + ]; - points = points.map(([x, y]) => [x, y + MINUS_MAGIC_NUMBER/2*height]) + points = points.map(([x, y]) => [x, y + MINUS_MAGIC_NUMBER/2*height]); - const extraSegmentCorners = [] + const extraSegmentCorners = []; for (let i = 0; i < 6; i++) { - const nextId = wrap(i+1, 0, 5) - const point = points[i] - const next = points[nextId] - const mid = [0, 1].map(axis => (point[axis] + next[axis])/2) - extraSegmentCorners.push(mid) - } - - const center = [x+width/2, y+height/2] + const nextId = wrap(i+1, 0, 5); + const point = points[i]; + const next = points[nextId]; + const mid = [0, 1].map(axis => (point[axis] + next[axis])/2); + extraSegmentCorners.push(mid); + }; + + const center = [x+width/2, y+height/2]; const segmentPoints = points.map((p, i) => { - const offset = 1 - const id = wrap(i+offset, 0, 5) - const point = points[wrap(i+offset+1, 0, 5)] - const nextId = wrap(i+offset+1, 0, 5) - const nextMid = extraSegmentCorners[nextId] - const mid = extraSegmentCorners[id] - return [center, mid, point, nextMid] - }) - - const [head, ...tail] = points - - const path = new Path2D() - path.moveTo(...head) + const offset = 1; + const id = wrap(i+offset, 0, 5); + const point = points[wrap(i+offset+1, 0, 5)]; + const nextId = wrap(i+offset+1, 0, 5); + const nextMid = extraSegmentCorners[nextId]; + const mid = extraSegmentCorners[id]; + return [center, mid, point, nextMid]; + }); + + const [head, ...tail] = points; + + const path = new Path2D(); + path.moveTo(...head); for (const point of tail) { - path.lineTo(...point) - } - path.closePath() + path.lineTo(...point); + }; + path.closePath(); - colourTodeContext.fillStyle = atom.colour - colourTodeContext.fill(path) + colourTodeContext.fillStyle = atom.colour; + colourTodeContext.fill(path); if (atom.ons !== undefined) { for (let i = 0; i < 6; i++) { - if (!atom.ons[i]) continue - const [shead, ...stail] = segmentPoints[i] - const spath = new Path2D() - spath.moveTo(...shead) + if (!atom.ons[i]) continue; + const [shead, ...stail] = segmentPoints[i]; + const spath = new Path2D(); + spath.moveTo(...shead); for (const point of stail) { - spath.lineTo(...point) - } - spath.closePath() - colourTodeContext.fillStyle = Colour.Silver - colourTodeContext.fill(spath) - colourTodeContext.lineWidth = 1 / CT_SCALE - colourTodeContext.strokeStyle = Colour.Silver - colourTodeContext.stroke(spath) - } - } + spath.lineTo(...point); + }; + spath.closePath(); + colourTodeContext.fillStyle = Colour.Silver; + colourTodeContext.fill(spath); + colourTodeContext.lineWidth = 1 / CT_SCALE; + colourTodeContext.strokeStyle = Colour.Silver; + colourTodeContext.stroke(spath); + }; + }; if (atom.hasBorder) { - colourTodeContext.lineWidth = BORDER_THICKNESS*1.5 - colourTodeContext.strokeStyle = atom.borderColour - colourTodeContext.stroke(path) + colourTodeContext.lineWidth = BORDER_THICKNESS*1.5; + colourTodeContext.strokeStyle = atom.borderColour; + colourTodeContext.stroke(path); if (atom.parent.isSquare) { - SYMMETRY_TOGGLE_Y.drawY(atom, atom.size - 8, 4) - } - } + SYMMETRY_TOGGLE_Y.drawY(atom, atom.size - 8, 4); + }; + }; }, getValue: (atom) => { - let score = 0 + let score = 0; for (const on of atom.ons) { - if (on) score++ - } - return score + if (on) score++; + }; + return score; }, click: (atom) => { if (atom.expanded) { - atom.unexpand(atom) + atom.unexpand(atom); } else { - atom.expand(atom) - } + atom.expand(atom); + }; }, unexpand: (atom) => { - atom.expanded = false + atom.expanded = false; for (const thing of atom.handles) { - deleteChild(atom, thing) - } + deleteChild(atom, thing); + }; for (const thing of atom.buttons) { - deleteChild(atom, thing) - } + deleteChild(atom, thing); + }; - atom.handles = [] - atom.buttons = [] + atom.handles = []; + atom.buttons = []; }, expand: (atom) => { - atom.expanded = true - atom.handles = [] - atom.buttons = [] + atom.expanded = true; + atom.handles = []; + atom.buttons = []; - const {width, height} = atom + const {width, height} = atom; - const edge = width*MINUS_MAGIC_NUMBER*1.67 + const edge = width*MINUS_MAGIC_NUMBER*1.67; const handlePositions = [ [width, height/2 - HEXAGON_HANDLE.height/2], [width - edge, height - HEXAGON_HANDLE.height/2], @@ -6755,7 +6747,7 @@ registerRule( [0, height/2 - HEXAGON_HANDLE.height/2], [edge, 0 - HEXAGON_HANDLE.height/2], [width - edge, 0 - HEXAGON_HANDLE.height/2], - ] + ]; let buttonPositions = [ [width, height/2], @@ -6764,198 +6756,198 @@ registerRule( [0, height/2], [edge, 0], [width - edge, 0], - ] + ]; buttonPositions = buttonPositions.map(([x, y], i) => { - const [tx, ty] = [x - atom.width/2, y - atom.height/2] - let [sx, sy] = [] + const [tx, ty] = [x - atom.width/2, y - atom.height/2]; + let [sx, sy] = []; if (i % 3 === 0) { - ;[sx, sy] = [tx * 2.2, ty * 2.2] +[sx, sy] = [tx * 2.2, ty * 2.2]; } else { - ;[sx, sy] = [tx * 2, ty * 2] - } - return [sx + atom.width/2, sy + atom.height/2] - }) +[sx, sy] = [tx * 2, ty * 2]; + }; + return [sx + atom.width/2, sy + atom.height/2]; + }); for (let i = 0; i < 6; i++) { - const handle = createChild(atom, HEXAGON_HANDLE) - handle.rotation = i - handle.x = handlePositions[i][0] - HEXAGON_HANDLE.width/2 - handle.y = handlePositions[i][1] - atom.handles.push(handle) - - const button = createChild(atom, HEXAGON_BUTTON) - button.x = buttonPositions[i][0] - HEXAGON_BUTTON.size/2 - button.y = buttonPositions[i][1] - HEXAGON_BUTTON.size/2 - atom.buttons.push(button) - button.id = i + const handle = createChild(atom, HEXAGON_HANDLE); + handle.rotation = i; + handle.x = handlePositions[i][0] - HEXAGON_HANDLE.width/2; + handle.y = handlePositions[i][1]; + atom.handles.push(handle); + + const button = createChild(atom, HEXAGON_BUTTON); + button.x = buttonPositions[i][0] - HEXAGON_BUTTON.size/2; + button.y = buttonPositions[i][1] - HEXAGON_BUTTON.size/2; + atom.buttons.push(button); + button.id = i; if (atom.ons[i]) { - button.inner.selected = true - button.inner.colour = Colour.Silver - } - - } + button.inner.selected = true; + button.inner.colour = Colour.Silver; + }; + + }; }, construct: (atom) => { - atom.ons = [false, false, false, false, false, false] + atom.ons = [false, false, false, false, false, false]; }, updateValue: (atom) => { - const channel = CHANNEL_IDS[atom.variable] - const addZero = !atom.ons[1] && !atom.ons[0] && !atom.ons[5] - const subtractZero = !atom.ons[2] && !atom.ons[3] && !atom.ons[4] - const bothZero = !addZero && !subtractZero - const addValues = [addZero || bothZero, atom.ons[1], atom.ons[0], atom.ons[5], false, false, false, false, false, false] - const subtractValues = [subtractZero || bothZero, atom.ons[2], atom.ons[3], atom.ons[4], false, false, false, false, false, false] - const add = makeNumber({values: addValues}) - const subtract = makeNumber({values: subtractValues}) - - const value = makeNumber({channel, variable: atom.variable, add, subtract}) - atom.value = value + const channel = CHANNEL_IDS[atom.variable]; + const addZero = !atom.ons[1] && !atom.ons[0] && !atom.ons[5]; + const subtractZero = !atom.ons[2] && !atom.ons[3] && !atom.ons[4]; + const bothZero = !addZero && !subtractZero; + const addValues = [addZero || bothZero, atom.ons[1], atom.ons[0], atom.ons[5], false, false, false, false, false, false]; + const subtractValues = [subtractZero || bothZero, atom.ons[2], atom.ons[3], atom.ons[4], false, false, false, false, false, false]; + const add = makeNumber({values: addValues}); + const subtract = makeNumber({values: subtractValues}); + + const value = makeNumber({channel, variable: atom.variable, add, subtract}); + atom.value = value; }, hover: (atom) => { - const {x, y} = getAtomPosition(atom) - let left = x - let top = y - let right = x + atom.width - let bottom = y + atom.height + const {x, y} = getAtomPosition(atom); + let left = x; + let top = y; + let right = x + atom.width; + let bottom = y + atom.height; for (const paddle of paddles) { - const {x: px, y: py} = getAtomPosition(paddle) - const pright = px + paddle.width - const ptop = py - const pbottom = py + paddle.height + const {x: px, y: py} = getAtomPosition(paddle); + const pright = px + paddle.width; + const ptop = py; + const pbottom = py + paddle.height; if (paddle.chance === undefined && paddle.expanded && left <= pright && right >= pright && ((top < pbottom && top > ptop) || (bottom > ptop && bottom < pbottom))) { if (atom.highlightPaddle !== undefined) { - deleteChild(atom, atom.highlightPaddle) - } - - atom.highlight = createChild(atom, HIGHLIGHT, {bottom: true}) - atom.highlight.width = HIGHLIGHT_THICKNESS - atom.highlight.height = paddle.height - atom.highlight.y = ptop - atom.highlight.x = pright - HIGHLIGHT_THICKNESS/2 - return paddle - } - } - - // let winningDistance = Infinity - // let winningSquare = undefined - // let winningSlot = undefined - - // const atoms = getAllBaseAtoms() + deleteChild(atom, atom.highlightPaddle); + }; + + atom.highlight = createChild(atom, HIGHLIGHT, {bottom: true}); + atom.highlight.width = HIGHLIGHT_THICKNESS; + atom.highlight.height = paddle.height; + atom.highlight.y = ptop; + atom.highlight.x = pright - HIGHLIGHT_THICKNESS/2; + return paddle; + }; + }; + + // let winningDistance = Infinity; + // let winningSquare = undefined; + // let winningSlot = undefined; + + // const atoms = getAllBaseAtoms(); // for (const other of atoms) { - // if (other === atom) continue - // if (!other.isSquare) continue - // if (!other.expanded) continue + // if (other === atom) continue; + // if (!other.isSquare) continue; + // if (!other.expanded) continue; - // const {x: px, y: py} = getAtomPosition(other.pickerPad) - // const pleft = px - // const pright = px + other.pickerPad.width - // const ptop = py - // const pbottom = py + other.pickerPad.height + // const {x: px, y: py} = getAtomPosition(other.pickerPad); + // const pleft = px; + // const pright = px + other.pickerPad.width; + // const ptop = py; + // const pbottom = py + other.pickerPad.height; - // if (left > pright) continue - // if (right < pleft) continue - // if (bottom < ptop) continue - // if (top > pbottom) continue + // if (left > pright) continue; + // if (right < pleft) continue; + // if (bottom < ptop) continue; + // if (top > pbottom) continue; - // const slots = ["red", "green", "blue"].filter(slot => other[slot] === undefined) - // if (slots.length === 0) continue - // const {x: ax, y: ay} = getAtomPosition(other) + // const slots = ["red", "green", "blue"].filter(slot => other[slot] === undefined); + // if (slots.length === 0) continue; + // const {x: ax, y: ay} = getAtomPosition(other); // for (const slot of slots) { - // const slotId = CHANNEL_IDS[slot] - // const sx = ax + other.size + OPTION_MARGIN*2 + slotId*(COLOURTODE_SQUARE.size + COLOURTODE_PICKER_PAD_MARGIN) - // const sy = ay + OPTION_MARGIN - // const distance = Math.hypot(x - sx, y - sy) + // const slotId = CHANNEL_IDS[slot]; + // const sx = ax + other.size + OPTION_MARGIN*2 + slotId*(COLOURTODE_SQUARE.size + COLOURTODE_PICKER_PAD_MARGIN); + // const sy = ay + OPTION_MARGIN; + // const distance = Math.hypot(x - sx, y - sy); // if (distance < winningDistance) { - // winningDistance = distance - // winningSlot = slot - // winningSquare = other - // } - // } + // winningDistance = distance; + // winningSlot = slot; + // winningSquare = other; + // }; + // }; // if (winningSquare !== undefined) { - // const {x: ax, y: ay} = getAtomPosition(winningSquare) - // const slotId = CHANNEL_IDS[winningSlot] + // const {x: ax, y: ay} = getAtomPosition(winningSquare); + // const slotId = CHANNEL_IDS[winningSlot]; - // atom.highlight = createChild(atom, HIGHLIGHT, {bottom: true}) - // atom.highlight.hasBorder = true - // atom.highlight.x = ax + winningSquare.size + OPTION_MARGIN + slotId*(OPTION_MARGIN+winningSquare.size) - // atom.highlight.y = ay - // atom.highlight.width = OPTION_MARGIN*2+winningSquare.size - // atom.highlightedAtom = winningSquare - // atom.highlightedSlot = winningSlot - // } - // } + // atom.highlight = createChild(atom, HIGHLIGHT, {bottom: true}); + // atom.highlight.hasBorder = true; + // atom.highlight.x = ax + winningSquare.size + OPTION_MARGIN + slotId*(OPTION_MARGIN+winningSquare.size); + // atom.highlight.y = ay; + // atom.highlight.width = OPTION_MARGIN*2+winningSquare.size; + // atom.highlightedAtom = winningSquare; + // atom.highlightedSlot = winningSlot; + // }; + // }; - // return winningSquare + // return winningSquare; }, place: (atom, paddle) => { if (paddle.isPaddle) { - atom.attached = true - giveChild(paddle, atom) - - paddle.chance = atom - updatePaddleSize(paddle) - - atom.dx = 0 - atom.dy = 0 - } + atom.attached = true; + giveChild(paddle, atom); + + paddle.chance = atom; + updatePaddleSize(paddle); + + atom.dx = 0; + atom.dy = 0; + } ; // else if (paddle.isSquare) { - // const square = paddle - // atom.variable = atom.highlightedSlot - // atom.updateValue(atom) - // const slotId = CHANNEL_IDS[atom.highlightedSlot] - // square.receiveNumber(square, atom.value, slotId, {expanded: atom.expanded, numberAtom: atom}) - // deleteAtom(atom) - // atom.dx = 0 - // atom.dy = 0 - // } + // const square = paddle; + // atom.variable = atom.highlightedSlot; + // atom.updateValue(atom); + // const slotId = CHANNEL_IDS[atom.highlightedSlot]; + // square.receiveNumber(square, atom.value, slotId, {expanded: atom.expanded, numberAtom: atom}); + // deleteAtom(atom); + // atom.dx = 0; + // atom.dy = 0; + // }; }, drag: (atom) => { if (atom.parent.isPaddle) { - const paddle = atom.parent - freeChild(paddle, atom) - paddle.chance = undefined - updatePaddleSize(paddle) + const paddle = atom.parent; + freeChild(paddle, atom); + paddle.chance = undefined; + updatePaddleSize(paddle); } else if (atom.parent.isSquare) { - const square = atom.parent - square[atom.variable] = undefined - const channelId = CHANNEL_IDS[atom.variable] - square.receiveNumber(square, undefined, channelId) - freeChild(square, atom) - atom.attached = false - } + const square = atom.parent; + square[atom.variable] = undefined; + const channelId = CHANNEL_IDS[atom.variable]; + square.receiveNumber(square, undefined, channelId); + freeChild(square, atom); + atom.attached = false; + }; - return atom + return atom; }, rightDraggable: true, rightDrag: (atom) => { - const clone = atom.clone(atom) - registerAtom(clone) - hand.offset.x -= atom.x - clone.x - hand.offset.y -= atom.y - clone.y - return clone + const clone = atom.clone(atom); + registerAtom(clone); + hand.offset.x -= atom.x - clone.x; + hand.offset.y -= atom.y - clone.y; + return clone; }, clone: (atom) => { - const clone = makeAtom(COLOURTODE_HEXAGON) + const clone = makeAtom(COLOURTODE_HEXAGON); for (let i = 0; i < 6; i++) { - clone.ons[i] = atom.ons[i] - } + clone.ons[i] = atom.ons[i]; + }; if (atom.expanded) { - clone.expand(clone) - } - const {x, y} = getAtomPosition(atom) - clone.x = x - clone.y = y - return clone - } - } + clone.expand(clone); + }; + const {x, y} = getAtomPosition(atom); + clone.x = x; + clone.y = y; + return clone; + }; + }; const rotate = ([x, y], [ox, oy], radians) => { const [dx, dy] = [x - ox, y - oy]; @@ -6976,38 +6968,38 @@ registerRule( grab: (atom) => atom.parent, behindChildren: true, draw: (atom) => { - CIRCLE.draw(atom) + CIRCLE.draw(atom); }, construct: (atom) => { - atom.inner = createChild(atom, HEXAGON_BUTTON_INNER, {bottom: false}) - atom.inner.x = atom.width/2 - atom.inner.width/2 - atom.inner.y = atom.height/2 - atom.inner.height/2 + atom.inner = createChild(atom, HEXAGON_BUTTON_INNER, {bottom: false}); + atom.inner.x = atom.width/2 - atom.inner.width/2; + atom.inner.y = atom.height/2 - atom.inner.height/2; }, click: (atom) => { if (atom.inner.selected) { - atom.inner.selected = false - atom.inner.colour = Colour.Grey + atom.inner.selected = false; + atom.inner.colour = Colour.Grey; } else { - atom.inner.selected = true - atom.inner.colour = Colour.Silver - } + atom.inner.selected = true; + atom.inner.colour = Colour.Silver; + }; + + const hexagon = atom.parent; + hexagon.ons[atom.id] = atom.inner.selected; - const hexagon = atom.parent - hexagon.ons[atom.id] = atom.inner.selected - if (hexagon.parent.isPaddle) { - const paddle = hexagon.parent - updatePaddleSize(paddle) + const paddle = hexagon.parent; + updatePaddleSize(paddle); } else if (hexagon.parent.isSquare) { - const square = hexagon.parent - hexagon.updateValue(hexagon) - const slotId = CHANNEL_IDS[hexagon.variable] - square.receiveNumber(square, hexagon.value, slotId, {expanded: hexagon.expanded, numberAtom: hexagon}) - } - - bringAtomToFront(atom.parent) - } - } + const square = hexagon.parent; + hexagon.updateValue(hexagon); + const slotId = CHANNEL_IDS[hexagon.variable]; + square.receiveNumber(square, hexagon.value, slotId, {expanded: hexagon.expanded, numberAtom: hexagon}); + }; + + bringAtomToFront(atom.parent); + }; + }; const HEXAGON_BUTTON_INNER = { size: COLOURTODE_SQUARE.size * 2/3, @@ -7019,17 +7011,17 @@ registerRule( hasBorder: true, borderColour: Colour.Black, colour: Colour.Grey, - } + }; const HEXAGON_HANDLE = { offscreen: COLOURTODE_RECTANGLE.offscreen, overlaps: (atom, x, y) => { - atom.y -= atom.height/2 - atom.height *= 2 - const result = COLOURTODE_RECTANGLE.overlaps(atom, x, y) - atom.height /= 2 - atom.y += atom.height/2 - return result + atom.y -= atom.height/2; + atom.height *= 2; + const result = COLOURTODE_RECTANGLE.overlaps(atom, x, y); + atom.height /= 2; + atom.y += atom.height/2; + return result; }, colour: Colour.Grey, rotation: 0, @@ -7040,68 +7032,68 @@ registerRule( height: COLOURTODE_SQUARE.size / 3, draw: (atom) => { - const {x, y} = getAtomPosition(atom) - const {width, height} = atom + const {x, y} = getAtomPosition(atom); + const {width, height} = atom; - const path = new Path2D() + const path = new Path2D(); let points = [ [x, y], [x+width, y], [x+width, y+height], [x, y+height], - ] - + ]; + if (atom.rotation > 0) { - points = points.map(point => rotate(point, [x+width/2, y+height/2], atom.rotation * Math.PI/3)) - } + points = points.map(point => rotate(point, [x+width/2, y+height/2], atom.rotation * Math.PI/3)); + }; - const [head, ...tail] = points + const [head, ...tail] = points; - path.moveTo(...head) + path.moveTo(...head); for (const point of tail) { - path.lineTo(...point) - } + path.lineTo(...point); + }; - colourTodeContext.fillStyle = atom.colour - colourTodeContext.fill(path) - } - } + colourTodeContext.fillStyle = atom.colour; + colourTodeContext.fill(path); + }; + }; const COLOURTODE_CHANNEL_SELECTION_END = { draw: (atom) => { - const {x, y} = getAtomPosition(atom) + const {x, y} = getAtomPosition(atom); - /*let colour = "pink" + /*let colour = "pink"; if (atom.parent !== COLOURTODE_BASE_PARENT) { if (atom.parent.parent !== COLOURTODE_BASE_PARENT) { - const colours = getSplashesArrayFromArray(atom.parent.parent.value) - colour = colours[Random.Uint32 % colours.length] - } - }*/ + const colours = getSplashesArrayFromArray(atom.parent.parent.value); + colour = colours[Random.Uint32 % colours.length]; + }; + }*/; - /*colourTodeContext.fillStyle = "#000000" - colourTodeContext.globalCompositeOperation = "lighten" - colourTodeContext.fillRect(x, y, atom.width, atom.height) - colourTodeContext.globalCompositeOperation = "source-over" + /*colourTodeContext.fillStyle = "#000000"; + colourTodeContext.globalCompositeOperation = "lighten"; + colourTodeContext.fillRect(x, y, atom.width, atom.height); + colourTodeContext.globalCompositeOperation = "source-over"; - colourTodeContext.filter = "invert(1) saturate(0%) brightness(67.5%) contrast(10000%)" + colourTodeContext.filter = "invert(1) saturate(0%) brightness(67.5%) contrast(10000%)"; - const X = Math.round(x) - const Y = Math.round(y) - const W = Math.round(atom.width) - const H = Math.round(atom.height) + const X = Math.round(x); + const Y = Math.round(y); + const W = Math.round(atom.width); + const H = Math.round(atom.height); - colourTodeContext.drawImage(colourTodeCanvas, X, Y, W, H, X, Y, W, H) - colourTodeContext.filter = "none"*/ + colourTodeContext.drawImage(colourTodeCanvas, X, Y, W, H, X, Y, W, H); + colourTodeContext.filter = "none"*/; - const X = Math.round(x) - const Y = Math.round(y) - const W = Math.round(atom.width) - const H = Math.round(atom.height) + const X = Math.round(x); + const Y = Math.round(y); + const W = Math.round(atom.width); + const H = Math.round(atom.height); + + colourTodeContext.fillStyle = Colour.Grey; + colourTodeContext.fillRect(X, Y, W, H); - colourTodeContext.fillStyle = Colour.Grey - colourTodeContext.fillRect(X, Y, W, H) - }, overlaps: COLOURTODE_RECTANGLE.overlaps, offscreen: COLOURTODE_RECTANGLE.offscreen, @@ -7113,54 +7105,53 @@ registerRule( grab: (atom) => atom.parent.expanded? atom : atom.parent, touch: (atom) => atom.parent.expanded? atom : atom.parent, cursor: (atom) => { - return atom.parent.expanded? "ns-resize" : "pointer" + return atom.parent.expanded? "ns-resize" : "pointer"; }, move: (atom) => { - atom.parent.positionSelectionBack(atom.parent) + atom.parent.positionSelectionBack(atom.parent); }, drop: (atom) => { - let distanceFromMiddle = Math.round((atom.y+CHANNEL_HEIGHT/2) / OPTION_SPACING) + let distanceFromMiddle = Math.round((atom.y+CHANNEL_HEIGHT/2) / OPTION_SPACING); - const oldNumber = atom.parent.value + const oldNumber = atom.parent.value; - let [startId, endId] = atom.parent.getStartAndEndId(atom.parent) - let centerId = atom.parent.getCenterId(atom.parent) + let [startId, endId] = atom.parent.getStartAndEndId(atom.parent); + let centerId = atom.parent.getCenterId(atom.parent); if (atom.isTop) { - endId = centerId - distanceFromMiddle - } + endId = centerId - distanceFromMiddle; + }; if (!atom.isTop) { - startId = centerId - (distanceFromMiddle-1) - } + startId = centerId - (distanceFromMiddle-1); + }; - const values = [false, false, false, false, false, false, false, false, false, false] + const values = [false, false, false, false, false, false, false, false, false, false]; for (let i = startId; i <= endId; i++) { - values[i] = true - } + values[i] = true; + }; - const number = makeNumber({channel: oldNumber.channel, values}) - atom.parent.value = number - atom.parent.deleteOptions(atom.parent) - atom.parent.createOptions(atom.parent) + const number = makeNumber({channel: oldNumber.channel, values}); + atom.parent.value = number; + atom.parent.deleteOptions(atom.parent); + atom.parent.createOptions(atom.parent); - atom.dx = 0 - atom.dy = 0 + atom.dx = 0; + atom.dy = 0; - if (atom.parent.parent.isSquare) { - const square = atom.parent.parent - const channel = CHANNEL_IDS[atom.parent.channelSlot] - square.receiveNumber(square, number, channel) - } + const square = atom.parent.parent; + const channel = CHANNEL_IDS[atom.parent.channelSlot]; + square.receiveNumber(square, number, channel); + }; if (atom.parent.parent.isPaddle) { - const paddle = atom.parent.parent - updatePaddleSize(paddle) - } + const paddle = atom.parent.parent; + updatePaddleSize(paddle); + }; }, dragLockX: true, - } + }; const COLOURTODE_CHANNEL_SELECTION_SIDE = { overlaps: COLOURTODE_RECTANGLE.overlaps, @@ -7173,7 +7164,7 @@ registerRule( dragLockX: true, draw: COLOURTODE_RECTANGLE.draw, colour: Colour.Grey, - } + }; const COLOURTODE_PICKER_CHANNEL_OPTION = { draw: COLOURTODE_RECTANGLE.draw, @@ -7183,7 +7174,7 @@ registerRule( width: COLOURTODE_SQUARE.size, grab: (atom) => atom.parent, hasBorder: true, - + colourTicker: Infinity, colours: [999], colourId: 0, @@ -7191,304 +7182,304 @@ registerRule( update: (atom) => { if (atom.needsColoursUpdateCountdown >= 0) { - atom.needsColoursUpdateCountdown-- + atom.needsColoursUpdateCountdown--; if (atom.needsColoursUpdateCountdown < 0) { - atom.needsColoursUpdate = true - } - } + atom.needsColoursUpdate = true; + }; + }; if (atom.needsColoursUpdate) { - atom.updateColours(atom) - atom.needsColoursUpdateCountdown = -1 - atom.needsColoursUpdate = false - } + atom.updateColours(atom); + atom.needsColoursUpdateCountdown = -1; + atom.needsColoursUpdate = false; + }; }, - getId: (atom) => { - const parent = atom.parent - const centerId = parent.getCenterId(parent) - const offset = atom.y / OPTION_SPACING - return centerId - offset + getId: (atom) => { ; + const parent = atom.parent; + const centerId = parent.getCenterId(parent); + const offset = atom.y / OPTION_SPACING; + return centerId - offset; }, updateColours: (atom) => { - atom.isGradient = true + atom.isGradient = true; atom.gradient = getGradientImageFromColours({ colours: atom.colours, width: atom.width * CT_SCALE, height: atom.height * CT_SCALE, - gradient: atom.gradient - }) + gradient: atom.gradient; + }); }, touch: (atom) => { - const id = atom.getId(atom) - if (atom.parent.value.values[id]) return atom.parent - return atom + const id = atom.getId(atom); + if (atom.parent.value.values[id]) return atom.parent; + return atom; }, click: (atom) => { - const values = [false, false, false, false, false, false, false, false, false, false] - values[atom.value] = true - const number = makeNumber({values, channel: atom.parent.value.channel}) - const parent = atom.parent - parent.value = number - parent.deleteOptions(parent) - parent.createOptions(parent) - parent.needsColoursUpdate = true + const values = [false, false, false, false, false, false, false, false, false, false]; + values[atom.value] = true; + const number = makeNumber({values, channel: atom.parent.value.channel}); + const parent = atom.parent; + parent.value = number; + parent.deleteOptions(parent); + parent.createOptions(parent); + parent.needsColoursUpdate = true; if (parent.parent.isSquare) { - const square = parent.parent - const channel = CHANNEL_IDS[parent.channelSlot] - square.receiveNumber(square, number, channel) - } + const square = parent.parent; + const channel = CHANNEL_IDS[parent.channelSlot]; + square.receiveNumber(square, number, channel); + }; if (parent.parent.isPaddle) { - const paddle = parent.parent - updatePaddleSize(paddle) - } + const paddle = parent.parent; + updatePaddleSize(paddle); + }; }, construct: (atom) => { if (atom.pityTop) { - const topPity = createChild(atom, COLOURTODE_OPTION_PADDING) - topPity.y = -topPity.height - } + const topPity = createChild(atom, COLOURTODE_OPTION_PADDING); + topPity.y = -topPity.height; + }; if (atom.pityBottom) { - const bottomPity = createChild(atom, COLOURTODE_OPTION_PADDING) - bottomPity.y = atom.height - } + const bottomPity = createChild(atom, COLOURTODE_OPTION_PADDING); + bottomPity.y = atom.height; + }; - //TODO: add cursor pity on the sides too - } - } + //TODO: add cursor pity on the sides too; + }; + }; const CHANNEL_VARIABLES = [ "red", "green", "blue", - ] + ]; - // DIAMOND - // Ctrl+F: dedef + // DIAMOND; + // Ctrl+F: dedef; const COLOURTODE_TALL_RECTANGLE = { behindChildren: true, highlighter: true, rightDraggable: true, rightDrag: (atom) => { - const clone = makeAtom(COLOURTODE_TALL_RECTANGLE) - registerAtom(clone) - const {x, y} = getAtomPosition(atom) - hand.offset.x -= atom.x - x - hand.offset.y -= atom.y - y - clone.variable = atom.variable + const clone = makeAtom(COLOURTODE_TALL_RECTANGLE); + registerAtom(clone); + const {x, y} = getAtomPosition(atom); + hand.offset.x -= atom.x - x; + hand.offset.y -= atom.y - y; + clone.variable = atom.variable; if (atom.expanded) { - clone.expand(clone) - } - clone.updateAppearance(clone) - return clone + clone.expand(clone); + }; + clone.updateAppearance(clone); + return clone; }, drag: (atom) => { if (atom.parent.isSquare) { - const square = atom.parent - square[atom.channelSlot] = undefined - const channelId = CHANNEL_IDS[atom.channelSlot] - square.receiveNumber(square, undefined, channelId) - freeChild(square, atom) - atom.updateAppearance(atom) - atom.attached = false + const square = atom.parent; + square[atom.channelSlot] = undefined; + const channelId = CHANNEL_IDS[atom.channelSlot]; + square.receiveNumber(square, undefined, channelId); + freeChild(square, atom); + atom.updateAppearance(atom); + atom.attached = false; } else if (atom.parent.isTallRectangle) { - const diamond = atom.parent - freeChild(diamond, atom) - diamond.operationAtoms[atom.highlightedSlot] = undefined - const operationName = atom.highlightedSlot === "padTop"? "add" : "subtract" - diamond.value[operationName] = undefined + const diamond = atom.parent; + freeChild(diamond, atom); + diamond.operationAtoms[atom.highlightedSlot] = undefined; + const operationName = atom.highlightedSlot === "padTop"? "add" : "subtract"; + diamond.value[operationName] = undefined; if (atom.expanded) { - atom.unexpand(atom) - atom.expand(atom) - } - atom.attached = false + atom.unexpand(atom); + atom.expand(atom); + }; + atom.attached = false; if (diamond.expanded) { - diamond.unexpand(diamond) - diamond.expand(diamond) + diamond.unexpand(diamond); + diamond.expand(diamond); } else { - const handle = atom.highlightedSlot === "padTop"? "handleTop" : "handleBottom" - deleteChild(diamond, diamond[handle], {quiet: true}) - deleteChild(diamond, diamond[atom.highlightedSlot], {quiet: true}) - diamond.expand(diamond) - diamond.unexpand(diamond) - } - } - return atom + const handle = atom.highlightedSlot === "padTop"? "handleTop" : "handleBottom"; + deleteChild(diamond, diamond[handle], {quiet: true}); + deleteChild(diamond, diamond[atom.highlightedSlot], {quiet: true}); + diamond.expand(diamond); + diamond.unexpand(diamond); + }; + }; + return atom; }, hover: (atom) => { - const {x, y} = getAtomPosition(atom) - const left = x - const top = y - const right = x + atom.width - const bottom = y + atom.height + const {x, y} = getAtomPosition(atom); + const left = x; + const top = y; + const right = x + atom.width; + const bottom = y + atom.height; - let winningDistance = Infinity - let winningSquare = undefined - let winningSlot = undefined + let winningDistance = Infinity; + let winningSquare = undefined; + let winningSlot = undefined; - const atoms = getAllBaseAtoms() + const atoms = getAllBaseAtoms(); for (const other of atoms) { - if (other === atom) continue + if (other === atom) continue; if (other.isTallRectangle) { - if (!other.expanded) continue - const slotNames = ["padTop", "padBottom"] + if (!other.expanded) continue; + const slotNames = ["padTop", "padBottom"]; for (const slotName of slotNames) { - - let endAtom = other + + let endAtom = other; while (endAtom.isTallRectangle && endAtom.operationAtoms[slotName] !== undefined) { - endAtom = endAtom.operationAtoms[slotName] - } - - if (!endAtom.isTallRectangle) continue - if (!endAtom.expanded) continue + endAtom = endAtom.operationAtoms[slotName]; + }; - const slot = endAtom[slotName] - const {x: px, y: py} = getAtomPosition(slot) - const pleft = px - const pright = px + slot.width - const ptop = py - const pbottom = py + slot.height + if (!endAtom.isTallRectangle) continue; + if (!endAtom.expanded) continue; - if (left > pright) continue - if (right < pleft) continue - if (bottom < ptop) continue - if (top > pbottom) continue + const slot = endAtom[slotName]; + const {x: px, y: py} = getAtomPosition(slot); + const pleft = px; + const pright = px + slot.width; + const ptop = py; + const pbottom = py + slot.height; - atom.highlightedSlot = slotName - return slot + if (left > pright) continue; + if (right < pleft) continue; + if (bottom < ptop) continue; + if (top > pbottom) continue; - } - continue - } + atom.highlightedSlot = slotName; + return slot; + + }; + continue; + }; - if (!other.isSquare) continue - if (!other.expanded) continue + if (!other.isSquare) continue; + if (!other.expanded) continue; - const {x: px, y: py} = getAtomPosition(other.pickerPad) - const pleft = px - const pright = px + other.pickerPad.width - const ptop = py - const pbottom = py + other.pickerPad.height + const {x: px, y: py} = getAtomPosition(other.pickerPad); + const pleft = px; + const pright = px + other.pickerPad.width; + const ptop = py; + const pbottom = py + other.pickerPad.height; - if (left > pright) continue - if (right < pleft) continue - if (bottom < ptop) continue - if (top > pbottom) continue + if (left > pright) continue; + if (right < pleft) continue; + if (bottom < ptop) continue; + if (top > pbottom) continue; - const slots = ["red", "green", "blue"].filter(slot => other[slot] === undefined) - if (slots.length === 0) continue - const {x: ax, y: ay} = getAtomPosition(other) + const slots = ["red", "green", "blue"].filter(slot => other[slot] === undefined); + if (slots.length === 0) continue; + const {x: ax, y: ay} = getAtomPosition(other); for (const slot of slots) { - const slotId = CHANNEL_IDS[slot] - const sx = ax + other.size + OPTION_MARGIN*2 + slotId*(COLOURTODE_SQUARE.size + COLOURTODE_PICKER_PAD_MARGIN) - const sy = ay + OPTION_MARGIN - const distance = Math.hypot(x - sx, y - sy) + const slotId = CHANNEL_IDS[slot]; + const sx = ax + other.size + OPTION_MARGIN*2 + slotId*(COLOURTODE_SQUARE.size + COLOURTODE_PICKER_PAD_MARGIN); + const sy = ay + OPTION_MARGIN; + const distance = Math.hypot(x - sx, y - sy); if (distance < winningDistance) { - winningDistance = distance - winningSlot = slot - winningSquare = other - } - } + winningDistance = distance; + winningSlot = slot; + winningSquare = other; + }; + }; if (winningSquare !== undefined) { - const {x: ax, y: ay} = getAtomPosition(winningSquare) - const slotId = CHANNEL_IDS[winningSlot] + const {x: ax, y: ay} = getAtomPosition(winningSquare); + const slotId = CHANNEL_IDS[winningSlot]; - atom.highlight = createChild(atom, HIGHLIGHT, {bottom: true}) - atom.highlight.hasBorder = true - atom.highlight.x = ax + winningSquare.size + OPTION_MARGIN + slotId*(OPTION_MARGIN+winningSquare.size) - atom.highlight.y = ay - atom.highlight.width = OPTION_MARGIN*2+winningSquare.size - atom.highlightedAtom = winningSquare - atom.highlightedSlot = winningSlot - } - - return - } + atom.highlight = createChild(atom, HIGHLIGHT, {bottom: true}); + atom.highlight.hasBorder = true; + atom.highlight.x = ax + winningSquare.size + OPTION_MARGIN + slotId*(OPTION_MARGIN+winningSquare.size); + atom.highlight.y = ay; + atom.highlight.width = OPTION_MARGIN*2+winningSquare.size; + atom.highlightedAtom = winningSquare; + atom.highlightedSlot = winningSlot; + }; + + return; + }; }, place: (atom, highlightedAtom) => { - atom.attached = true - atom.dx = 0 - atom.dy = 0 + atom.attached = true; + atom.dx = 0; + atom.dy = 0; if (!highlightedAtom.isSquare) { - const diamond = highlightedAtom.parent - //diamond.unexpand(diamond) - - const operationName = atom.highlightedSlot === "padTop"? "add" : "subtract" - diamond.value[operationName] = atom.value - diamond.operationAtoms[atom.highlightedSlot] = atom - atom.x = 0 - atom.y = highlightedAtom.y + highlightedAtom.height/2 - atom.height/2 - giveChild(diamond, atom) - //diamond.expand(diamond) + const diamond = highlightedAtom.parent; + //diamond.unexpand(diamond); + + const operationName = atom.highlightedSlot === "padTop"? "add" : "subtract"; + diamond.value[operationName] = atom.value; + diamond.operationAtoms[atom.highlightedSlot] = atom; + atom.x = 0; + atom.y = highlightedAtom.y + highlightedAtom.height/2 - atom.height/2; + giveChild(diamond, atom); + //diamond.expand(diamond); if (atom.expanded) { - atom.unexpand(atom) - atom.expand(atom) - } + atom.unexpand(atom); + atom.expand(atom); + }; - return - } + return; + }; - const square = atom.highlightedAtom - const slotId = CHANNEL_IDS[atom.highlightedSlot] - square.receiveNumber(square, atom.value, slotId, {expanded: atom.expanded, numberAtom: atom}) - deleteAtom(atom) + const square = atom.highlightedAtom; + const slotId = CHANNEL_IDS[atom.highlightedSlot]; + square.receiveNumber(square, atom.value, slotId, {expanded: atom.expanded, numberAtom: atom}); + deleteAtom(atom); }, draw: (atom) => { - const {x, y} = getAtomPosition(atom) - - let size = atom.size - //if (atom.isTool) size -= BORDER_THICKNESS*2.5 - - const height = size - const width = size - - const left = (x) - let right = left + (width) - let top = (y) - //if (atom.isTool) top += BORDER_THICKNESS*1.25 - let bottom = top + (height) - const middleY = top + (height/2) - const middleX = left + (width/2) - - colourTodeContext.fillStyle = atom.colour - const path = new Path2D() - - path.moveTo(...[middleX, top].map(n => (n))) - path.lineTo(...[right, middleY].map(n => (n))) - path.lineTo(...[middleX, bottom].map(n => (n))) - path.lineTo(...[left, middleY].map(n => (n))) - - path.closePath() - colourTodeContext.fillStyle = atom.colour - colourTodeContext.fill(path) + const {x, y} = getAtomPosition(atom); + + let size = atom.size; + //if (atom.isTool) size -= BORDER_THICKNESS*2.5; + + const height = size; + const width = size; + + const left = (x); + let right = left + (width); + let top = (y); + //if (atom.isTool) top += BORDER_THICKNESS*1.25; + let bottom = top + (height); + const middleY = top + (height/2); + const middleX = left + (width/2); + + colourTodeContext.fillStyle = atom.colour; + const path = new Path2D(); + + path.moveTo(...[middleX, top].map(n => (n))); + path.lineTo(...[right, middleY].map(n => (n))); + path.lineTo(...[middleX, bottom].map(n => (n))); + path.lineTo(...[left, middleY].map(n => (n))); + + path.closePath(); + colourTodeContext.fillStyle = atom.colour; + colourTodeContext.fill(path); if (atom.hasBorder) { - colourTodeContext.lineWidth = BORDER_THICKNESS - colourTodeContext.strokeStyle = atom.borderColour + colourTodeContext.lineWidth = BORDER_THICKNESS; + colourTodeContext.strokeStyle = atom.borderColour; if (atom.isTool) { - colourTodeContext.lineWidth = BORDER_THICKNESS*1.5 - colourTodeContext.strokeStyle = toolBorderColours[atom.colour.splash] - } - colourTodeContext.stroke(path) - } + colourTodeContext.lineWidth = BORDER_THICKNESS*1.5; + colourTodeContext.strokeStyle = toolBorderColours[atom.colour.splash]; + }; + colourTodeContext.stroke(path); + }; }, offscreen: COLOURTODE_RECTANGLE.offscreen, overlaps: COLOURTODE_RECTANGLE.overlaps, @@ -7498,15 +7489,15 @@ registerRule( height: CHANNEL_HEIGHT + OPTION_MARGIN/3*2, width: CHANNEL_HEIGHT + OPTION_MARGIN/3*2, construct: (atom) => { - atom.variable = CHANNEL_VARIABLES[Random.Uint8 % 3] - atom.value = makeNumber({variable: atom.variable}) - atom.updateAppearance(atom) + atom.variable = CHANNEL_VARIABLES[Random.Uint8 % 3]; + atom.value = makeNumber({variable: atom.variable}); + atom.updateAppearance(atom); if (!atom.isTool) { - atom.width += BORDER_THICKNESS/2 - atom.height += BORDER_THICKNESS/2 - atom.size += BORDER_THICKNESS/2 - } - atom.operationAtoms = {padTop: undefined, padBottom: undefined} + atom.width += BORDER_THICKNESS/2; + atom.height += BORDER_THICKNESS/2; + atom.size += BORDER_THICKNESS/2; + }; + atom.operationAtoms = {padTop: undefined, padBottom: undefined}; }, makeOperationAtoms: (atom) => { @@ -7514,198 +7505,198 @@ registerRule( if (atom.operationAtoms.padtop === undefined) { if (atom.value.add.variable === undefined) { - const operationAtom = createChild(atom, COLOURTODE_PICKER_CHANNEL) - operationAtom.value = atom.value.add - atom.operationAtoms.padTop = operationAtom - operationAtom.x = atom.padTop.x + OPTION_MARGIN - operationAtom.y = atom.padTop.y + atom.padTop.height/2 - operationAtom.height/2 - operationAtom.highlightedSlot = "padTop" + const operationAtom = createChild(atom, COLOURTODE_PICKER_CHANNEL); + operationAtom.value = atom.value.add; + atom.operationAtoms.padTop = operationAtom; + operationAtom.x = atom.padTop.x + OPTION_MARGIN; + operationAtom.y = atom.padTop.y + atom.padTop.height/2 - operationAtom.height/2; + operationAtom.highlightedSlot = "padTop"; } else { - const operationAtom = createChild(atom, COLOURTODE_TALL_RECTANGLE) - operationAtom.value = atom.value.add - operationAtom.variable = atom.value.add.variable - operationAtom.makeOperationAtoms(operationAtom) - operationAtom.highlightedSlot = "padTop" - operationAtom.x = 0 - operationAtom.y = atom.padTop.y + atom.padTop.height/2 - operationAtom.height/2 - operationAtom.updateAppearance(operationAtom) - atom.operationAtoms.padTop = operationAtom - } - } - } + const operationAtom = createChild(atom, COLOURTODE_TALL_RECTANGLE); + operationAtom.value = atom.value.add; + operationAtom.variable = atom.value.add.variable; + operationAtom.makeOperationAtoms(operationAtom); + operationAtom.highlightedSlot = "padTop"; + operationAtom.x = 0; + operationAtom.y = atom.padTop.y + atom.padTop.height/2 - operationAtom.height/2; + operationAtom.updateAppearance(operationAtom); + atom.operationAtoms.padTop = operationAtom; + }; + }; + }; if (atom.value.subtract !== undefined) { if (atom.operationAtoms.padBottom === undefined) { if (atom.value.subtract.variable === undefined) { - const operationAtom = createChild(atom, COLOURTODE_PICKER_CHANNEL) - operationAtom.value = atom.value.subtract - atom.operationAtoms.padBottom = operationAtom - operationAtom.x = atom.padBottom.x + OPTION_MARGIN - operationAtom.y = atom.padBottom.y + atom.padBottom.height/2 - operationAtom.height/2 - operationAtom.highlightedSlot = "padBottom" + const operationAtom = createChild(atom, COLOURTODE_PICKER_CHANNEL); + operationAtom.value = atom.value.subtract; + atom.operationAtoms.padBottom = operationAtom; + operationAtom.x = atom.padBottom.x + OPTION_MARGIN; + operationAtom.y = atom.padBottom.y + atom.padBottom.height/2 - operationAtom.height/2; + operationAtom.highlightedSlot = "padBottom"; } else { - } - } - } + }; + }; + }; }, updateAppearance: (atom) => { if (atom.variable === "red") { - atom.colour = Colour.Red + atom.colour = Colour.Red; } else if (atom.variable === "green") { - atom.colour = Colour.Green + atom.colour = Colour.Green; } else if (atom.variable === "blue") { - atom.colour = Colour.Blue - } + atom.colour = Colour.Blue; + }; - atom.borderColour = borderColours[atom.colour.splash] + atom.borderColour = borderColours[atom.colour.splash]; }, expanded: false, click: (atom) => { if (!atom.expanded) { - atom.expand(atom) + atom.expand(atom); } else { - atom.unexpand(atom) - } + atom.unexpand(atom); + }; }, expand: (atom) => { - atom.expanded = true + atom.expanded = true; if (atom.value.add === undefined) { if (atom.y < 0 || !(atom.parent.isTallRectangle && atom.parent.operationAtoms.padBottom === atom)) { - atom.handleTop = createChild(atom, SYMMETRY_HANDLE) - atom.handleTop.width = atom.handleTop.height - atom.handleTop.height *= 2 - atom.handleTop.y = atom.height/2 - atom.handleTop.height - atom.handleTop.x = atom.width/2 - atom.handleTop.width/2 - atom.handleTop.behindParent = true - - atom.padTop = createChild(atom, SYMMETRY_PAD) - atom.padTop.height = COLOURTODE_PICKER_PAD.height - atom.padTop.width = COLOURTODE_SQUARE.size + COLOURTODE_PICKER_PAD_MARGIN*2 - atom.padTop.x = atom.width/2 - atom.padTop.width/2 - atom.padTop.y = -atom.padTop.height - OPTION_MARGIN - } - } + atom.handleTop = createChild(atom, SYMMETRY_HANDLE); + atom.handleTop.width = atom.handleTop.height; + atom.handleTop.height *= 2; + atom.handleTop.y = atom.height/2 - atom.handleTop.height; + atom.handleTop.x = atom.width/2 - atom.handleTop.width/2; + atom.handleTop.behindParent = true; + + atom.padTop = createChild(atom, SYMMETRY_PAD); + atom.padTop.height = COLOURTODE_PICKER_PAD.height; + atom.padTop.width = COLOURTODE_SQUARE.size + COLOURTODE_PICKER_PAD_MARGIN*2; + atom.padTop.x = atom.width/2 - atom.padTop.width/2; + atom.padTop.y = -atom.padTop.height - OPTION_MARGIN; + }; + }; if (atom.value.subtract === undefined) { if (atom.y > 0 || !(atom.parent.isTallRectangle && atom.parent.operationAtoms.padTop === atom)) { - atom.handleBottom = createChild(atom, SYMMETRY_HANDLE) - atom.handleBottom.width = atom.handleBottom.height - atom.handleBottom.height *= 2 - atom.handleBottom.y = atom.height/2 - atom.handleBottom.x = atom.width/2 - atom.handleBottom.width/2 - atom.handleBottom.behindParent = true - - atom.padBottom = createChild(atom, SYMMETRY_PAD) - atom.padBottom.height = COLOURTODE_PICKER_PAD.height - atom.padBottom.width = COLOURTODE_SQUARE.size + COLOURTODE_PICKER_PAD_MARGIN*2 - atom.padBottom.x = atom.width/2 - atom.padBottom.width/2 - atom.padBottom.y = atom.height + OPTION_MARGIN - } - } - - atom.handleRight = createChild(atom, SYMMETRY_HANDLE) - atom.handleRight.y = atom.height/2 - atom.handleRight.height/2 - atom.handleRight.x = atom.width/2 - atom.handleRight.width *= 2.5 - atom.handleRight.behindParent = true - - atom.padRight = createChild(atom, SYMMETRY_PAD) - atom.padRight.height = COLOURTODE_PICKER_PAD.height - atom.padRight.width = OPTION_MARGIN + (atom.width+OPTION_MARGIN/1.5)*3 - atom.padRight.y = atom.height/2 - atom.padRight.height/2 - atom.padRight.x = atom.width/2 + (COLOURTODE_SQUARE.size + COLOURTODE_PICKER_PAD_MARGIN*2)/2 + OPTION_MARGIN - - /*atom.handleLeft = createChild(atom, SYMMETRY_HANDLE) - atom.handleLeft.y = atom.height/2 - atom.handleLeft.height/2 - atom.handleLeft.x = -atom.width/2 - atom.handleLeft.width - atom.handleLeft.width *= 2 - - atom.padLeft = createChild(atom, SYMMETRY_PAD) - atom.padLeft.height = COLOURTODE_PICKER_PAD.height - atom.padLeft.width = OPTION_MARGIN + (atom.width+OPTION_MARGIN/1.5)*3 - atom.padLeft.y = atom.height/2 - atom.padRight.height/2 - atom.padLeft.x = -atom.padLeft.width - OPTION_MARGIN*/ - - atom.red = createChild(atom, DIAMOND_CHOICE) - atom.red.x = atom.padRight.x + OPTION_MARGIN/Math.SQRT2 - atom.red.borderColour = Colour.Red - atom.red.colour = Colour.Black - atom.red.value = "red" - - atom.green = createChild(atom, DIAMOND_CHOICE) - atom.green.x = atom.padRight.x + OPTION_MARGIN/Math.SQRT2 + (atom.green.width+OPTION_MARGIN)*1 - atom.green.borderColour = Colour.Green - atom.green.colour = Colour.Black - atom.green.value = "green" - - atom.blue = createChild(atom, DIAMOND_CHOICE) - atom.blue.x = atom.padRight.x + OPTION_MARGIN/Math.SQRT2 + (atom.blue.width+OPTION_MARGIN)*2 - atom.blue.borderColour = Colour.Blue - atom.blue.colour = Colour.Black - atom.blue.value = "blue" - - atom.winnerPin = createChild(atom, DIAMOND_PIN) - atom.winnerPin.x = atom[atom.variable].x + atom.winnerPin.width/2 - atom.winnerPin.y = atom.winnerPin.height/2 - atom.winnerPin.colour = atom[atom.variable].borderColour - atom.winnerPin.borderColour = atom.winnerPin.colour + atom.handleBottom = createChild(atom, SYMMETRY_HANDLE); + atom.handleBottom.width = atom.handleBottom.height; + atom.handleBottom.height *= 2; + atom.handleBottom.y = atom.height/2; + atom.handleBottom.x = atom.width/2 - atom.handleBottom.width/2; + atom.handleBottom.behindParent = true; + + atom.padBottom = createChild(atom, SYMMETRY_PAD); + atom.padBottom.height = COLOURTODE_PICKER_PAD.height; + atom.padBottom.width = COLOURTODE_SQUARE.size + COLOURTODE_PICKER_PAD_MARGIN*2; + atom.padBottom.x = atom.width/2 - atom.padBottom.width/2; + atom.padBottom.y = atom.height + OPTION_MARGIN; + }; + }; + + atom.handleRight = createChild(atom, SYMMETRY_HANDLE); + atom.handleRight.y = atom.height/2 - atom.handleRight.height/2; + atom.handleRight.x = atom.width/2; + atom.handleRight.width *= 2.5; + atom.handleRight.behindParent = true; + + atom.padRight = createChild(atom, SYMMETRY_PAD); + atom.padRight.height = COLOURTODE_PICKER_PAD.height; + atom.padRight.width = OPTION_MARGIN + (atom.width+OPTION_MARGIN/1.5)*3; + atom.padRight.y = atom.height/2 - atom.padRight.height/2; + atom.padRight.x = atom.width/2 + (COLOURTODE_SQUARE.size + COLOURTODE_PICKER_PAD_MARGIN*2)/2 + OPTION_MARGIN; + + /*atom.handleLeft = createChild(atom, SYMMETRY_HANDLE); + atom.handleLeft.y = atom.height/2 - atom.handleLeft.height/2; + atom.handleLeft.x = -atom.width/2 - atom.handleLeft.width; + atom.handleLeft.width *= 2; + + atom.padLeft = createChild(atom, SYMMETRY_PAD); + atom.padLeft.height = COLOURTODE_PICKER_PAD.height; + atom.padLeft.width = OPTION_MARGIN + (atom.width+OPTION_MARGIN/1.5)*3; + atom.padLeft.y = atom.height/2 - atom.padRight.height/2; + atom.padLeft.x = -atom.padLeft.width - OPTION_MARGIN*/; + + atom.red = createChild(atom, DIAMOND_CHOICE); + atom.red.x = atom.padRight.x + OPTION_MARGIN/Math.SQRT2; + atom.red.borderColour = Colour.Red; + atom.red.colour = Colour.Black; + atom.red.value = "red"; + + atom.green = createChild(atom, DIAMOND_CHOICE); + atom.green.x = atom.padRight.x + OPTION_MARGIN/Math.SQRT2 + (atom.green.width+OPTION_MARGIN)*1; + atom.green.borderColour = Colour.Green; + atom.green.colour = Colour.Black; + atom.green.value = "green"; + + atom.blue = createChild(atom, DIAMOND_CHOICE); + atom.blue.x = atom.padRight.x + OPTION_MARGIN/Math.SQRT2 + (atom.blue.width+OPTION_MARGIN)*2; + atom.blue.borderColour = Colour.Blue; + atom.blue.colour = Colour.Black; + atom.blue.value = "blue"; + + atom.winnerPin = createChild(atom, DIAMOND_PIN); + atom.winnerPin.x = atom[atom.variable].x + atom.winnerPin.width/2; + atom.winnerPin.y = atom.winnerPin.height/2; + atom.winnerPin.colour = atom[atom.variable].borderColour; + atom.winnerPin.borderColour = atom.winnerPin.colour; for (const operation of ["padTop", "padBottom"]) { - const operationAtom = atom.operationAtoms[operation] - if (operationAtom === undefined) continue - registerAtom(operationAtom) - giveChild(atom, operationAtom) - } + const operationAtom = atom.operationAtoms[operation]; + if (operationAtom === undefined) continue; + registerAtom(operationAtom); + giveChild(atom, operationAtom); + }; for (const child of atom.children) { - if (!child.isTallRectangle) continue + if (!child.isTallRectangle) continue; if (child.expanded) { - child.unexpand(child) - child.expand(child) - } - } + child.unexpand(child); + child.expand(child); + }; + }; }, unexpand: (atom) => { - atom.expanded = false + atom.expanded = false; - deleteChild(atom, atom.red) - deleteChild(atom, atom.green) - deleteChild(atom, atom.blue) + deleteChild(atom, atom.red); + deleteChild(atom, atom.green); + deleteChild(atom, atom.blue); + + deleteChild(atom, atom.padRight); + deleteChild(atom, atom.handleRight); + deleteChild(atom, atom.winnerPin); - deleteChild(atom, atom.padRight) - deleteChild(atom, atom.handleRight) - deleteChild(atom, atom.winnerPin) - if (atom.value.add === undefined) { - deleteChild(atom, atom.padTop, {quiet: true}) - deleteChild(atom, atom.handleTop, {quiet: true}) - } - + deleteChild(atom, atom.padTop, {quiet: true}); + deleteChild(atom, atom.handleTop, {quiet: true}); + }; + if (atom.value.subtract === undefined) { - deleteChild(atom, atom.padBottom, {quiet: true}) - deleteChild(atom, atom.handleBottom, {quiet: true}) - } - - /*deleteChild(atom, atom.padLeft) - deleteChild(atom, atom.handleLeft)*/ + deleteChild(atom, atom.padBottom, {quiet: true}); + deleteChild(atom, atom.handleBottom, {quiet: true}); + }; + + /*deleteChild(atom, atom.padLeft); + deleteChild(atom, atom.handleLeft)*/; - /*for (const opera + /*for (const opera; tion of ["padTop", "padBottom"]) { - const operationAtom = atom.operationAtoms[operation] - if (operationAtom === undefined) continue - deleteChild(atom, operationAtom) - }*/ + const operationAtom = atom.operationAtoms[operation]; + if (operationAtom === undefined) continue; + deleteChild(atom, operationAtom); + }*/; - } - } + }; + }; const DIAMOND_CHOICE = { draw: (atom) => { - COLOURTODE_TALL_RECTANGLE.draw(atom) + COLOURTODE_TALL_RECTANGLE.draw(atom); }, offscreen: COLOURTODE_RECTANGLE.offscreen, overlaps: COLOURTODE_RECTANGLE.overlaps, @@ -7715,39 +7706,39 @@ registerRule( width: CHANNEL_HEIGHT + OPTION_MARGIN/3*2, grab: (atom) => atom.parent, click: (atom) => { - if (atom.value === atom.parent.variable) return + if (atom.value === atom.parent.variable) return; - atom.parent.variable = atom.value - atom.parent.value.variable = atom.value + atom.parent.variable = atom.value; + atom.parent.value.variable = atom.value; - atom.parent.winnerPin.x = atom.x + atom.parent.winnerPin.width/2 - atom.parent.winnerPin.colour = atom.borderColour - atom.parent.winnerPin.borderColour = atom.borderColour + atom.parent.winnerPin.x = atom.x + atom.parent.winnerPin.width/2; + atom.parent.winnerPin.colour = atom.borderColour; + atom.parent.winnerPin.borderColour = atom.borderColour; - atom.parent.updateAppearance(atom.parent) + atom.parent.updateAppearance(atom.parent); - const diamond = atom.parent - let topDiamond = diamond - let top = diamond.parent + const diamond = atom.parent; + let topDiamond = diamond; + let top = diamond.parent; while (!top.isSquare) { - if (top === COLOURTODE_BASE_PARENT) return - topDiamond = top - top = top.parent - } + if (top === COLOURTODE_BASE_PARENT) return; + topDiamond = top; + top = top.parent; + }; - let channelNumber = 0 - if (topDiamond.channelSlot === "green") channelNumber = 1 - if (topDiamond.channelSlot === "blue") channelNumber = 2 + let channelNumber = 0; + if (topDiamond.channelSlot === "green") channelNumber = 1; + if (topDiamond.channelSlot === "blue") channelNumber = 2; - const topChannel = top.variableAtoms[channelNumber] - top.receiveNumber(top, topChannel.value, channelNumber, {expanded: topChannel.expanded, numberAtom: topChannel}) + const topChannel = top.variableAtoms[channelNumber]; + top.receiveNumber(top, topChannel.value, channelNumber, {expanded: topChannel.expanded, numberAtom: topChannel}); + + }; + }; - } - } - const DIAMOND_PIN = { draw: (atom) => { - COLOURTODE_TALL_RECTANGLE.draw(atom) + COLOURTODE_TALL_RECTANGLE.draw(atom); }, offscreen: COLOURTODE_RECTANGLE.offscreen, overlaps: COLOURTODE_RECTANGLE.overlaps, @@ -7757,8 +7748,8 @@ registerRule( width: (CHANNEL_HEIGHT + OPTION_MARGIN/3*2) / 2, grab: (atom) => atom.parent, touch: (atom) => atom.parent, - } - + }; + const COLOURTODE_OPTION_PADDING = { draw: () => {}, overlaps: COLOURTODE_RECTANGLE.overlaps, @@ -7771,12 +7762,12 @@ registerRule( y: 0, x: 0, //dragOnly: true, - } + }; - paddles = [] + paddles = []; - // Ctrl+F: addef - const PADDLE_MARGIN = COLOURTODE_SQUARE.size/2 + // Ctrl+F: addef; + const PADDLE_MARGIN = COLOURTODE_SQUARE.size/2; const PADDLE = { stayAtBack: true, attached: true, @@ -7787,169 +7778,169 @@ registerRule( overlaps: COLOURTODE_RECTANGLE.overlaps, offscreen: COLOURTODE_RECTANGLE.offscreen, colour: Colour.Grey, - size: COLOURTODE_SQUARE.size + OPTION_MARGIN*4, //for legacy + size: COLOURTODE_SQUARE.size + OPTION_MARGIN*4, //for legacy; width: COLOURTODE_SQUARE.size + OPTION_MARGIN*4, height: COLOURTODE_SQUARE.size + OPTION_MARGIN*4, dragOnly: true, dragLockY: true, scroll: 0, rightTriangle: undefined, - x: Math.round(PADDLE_MARGIN), //needed for handle creation + x: Math.round(PADDLE_MARGIN), //needed for handle creation; y: COLOURTODE_SQUARE.size + OPTION_MARGIN + PADDLE_MARGIN, construct: (paddle) => { - paddle.cellAtoms = [] - paddle.slots = [] + paddle.cellAtoms = []; + paddle.slots = []; - const handle = createChild(paddle, PADDLE_HANDLE) - paddle.handle = handle - paddle.setLimits(paddle) - paddle.x = paddle.minX - paddle.expanded = false + const handle = createChild(paddle, PADDLE_HANDLE); + paddle.handle = handle; + paddle.setLimits(paddle); + paddle.x = paddle.minX; + paddle.expanded = false; - paddle.pinhole = createChild(handle, PIN_HOLE) + paddle.pinhole = createChild(handle, PIN_HOLE); - paddle.dummyLeft = createChild(paddle, SLOT) - paddle.dummyLeft.visible = false + paddle.dummyLeft = createChild(paddle, SLOT); + paddle.dummyLeft.visible = false; - paddle.dummyRight = createChild(paddle, SLOT) - paddle.dummyRight.visible = false + paddle.dummyRight = createChild(paddle, SLOT); + paddle.dummyRight.visible = false; - updatePaddleSize(paddle) + updatePaddleSize(paddle); }, setLimits: (paddle) => { - paddle.maxX = paddle.handle.width - paddle.minX = paddle.handle.width - paddle.width + paddle.maxX = paddle.handle.width; + paddle.minX = paddle.handle.width - paddle.width; }, drop: (paddle) => { - const distanceFromMax = paddle.maxX - paddle.x - const distanceFromMin = paddle.x - paddle.minX + const distanceFromMax = paddle.maxX - paddle.x; + const distanceFromMin = paddle.x - paddle.minX; if (distanceFromMax < distanceFromMin) { - paddle.x = paddle.maxX - paddle.expanded = true - updatePaddleRule(paddle) + paddle.x = paddle.maxX; + paddle.expanded = true; + updatePaddleRule(paddle); if (paddles.last === paddle) { - createPaddle() - } + createPaddle(); + }; } else { - paddle.x = paddle.minX - paddle.expanded = false - updatePaddleRule(paddle) + paddle.x = paddle.minX; + paddle.expanded = false; + updatePaddleRule(paddle); if (paddles.last !== paddle) { - deletePaddle(paddle) - } - } - paddle.dx = 0 + deletePaddle(paddle); + }; + }; + paddle.dx = 0; }, click: (paddle) => { - const cells = makeDiagramCellsFromCellAtoms(paddle.cellAtoms) - const diagram = makeDiagram({left: cells}) - setBrushColour(diagram) + const cells = makeDiagramCellsFromCellAtoms(paddle.cellAtoms); + const diagram = makeDiagram({left: cells}); + setBrushColour(diagram); }, drag: (paddle, x, y) => { if (false && paddle.pinhole.locked) { - const square = makeAtom(COLOURTODE_SQUARE) - hand.offset.x = -square.width/2 - hand.offset.y = -square.height/2 - const cells = makeDiagramCellsFromCellAtoms(paddle.cellAtoms) - const diagram = makeDiagram({left: cells}) - normaliseDiagram(diagram) - - square.value = diagram - registerAtom(square) - state.brush.colour = makeDiagram({left: [makeDiagramCell({content: diagram})]}) - square.update(square) - return square - } - return paddle + const square = makeAtom(COLOURTODE_SQUARE); + hand.offset.x = -square.width/2; + hand.offset.y = -square.height/2; + const cells = makeDiagramCellsFromCellAtoms(paddle.cellAtoms); + const diagram = makeDiagram({left: cells}); + normaliseDiagram(diagram); + + square.value = diagram; + registerAtom(square); + state.brush.colour = makeDiagram({left: [makeDiagramCell({content: diagram})]}); + square.update(square); + return square; + }; + return paddle; }, rightDraggable: true, getColour: (paddle) => { - let cellAtoms = paddle.cellAtoms + let cellAtoms = paddle.cellAtoms; if (cellAtoms.length === 0) { - - //const red = makeNumber({values: [true, true, true, true, true, true, true, true, true, true], channel: 0}) - //const green = makeNumber({values: [true, true, true, true, true, true, true, true, true, true], channel: 1}) - //const blue = makeNumber({values: [true, true, true, true, true, true, true, true, true, true], channel: 2}) - const leftClone = makeArray({channels: [undefined, undefined, undefined]}) - return leftClone + + //const red = makeNumber({values: [true, true, true, true, true, true, true, true, true, true], channel: 0}); + //const green = makeNumber({values: [true, true, true, true, true, true, true, true, true, true], channel: 1}); + //const blue = makeNumber({values: [true, true, true, true, true, true, true, true, true, true], channel: 2}); + const leftClone = makeArray({channels: [undefined, undefined, undefined]}); + return leftClone; } else if (cellAtoms.length === 1) { - const leftClone = cloneDragonArray(cellAtoms[0].value) - return leftClone - } - const cells = makeDiagramCellsFromCellAtoms(cellAtoms) - const diagram = makeDiagram({left: cells}) - normaliseDiagram(diagram) - return diagram + const leftClone = cloneDragonArray(cellAtoms[0].value); + return leftClone; + }; + const cells = makeDiagramCellsFromCellAtoms(cellAtoms); + const diagram = makeDiagram({left: cells}); + normaliseDiagram(diagram); + return diagram; }, rightDrag: (paddle) => { - let cellAtoms = paddle.cellAtoms + let cellAtoms = paddle.cellAtoms; if (cellAtoms.length === 0) { - - const square = makeAtom(COLOURTODE_SQUARE) - hand.offset.x = -square.width/2 - hand.offset.y = -square.height/2 - //const red = makeNumber({values: [true, true, true, true, true, true, true, true, true, true], channel: 0}) - //const green = makeNumber({values: [true, true, true, true, true, true, true, true, true, true], channel: 1}) - //const blue = makeNumber({values: [true, true, true, true, true, true, true, true, true, true], channel: 2}) - const leftClone = makeArray({channels: [undefined, undefined, undefined]}) - setBrushColour(leftClone) - registerAtom(square) - square.value = leftClone - square.update(square) - return square + + const square = makeAtom(COLOURTODE_SQUARE); + hand.offset.x = -square.width/2; + hand.offset.y = -square.height/2; + //const red = makeNumber({values: [true, true, true, true, true, true, true, true, true, true], channel: 0}); + //const green = makeNumber({values: [true, true, true, true, true, true, true, true, true, true], channel: 1}); + //const blue = makeNumber({values: [true, true, true, true, true, true, true, true, true, true], channel: 2}); + const leftClone = makeArray({channels: [undefined, undefined, undefined]}); + setBrushColour(leftClone); + registerAtom(square); + square.value = leftClone; + square.update(square); + return square; } else if (cellAtoms.length === 1) { - const leftClone = cloneDragonArray(cellAtoms[0].value) - const square = cellAtoms[0].clone(cellAtoms[0]) - hand.offset.x = -square.width/2 - hand.offset.y = -square.height/2 - setBrushColour(leftClone) - registerAtom(square) - square.value = leftClone - square.update(square) - return square - } - const square = makeAtom(COLOURTODE_SQUARE) - hand.offset.x = -square.width/2 - hand.offset.y = -square.height/2 - const cells = makeDiagramCellsFromCellAtoms(cellAtoms) - const diagram = makeDiagram({left: cells}) - normaliseDiagram(diagram) - - square.value = diagram - registerAtom(square) - setBrushColour(diagram) - square.update(square) - return square + const leftClone = cloneDragonArray(cellAtoms[0].value); + const square = cellAtoms[0].clone(cellAtoms[0]); + hand.offset.x = -square.width/2; + hand.offset.y = -square.height/2; + setBrushColour(leftClone); + registerAtom(square); + square.value = leftClone; + square.update(square); + return square; + }; + const square = makeAtom(COLOURTODE_SQUARE); + hand.offset.x = -square.width/2; + hand.offset.y = -square.height/2; + const cells = makeDiagramCellsFromCellAtoms(cellAtoms); + const diagram = makeDiagram({left: cells}); + normaliseDiagram(diagram); + + square.value = diagram; + registerAtom(square); + setBrushColour(diagram); + square.update(square); + return square; }, - } + }; const fillPoints = (colour, points) => { - - const path = new Path2D() - const [head, ...tail] = points - path.moveTo(...head.map(n => Math.round(n))) + + const path = new Path2D(); + const [head, ...tail] = points; + path.moveTo(...head.map(n => Math.round(n))); for (const point of tail) { - path.lineTo(...point.map(n => Math.round(n))) - } - path.closePath() + path.lineTo(...point.map(n => Math.round(n))); + }; + path.closePath(); - colourTodeContext.fillStyle = colour - colourTodeContext.fill(path) - } + colourTodeContext.fillStyle = colour; + colourTodeContext.fill(path); + }; const SLOT = { visible: true, @@ -7958,20 +7949,20 @@ registerRule( //hasBorder: true, //borderColour: Colour.Silver, draw: (atom) => { - //atom.colour = borderColours[atom.cellAtom.colour.splash] - //COLOURTODE_RECTANGLE.draw(atom) + //atom.colour = borderColours[atom.cellAtom.colour.splash]; + //COLOURTODE_RECTANGLE.draw(atom); + + if (!atom.visible) return; - if (!atom.visible) return - - const [x, y] = getAtomPosition(atom) + const [x, y] = getAtomPosition(atom); - const left = x - const right = x + atom.width - const top = y - const bottom = y + atom.height + const left = x; + const right = x + atom.width; + const top = y; + const bottom = y + atom.height; - /*const swidth = atom.width/10 - const sheight = atom.height/10 + /*const swidth = atom.width/10; + const sheight = atom.height/10; const stripes = [ [ @@ -8004,22 +7995,22 @@ registerRule( [right, top + sheight * 6], [right, top + sheight * 4], ], - ] + ]; for (const stripe of stripes) { - fillPoints(Colour.Black, stripe) - }*/ + fillPoints(Colour.Black, stripe); + }*/; - colourTodeContext.fillStyle = atom.colour - /*colourTodeContext.beginPath() - colourTodeContext.arc(x + atom.width/2, y+atom.height/2, atom.width / 5, 0, 2*Math.PI) - colourTodeContext.fill()*/ + colourTodeContext.fillStyle = atom.colour; + /*colourTodeContext.beginPath(); + colourTodeContext.arc(x + atom.width/2, y+atom.height/2, atom.width / 5, 0, 2*Math.PI); + colourTodeContext.fill()*/; - const w = atom.width/3 - const h = atom.width/3 - const X = x + atom.width/2 - w/2 - const Y = y + atom.height/2 - h/2 - colourTodeContext.fillRect(...[X, Y, w, h].map(n => Math.round(n))) + const w = atom.width/3; + const h = atom.width/3; + const X = x + atom.width/2 - w/2; + const Y = y + atom.height/2 - h/2; + colourTodeContext.fillRect(...[X, Y, w, h].map(n => Math.round(n))); }, offscreen: COLOURTODE_RECTANGLE.offscreen, @@ -8028,381 +8019,380 @@ registerRule( size: COLOURTODE_SQUARE.size, grab: (atom) => atom.parent, dragOnly: true, - } + }; - const cellAtomWidth = COLOURTODE_SQUARE.size - // Ctrl+F: adwww + const cellAtomWidth = COLOURTODE_SQUARE.size; + // Ctrl+F: adwww; const updatePaddleSize = (paddle) => { - - let width = PADDLE.width - let height = PADDLE.size + + let width = PADDLE.width; + let height = PADDLE.size; if (paddle.cellAtoms.length > 0) { - let top = Infinity - let bottom = -Infinity - let right = -Infinity - let left = Infinity + let top = Infinity; + let bottom = -Infinity; + let right = -Infinity; + let left = Infinity; for (const cellAtom of paddle.cellAtoms) { - const cx = cellAtom.x - const cy = cellAtom.y - const cleft = cx - const cright = cx + cellAtomWidth - const ctop = cy - const cbottom = cy + cellAtomWidth - - if (cleft < left) left = cleft - if (cright > right) right = cright - if (ctop < top) top = ctop - if (cbottom > bottom) bottom = cbottom - } + const cx = cellAtom.x; + const cy = cellAtom.y; + const cleft = cx; + const cright = cx + cellAtomWidth; + const ctop = cy; + const cbottom = cy + cellAtomWidth; + + if (cleft < left) left = cleft; + if (cright > right) right = cright; + if (ctop < top) top = ctop; + if (cbottom > bottom) bottom = cbottom; + }; - let topOffset = 0 - let leftOffset = 0 + let topOffset = 0; + let leftOffset = 0; - const yPadding = (PADDLE.height/2 - COLOURTODE_SQUARE.size/2) - const xPadding = (PADDLE.width/2 - COLOURTODE_SQUARE.size/2) + const yPadding = (PADDLE.height/2 - COLOURTODE_SQUARE.size/2); + const xPadding = (PADDLE.width/2 - COLOURTODE_SQUARE.size/2); - const desiredTop = yPadding - const desiredLeft = xPadding + const desiredTop = yPadding; + const desiredLeft = xPadding; if (top !== desiredTop) { - topOffset = desiredTop - top - bottom += topOffset - } + topOffset = desiredTop - top; + bottom += topOffset; + }; if (left !== desiredLeft) { - leftOffset = desiredLeft - left - right += leftOffset - } + leftOffset = desiredLeft - left; + right += leftOffset; + }; for (const cellAtom of paddle.cellAtoms) { - cellAtom.y += topOffset - cellAtom.x += leftOffset - } + cellAtom.y += topOffset; + cellAtom.x += leftOffset; + }; - const desiredWidth = right + xPadding - const desiredHeight = bottom + yPadding + const desiredWidth = right + xPadding; + const desiredHeight = bottom + yPadding; - width = desiredWidth - height = desiredHeight + width = desiredWidth; + height = desiredHeight; - } + }; if (paddle.rightTriangle !== undefined) { - paddle.rightTriangle.x = width - paddle.rightTriangle.y = height/2 - paddle.rightTriangle.height/2 - width = width+width + paddle.rightTriangle.width - } - + paddle.rightTriangle.x = width; + paddle.rightTriangle.y = height/2 - paddle.rightTriangle.height/2; + width = width+width + paddle.rightTriangle.width; + }; + if (paddle.hasSymmetry || paddle.chance !== undefined) { - width += SYMMETRY_CIRCLE.size/3 - } + width += SYMMETRY_CIRCLE.size/3; + }; - paddle.width = width - paddle.height = height - paddle.setLimits(paddle) + paddle.width = width; + paddle.height = height; + paddle.setLimits(paddle); - //=============================// - // ARRANGING PADDLE's CHILDREN // - //=============================// + //=============================//; + // ARRANGING PADDLE's CHILDREN //; + //=============================//; for (const slot of paddle.slots) { - deleteChild(paddle, slot) - } - paddle.slots = [] + deleteChild(paddle, slot); + }; + paddle.slots = []; if (paddle.rightTriangle !== undefined) { for (const cellAtom of paddle.cellAtoms) { - const slot = createChild(paddle, SLOT, {bottom: true}) - cellAtom.slot = slot - paddle.slots.push(slot) - slot.x = cellAtom.x + paddle.rightTriangle.x + paddle.rightTriangle.width - slot.y = cellAtom.y - slot.cellAtom = cellAtom + const slot = createChild(paddle, SLOT, {bottom: true}); + cellAtom.slot = slot; + paddle.slots.push(slot); + slot.x = cellAtom.x + paddle.rightTriangle.x + paddle.rightTriangle.width; + slot.y = cellAtom.y; + slot.cellAtom = cellAtom; if (cellAtom.slotted !== undefined) { - cellAtom.slotted.x = cellAtom.x + paddle.rightTriangle.x + paddle.rightTriangle.width - cellAtom.slotted.y = cellAtom.y - slot.colour = Colour.Grey - } - - } - } + cellAtom.slotted.x = cellAtom.x + paddle.rightTriangle.x + paddle.rightTriangle.width; + cellAtom.slotted.y = cellAtom.y; + slot.colour = Colour.Grey; + }; + + }; + }; if (paddle.rightTriangle !== undefined) { if (paddle.cellAtoms[0] !== undefined && paddle.cellAtoms[0].slot !== undefined) { - paddle.offset = paddle.cellAtoms[0].slot.x - paddle.cellAtoms[0].x + paddle.offset = paddle.cellAtoms[0].slot.x - paddle.cellAtoms[0].x; } else { - paddle.offset = 0 - } - } + paddle.offset = 0; + }; + }; if (paddle.symmetryCircle !== undefined) { - paddle.symmetryCircle.x = paddle.width - paddle.symmetryCircle.width/2 - paddle.symmetryCircle.y = paddle.height/2 - paddle.symmetryCircle.height/2 - } + paddle.symmetryCircle.x = paddle.width - paddle.symmetryCircle.width/2; + paddle.symmetryCircle.y = paddle.height/2 - paddle.symmetryCircle.height/2; + }; if (paddle.chance !== undefined) { - paddle.chance.x = paddle.width - paddle.chance.width/2 - paddle.chance.y = paddle.height/2 - paddle.chance.height/2 - } + paddle.chance.x = paddle.width - paddle.chance.width/2; + paddle.chance.y = paddle.height/2 - paddle.chance.height/2; + }; if (paddle.chance !== undefined && paddle.symmetryCircle !== undefined) { - paddle.symmetryCircle.y -= paddle.symmetryCircle.height/2 - paddle.chance.y += paddle.symmetryCircle.height/2 + paddle.symmetryCircle.y -= paddle.symmetryCircle.height/2; + paddle.chance.y += paddle.symmetryCircle.height/2; if (paddle.height > 100) { - paddle.symmetryCircle.y -= OPTION_MARGIN/2 - paddle.chance.y += OPTION_MARGIN/2 - } - } - - paddle.handle.y = paddle.height/2 - paddle.handle.height/2 + paddle.symmetryCircle.y -= OPTION_MARGIN/2; + paddle.chance.y += OPTION_MARGIN/2; + }; + }; + + paddle.handle.y = paddle.height/2 - paddle.handle.height/2; if (paddle.cellAtoms.length === 0) { - paddle.dummyLeft.x = PADDLE_MARGIN - paddle.dummyLeft.y = paddle.height/2 - paddle.dummyLeft.height/2 - - paddle.dummyRight.x = paddle.width - PADDLE_MARGIN - paddle.dummyLeft.width - paddle.dummyRight.y = paddle.height/2 - paddle.dummyRight.height/2 - } + paddle.dummyLeft.x = PADDLE_MARGIN; + paddle.dummyLeft.y = paddle.height/2 - paddle.dummyLeft.height/2; - updatePaddleRule(paddle) - positionPaddles() - } + paddle.dummyRight.x = paddle.width - PADDLE_MARGIN - paddle.dummyLeft.width; + paddle.dummyRight.y = paddle.height/2 - paddle.dummyRight.height/2; + }; + + updatePaddleRule(paddle); + positionPaddles(); + }; const isDragonArraySingleColour = (array) => { - const splashes = getSplashesSetFromArray(array) - return splashes.size === 1 - } + const splashes = getSplashesSetFromArray(array); + return splashes.size === 1; + }; const isDragonArrayEqual = (a, b) => { for (let i = 0; i < 3; i++) { - const achannel = a.channels[i] - const bchannel = b.channels[i] - if (achannel === undefined && bchannel !== undefined) return false - if (achannel !== undefined && bchannel === undefined) return false - if (achannel === undefined && bchannel === undefined) continue - if (achannel.variable !== bchannel.variable) return false - } + const achannel = a.channels[i]; + const bchannel = b.channels[i]; + if (achannel === undefined && bchannel !== undefined) return false; + if (achannel !== undefined && bchannel === undefined) return false; + if (achannel === undefined && bchannel === undefined) continue; + if (achannel.variable !== bchannel.variable) return false; + }; - const asplashes = getSplashesArrayFromArray(a) - const bsplashes = getSplashesArrayFromArray(b) + const asplashes = getSplashesArrayFromArray(a); + const bsplashes = getSplashesArrayFromArray(b); for (const asplash of asplashes) { - const id = bsplashes.indexOf(asplash) - if (id === -1) return false - bsplashes.splice(id, 1) - } + const id = bsplashes.indexOf(asplash); + if (id === -1) return false; + bsplashes.splice(id, 1); + }; - if (bsplashes.length > 0) return false + if (bsplashes.length > 0) return false; - return true + return true; - } + }; const applyRangeStamp = (stampeds, value) => { - if (value.stamp) return //already got a manual stamp - const isSingle = isDragonArraySingleColour(value) + if (value.stamp) return //already got a manual stamp; + const isSingle = isDragonArraySingleColour(value); if (!isSingle) { - let newStamp = undefined + let newStamp = undefined; for (let i = 0; i < stampeds.length; i++) { - const stamped = stampeds[i] + const stamped = stampeds[i]; if (isDragonArrayEqual(stamped, value)) { - newStamp = i - break - } - } + newStamp = i; + break; + }; + }; if (newStamp === undefined) { - newStamp = stampeds.length - stampeds.push(value) - } - value.stamp = newStamp.toString() - } - } + newStamp = stampeds.length; + stampeds.push(value); + }; + value.stamp = newStamp.toString(); + }; + }; const getTopLeftOfCellAtoms = (cellAtoms) => { - let smallestX = Infinity - let smallestY = Infinity - let leader = undefined + let smallestX = Infinity; + let smallestY = Infinity; + let leader = undefined; for (const cellAtom of cellAtoms) { if (cellAtom.x <= smallestX) { if (cellAtom.y <= smallestY) { - leader = cellAtom - smallestX = cellAtom.x - smallestY = cellAtom.y - } - } - } - - return leader - } + leader = cellAtom; + smallestX = cellAtom.x; + smallestY = cellAtom.y; + }; + }; + }; + + return leader; + }; const getOrderedCellAtoms = (cellAtoms) => { const orderedCellAtoms = [...cellAtoms].sort((a, b) => { - if (a.x < b.x) return -1 - if (a.x > b.x) return 1 - if (a.y < b.y) return -1 - if (a.y > b.y) return 1 - return 0 - }) - return orderedCellAtoms - } + if (a.x < b.x) return -1; + if (a.x > b.x) return 1; + if (a.y < b.y) return -1; + if (a.y > b.y) return 1; + return 0; + }); + return orderedCellAtoms; + }; const makeDiagramCellsFromCellAtoms = (cellAtoms) => { - const orderedCellAtoms = getOrderedCellAtoms(cellAtoms) - const origin = orderedCellAtoms[0] - const diagramCells = [] + const orderedCellAtoms = getOrderedCellAtoms(cellAtoms); + const origin = orderedCellAtoms[0]; + const diagramCells = []; for (const cellAtom of cellAtoms) { - const x = (cellAtom.x - origin.x) / cellAtom.width - const y = (cellAtom.y - origin.y) / cellAtom.height + const x = (cellAtom.x - origin.x) / cellAtom.width; + const y = (cellAtom.y - origin.y) / cellAtom.height; - const leftClone = cloneDragonArray(cellAtom.value) //TODO: should act different for multis - const diagramCell = makeDiagramCell({x, y, content: leftClone}) - diagramCells.push(diagramCell) + const leftClone = cloneDragonArray(cellAtom.value) //TODO: should act different for multis; + const diagramCell = makeDiagramCell({x, y, content: leftClone}); + diagramCells.push(diagramCell); - } + }; - return diagramCells + return diagramCells; - } + }; const updatePaddleRule = (paddle) => { - if (!paddle.expanded) return + if (!paddle.expanded) return; if (paddle.rightTriangle !== undefined) { if (paddle.pinhole.locked) { - paddle.rightTriangle.colour = Colour.splash(999) + paddle.rightTriangle.colour = Colour.splash(999); } else { - paddle.rightTriangle.colour = Colour.splash(0) - } - } + paddle.rightTriangle.colour = Colour.splash(0); + }; + }; - let transformations = DRAGON_TRANSFORMATIONS.NONE + let transformations = DRAGON_TRANSFORMATIONS.NONE; if (paddle.hasSymmetry) { - const [x, y, r] = getXYR(paddle.symmetryCircle.value) + const [x, y, r] = getXYR(paddle.symmetryCircle.value); - const isX = x > 0 - const isY = y > 0 - const isR = r > 0 + const isX = x > 0; + const isY = y > 0; + const isR = r > 0; - let key = `${isY? "X" : ""}${isX? "Y" : ""}${isR? "R" : ""}` - if (key === "") key = "NONE" - else if (key === "XR" || key === "YR") key = "XYR" + let key = `${isY? "X" : ""}${isX? "Y" : ""}${isR? "R" : ""}`; + if (key === "") key = "NONE"; + else if (key === "XR" || key === "YR") key = "XYR"; - transformations = DRAGON_TRANSFORMATIONS[key] - } + transformations = DRAGON_TRANSFORMATIONS[key]; + }; - const orderedCellAtoms = getOrderedCellAtoms(paddle.cellAtoms) - const origin = orderedCellAtoms[0] - const left = [] - const right = [] - const stampeds = [] + const orderedCellAtoms = getOrderedCellAtoms(paddle.cellAtoms); + const origin = orderedCellAtoms[0]; + const left = []; + const right = []; + const stampeds = []; for (const cellAtom of orderedCellAtoms) { - const x = (cellAtom.x - origin.x) / cellAtom.width - const y = (cellAtom.y - origin.y) / cellAtom.height + const x = (cellAtom.x - origin.x) / cellAtom.width; + const y = (cellAtom.y - origin.y) / cellAtom.height; - //======// - // LEFT // - //======// + //======//; + // LEFT //; + //======//; if (cellAtom.isLeftSlot) { - const red = makeNumber({values: [true, true, true, true, true, true, true, true, true, true], channel: 0}) - const green = makeNumber({values: [true, true, true, true, true, true, true, true, true, true], channel: 1}) - const blue = makeNumber({values: [true, true, true, true, true, true, true, true, true, true], channel: 2}) - const leftClone = makeArray({channels: [red, green, blue]}) - applyRangeStamp(stampeds, leftClone) - const diagramCell = makeDiagramCell({x, y, content: leftClone}) - left.push(diagramCell) + const red = makeNumber({values: [true, true, true, true, true, true, true, true, true, true], channel: 0}); + const green = makeNumber({values: [true, true, true, true, true, true, true, true, true, true], channel: 1}); + const blue = makeNumber({values: [true, true, true, true, true, true, true, true, true, true], channel: 2}); + const leftClone = makeArray({channels: [red, green, blue]}); + applyRangeStamp(stampeds, leftClone); + const diagramCell = makeDiagramCell({x, y, content: leftClone}); + left.push(diagramCell); } else if (cellAtom.value.isDiagram) { - // Check for every mini-cell - const orderedMiniLeftCells = getOrderedCellAtoms(cellAtom.value.left) + // Check for every mini-cell; + const orderedMiniLeftCells = getOrderedCellAtoms(cellAtom.value.left); for (const miniDiagramCell of orderedMiniLeftCells) { - const miniClone = cloneDragonArray(miniDiagramCell.content) - applyRangeStamp(stampeds, miniClone) - const miniX = x + miniDiagramCell.x - const miniY = y + miniDiagramCell.y + const miniClone = cloneDragonArray(miniDiagramCell.content); + applyRangeStamp(stampeds, miniClone); + const miniX = x + miniDiagramCell.x; + const miniY = y + miniDiagramCell.y; const diagramCell = makeDiagramCell({ x: miniX, y: miniY, width: miniDiagramCell.width, height: miniDiagramCell.height, content: miniClone, - }) - left.push(diagramCell) - } + }); + left.push(diagramCell); + }; } else { - - // Just check for a single cell - const leftClone = cloneDragonArray(cellAtom.value) - applyRangeStamp(stampeds, leftClone) - const diagramCell = makeDiagramCell({x, y, content: leftClone}) - left.push(diagramCell) - } - - //=======// - // RIGHT // - //=======// + // Just check for a single cell; + const leftClone = cloneDragonArray(cellAtom.value); + applyRangeStamp(stampeds, leftClone); + const diagramCell = makeDiagramCell({x, y, content: leftClone}); + left.push(diagramCell); + }; + + //=======//; + // RIGHT //; + //=======//; - // Merge!!! + // Merge!!!; if (!cellAtom.isLeftSlot && cellAtom.value.isDiagram) { - const maxiLeft = makeMaximisedDiagram(cellAtom.value) - const [maxiWidth, maxiHeight] = getDiagramDimensions(maxiLeft) - + const maxiLeft = makeMaximisedDiagram(cellAtom.value); + const [maxiWidth, maxiHeight] = getDiagramDimensions(maxiLeft); + const mergeCell = makeDiagramCell({ x, y, instruction: DRAGON_INSTRUCTION.merge, splitX: maxiWidth, splitY: maxiHeight, - }) + }); - right.push(mergeCell) - } + right.push(mergeCell); + }; - const rightContent = cellAtom.slotted === undefined? undefined : cellAtom.slotted.value + const rightContent = cellAtom.slotted === undefined? undefined : cellAtom.slotted.value; if (rightContent === undefined) { const nothingCell = makeDiagramCell({ x, y, instruction: DRAGON_INSTRUCTION.nothing, - }) + }); - right.push(nothingCell) + right.push(nothingCell); } else if (rightContent.isDiagram) { - // Split the cell into mini-cells! - const maxiRight = makeMaximisedDiagram(rightContent) - const [maxiWidth, maxiHeight] = getDiagramDimensions(maxiRight) - + // Split the cell into mini-cells!; + const maxiRight = makeMaximisedDiagram(rightContent); + const [maxiWidth, maxiHeight] = getDiagramDimensions(maxiRight); + const splitCell = makeDiagramCell({ x, y, instruction: DRAGON_INSTRUCTION.split, splitX: maxiWidth, splitY: maxiHeight, - }) + }); - right.push(splitCell) + right.push(splitCell); - // Recolour every mini-cell! - const orderedMiniRightCells = getOrderedCellAtoms(rightContent.left) + // Recolour every mini-cell!; + const orderedMiniRightCells = getOrderedCellAtoms(rightContent.left); for (const miniDiagramCell of orderedMiniRightCells) { - const miniClone = cloneDragonArray(miniDiagramCell.content) - applyRangeStamp(stampeds, miniClone) - const miniX = x + miniDiagramCell.x - const miniY = y + miniDiagramCell.y + const miniClone = cloneDragonArray(miniDiagramCell.content); + applyRangeStamp(stampeds, miniClone); + const miniX = x + miniDiagramCell.x; + const miniY = y + miniDiagramCell.y; const diagramCell = makeDiagramCell({ x: miniX, y: miniY, @@ -8410,107 +8400,107 @@ registerRule( height: miniDiagramCell.height, content: miniClone, instruction: DRAGON_INSTRUCTION.recolour, - }) - right.push(diagramCell) - } + }); + right.push(diagramCell); + }; } else { - // Just recolour a single cell - const rightClone = cloneDragonArray(rightContent) - applyRangeStamp(stampeds, rightClone) - const rightDiagramCell = makeDiagramCell({x, y, content: rightClone}) - right.push(rightDiagramCell) - } - } - - const diagram = makeMaximisedDiagram(makeDiagram({left, right})) + // Just recolour a single cell; + const rightClone = cloneDragonArray(rightContent); + applyRangeStamp(stampeds, rightClone); + const rightDiagramCell = makeDiagramCell({x, y, content: rightClone}); + right.push(rightDiagramCell); + }; + }; + + const diagram = makeMaximisedDiagram(makeDiagram({left, right})); - const locked = paddle.pinhole.locked - const chance = paddle.chance === undefined? undefined : paddle.chance.getValue(paddle.chance) - const rule = makeRule({steps: [diagram], transformations, locked, chance}) - paddle.rule = rule + const locked = paddle.pinhole.locked; + const chance = paddle.chance === undefined? undefined : paddle.chance.getValue(paddle.chance); + const rule = makeRule({steps: [diagram], transformations, locked, chance}); + paddle.rule = rule; if (paddle.registry !== undefined) { - unregisterRegistry(paddle.registry) - } + unregisterRegistry(paddle.registry); + }; if (locked && paddle.rightTriangle !== undefined) { - //debugRule(rule) - paddle.registry = registerRule(rule) - //debugRegistry(paddle.registry, {redundants: false}) - } - } + //debugRule(rule); + paddle.registry = registerRule(rule); + //debugRegistry(paddle.registry, {redundants: false}); + }; + }; const getAllAtoms = (pool = state.colourTode.atoms) => { - const atoms = [...pool] + const atoms = [...pool]; for (const atom of atoms) { - atoms.push(...getAllAtoms(atom.children)) - } - return atoms - } + atoms.push(...getAllAtoms(atom.children)); + }; + return atoms; + }; const getAllBaseAtoms = () => { - const atoms = [...state.colourTode.atoms] + const atoms = [...state.colourTode.atoms]; for (const paddle of paddles) { for (const child of paddle.children) { - if (child.isPinhole) continue - if (child.isPaddleHandle) continue - atoms.push(child) - } - } + if (child.isPinhole) continue; + if (child.isPaddleHandle) continue; + atoms.push(child); + }; + }; for (const atom of atoms) { - if (atom.isSquare && atom.expanded) atoms.push(...atom.children) - } - return atoms - } + if (atom.isSquare && atom.expanded) atoms.push(...atom.children); + }; + return atoms; + }; const positionPaddles = () => { if (paddles.length > 1) { - unlockMenuTool("triangle") - } - + unlockMenuTool("triangle"); + }; + if (paddles.length > 2) { - let ruleCount = 0 + let ruleCount = 0; for (const paddle of paddles) { if (paddle.rightTriangle !== undefined) { - ruleCount++ - } - } + ruleCount++; + }; + }; if (ruleCount >= 2) { - unlockMenuTool("hexagon") - } - } + unlockMenuTool("hexagon"); + }; + }; - let previous = undefined + let previous = undefined; for (const paddle of paddles) { if (previous === undefined) { - paddle.y = PADDLE.y + PADDLE.scroll - previous = paddle - continue - } - - paddle.y = previous.y + previous.height + PADDLE_MARGIN - previous = paddle - //bringAtomToBack(paddle) //causes bug where circle tool disappears but really shouldn't :( - } - } + paddle.y = PADDLE.y + PADDLE.scroll; + previous = paddle; + continue; + }; + + paddle.y = previous.y + previous.height + PADDLE_MARGIN; + previous = paddle; + //bringAtomToBack(paddle) //causes bug where circle tool disappears but really shouldn't :(; + }; + }; const deletePaddle = (paddle, id = paddles.indexOf(paddle)) => { - paddles.splice(id, 1) + paddles.splice(id, 1); if (paddle.registry !== undefined) { - unregisterRegistry(paddle.registry) - } - deleteAtom(paddle) - positionPaddles() - } + unregisterRegistry(paddle.registry); + }; + deleteAtom(paddle); + positionPaddles(); + }; const createPaddle = () => { - const paddle = makeAtom(PADDLE) - paddles.push(paddle) - positionPaddles() - registerAtom(paddle) - return paddle - } + const paddle = makeAtom(PADDLE); + paddles.push(paddle); + positionPaddles(); + registerAtom(paddle); + return paddle; + }; const PADDLE_HANDLE = { isPaddleHandle: true, @@ -8525,10 +8515,10 @@ registerRule( y: PADDLE.size/2 - PADDLE.x/2, touch: (atom) => atom.parent.pinhole, grab: (atom) => { - //if (atom.parent.pinhole.locked) return - return atom.parent.pinhole + //if (atom.parent.pinhole.locked) return ; + return atom.parent.pinhole; }, - } + }; const PIN_HOLE = { isPinhole: true, @@ -8537,16 +8527,16 @@ registerRule( borderScale: 1/2, borderColour: Colour.Black, draw: (atom) => { - return + return; if (atom.locked) { - atom.hasBorder = true - atom.colour = Colour.Grey - } + atom.hasBorder = true; + atom.colour = Colour.Grey ; + }; else { - atom.hasBorder = false - atom.colour = Colour.Black - } - CIRCLE.draw(atom) + atom.hasBorder = false; + atom.colour = Colour.Black; + }; + CIRCLE.draw(atom); }, overlaps: CIRCLE.overlaps, offscreen: CIRCLE.offscreen, @@ -8555,83 +8545,83 @@ registerRule( y: OPTION_MARGIN/2/2, x: OPTION_MARGIN/2/2, click: (atom) => { - return - const handle = atom.parent - const paddle = handle.parent + return; + const handle = atom.parent; + const paddle = handle.parent; if (atom.locked) { - atom.locked = false - paddle.grabbable = true - //handle.grabbable = true - handle.draggable = true - paddle.draggable = true - atom.draggable = true - //paddle.dragOnly = true - updatePaddleRule(paddle) - } + atom.locked = false; + paddle.grabbable = true; + //handle.grabbable = true; + handle.draggable = true; + paddle.draggable = true; + atom.draggable = true; + //paddle.dragOnly = true; + updatePaddleRule(paddle); + } ; else { - atom.locked = true - //paddle.grabbable = false - //handle.grabbable = false - handle.draggable = false - //paddle.draggable = false - atom.draggable = false + atom.locked = true; + //paddle.grabbable = false; + //handle.grabbable = false; + handle.draggable = false; + //paddle.draggable = false; + atom.draggable = false; for (const cellAtom of paddle.cellAtoms) { if (cellAtom.expanded) { - cellAtom.unexpand(cellAtom) - } + cellAtom.unexpand(cellAtom); + }; if (cellAtom.slotted !== undefined) { - const slotted = cellAtom.slotted + const slotted = cellAtom.slotted; if (slotted.expanded) { - slotted.unexpand(slotted) - } - } + slotted.unexpand(slotted); + }; + }; if (cellAtom.joins.length > 0 && cellAtom.joinExpanded) { - cellAtom.joinUnepxand(cellAtom) - } - } + cellAtom.joinUnepxand(cellAtom); + }; + }; /*if (paddle.hasSymmetry) { if (paddle.symmetryCircle.expanded) { - paddle.symmetryCircle.unexpand(paddle.symmetryCircle) - } - }*/ + paddle.symmetryCircle.unexpand(paddle.symmetryCircle); + }; + }*/; if (paddle.cellAtoms.length === 0) { - paddle.grabbable = false - paddle.draggable = false - } - //paddle.dragOnly = false - updatePaddleRule(paddle) - } + paddle.grabbable = false; + paddle.draggable = false; + }; + //paddle.dragOnly = false; + updatePaddleRule(paddle); + }; }, grab: (atom) => atom.parent.parent, - - } - const SYMMETRY_TOGGLINGS = new Map() - SYMMETRY_TOGGLINGS.set(0, DRAGON_TRANSFORMATIONS.NONE) - SYMMETRY_TOGGLINGS.set(100, DRAGON_TRANSFORMATIONS.X) - SYMMETRY_TOGGLINGS.set(10, DRAGON_TRANSFORMATIONS.Y) - SYMMETRY_TOGGLINGS.set(110, DRAGON_TRANSFORMATIONS.XY) - SYMMETRY_TOGGLINGS.set(1, DRAGON_TRANSFORMATIONS.R) - SYMMETRY_TOGGLINGS.set(111, DRAGON_TRANSFORMATIONS.XYR) - SYMMETRY_TOGGLINGS.set(101, DRAGON_TRANSFORMATIONS.XYR) - SYMMETRY_TOGGLINGS.set(11, DRAGON_TRANSFORMATIONS.XYR) + }; + + const SYMMETRY_TOGGLINGS = new Map(); + SYMMETRY_TOGGLINGS.set(0, DRAGON_TRANSFORMATIONS.NONE); + SYMMETRY_TOGGLINGS.set(100, DRAGON_TRANSFORMATIONS.X); + SYMMETRY_TOGGLINGS.set(10, DRAGON_TRANSFORMATIONS.Y); + SYMMETRY_TOGGLINGS.set(110, DRAGON_TRANSFORMATIONS.XY); + SYMMETRY_TOGGLINGS.set(1, DRAGON_TRANSFORMATIONS.R); + SYMMETRY_TOGGLINGS.set(111, DRAGON_TRANSFORMATIONS.XYR); + SYMMETRY_TOGGLINGS.set(101, DRAGON_TRANSFORMATIONS.XYR); + SYMMETRY_TOGGLINGS.set(11, DRAGON_TRANSFORMATIONS.XYR); - const getXYR = getRGB + const getXYR = getRGB; - // Ctrl+F: cedef + // Ctrl+F: cedef; const SYMMETRY_CIRCLE = { hasBorder: true, draw: (atom) => { - CIRCLE.draw(atom) - if (atom.value === undefined) return - const [x, y, r] = getXYR(atom.value) - if (x > 0) SYMMETRY_TOGGLE_X.drawX(atom) - if (y > 0) SYMMETRY_TOGGLE_Y.drawY(atom) - if (r > 0) SYMMETRY_TOGGLE_R.drawR(atom) + CIRCLE.draw(atom); + if (atom.value === undefined) return; + const [x, y, r] = getXYR(atom.value); + if (x > 0) SYMMETRY_TOGGLE_X.drawX(atom); + if (y > 0) SYMMETRY_TOGGLE_Y.drawY(atom); + if (r > 0) SYMMETRY_TOGGLE_R.drawR(atom); }, offscreen: CIRCLE.offscreen, overlaps: CIRCLE.overlaps, @@ -8641,153 +8631,153 @@ registerRule( colour: Colour.Black, value: 0, click: (atom) => { - + if (atom.expanded) { - atom.unexpand(atom) - } + atom.unexpand(atom); + }; else { - atom.expand(atom) - } + atom.expand(atom); + }; }, expand: (atom) => { - atom.pad = createChild(atom, SYMMETRY_PAD) - atom.handle = createChild(atom, SYMMETRY_HANDLE) - atom.handle.width += OPTION_MARGIN - atom.expanded = true + atom.pad = createChild(atom, SYMMETRY_PAD); + atom.handle = createChild(atom, SYMMETRY_HANDLE); + atom.handle.width += OPTION_MARGIN; + atom.expanded = true; - const [x, y, r] = getXYR(atom.value) - atom.xToggle = createChild(atom, SYMMETRY_TOGGLE_X) - atom.yToggle = createChild(atom, SYMMETRY_TOGGLE_Y) - atom.rToggle = createChild(atom, SYMMETRY_TOGGLE_R) + const [x, y, r] = getXYR(atom.value); + atom.xToggle = createChild(atom, SYMMETRY_TOGGLE_X); + atom.yToggle = createChild(atom, SYMMETRY_TOGGLE_Y); + atom.rToggle = createChild(atom, SYMMETRY_TOGGLE_R); - if (x > 0) atom.xToggle.value = true - if (y > 0) atom.yToggle.value = true - if (r > 0) atom.rToggle.value = true + if (x > 0) atom.xToggle.value = true; + if (y > 0) atom.yToggle.value = true; + if (r > 0) atom.rToggle.value = true; }, unexpand: (atom) => { - deleteChild(atom, atom.pad) - deleteChild(atom, atom.handle) - deleteChild(atom, atom.xToggle) - deleteChild(atom, atom.yToggle) - deleteChild(atom, atom.rToggle) - atom.expanded = false - }, - + deleteChild(atom, atom.pad); + deleteChild(atom, atom.handle); + deleteChild(atom, atom.xToggle); + deleteChild(atom, atom.yToggle); + deleteChild(atom, atom.rToggle); + atom.expanded = false; + }, + size: COLOURTODE_SQUARE.size, update: (atom) => { - - const {x, y} = getAtomPosition(atom) - const id = state.colourTode.atoms.indexOf(atom) - const left = x - const top = y - const right = x + atom.width - const bottom = y + atom.height + const {x, y} = getAtomPosition(atom); + + const id = state.colourTode.atoms.indexOf(atom); + const left = x; + const top = y; + const right = x + atom.width; + const bottom = y + atom.height; if (hand.content === atom) for (const paddle of paddles) { - const pid = state.colourTode.atoms.indexOf(paddle) - const {x: px, y: py} = getAtomPosition(paddle) - const pright = px + paddle.width - const ptop = py - const pbottom = py + paddle.height + const pid = state.colourTode.atoms.indexOf(paddle); + const {x: px, y: py} = getAtomPosition(paddle); + const pright = px + paddle.width; + const ptop = py; + const pbottom = py + paddle.height; - //if (paddle.pinhole.locked) continue + //if (paddle.pinhole.locked) continue; if (!paddle.hasSymmetry && paddle.expanded && id > pid && left <= pright && right >= pright && ((top < pbottom && top > ptop) || (bottom > ptop && bottom < pbottom))) { if (atom.highlightPaddle !== undefined) { - deleteChild(atom, atom.highlightPaddle) - } + deleteChild(atom, atom.highlightPaddle); + }; - atom.highlightPaddle = createChild(atom, HIGHLIGHT, {bottom: true}) - atom.highlightPaddle.width = HIGHLIGHT_THICKNESS - atom.highlightPaddle.height = paddle.height - atom.highlightPaddle.y = ptop - atom.highlightPaddle.x = pright - HIGHLIGHT_THICKNESS/2 - atom.highlightedPaddle = paddle - return - } + atom.highlightPaddle = createChild(atom, HIGHLIGHT, {bottom: true}); + atom.highlightPaddle.width = HIGHLIGHT_THICKNESS; + atom.highlightPaddle.height = paddle.height; + atom.highlightPaddle.y = ptop; + atom.highlightPaddle.x = pright - HIGHLIGHT_THICKNESS/2; + atom.highlightedPaddle = paddle; + return; + }; - } + }; if (atom.highlightPaddle !== undefined) { - deleteChild(atom, atom.highlightPaddle) - atom.highlightPaddle = undefined - atom.highlightedPaddle = undefined - } + deleteChild(atom, atom.highlightPaddle); + atom.highlightPaddle = undefined; + atom.highlightedPaddle = undefined; + }; }, drop: (atom) => { if (!atom.attached) { if (atom.highlightedPaddle !== undefined) { - const paddle = atom.highlightedPaddle - atom.attached = true - giveChild(paddle, atom) - - paddle.hasSymmetry = true - paddle.symmetryCircle = atom - updatePaddleSize(paddle) - - atom.dx = 0 - atom.dy = 0 - - /*atom.x = paddle.width -atom.width/2 - atom.y = paddle.height/2 - atom.height/2*/ + const paddle = atom.highlightedPaddle; + atom.attached = true; + giveChild(paddle, atom); + + paddle.hasSymmetry = true; + paddle.symmetryCircle = atom; + updatePaddleSize(paddle); + + atom.dx = 0; + atom.dy = 0; + + /*atom.x = paddle.width -atom.width/2; + atom.y = paddle.height/2 - atom.height/2*/; /*if (paddle.pinhole.locked && atom.expanded) { - atom.unexpand(atom) - }*/ + atom.unexpand(atom); + }*/; + + }; + }; - } - } - }, drag: (atom) => { if (atom.attached) { - const paddle = atom.parent + const paddle = atom.parent; /*if (paddle.pinhole.locked) { - const clone = makeAtom(SYMMETRY_CIRCLE) - clone.value = atom.value - const {x, y} = getAtomPosition(atom) - hand.offset.x -= atom.x - x - hand.offset.y -= atom.y - y - clone.x = x - clone.y = y - registerAtom(clone) - return clone - }*/ - - atom.attached = false - freeChild(paddle, atom) - paddle.hasSymmetry = false - paddle.symmetryCircle = undefined - updatePaddleSize(paddle) - } - - return atom + const clone = makeAtom(SYMMETRY_CIRCLE); + clone.value = atom.value; + const {x, y} = getAtomPosition(atom); + hand.offset.x -= atom.x - x; + hand.offset.y -= atom.y - y; + clone.x = x; + clone.y = y; + registerAtom(clone); + return clone; + }*/; + + atom.attached = false; + freeChild(paddle, atom); + paddle.hasSymmetry = false; + paddle.symmetryCircle = undefined; + updatePaddleSize(paddle); + }; + + return atom; }, rightDraggable: true, rightDrag: (atom) => { - const clone = makeAtom(SYMMETRY_CIRCLE) - clone.value = atom.value - const {x, y} = getAtomPosition(atom) - hand.offset.x -= atom.x - x - hand.offset.y -= atom.y - y - clone.x = x - clone.y = y - registerAtom(clone) - return clone + const clone = makeAtom(SYMMETRY_CIRCLE); + clone.value = atom.value; + const {x, y} = getAtomPosition(atom); + hand.offset.x -= atom.x - x; + hand.offset.y -= atom.y - y; + clone.x = x; + clone.y = y; + registerAtom(clone); + return clone; }, - } + }; - const HIGHLIGHT_THICKNESS = BORDER_THICKNESS + const HIGHLIGHT_THICKNESS = BORDER_THICKNESS; const HIGHLIGHT = { behindParent: true, draw: COLOURTODE_RECTANGLE.draw, @@ -8800,7 +8790,7 @@ registerRule( borderColour: Colour.splash(999), hasAbsolutePosition: true, hasInner: false, - } + }; const TRIANGLE_PAD = { draw: COLOURTODE_RECTANGLE.draw, @@ -8813,7 +8803,7 @@ registerRule( y: -SYMMETRY_CIRCLE.size/2 + OPTION_MARGIN/2, colour: Colour.Grey, grab: (atom) => atom.parent, - } + }; const TRIANGLE_HANDLE = { draw: COLOURTODE_RECTANGLE.draw, @@ -8826,7 +8816,7 @@ registerRule( y: SYMMETRY_CIRCLE.size/2 - (SYMMETRY_CIRCLE.size / 3)/2, colour: Colour.Grey, grab: (atom) => atom.parent, - } + }; const SYMMETRY_PAD = { draw: COLOURTODE_RECTANGLE.draw, @@ -8839,7 +8829,7 @@ registerRule( y: -(SYMMETRY_CIRCLE.size * 3)/3 + OPTION_MARGIN/2, colour: Colour.Grey, grab: (atom) => atom.parent, - } + }; const SYMMETRY_HANDLE = { draw: COLOURTODE_RECTANGLE.draw, @@ -8853,527 +8843,527 @@ registerRule( y: SYMMETRY_CIRCLE.size/2 - (SYMMETRY_CIRCLE.size / 3)/2, colour: Colour.Grey, grab: (atom) => atom.parent, - } + }; const rotateTriangleRotation = (rotation, clockwise) => { - clockwise = !clockwise + clockwise = !clockwise; switch (rotation) { - case "right": return clockwise ? "down" : "up" - case "down": return clockwise ? "left" : "right" - case "left": return clockwise ? "up" : "down" - case "up": return clockwise ? "right" : "left" - } + case "right": return clockwise ? "down" : "up"; + case "down": return clockwise ? "left" : "right"; + case "left": return clockwise ? "up" : "down"; + case "up": return clockwise ? "right" : "left"; + }; - throw new Error("Invalid rotation or clockwiseness") - } + throw new Error("Invalid rotation or clockwiseness"); + }; const TRIANGLE_PICK_UP = { hasBorder: true, colour: Colour.Black, borderColour: Colour.Black, draw: (atom) => { - // atom.colour = atom.value? Colour.Silver : Colour.Black - // atom.colour = Colour.Black - TRIANGLE_UP.draw(atom) + // atom.colour = atom.value? Colour.Silver : Colour.Black; + // atom.colour = Colour.Black; + TRIANGLE_UP.draw(atom); }, touch: (atom) => { - atom.colour = Colour.Silver - return atom + atom.colour = Colour.Silver; + return atom; }, click: (atom) => { - - const triangle = atom.parent - // triangle.upPick.value = false - // triangle.rightPick.value = false - // triangle.downPick.value = false - atom.colour = Colour.Black - - triangle.direction = rotateTriangleRotation(triangle.direction, true) - atom.value = true - - triangle.updateValue(triangle) - const parent = triangle.parent + + const triangle = atom.parent; + // triangle.upPick.value = false; + // triangle.rightPick.value = false; + // triangle.downPick.value = false; + atom.colour = Colour.Black; + + triangle.direction = rotateTriangleRotation(triangle.direction, true); + atom.value = true; + + triangle.updateValue(triangle); + const parent = triangle.parent; if (parent.isSquare) { - parent.receiveNumber(parent, triangle.value, triangle.channelId, {expanded: triangle.expanded, numberAtom: triangle}) - } + parent.receiveNumber(parent, triangle.value, triangle.channelId, {expanded: triangle.expanded, numberAtom: triangle}); + }; }, offscreen: TRIANGLE_UP.offscreen, overlaps: TRIANGLE_UP.overlaps, - + value: false, size: COLOURTODE_SQUARE.size - OPTION_MARGIN*1.5, grab: (atom) => atom.parent, x: TRIANGLE_PAD.x + TRIANGLE_PAD.width/2 - (COLOURTODE_SQUARE.size - OPTION_MARGIN*1.5)/2, y: TRIANGLE_PAD.y + OPTION_MARGIN*1.5/2, - } + }; const TRIANGLE_PICK_DOWN = { hasBorder: true, colour: Colour.Black, borderColour: Colour.Black, draw: (atom) => { - // atom.colour = atom.value? Colour.Silver : Colour.Black - // atom.colour = Colour.Black - TRIANGLE_DOWN.draw(atom) + // atom.colour = atom.value? Colour.Silver : Colour.Black; + // atom.colour = Colour.Black; + TRIANGLE_DOWN.draw(atom); }, touch: (atom) => { - atom.colour = Colour.Silver - return atom + atom.colour = Colour.Silver; + return atom; }, click: (atom) => { - - const triangle = atom.parent - // triangle.upPick.value = false - // triangle.rightPick.value = false - // triangle.downPick.value = false - atom.colour = Colour.Black - - triangle.direction = rotateTriangleRotation(triangle.direction, false) - atom.value = true - - - triangle.updateValue(triangle) - const parent = triangle.parent + + const triangle = atom.parent; + // triangle.upPick.value = false; + // triangle.rightPick.value = false; + // triangle.downPick.value = false; + atom.colour = Colour.Black; + + triangle.direction = rotateTriangleRotation(triangle.direction, false); + atom.value = true; + + + triangle.updateValue(triangle); + const parent = triangle.parent; if (parent.isSquare) { - parent.receiveNumber(parent, triangle.value, triangle.channelId, {expanded: triangle.expanded, numberAtom: triangle}) - } + parent.receiveNumber(parent, triangle.value, triangle.channelId, {expanded: triangle.expanded, numberAtom: triangle}); + }; }, offscreen: TRIANGLE_DOWN.offscreen, overlaps: TRIANGLE_DOWN.overlaps, - + value: false, size: COLOURTODE_SQUARE.size - OPTION_MARGIN*1.5, grab: (atom) => atom.parent, x: TRIANGLE_PAD.x + TRIANGLE_PAD.width/2 - (COLOURTODE_SQUARE.size - OPTION_MARGIN*1.5)/2, y: TRIANGLE_PAD.y + TRIANGLE_PAD.height - (COLOURTODE_SQUARE.size - OPTION_MARGIN*1.5) - OPTION_MARGIN/2, - } - + }; + const SYMMETRY_TOGGLE_X = { hasBorder: true, borderColour: Colour.Black, colour: Colour.Grey, draw: (atom) => { - atom.colour = atom.value? Colour.Silver : Colour.Grey - CIRCLE.draw(atom) - atom.drawX(atom) + atom.colour = atom.value? Colour.Silver : Colour.Grey; + CIRCLE.draw(atom); + atom.drawX(atom); }, drawX: (atom) => { - const {x, y} = getAtomPosition(atom) + const {x, y} = getAtomPosition(atom); - const W = (atom.size) - const H = (BORDER_THICKNESS*1.0) - const X = (x) - const Y = (y + atom.size/2 - BORDER_THICKNESS*1.0/2) + const W = (atom.size); + const H = (BORDER_THICKNESS*1.0); + const X = (x); + const Y = (y + atom.size/2 - BORDER_THICKNESS*1.0/2); - colourTodeContext.fillStyle = atom.borderColour - colourTodeContext.fillRect(X, Y, W, H) + colourTodeContext.fillStyle = atom.borderColour; + colourTodeContext.fillRect(X, Y, W, H); }, offscreen: CIRCLE.offscreen, overlaps: CIRCLE.overlaps, expanded: false, click: (atom) => { - atom.value = !atom.value - let [x, y, r] = getXYR(atom.parent.value) - x = atom.value? 100 : 0 - atom.parent.value = x+y+r - const circle = atom.parent + atom.value = !atom.value; + let [x, y, r] = getXYR(atom.parent.value); + x = atom.value? 100 : 0; + atom.parent.value = x+y+r; + const circle = atom.parent; if (circle.parent !== COLOURTODE_BASE_PARENT) { - const paddle = circle.parent - updatePaddleRule(paddle) - } + const paddle = circle.parent; + updatePaddleRule(paddle); + }; }, value: false, size: COLOURTODE_SQUARE.size - OPTION_MARGIN, grab: (atom) => atom.parent, x: SYMMETRY_PAD.x + SYMMETRY_PAD.width/2 - (COLOURTODE_SQUARE.size - OPTION_MARGIN)/2, y: SYMMETRY_PAD.y + OPTION_MARGIN/2, - } - + }; + const SYMMETRY_TOGGLE_Y = { hasBorder: true, borderColour: Colour.Black, colour: Colour.Grey, draw: (atom) => { - atom.colour = atom.value? Colour.Silver : Colour.Grey - CIRCLE.draw(atom) - atom.drawY(atom) + atom.colour = atom.value? Colour.Silver : Colour.Grey; + CIRCLE.draw(atom); + atom.drawY(atom); }, drawY: (atom, height = atom.size, offset = 0) => { - const {x, y} = getAtomPosition(atom) + const {x, y} = getAtomPosition(atom); - const W = (BORDER_THICKNESS*1.0) - const H = (height) - const X = (x + atom.size/2 - BORDER_THICKNESS*1.0/2) - const Y = (y) + offset + const W = (BORDER_THICKNESS*1.0); + const H = (height); + const X = (x + atom.size/2 - BORDER_THICKNESS*1.0/2); + const Y = (y) + offset; - colourTodeContext.fillStyle = atom.borderColour - colourTodeContext.fillRect(X, Y, W, H) + colourTodeContext.fillStyle = atom.borderColour; + colourTodeContext.fillRect(X, Y, W, H); }, offscreen: CIRCLE.offscreen, overlaps: CIRCLE.overlaps, expanded: false, click: (atom) => { - atom.value = !atom.value - let [x, y, r] = getXYR(atom.parent.value) - y = atom.value? 10 : 0 - atom.parent.value = x+y+r - const circle = atom.parent + atom.value = !atom.value; + let [x, y, r] = getXYR(atom.parent.value); + y = atom.value? 10 : 0; + atom.parent.value = x+y+r; + const circle = atom.parent; if (circle.parent !== COLOURTODE_BASE_PARENT) { - const paddle = circle.parent - updatePaddleRule(paddle) - } + const paddle = circle.parent; + updatePaddleRule(paddle); + }; }, value: false, size: COLOURTODE_SQUARE.size - OPTION_MARGIN, grab: (atom) => atom.parent, x: SYMMETRY_PAD.x + SYMMETRY_PAD.width/2 - (COLOURTODE_SQUARE.size - OPTION_MARGIN)/2, y: OPTION_MARGIN/2, - } - + }; + const SYMMETRY_TOGGLE_R = { hasBorder: true, borderColour: Colour.Black, colour: Colour.Grey, draw: (atom) => { - atom.colour = atom.value? Colour.Silver : Colour.Grey - CIRCLE.draw(atom) - atom.drawR(atom) + atom.colour = atom.value? Colour.Silver : Colour.Grey; + CIRCLE.draw(atom); + atom.drawR(atom); }, drawR: (atom) => { - const {x, y} = getAtomPosition(atom) - - let X = (x + atom.size/2) - let Y = (y + atom.size/2) - let R = atom.size/2 - (BORDER_THICKNESS*1.5)*2 - - colourTodeContext.fillStyle = atom.borderColour - colourTodeContext.beginPath() - colourTodeContext.arc(X, Y, R, 0, 2*Math.PI) - colourTodeContext.fill() - - R -= BORDER_THICKNESS - colourTodeContext.fillStyle = atom.colour - colourTodeContext.beginPath() - colourTodeContext.arc(X, Y, R, 0, 2*Math.PI) - colourTodeContext.fill() + const {x, y} = getAtomPosition(atom); + + let X = (x + atom.size/2); + let Y = (y + atom.size/2); + let R = atom.size/2 - (BORDER_THICKNESS*1.5)*2; + + colourTodeContext.fillStyle = atom.borderColour; + colourTodeContext.beginPath(); + colourTodeContext.arc(X, Y, R, 0, 2*Math.PI); + colourTodeContext.fill(); + + R -= BORDER_THICKNESS; + colourTodeContext.fillStyle = atom.colour; + colourTodeContext.beginPath(); + colourTodeContext.arc(X, Y, R, 0, 2*Math.PI); + colourTodeContext.fill(); }, offscreen: CIRCLE.offscreen, overlaps: CIRCLE.overlaps, expanded: false, click: (atom) => { - atom.value = !atom.value - let [x, y, r] = getXYR(atom.parent.value) - r = atom.value? 1 : 0 - atom.parent.value = x+y+r - const circle = atom.parent + atom.value = !atom.value; + let [x, y, r] = getXYR(atom.parent.value); + r = atom.value? 1 : 0; + atom.parent.value = x+y+r; + const circle = atom.parent; if (circle.parent !== COLOURTODE_BASE_PARENT) { - const paddle = circle.parent - updatePaddleRule(paddle) - } + const paddle = circle.parent; + updatePaddleRule(paddle); + }; }, value: false, size: COLOURTODE_SQUARE.size - OPTION_MARGIN, grab: (atom) => atom.parent, x: SYMMETRY_PAD.x + SYMMETRY_PAD.width/2 - (COLOURTODE_SQUARE.size - OPTION_MARGIN)/2, y: SYMMETRY_PAD.y + SYMMETRY_PAD.height - (COLOURTODE_SQUARE.size - OPTION_MARGIN) - OPTION_MARGIN/2, - } + }; - //====================// - // COLOURTODE - TOOLS // - //====================// + //====================//; + // COLOURTODE - TOOLS //; + //====================//; const makeSquareFromValue = (value) => { - const newAtom = makeAtom({...COLOURTODE_SQUARE}) - newAtom.value = cloneDragonArray(value) + const newAtom = makeAtom({...COLOURTODE_SQUARE}); + newAtom.value = cloneDragonArray(value); if (newAtom.value !== undefined) { if (newAtom.value.joins !== undefined) { for (const j of newAtom.value.joins) { - const joinAtom = makeSquareFromValue(j) - newAtom.joins.push(joinAtom) - } - } - newAtom.stamp = newAtom.value.stamp + const joinAtom = makeSquareFromValue(j); + newAtom.joins.push(joinAtom); + }; + }; + newAtom.stamp = newAtom.value.stamp; + + }; - } - if (!newAtom.value.isDiagram) { for (let i = 0; i < 3; i++) { - const channel = newAtom.value.channels[i] - if (channel === undefined) continue - if (channel.variable === undefined) continue - - const triangle = makeAtom(COLOURTODE_TRIANGLE) - newAtom.variableAtoms[i] = triangle - triangle.highlightedSlot = CHANNEL_NAMES[i] - triangle.channelId = i - - const leftVariable = i - 1 < 0? CHANNEL_NAMES[2] : CHANNEL_NAMES[i-1] - const rightVariable = i + 1 > 2? CHANNEL_NAMES[0] : CHANNEL_NAMES[i+1] - - if (channel.subtract) triangle.direction = "down" - else if (channel.add) triangle.direction = "up" - else if (channel.variable === leftVariable) triangle.direction = "left" - else if (channel.variable === rightVariable) triangle.direction = "right" - - triangle.updateValue(triangle) - - // const hexagon = makeAtom(COLOURTODE_HEXAGON) - // newAtom.variableAtoms[i] = hexagon - // hexagon.variable = channel.variable - // const {add, subtract} = channel - // hexagon.ons = [add.values[2], add.values[1], subtract.values[1], subtract.values[2], subtract.values[3], add.values[3]] - // hexagon.updateValue(hexagon) - } + const channel = newAtom.value.channels[i]; + if (channel === undefined) continue; + if (channel.variable === undefined) continue; - } + const triangle = makeAtom(COLOURTODE_TRIANGLE); + newAtom.variableAtoms[i] = triangle; + triangle.highlightedSlot = CHANNEL_NAMES[i]; + triangle.channelId = i; + + const leftVariable = i - 1 < 0? CHANNEL_NAMES[2] : CHANNEL_NAMES[i-1]; + const rightVariable = i + 1 > 2? CHANNEL_NAMES[0] : CHANNEL_NAMES[i+1]; + + if (channel.subtract) triangle.direction = "down"; + else if (channel.add) triangle.direction = "up"; + else if (channel.variable === leftVariable) triangle.direction = "left"; + else if (channel.variable === rightVariable) triangle.direction = "right"; + + triangle.updateValue(triangle); + + // const hexagon = makeAtom(COLOURTODE_HEXAGON); + // newAtom.variableAtoms[i] = hexagon; + // hexagon.variable = channel.variable; + // const {add, subtract} = channel; + // hexagon.ons = [add.values[2], add.values[1], subtract.values[1], subtract.values[2], subtract.values[3], add.values[3]]; + // hexagon.updateValue(hexagon); + }; + + }; if (newAtom.value !== undefined && newAtom.value.isDiagram) { - newAtom.update(newAtom) - } + newAtom.update(newAtom); + }; - return newAtom - } + return newAtom; + }; - let menuRight = 10 + let menuRight = 10; const COLOURTODE_TOOL = { element: COLOURTODE_SQUARE, draw: (atom) => { if ((atom.previousBrushColour !== state.brush.colour) || atom.toolbarNeedsColourUpdate) { - atom.update(atom) - } + atom.update(atom); + }; if (atom.unlocked) { - atom.element.draw(atom) - } + atom.element.draw(atom); + }; }, overlaps: (atom, x, y) => atom.element.overlaps(atom, x, y), grab: (atom, x, y) => { - return atom + return atom; }, drag: (atom) => { if (atom === squareTool) { - const newAtom = makeSquareFromValue(atom.value) - registerAtom(newAtom) - return newAtom - } + const newAtom = makeSquareFromValue(atom.value); + registerAtom(newAtom); + return newAtom; + }; - const newAtom = makeAtom({...atom.element, x: atom.x, y: atom.y}) - registerAtom(newAtom) + const newAtom = makeAtom({...atom.element, x: atom.x, y: atom.y}); + registerAtom(newAtom); if (newAtom.value !== undefined) { - /* + /*; if (newAtom.value.joins !== undefined) { for (const j of newAtom.value.joins) { - const joinAtom = makeAtom(COLOURTODE_SQUARE) - joinAtom.value = j - newAtom.joins.push(joinAtom) - } - } - */ - } + const joinAtom = makeAtom(COLOURTODE_SQUARE); + joinAtom.value = j; + newAtom.joins.push(joinAtom); + }; + }; + */; + }; - return newAtom + return newAtom; }, cursor: () => "move", - } + }; - let menuId = 0 + let menuId = 0; const addMenuTool = (element, unlockName) => { - const {width = COLOURTODE_SQUARE.size, height = COLOURTODE_SQUARE.size, size} = element - - let y = COLOURTODE_PICKER_PAD_MARGIN - if (height < COLOURTODE_SQUARE.size) { - y += (COLOURTODE_SQUARE.size - height)/2 - } - y += BORDER_THICKNESS - - const atom = makeAtom({...COLOURTODE_TOOL, width, height, size, x: Math.round(menuRight), y, element}) - atom.menuId = menuId - menuId++ - atom.attached = true - atom.isTool = true - atom.previousBrushColour = undefined - atom.colourId = 0 - atom.dcolourId = 1 - atom.colourTicker = Infinity - atom.hasBorder = true - menuRight += width - menuRight += OPTION_MARGIN + const {width = COLOURTODE_SQUARE.size, height = COLOURTODE_SQUARE.size, size} = element; - registerAtom(atom) + let y = COLOURTODE_PICKER_PAD_MARGIN; + if (height < COLOURTODE_SQUARE.size) { + y += (COLOURTODE_SQUARE.size - height)/2; + }; + y += BORDER_THICKNESS; + + const atom = makeAtom({...COLOURTODE_TOOL, width, height, size, x: Math.round(menuRight), y, element}); + atom.menuId = menuId; + menuId++; + atom.attached = true; + atom.isTool = true; + atom.previousBrushColour = undefined; + atom.colourId = 0; + atom.dcolourId = 1; + atom.colourTicker = Infinity; + atom.hasBorder = true; + menuRight += width; + menuRight += OPTION_MARGIN; + + registerAtom(atom); if (unlockName === undefined) { - atom.unlocked = true + atom.unlocked = true; } else { - atom.unlocked = false - atom.grabbable = false - unlocks[unlockName] = atom - if (UNLOCK_MODE) unlockMenuTool(unlockName) - } + atom.unlocked = false; + atom.grabbable = false; + unlocks[unlockName] = atom; + if (UNLOCK_MODE) unlockMenuTool(unlockName); + }; - return atom - } + return atom; + }; - unlocks = {} + unlocks = {}; const unlockMenuTool = (unlockName) => { - const unlock = unlocks[unlockName] - if (unlock.unlocked) return - unlock.unlocked = true - unlock.grabbable = true + const unlock = unlocks[unlockName]; + if (unlock.unlocked) return; + unlock.unlocked = true; + unlock.grabbable = true; - /*registerAtom(unlock) - menuRight += unlock.width - menuRight += OPTION_MARGIN*/ + /*registerAtom(unlock); + menuRight += unlock.width; + menuRight += OPTION_MARGIN*/; - } + }; + + squareTool = addMenuTool(COLOURTODE_SQUARE); + menuRight += BORDER_THICKNESS; + const triangleTool = addMenuTool(COLOURTODE_TRIANGLE, "triangle"); + //triangleTool.size -= BORDER_THICKNESS*1.5; + //triangleTool.y += BORDER_THICKNESS*1.5 / 2; + menuRight -= BORDER_THICKNESS; + const circleTool = addMenuTool(SYMMETRY_CIRCLE, "circle"); + const hexagonTool = addMenuTool(COLOURTODE_HEXAGON, "hexagon"); + // const wideRectangleTool = addMenuTool(COLOURTODE_PICKER_CHANNEL, "wide_rectangle"); + //menuRight += BORDER_THICKNESS; + const tallRectangleTool = {} //addMenuTool(COLOURTODE_TALL_RECTANGLE, "tall_rectangle"); + createPaddle(); + + squareTool.value = makeArrayFromSplash(state.brush.colour); + + circleTool.borderScale = 1; - squareTool = addMenuTool(COLOURTODE_SQUARE) - menuRight += BORDER_THICKNESS - const triangleTool = addMenuTool(COLOURTODE_TRIANGLE, "triangle") - //triangleTool.size -= BORDER_THICKNESS*1.5 - //triangleTool.y += BORDER_THICKNESS*1.5 / 2 - menuRight -= BORDER_THICKNESS - const circleTool = addMenuTool(SYMMETRY_CIRCLE, "circle") - const hexagonTool = addMenuTool(COLOURTODE_HEXAGON, "hexagon") - // const wideRectangleTool = addMenuTool(COLOURTODE_PICKER_CHANNEL, "wide_rectangle") - //menuRight += BORDER_THICKNESS - const tallRectangleTool = {} //addMenuTool(COLOURTODE_TALL_RECTANGLE, "tall_rectangle") - createPaddle() - - squareTool.value = makeArrayFromSplash(state.brush.colour) - - circleTool.borderScale = 1 - squareTool.update = (atom) => { if (atom.joinDrawId === undefined) { - atom.joinDrawId = -1 - atom.joinDrawTimer = 0 - } + atom.joinDrawId = -1; + atom.joinDrawTimer = 0; + }; - /* + /*; if (typeof state.brush.colour === "number") { - atom.value = makeArrayFromSplash(state.brush.colour) + atom.value = makeArrayFromSplash(state.brush.colour); } else { - const content = state.brush.colour.left[0].content - //atom.value = cloneDragonArray(content) + const content = state.brush.colour.left[0].content; + //atom.value = cloneDragonArray(content); if (atom === squareTool) { - atom.stamp = atom.value.stamp - } - }*/ + atom.stamp = atom.value.stamp; + }; + }*/; if (atom.value !== undefined && atom === squareTool) { if (atom.previousBrushColour !== state.brush.colour || atom.toolbarNeedsColourUpdate) { - atom.previousBrushColour = state.brush.colour + atom.previousBrushColour = state.brush.colour; if (atom.multiAtoms === undefined) { - atom.multiAtoms = [] - } + atom.multiAtoms = []; + }; for (const multiAtom of atom.multiAtoms) { - deleteChild(atom, multiAtom) - } + deleteChild(atom, multiAtom); + }; - atom.multiAtoms = [] + atom.multiAtoms = []; if (atom.value.isDiagram) { - const diagram = atom.value - const [diagramWidth, diagramHeight] = getDiagramDimensions(diagram) - const cellAtomWidth = atom.width / diagramWidth - const cellAtomHeight = atom.height / diagramHeight + const diagram = atom.value; + const [diagramWidth, diagramHeight] = getDiagramDimensions(diagram); + const cellAtomWidth = atom.width / diagramWidth; + const cellAtomHeight = atom.height / diagramHeight; for (const diagramCell of diagram.left) { - const multiAtom = createChild(atom, COLOURTODE_SQUARE) - multiAtom.x = diagramCell.x * cellAtomWidth - multiAtom.y = diagramCell.y * cellAtomHeight - multiAtom.width = diagramCell.width * cellAtomWidth - multiAtom.height = diagramCell.height * cellAtomHeight - multiAtom.value = diagramCell.content - multiAtom.update(multiAtom) - atom.multiAtoms.push(multiAtom) - } - } - } - } - - const valueClone = cloneDragonArray(atom.value) - atom.colours = getSplashesArrayFromArray(valueClone) + const multiAtom = createChild(atom, COLOURTODE_SQUARE); + multiAtom.x = diagramCell.x * cellAtomWidth; + multiAtom.y = diagramCell.y * cellAtomHeight; + multiAtom.width = diagramCell.width * cellAtomWidth; + multiAtom.height = diagramCell.height * cellAtomHeight; + multiAtom.value = diagramCell.content; + multiAtom.update(multiAtom); + atom.multiAtoms.push(multiAtom); + }; + }; + }; + }; + + const valueClone = cloneDragonArray(atom.value); + atom.colours = getSplashesArrayFromArray(valueClone); if (atom.colourId >= atom.colours.length) { - atom.colourId = 0 - } - //atom.colour = Colour.splash(atom.colours[atom.colourId]) + atom.colourId = 0; + }; + //atom.colour = Colour.splash(atom.colours[atom.colourId]); if (atom.toolbarNeedsColourUpdate && atom === squareTool) { - atom.toolbarNeedsColourUpdate = false - atom.isGradient = true - atom.joins = [] + atom.toolbarNeedsColourUpdate = false; + atom.isGradient = true; + atom.joins = []; for (const joinValue of atom.value.joins) { - const joinSquare = makeSquareFromValue(joinValue) - atom.joins.push(joinSquare) - } - COLOURTODE_SQUARE.updateGradient(atom) + const joinSquare = makeSquareFromValue(joinValue); + atom.joins.push(joinSquare); + }; + COLOURTODE_SQUARE.updateGradient(atom); } else { - atom.colour = Colour.splash(999) - atom.borderColour = Colour.splash(999) - } - } + atom.colour = Colour.splash(999); + atom.borderColour = Colour.splash(999); + }; + }; + + triangleTool.update = squareTool.update; + circleTool.update = squareTool.update; + // wideRectangleTool.update = squareTool.update; + tallRectangleTool.update = squareTool.update; + hexagonTool.update = squareTool.update; - triangleTool.update = squareTool.update - circleTool.update = squareTool.update - // wideRectangleTool.update = squareTool.update - tallRectangleTool.update = squareTool.update - hexagonTool.update = squareTool.update - - //=========// - // SHARING // - //=========// + //=========//; + // SHARING //; + //=========//; on.keydown(e => { if ((e.ctrlKey || e.metaKey) && e.key === 's') { - e.preventDefault() - savePaddles() + e.preventDefault(); + savePaddles(); } else if ((e.ctrlKey || e.metaKey) && e.key === 'o') { - e.preventDefault() - openPaddles() + e.preventDefault(); + openPaddles(); } else if ((e.ctrlKey || e.metaKey) && e.key === 'c') { - e.preventDefault() - copyPaddles() - } - }, {passive: false}) + e.preventDefault(); + copyPaddles(); + }; + }, {passive: false}); on.paste(async e => { - const pack = e.clipboardData.getData('text') + const pack = e.clipboardData.getData('text'); if (pack !== "") { - unpackPaddles(pack) - return - } + unpackPaddles(pack); + return; + }; - const item = e.clipboardData.items[0] - const file = item.getAsFile() - const p = await file.text() - unpackPaddles(p) - }) + const item = e.clipboardData.items[0]; + const file = item.getAsFile(); + const p = await file.text(); + unpackPaddles(p); + }); on.dragover(e => { e.stopPropagation(); - e.preventDefault() - }, {passive: false}) + e.preventDefault(); + }, {passive: false}); on.drop(async (e) => { e.stopPropagation(); - e.preventDefault() - const item = e.dataTransfer.items[0] - const file = item.getAsFile() - const p = await file.text() - unpackPaddles(p) - }, {passive: false}) + e.preventDefault(); + const item = e.dataTransfer.items[0]; + const file = item.getAsFile(); + const p = await file.text(); + unpackPaddles(p); + }, {passive: false}); - const PADDLE_PACK = {} - const PADDLE_UNPACK = {} + const PADDLE_PACK = {}; + const PADDLE_UNPACK = {}; PADDLE_PACK.cellAtoms = (paddle, value) => { - const cellAtoms = [] + const cellAtoms = []; for (const atom of value) { cellAtoms.push({ isLeftSlot: atom.isLeftSlot, @@ -9381,166 +9371,166 @@ registerRule( x: atom.x, y: atom.y, slotted: atom.slotted ? atom.slotted.value : undefined, - }) - } - return cellAtoms - } + }); + }; + return cellAtoms; + }; - let loadedColour = false + let loadedColour = false; PADDLE_UNPACK.cellAtoms = (paddle, value) => { - const atoms = [] + const atoms = []; for (const v of value) { if (!loadedColour) { if (!v.isLeftSlot) { - setBrushColour(v.value) - } - loadedColour = true - } - const square = v.isLeftSlot ? makeAtom(SLOT) : makeSquareFromValue(v.value) - square.isLeftSlot = v.isLeftSlot - registerAtom(square) - giveChild(paddle, square) - square.attached = true - square.x = v.x - square.y = v.y - square.highlightedSide = "left" - atoms.push(square) + setBrushColour(v.value); + }; + loadedColour = true; + }; + const square = v.isLeftSlot ? makeAtom(SLOT) : makeSquareFromValue(v.value); + square.isLeftSlot = v.isLeftSlot; + registerAtom(square); + giveChild(paddle, square); + square.attached = true; + square.x = v.x; + square.y = v.y; + square.highlightedSide = "left"; + atoms.push(square); if (v.slotted !== undefined) { - const slotted = makeSquareFromValue(v.slotted) - registerAtom(slotted) - giveChild(paddle, slotted) - slotted.attached = true - slotted.cellAtom = square - slotted.highlightedSide = "slot" - slotted.slottee = true - square.slotted = slotted - } - } - return atoms - } + const slotted = makeSquareFromValue(v.slotted); + registerAtom(slotted); + giveChild(paddle, slotted); + slotted.attached = true; + slotted.cellAtom = square; + slotted.highlightedSide = "slot"; + slotted.slottee = true; + square.slotted = slotted; + }; + }; + return atoms; + }; PADDLE_PACK.symmetryCircle = (paddle, value) => { - if (value === undefined) return - return value.value - } + if (value === undefined) return; + return value.value; + }; PADDLE_UNPACK.symmetryCircle = (paddle, value) => { - const circle = createChild(paddle, SYMMETRY_CIRCLE) - circle.value = value - return circle - } + const circle = createChild(paddle, SYMMETRY_CIRCLE); + circle.value = value; + return circle; + }; PADDLE_PACK.chance = (paddle, value) => { - if (value === undefined) return - return value.ons - } + if (value === undefined) return; + return value.ons; + }; PADDLE_UNPACK.chance = (paddle, value) => { - const hex = createChild(paddle, COLOURTODE_HEXAGON) - hex.ons = value - return hex - } + const hex = createChild(paddle, COLOURTODE_HEXAGON); + hex.ons = value; + return hex; + }; - const keep = (paddle, value) => value - PADDLE_PACK.expanded = keep - PADDLE_PACK.x = keep - PADDLE_PACK.y = keep - PADDLE_PACK.width = keep - PADDLE_PACK.height = keep - PADDLE_PACK.hasSymmetry = keep - - PADDLE_UNPACK.expanded = keep - PADDLE_UNPACK.x = keep - PADDLE_UNPACK.y = keep - PADDLE_UNPACK.width = keep - PADDLE_UNPACK.height = keep - PADDLE_UNPACK.hasSymmetry = keep + const keep = (paddle, value) => value; + PADDLE_PACK.expanded = keep; + PADDLE_PACK.x = keep; + PADDLE_PACK.y = keep; + PADDLE_PACK.width = keep; + PADDLE_PACK.height = keep; + PADDLE_PACK.hasSymmetry = keep; + + PADDLE_UNPACK.expanded = keep; + PADDLE_UNPACK.x = keep; + PADDLE_UNPACK.y = keep; + PADDLE_UNPACK.width = keep; + PADDLE_UNPACK.height = keep; + PADDLE_UNPACK.hasSymmetry = keep; PADDLE_PACK.pinhole = (paddle, value) => { - return value.locked - } + return value.locked; + }; PADDLE_UNPACK.pinhole = (paddle, value) => { - paddle.pinhole.locked = value - return paddle.pinhole - } + paddle.pinhole.locked = value; + return paddle.pinhole; + }; PADDLE_PACK.rightTriangle = (paddle, value) => { - return value !== undefined - } + return value !== undefined; + }; PADDLE_UNPACK.rightTriangle = (paddle, value) => { - if (!value) return undefined - const arrow = createChild(paddle, COLOURTODE_TRIANGLE) - return arrow - } + if (!value) return undefined; + const arrow = createChild(paddle, COLOURTODE_TRIANGLE); + return arrow; + }; const packPaddles = () => { - const packedPaddles = [] + const packedPaddles = []; for (const paddle of paddles) { - const packedPaddle = {} + const packedPaddle = {}; for (const key in paddle) { - const packer = PADDLE_PACK[key] - if (packer === undefined) continue - const v = packer(paddle, paddle[key]) + const packer = PADDLE_PACK[key]; + if (packer === undefined) continue; + const v = packer(paddle, paddle[key]); if (v !== undefined) { - packedPaddle[key] = v - } - } - packedPaddles.push(packedPaddle) - } - return JSON.stringify(packedPaddles) - } + packedPaddle[key] = v; + }; + }; + packedPaddles.push(packedPaddle); + }; + return JSON.stringify(packedPaddles) ; + }; const unpackPaddles = (pack) => { if (middleClicked) { - middleClicked = false - return - } - - loadedColour = false - unlockMenuTool("triangle") - unlockMenuTool("circle") - unlockMenuTool("hexagon") - // unlockMenuTool("wide_rectangle") + middleClicked = false; + return; + }; + + loadedColour = false; + unlockMenuTool("triangle"); + unlockMenuTool("circle"); + unlockMenuTool("hexagon"); + // unlockMenuTool("wide_rectangle"); try { while (paddles.length > 0) { - deletePaddle(paddles[paddles.length-1]) - } + deletePaddle(paddles[paddles.length-1]); + }; for (const packed of JSON.parse(pack)) { - const paddle = createPaddle() + const paddle = createPaddle(); for (const key in packed) { - const unpacker = PADDLE_UNPACK[key] - if (unpacker === undefined) continue - const v = unpacker(paddle, packed[key]) + const unpacker = PADDLE_UNPACK[key]; + if (unpacker === undefined) continue; + const v = unpacker(paddle, packed[key]); if (v !== undefined) { - paddle[key] = v - } - } - updatePaddleSize(paddle) - updatePaddleRule(paddle) - } - positionPaddles() + paddle[key] = v; + }; + }; + updatePaddleSize(paddle); + updatePaddleRule(paddle); + }; + positionPaddles(); } catch(e) { - console.error(e) - alert("Error loading rules... Sorry! Please contact @todepond :)") - } - } + console.error(e); + alert("Error loading rules... Sorry! Please contact @todepond :)"); + }; + }; const download = (content, fileName, contentType) => { - var a = document.createElement("a") - var file = new Blob([content], {type: contentType}) - a.href = URL.createObjectURL(file) - a.download = fileName - a.click() - } - + var a = document.createElement("a"); + var file = new Blob([content], {type: contentType}); + a.href = URL.createObjectURL(file); + a.download = fileName; + a.click(); + }; + const savePaddles = async () => { const pack = packPaddles(paddles); if (window.showSaveFilePicker) { - // Use the Native File System API if available + // Use the Native File System API if available; try { const result = await showSaveFilePicker({ excludeAcceptAllOption: true, @@ -9548,17 +9538,17 @@ registerRule( startIn: 'downloads', types: [{ description: 'JSON', - accept: {'application/json': [".json"]} + accept: {'application/json': [".json"]}; }], - }) + }); const writable = await result.createWritable(); await writable.write(pack); await writable.close(); } catch (err) { console.error('Failed to save file:', err); - } + }; } else { - // Fallback to the Blob and link method + // Fallback to the Blob and link method; const blob = new Blob([pack], {type: 'application/json'}); const url = URL.createObjectURL(blob); @@ -9567,30 +9557,30 @@ registerRule( link.download = 'spell.json'; link.click(); URL.revokeObjectURL(url); - } - } + }; + }; const openPaddles = () => { - const opener = document.createElement('input') - opener.type = "file" + const opener = document.createElement('input'); + opener.type = "file"; opener.onchange = async e => { - const file = opener.files[0] - const pack = await file.text() - unpackPaddles(pack) - Keyboard.Control = false - } - opener.click() - Keyboard.Control = false - } + const file = opener.files[0]; + const pack = await file.text(); + unpackPaddles(pack); + Keyboard.Control = false; + }; + opener.click(); + Keyboard.Control = false; + }; const copyPaddles = () => { - const pack = packPaddles(paddles) - print(pack) - navigator.clipboard.writeText(pack) - } + const pack = packPaddles(paddles); + print(pack); + navigator.clipboard.writeText(pack); + }; -}) +}); -//============================================================= -// just let go +//=============================================================; +// just let go; // of what you know From 93ffd792392cd38e89f88192dece71be619e9364 Mon Sep 17 00:00:00 2001 From: inyourface34456 <62214409+inyourface34456@users.noreply.github.com> Date: Thu, 16 May 2024 13:33:13 -0400 Subject: [PATCH 2/7] Update the-one-true-todey-file-of-cellpond.js mor --- the-one-true-todey-file-of-cellpond.js | 967 ++++++++++++------------- 1 file changed, 483 insertions(+), 484 deletions(-) diff --git a/the-one-true-todey-file-of-cellpond.js b/the-one-true-todey-file-of-cellpond.js index 6758a20..736e2d3 100644 --- a/the-one-true-todey-file-of-cellpond.js +++ b/the-one-true-todey-file-of-cellpond.js @@ -64,7 +64,7 @@ document.addEventListener('mousedown', function(event) { const urlParams = new URLSearchParams(window.location.search); const NO_SECRET_MODE = urlParams.has("nosecret"); -const NO_FOOLS_MODE = urlParams.has("nofools"); + const UNLOCK_MODE = urlParams.has("unlock"); const SCALE = urlParams.get("scale") ?? 1; const DPR = urlParams.get("dpr") ?? devicePixelRatio; @@ -73,7 +73,7 @@ if (NO_SECRET_MODE) { localStorage.setItem("secretHasAlreadyBeenRevealed", "true"); } -const secretHasAlreadyBeenRevealed = localStorage.getItem("secretHasAlreadyBeenRevealed"); + //========//; @@ -96,7 +96,7 @@ const TODEPOND_COLOURS = [ Colour.White.splash, ]; -const TODEPOND_RAINBOW_COLOURS = TODEPOND_COLOURS.slice(0, -4); + const getRGB = (splash) => { const gb = splash % 100; @@ -2121,7 +2121,6 @@ on.load(() => { const behaveFunction = (origin, redraw) => { - let count = 1; for (const stepFunction of stepFunctions) { const drawn = stepFunction(origin, redraw); if (drawn !== undefined) return drawn; @@ -2519,8 +2518,8 @@ on.load(() => { const BLUE = makeArrayFromSplash(Colour.Blue.splash); const YELLOW = makeArrayFromSplash(Colour.Yellow.splash); const PURPLE = makeArrayFromSplash(Colour.Cyan.splash - 111); - const RED = makeArrayFromSplash(Colour.Red.splash); - let [RED_R, RED_G, RED_B] = getRGB(Colour.Red.splash); + + let [RED_R, RED_G] = getRGB(Colour.Red.splash); RED_R /= 100; RED_G /= 10; /*BLACK.channels[0].values[RED_R] = true; @@ -3955,28 +3954,28 @@ registerRule(; } colourTodeContext.fill("evenodd"); } - }; + } else { if (atom.borderColour === undefined) { colourTodeContext.strokeStyle = borderColours[atom.colour.splash]; - }; + } else { colourTodeContext.strokeStyle = atom.borderColour; - }; + } X = Math.round(x + 0.5) - 0.5; Y = Math.round(y + 0.5) - 0.5; colourTodeContext.lineWidth = BORDER_THICKNESS; colourTodeContext.strokeRect(X, Y, W, H); - }; - }; + } + } else { colourTodeContext.fillStyle = atom.colour; colourTodeContext.fillRect(X, Y, W, H); - }; + } }, offscreen: (atom) => { @@ -3996,7 +3995,7 @@ registerRule(; let border = BORDER_THICKNESS; if (atom.isTool || atom.isSquare || atom.isTallRectangle) { border *= 1.5; - }; + } const left = x; const right = x + atom.width; const top = y; @@ -4022,14 +4021,14 @@ registerRule(; if (atom.hasBorder) { if (atom.isTool) { atom.borderColour = toolBorderColours[atom.colour.splash]; - }; + } colourTodeContext.fillStyle = atom.borderColour !== undefined? atom.borderColour : Colour.Void; colourTodeContext.beginPath(); colourTodeContext.arc(X, Y, R, 0, 2*Math.PI); colourTodeContext.fill(); let borderScale = atom.borderScale !== undefined? atom.borderScale : 1.0; R = (atom.width/2 - BORDER_THICKNESS*1.5 * borderScale); - }; + } colourTodeContext.fillStyle = atom.colour; colourTodeContext.beginPath(); @@ -4049,8 +4048,8 @@ registerRule(; if (x === sx && y === sy) { //if (cellAtom.isLeftSlot) continue; return true; - }; - }; + } + } return false; }; @@ -4060,8 +4059,8 @@ registerRule(; const {x, y} = getAtomPosition(cellAtom); if (x === sx && y === sy) { if (cellAtom.isLeftSlot || cellAtom.isSlot) return true; - }; - }; + } + } return false; }; @@ -4075,7 +4074,7 @@ registerRule(; state.brush.colour = makeDiagram({left: [diagramCell]}); squareTool.value = diagramCell.content; squareTool.toolbarNeedsColourUpdate = true; - }; + } }; // Ctrl+F: sqdef; @@ -4100,24 +4099,24 @@ registerRule(; atom.joinUnepxand(atom); } else { atom.joinExpand(atom); - }; - }; - }; + } + } + } else if (atom.value.isDiagram) { - }; + } else if (!atom.expanded) { if (atom.parent === COLOURTODE_BASE_PARENT || !atom.parent.isPaddle) { atom.expand(atom); - }; + } - }; + } else { atom.unexpand(atom); - }; + } setBrushColour(atom.value); }, @@ -4129,7 +4128,7 @@ registerRule(; // unlockMenuTool("hexagon"); // unlockMenuTool("wide_rectangle"); unlockMenuTool("triangle"); - }; + } }, unexpand: (atom) => { @@ -4152,7 +4151,7 @@ registerRule(; if (atom.value.channels[2] !== undefined) { if (atom.value.channels[2].variable === undefined) { const blue = createChild(atom, COLOURTODE_PICKER_CHANNEL); - blue.channelSlot = "blue" //note: a colour doesn't necessarily have to be in its own channel slot; + blue.channelSlot = "blue"; //note: a colour doesn't necessarily have to be in its own channel slot; blue.x += COLOURTODE_PICKER_PAD_MARGIN + 3 * (COLOURTODE_SQUARE.size + COLOURTODE_PICKER_PAD_MARGIN); blue.value = atom.value.channels[2]; blue.needsColoursUpdate = true; @@ -4173,13 +4172,13 @@ registerRule(; hexagon.attached = true; atom.blue = hexagon; - }; - }; + } + } if (atom.value.channels[1] !== undefined) { if (atom.value.channels[1].variable === undefined) { const green = createChild(atom, COLOURTODE_PICKER_CHANNEL); - green.channelSlot = "green" //note: a colour doesn't necessarily have to be in its own channel slot; + green.channelSlot = "green"; //note: a colour doesn't necessarily have to be in its own channel slot; green.x += COLOURTODE_PICKER_PAD_MARGIN + 2 * (COLOURTODE_SQUARE.size + COLOURTODE_PICKER_PAD_MARGIN); green.value = atom.value.channels[1]; green.needsColoursUpdate = true; @@ -4200,13 +4199,13 @@ registerRule(; hexagon.attached = true; atom.green = hexagon; - }; - }; + } + } if (atom.value.channels[0] !== undefined) { if (atom.value.channels[0].variable === undefined) { const red = createChild(atom, COLOURTODE_PICKER_CHANNEL); - red.channelSlot = "red" //note: a colour doesn't necessarily have to be in its own channel slot; + red.channelSlot = "red"; //note: a colour doesn't necessarily have to be in its own channel slot; red.x += COLOURTODE_PICKER_PAD_MARGIN + COLOURTODE_SQUARE.size + COLOURTODE_PICKER_PAD_MARGIN; red.value = atom.value.channels[0]; red.needsColoursUpdate = true; @@ -4225,8 +4224,8 @@ registerRule(; triangle.attached = true; atom.red = triangle; - }; - }; + } + } }, deletePicker: (atom) => { @@ -4235,15 +4234,15 @@ registerRule(; if (atom.red) { atom.deletedRedOptions = atom.red.options; deleteChild(atom, atom.red); - }; + } if (atom.green) { atom.deletedGreenOptions = atom.green.options; deleteChild(atom, atom.green); - }; + } if (atom.blue) { atom.deletedBlueOptions = atom.blue.options; deleteChild(atom, atom.blue); - }; + } }, receiveNumber: (atom, number, channel = number.channel, {expanded, numberAtom} = {}) => { @@ -4254,18 +4253,18 @@ registerRule(; if (atom.variableAtoms === undefined) { atom.variableAtoms = [undefined, undefined, undefined]; - }; + } if (number !== undefined && number.variable !== undefined) { atom.variableAtoms[channel] = numberAtom; } else { atom.variableAtoms[channel] = undefined; - }; + } if (expanded !== undefined) { const channelName = CHANNEL_NAMES[channel]; atom[`${channelName}Expanded`] = expanded; - }; + } atom.value.channels[channel] = number; @@ -4275,12 +4274,12 @@ registerRule(; atom.colourTicker = Infinity; /*const diagramCell = makeDiagramCell({content: atom.value}); - state.brush.colour = makeDiagram({left: [diagramCell]})*/; + state.brush.colour = makeDiagram({left: [diagramCell]})*/ if (atom.parent !== COLOURTODE_BASE_PARENT) { const paddle = atom.parent; updatePaddleRule(paddle); - }; + } const brushDiagramCell = makeDiagramCell({content: atom.value}); state.brush.colour = makeDiagram({left: [brushDiagramCell]}); @@ -4297,10 +4296,10 @@ registerRule(; atom.needsColoursUpdate = true; /*const r = Random.Uint8 % 10; const g = Random.Uint8 % 10; - const b = Random.Uint8 % 10*/; + const b = Random.Uint8 % 10*/ /*const r = Random.Uint8 % 10; const g = Random.Uint8 % 10; - const b = Random.Uint8 % 10*/; + const b = Random.Uint8 % 10*/ //atom.value = makeArrayFromSplash(r*100 + g*10 + b); //atom.value = makeArrayFromSplash(555); //const splash = TODEPOND_COLOURS[Random.Uint8 % TODEPOND_COLOURS.length]; @@ -4308,7 +4307,7 @@ registerRule(; atom.value = makeArrayFromSplash(state.brush.colour); } else { atom.value = cloneDragonArray(state.brush.colour.left[0].content); - }; + } atom.colourId = 0; atom.dcolourId = 1; @@ -4335,7 +4334,7 @@ registerRule(; for (const join of atom.joins) { join.updateGradient(join); joinGradients.push(join.gradient); - }; + } atom.headGradient = getGradientImageFromColours({ colours: atom.colours, width: atom.width * CT_SCALE, @@ -4348,7 +4347,7 @@ registerRule(; atom.gradient = getMergedGradient({ gradients, width: atom.width * CT_SCALE, - height: atom.height * CT_SCALE, + height: atom.height * CT_SCALE, stamp: atom.value.stamp, mergedGradient: atom.gradient, }); @@ -4361,7 +4360,7 @@ registerRule(; gradient: atom.gradient, stamp: atom.value.stamp, }); - }; + } }, // Ctrl+F: sqwww; @@ -4382,16 +4381,16 @@ registerRule(; multiAtom.height = diagramCell.height * cellAtomHeight; multiAtom.value = diagramCell.content; atom.multiAtoms.push(multiAtom); - }; - }; + } + } } else { if (atom.needsColoursUpdate) { atom.updateGradient(atom); atom.needsColoursUpdate = false; - }; - }; + } + } const {x, y} = getAtomPosition(atom); @@ -4407,7 +4406,7 @@ registerRule(; if (atom.highlight !== undefined) { deleteChild(atom, atom.highlight); atom.highlight = undefined; - }; + } if (atom.highlightedAtom === undefined) { const atoms = getAllBaseAtoms(); @@ -4416,7 +4415,7 @@ registerRule(; if (!other.isSquare) continue; if (other.joins.length > 0 && other.joinExpanded) { other = other.pickerPad; - }; + } const {x: ox, y: oy} = getAtomPosition(other); const oleft = ox; @@ -4434,7 +4433,7 @@ registerRule(; } else { if (other.parent !== COLOURTODE_BASE_PARENT) continue; atom.highlightedAtom = other; - }; + } atom.highlight = createChild(atom, HIGHLIGHT, {bottom: true}); atom.highlight.hasBorder = true; @@ -4446,8 +4445,8 @@ registerRule(; break; - }; - }; + } + } if (atom.highlightedAtom === undefined) for (const paddle of paddles) { @@ -4500,16 +4499,16 @@ registerRule(; atom.highlight.height = paddle.dummyLeft.height; atom.highlightedSide = "left"; atom.highlightedAtom = paddle; - }; + } break; - }; + } else if (paddle.rightTriangle !== undefined && left > pleft + paddle.rightTriangle.x) { let winningDistance = Infinity; - let winningSide = undefined; - let winningCellAtom = undefined; + let winningSide; + let winningCellAtom; for (const catom of paddle.cellAtoms) { const cellAtom = catom.slot; @@ -4530,36 +4529,36 @@ registerRule(; winningDistance = dspotCenter; winningCellAtom = cellAtom; winningSide = "slot"; - }; + } const dspotLeft = Math.hypot(x - spotLeft[0], y - spotLeft[1]); if (!isCellAtomSpotFilled(paddle, spotLeft, true) && dspotLeft < winningDistance) { winningDistance = dspotLeft; winningCellAtom = cellAtom; winningSide = "left"; - }; + } const dspotAbove = Math.hypot(x - spotAbove[0], y - spotAbove[1]); if (!isCellAtomSpotFilled(paddle, spotAbove, true) && dspotAbove < winningDistance) { winningDistance = dspotAbove; winningCellAtom = cellAtom; winningSide = "above"; - }; + } const dspotRight = Math.hypot(x - spotRight[0], y - spotRight[1]); if (!isCellAtomSpotFilled(paddle, spotRight, true) && dspotRight < winningDistance) { winningDistance = dspotRight; winningCellAtom = cellAtom; winningSide = "right"; - }; + } const dspotBelow = Math.hypot(x - spotBelow[0], y - spotBelow[1]); if (!isCellAtomSpotFilled(paddle, spotBelow, true) && dspotBelow < winningDistance) { winningDistance = dspotBelow; winningCellAtom = cellAtom; winningSide = "below"; - }; - }; + } + } const {x: cx, y: cy} = getAtomPosition(winningCellAtom); @@ -4567,28 +4566,28 @@ registerRule(; if (winningSide === "left" || winningSide === "right") { atom.highlight.width = HIGHLIGHT_THICKNESS; atom.highlight.height = winningCellAtom.height; - }; + } else if (winningSide === "above" || winningSide === "below") { atom.highlight.width = winningCellAtom.width; atom.highlight.height = HIGHLIGHT_THICKNESS; - }; + } if (winningSide === "left") { atom.highlight.x = cx - HIGHLIGHT_THICKNESS/2; atom.highlight.y = cy; - }; + } else if (winningSide === "right") { atom.highlight.x = cx - HIGHLIGHT_THICKNESS/2 + winningCellAtom.width; atom.highlight.y = cy; - }; + } else if (winningSide === "above") { atom.highlight.x = cx; atom.highlight.y = cy - HIGHLIGHT_THICKNESS/2; - }; + } else if (winningSide === "below") { atom.highlight.x = cx; atom.highlight.y = cy - HIGHLIGHT_THICKNESS/2 + winningCellAtom.height; - }; + } if (winningSide === "slot") { atom.highlight.width = COLOURTODE_SQUARE.size; @@ -4598,14 +4597,14 @@ registerRule(; atom.highlight.y = cy; atom.highlight.hasBorder = true; atom.highlight.colour = Colour.Grey; - }; + } atom.highlightedAtom = winningCellAtom; atom.highlightedSide = winningSide; break; - }; + } /*; else if (paddle.rightTriangle !== undefined && left > pleft + paddle.rightTriangle.x) { @@ -4635,12 +4634,12 @@ registerRule(; break; }; - */; + */ else { let winningDistance = Infinity; - let winningSide = undefined; - let winningCellAtom = undefined; + let winningSide; + let winningCellAtom; for (const cellAtom of paddle.cellAtoms) { const {x: cx, y: cy} = getAtomPosition(cellAtom); @@ -4660,36 +4659,36 @@ registerRule(; winningDistance = dspotCenter; winningCellAtom = cellAtom; winningSide = "slot"; - }; + } const dspotLeft = Math.hypot(x - spotLeft[0], y - spotLeft[1]); if (!isCellAtomSpotFilled(paddle, spotLeft) && dspotLeft < winningDistance) { winningDistance = dspotLeft; winningCellAtom = cellAtom; winningSide = "left"; - }; + } const dspotAbove = Math.hypot(x - spotAbove[0], y - spotAbove[1]); if (!isCellAtomSpotFilled(paddle, spotAbove) && dspotAbove < winningDistance) { winningDistance = dspotAbove; winningCellAtom = cellAtom; winningSide = "above"; - }; + } const dspotRight = Math.hypot(x - spotRight[0], y - spotRight[1]); if (!isCellAtomSpotFilled(paddle, spotRight) && dspotRight < winningDistance) { winningDistance = dspotRight; winningCellAtom = cellAtom; winningSide = "right"; - }; + } const dspotBelow = Math.hypot(x - spotBelow[0], y - spotBelow[1]); if (!isCellAtomSpotFilled(paddle, spotBelow) && dspotBelow < winningDistance) { winningDistance = dspotBelow; winningCellAtom = cellAtom; winningSide = "below"; - }; - }; + } + } const {x: cx, y: cy} = getAtomPosition(winningCellAtom); @@ -4697,28 +4696,28 @@ registerRule(; if (winningSide === "left" || winningSide === "right") { atom.highlight.width = HIGHLIGHT_THICKNESS; atom.highlight.height = winningCellAtom.height; - }; + } else if (winningSide === "above" || winningSide === "below") { atom.highlight.width = winningCellAtom.width; atom.highlight.height = HIGHLIGHT_THICKNESS; - }; + } if (winningSide === "left") { atom.highlight.x = cx - HIGHLIGHT_THICKNESS/2; atom.highlight.y = cy; - }; + } else if (winningSide === "right") { atom.highlight.x = cx - HIGHLIGHT_THICKNESS/2 + winningCellAtom.width; atom.highlight.y = cy; - }; + } else if (winningSide === "above") { atom.highlight.x = cx; atom.highlight.y = cy - HIGHLIGHT_THICKNESS/2; - }; + } else if (winningSide === "below") { atom.highlight.x = cx; atom.highlight.y = cy - HIGHLIGHT_THICKNESS/2 + winningCellAtom.height; - }; + } if (winningSide === "slot") { atom.highlight.width = COLOURTODE_SQUARE.size; @@ -4728,24 +4727,24 @@ registerRule(; atom.highlight.y = cy; atom.highlight.hasBorder = true; atom.highlight.colour = Colour.Grey; - }; + } atom.highlightedAtom = winningCellAtom; atom.highlightedSide = winningSide; break; - }; + } - }; + } - }; + } if (atom.highlightedAtom === undefined && atom.highlight !== undefined) { deleteChild(atom, atom.highlight); atom.highlight = undefined; - }; + } }, @@ -4779,10 +4778,10 @@ registerRule(; atom.dx = 0; atom.dy = 0; giveChild(paddle, atom); - }; + } updatePaddleSize(paddle); - }; + } else if (atom.highlightedAtom.isSlot && atom.highlightedSide === "slot") { const slot = atom.highlightedAtom; const paddle = slot.parent; @@ -4797,7 +4796,7 @@ registerRule(; atom.slottee = true; updatePaddleSize(slot.parent); - }; + } else if (atom.highlightedAtom.isLeftSlot && atom.highlightedSide === "slot") { const slot = atom.highlightedAtom; const paddle = slot.parent; @@ -4812,12 +4811,12 @@ registerRule(; atom.slot = slot.slot; if (slot.slotted !== undefined) { slot.slotted.cellAtom = atom; - }; + } giveChild(paddle, atom); updatePaddleRule(paddle); deleteChild(paddle, slot); - }; + } else if (atom.highlightedAtom.isSlot && atom.highlightedSide !== "slot") { const slot = atom.highlightedAtom; const paddle = slot.parent; @@ -4836,7 +4835,7 @@ registerRule(; if (atom.expanded) { atom.unexpand(atom); - }; + } if (atom.highlightedSide === "left") { atom.x = slot.x - atom.width; @@ -4850,7 +4849,7 @@ registerRule(; } else if (atom.highlightedSide === "below") { atom.x = slot.x; atom.y = slot.y + slot.height; - }; + } dummy.x = atom.x - paddle.offset; dummy.y = atom.y; @@ -4860,7 +4859,7 @@ registerRule(; atom.dx = 0; atom.dy = 0; updatePaddleSize(paddle); - }; + } else if ((atom.highlightedAtom.isLeftSlot || atom.highlightedAtom.isSquare) && atom.highlightedAtom.parent.isPaddle) { const square = atom.highlightedAtom; const paddle = square.parent; @@ -4869,7 +4868,7 @@ registerRule(; paddle.cellAtoms.push(atom); if (atom.expanded) { atom.unexpand(atom); - }; + } if (atom.highlightedSide === "left") { atom.x = square.x - atom.width; @@ -4883,33 +4882,33 @@ registerRule(; } else if (atom.highlightedSide === "below") { atom.x = square.x; atom.y = square.y + square.height; - }; + } if (paddle.rightTriangle !== undefined && atom.slotted !== undefined) { registerAtom(atom.slotted); giveChild(paddle, atom.slotted); - }; + } atom.dx = 0; atom.dy = 0; updatePaddleSize(paddle); - }; + } else { const joinee = atom.highlightedAtom; const joiner = atom; if (joinee.expanded) { joinee.unexpand(joinee); - }; + } if (joiner.expanded) { joiner.unexpand(joiner); - }; + } if (joinee.joinExpanded) { joinee.joinUnepxand(joinee); - }; + } joinee.joins.push(joiner); deleteAtom(joiner); @@ -4923,17 +4922,17 @@ registerRule(; setBrushColour(joinee.value); - }; + } if (atom.expanded) { atom.unexpand(atom); - }; + } if (atom.joinExpanded) { atom.joinUnepxand(atom); - }; + } - }; + } }, joinExpand: (atom) => { @@ -4970,7 +4969,7 @@ registerRule(; joiner.isJoiner = true; joiner.touch = (atom) => atom.parent; //joiner.grab = (atom) => atom.parent; - }; + } atom.needsColoursUpdate = true; atom.colourTicker = Infinity; @@ -4978,8 +4977,8 @@ registerRule(; if (atom.multiAtoms !== undefined) { for (const multiAtom of atom.multiAtoms) { bringAtomToFront(multiAtom); - }; - }; + } + } atom.attached = false; @@ -4993,7 +4992,7 @@ registerRule(; for (let i = 0; i < atom.joins.length; i++) { const joiner = atom.joins[i]; deleteChild(atom, joiner); - }; + } atom.needsColoursUpdate = true; atom.colourTicker = Infinity; @@ -5029,7 +5028,7 @@ registerRule(; if (atom.joins.length > 0 && atom.joinExpanded) { return atom; - }; + } if (atom.isJoiner) { const id = atom.parent.joins.indexOf(atom); @@ -5038,11 +5037,11 @@ registerRule(; atom.parent.joinUnepxand(atom.parent); if (atom.parent.joins.length > 0) { atom.parent.joinExpand(atom.parent); - }; + } freeChild(atom.parent, atom); atom.isJoiner = false; atom.touch = COLOURTODE_SQUARE.touch; - }; + } if (atom.attached) { @@ -5058,11 +5057,11 @@ registerRule(; deleteChild(paddle, atom.cellAtom); const id = paddle.cellAtoms.indexOf(atom.cellAtom); paddle.cellAtoms.splice(id, 1); - }; + } atom.cellAtom = undefined; updatePaddleSize(paddle); return atom; - }; + } const {x, y} = atom; atom.attached = false; @@ -5083,11 +5082,11 @@ registerRule(; dummy.slotted = atom.slotted; dummy.slotted.cellAtom = dummy; atom.slotted = undefined; - }; + } //atom.slotted = undefined; updatePaddleSize(paddle); - }; + } return atom; }, @@ -5105,7 +5104,7 @@ registerRule(; const max = Math.max(width, height); const maxWidth = max / width; const maxHeight = max / height; - */; + */ const midWidth = maxWidth/2; const midHeight = maxHeight/2; @@ -5125,7 +5124,7 @@ registerRule(; const displacement = [px-x, py-y]; const distance = Math.hypot(...displacement); distances.push(distance); - }; + } return distances; }; @@ -5135,7 +5134,7 @@ registerRule(; //if (distance === min) scores.push(distance**2); //else scores.push(0.0); scores.push(distance**2); - }; + } return scores; }; @@ -5159,7 +5158,7 @@ registerRule(; const newLength = width * height * 4; if (mergedGradient.data.length !== newLength) { mergedGradient = new ImageData(width, height); - }; + } const count = gradients.length; const step = 2*Math.PI / count; @@ -5168,7 +5167,7 @@ registerRule(; const limits = gradients.map((gradient, i) => { let angle = i*step+step; /*while (angle < 0) angle += 2*Math.PI; - while (angle > 2*Math.PI) angle -= 2*Math.PI*/; + while (angle > 2*Math.PI) angle -= 2*Math.PI*/ return angle; }); @@ -5189,15 +5188,15 @@ registerRule(; if (id >= gradients.length) { id = 0; break; - }; - }; + } + } const diff = limits[id] - angle ; const prevId = (id-1 < 0)? limits.length-1 : id-1; const prevLimit = limits[prevId]; const prefDiff = prevLimit - angle; const nextId = (id+1 >= limits.length)? 0 : id+1; - let blendId = undefined; + let blendId; const pity = 0.05; if (Math.abs(prefDiff) < pity) { @@ -5212,7 +5211,7 @@ registerRule(; blend = true; blendScore = angle / pity / 2 + 0.5; blendId = prevId; - }; + } if (blend) { mergedGradient.data[i] = (gradients[id].data[i]*(blendScore) + gradients[blendId].data[i]*((1-blendScore))); mergedGradient.data[i+1] = (gradients[id].data[i+1]*(blendScore) + gradients[blendId].data[i+1]*((1-blendScore))); @@ -5223,11 +5222,11 @@ registerRule(; mergedGradient.data[i+1] = gradients[id].data[i+1]; mergedGradient.data[i+2] = gradients[id].data[i+2]; mergedGradient.data[i+3] = gradients[id].data[i+3]; - }; + } i += 4; - }; - }; + } + } return mergedGradient; }; @@ -5240,7 +5239,7 @@ registerRule(; const newLength = width * height * 4; if (gradient.data.length !== newLength) { gradient = new ImageData(width, height); - }; + } let minRed = Infinity; let maxRed = -Infinity; let minGreen = Infinity; @@ -5256,7 +5255,7 @@ registerRule(; if (g > maxGreen) maxGreen = g; if (b < minBlue) minBlue = b; if (b > maxBlue) maxBlue = b; - }; + } const makeGradientColour = (red, green, blue) => { @@ -5278,7 +5277,7 @@ registerRule(; makeGradientColour(0, 1, 0), makeGradientColour(1, 0, 0), - /*; + /* makeGradientColour(0, 1, 0), makeGradientColour(0, 1, 1), makeGradientColour(0, 0, 1), @@ -5290,7 +5289,7 @@ registerRule(; makeGradientColour(1, 0, 0), makeGradientColour(0, 0, 0), makeGradientColour(0, 0, 1), - */; + */ ]; @@ -5307,26 +5306,26 @@ registerRule(; const score = scores[j]; const colour = gradientColours[j]; [0, 1, 2].forEach(channel => sumValues[channel] += score * colour[channel]); - }; + } const values = sumValues.map(value => value / sumScore); if (stamp === "circle" && x >= width/4 && x < width*3/4 && y >= height/4 && y < height*3/4) { /*; gradient.data[i] = values[0] / 3*2; gradient.data[i+1] = values[1] / 3*2; gradient.data[i+2] = values[2] / 3*2; - */; + */ gradient.data[i+3] = 0; } else { gradient.data[i] = values[0]; gradient.data[i+1] = values[1]; gradient.data[i+2] = values[2]; gradient.data[i+3] = 255; - }; + } i += 4; if (i >= gradient.data.length) break; - }; - }; + } + } return gradient; }; @@ -5367,9 +5366,9 @@ registerRule(; if (atom.isTool) { //colourTodeContext.lineWidth = BORDER_THICKNESS*1.0; colourTodeContext.strokeStyle = toolBorderColours[atom.colour.splash]; - }; + } colourTodeContext.stroke(path); - }; + } }, overlaps: (atom, x, y) => { @@ -5439,7 +5438,7 @@ registerRule(; colourTodeContext.lineWidth = BORDER_THICKNESS*1.5; colourTodeContext.strokeStyle = atom.borderColour; colourTodeContext.stroke(path); - }; + } }, overlaps: (atom, x, y) => { @@ -5509,7 +5508,7 @@ registerRule(; colourTodeContext.lineWidth = BORDER_THICKNESS*1.5; colourTodeContext.strokeStyle = atom.borderColour; colourTodeContext.stroke(path); - }; + } }, overlaps: (atom, x, y) => { @@ -5588,9 +5587,9 @@ registerRule(; if (atom.isTool) { //colourTodeContext.lineWidth = BORDER_THICKNESS*1.0; colourTodeContext.strokeStyle = toolBorderColours[atom.colour.splash]; - }; + } colourTodeContext.stroke(path); - }; + } }, overlaps: (atom, x, y) => { @@ -5654,14 +5653,14 @@ registerRule(; atom.parent.pinhole.locked = !atom.parent.pinhole.locked; updatePaddleRule(atom.parent); return; - }; + } if (atom.expanded) { atom.unexpand(atom); - }; + } else { atom.expand(atom); - }; + } }, expand: (atom) => { @@ -5725,8 +5724,8 @@ registerRule(; // Return the highlight and the highlighted atom (the paddle); return paddle; - }; - }; + } + } // FIND A SQUARE TO STAMP????; if (true) { @@ -5753,12 +5752,12 @@ registerRule(; if (bottom < ptop) continue; return other; - }; + } // FIND A CHANNEL????; let winningDistance = Infinity; - let winningSquare = undefined; - let winningSlot = undefined; + let winningSquare; + let winningSlot; const atoms = getAllBaseAtoms(); for (const other of atoms) { @@ -5790,8 +5789,8 @@ registerRule(; winningDistance = distance; winningSlot = slot; winningSquare = other; - }; - }; + } + } if (winningSquare !== undefined) { @@ -5801,7 +5800,7 @@ registerRule(; if (atom.highlight !== undefined) { deleteChild(atom, atom.highlight); atom.highlight = undefined; - }; + } atom.highlight = createChild(atom, HIGHLIGHT, {bottom: true}); atom.highlight.hasBorder = true; @@ -5810,13 +5809,13 @@ registerRule(; atom.highlight.width = OPTION_MARGIN*2+winningSquare.size; atom.highlightedAtom = winningSquare; atom.highlightedSlot = winningSlot; - }; - }; + } + } return winningSquare; // return; - }; + } return undefined; }, @@ -5832,7 +5831,7 @@ registerRule(; if (atom.highlightedSlot === "red") atom.variable = "green"; else if (atom.highlightedSlot === "green") atom.variable = "blue"; else if (atom.highlightedSlot === "blue") atom.variable = "red"; - }; + } const add = atom.direction === "up" ? makeNumberFromInt(1) : undefined; const subtract = atom.direction === "down" ? makeNumberFromInt(1) : undefined; const value = makeNumber({channel: atom.channelId, variable: atom.variable, add, subtract}); @@ -5851,7 +5850,7 @@ registerRule(; atom.dx = 0; atom.dy = 0; return; - }; + } if (receiver.isSquare) { const square = receiver; @@ -5864,7 +5863,7 @@ registerRule(; square.stamp = undefined; square.value.stamp = undefined; square.needsColoursUpdate = true; - }; + } const diagramCell = makeDiagramCell({content: square.value}); state.brush.colour = makeDiagram({left: [diagramCell]}); @@ -5877,9 +5876,9 @@ registerRule(; if (square.parent.isPaddle) { updatePaddleRule(square.parent); - }; + } return; - }; + } if (receiver.isPaddle) { const paddle = receiver; @@ -5897,19 +5896,19 @@ registerRule(; if (cellAtom.slotted !== undefined) { registerAtom(cellAtom.slotted); giveChild(paddle, cellAtom.slotted); - }; - }; + } + } updatePaddleSize(paddle); if (atom.expanded) { atom.unexpand(atom); - }; + } atom.attached = true; unlockMenuTool("circle"); - }; + } }, @@ -5935,7 +5934,7 @@ registerRule(; freeChild(square, atom); square.receiveNumber(square, undefined, atom.channelId); return atom; - }; + } if (!atom.parent.isPaddle) return atom; const paddle = atom.parent; @@ -5966,13 +5965,13 @@ registerRule(; cellAtom.slotted.slottee = false; //deleteAtom(cellAtom.slotted); cellAtom.slotted = undefined; - }; - }; + } + } if (atom.colour !== Colour.splash(999)) { atom.hasBorder = true; atom.borderColour = Colour.Grey; - }; + } paddle.pinhole.locked = false; @@ -6041,7 +6040,7 @@ registerRule(; if (atom.expanded) { clone.createOptions(clone); clone.expanded = true; - }; + } return clone; }, @@ -6060,7 +6059,7 @@ registerRule(; atom.colourTicker = Infinity; // unlockMenuTool("wide_rectangle"); - }; + } else if (atom.parent.isTallRectangle) { const diamond = atom.parent; @@ -6078,15 +6077,15 @@ registerRule(; deleteChild(diamond, diamond[atom.highlightedSlot], {quiet: true}); diamond.expand(diamond); diamond.unexpand(diamond); - }; - }; + } + } else if (atom.parent.isPaddle) { const paddle = atom.parent; paddle.chance = undefined; freeChild(paddle, atom); updatePaddleSize(paddle); - }; + } return atom; }, @@ -6125,7 +6124,7 @@ registerRule(; atom.selectionBottom.y = atom.height; //atom.selectionBottom.x = -atom.selectionBottom.height; - }; + } else { //const optionSpacing = (atom.height + (COLOURTODE_SQUARE.size - CHANNEL_HEIGHT)/2); @@ -6139,7 +6138,7 @@ registerRule(; atom.selectionBottom.minY = atom.selectionTop.y + optionSpacing; atom.selectionBottom.maxY = bottom - atom.selectionBottom.height + optionSpacing; - }; + } atom.positionSelectionBack(atom); @@ -6155,7 +6154,7 @@ registerRule(; if (atom.parent.isTallRectangle) { const operationName = atom.highlightedSlot === "padTop"? "add" : "subtract"; atom.parent.value[operationName] = atom.value; - }; + } }, @@ -6176,11 +6175,11 @@ registerRule(; colours: atom.colours, width: atom.width * CT_SCALE, height: atom.height * CT_SCALE, - gradient: atom.gradient; + gradient: atom.gradient }); atom.updateColours(atom); - }; - }; + } + } if (!atom.expanded && atom.needsColoursUpdate) { atom.needsColoursUpdate = false; @@ -6192,20 +6191,20 @@ registerRule(; for (let i = 0; i < 3; i++) { if (i === atom.value.channel) { channels[i] = atom.value; - }; + } else { const values = [true, false, false, false, false, false, false, false, false, false]; channels[i] = makeNumber({values, channel: i}); - }; - }; + } + } const array = makeArray({channels}); atom.colours = getSplashesArrayFromArray(array); - }; + } else { - let array = undefined; + let array; for (let i = 0; i < 10; i++) { const v = atom.value.values[i]; @@ -6215,20 +6214,20 @@ registerRule(; array = join; } else { array.joins.push(join); - }; - }; + } + } atom.colours = getSplashesArrayFromArray(array); - }; + } atom.isGradient = true; atom.gradient = getGradientImageFromColours({ colours: atom.colours, width: atom.width * CT_SCALE, height: atom.height * CT_SCALE, - gradient: atom.gradient; + gradient: atom.gradient }); - }; + } atom.highlightedAtom = undefined; if (hand.content === atom && hand.state === HAND.DRAGGING) { @@ -6242,11 +6241,11 @@ registerRule(; if (atom.highlight !== undefined) { deleteChild(atom, atom.highlight); atom.highlight = undefined; - }; + } let winningDistance = Infinity; - let winningSquare = undefined; - let winningSlot = undefined; + let winningSquare; + let winningSlot; const atoms = getAllBaseAtoms(); for (const square of atoms) { @@ -6261,7 +6260,7 @@ registerRule(; while (endAtom.isTallRectangle && endAtom.operationAtoms[slotName] !== undefined) { endAtom = endAtom.operationAtoms[slotName]; - }; + } if (!endAtom.isTallRectangle) continue; if (!endAtom.expanded) continue; @@ -6282,7 +6281,7 @@ registerRule(; atom.highlightedAtom = slot; break; - }; + } } else { if (!square.isSquare) continue; @@ -6314,11 +6313,11 @@ registerRule(; winningDistance = distance; winningSquare = square; winningSlot = slot; - }; - }; - }; + } + } + } - }; + } if (winningSquare !== undefined) { @@ -6369,15 +6368,15 @@ registerRule(; return; }; }; - */; - }; + */ + } - }; + } if (atom.highlightedAtom === undefined && atom.highlight !== undefined) { deleteChild(atom, atom.highlight); atom.highlight = undefined; - }; + } /*; if (atom.highlightPaddle !== undefined) { @@ -6385,7 +6384,7 @@ registerRule(; atom.highlightPaddle = undefined; atom.highlightedPaddle = undefined; }; - */; + */ }, @@ -6401,7 +6400,7 @@ registerRule(; atom.dy = 0; square[atom.highlightedSlot] = atom; atom.y = OPTION_MARGIN; - atom.x = square.size + OPTION_MARGIN*2 + slotId*(OPTION_MARGIN*square.size)*/; + atom.x = square.size + OPTION_MARGIN*2 + slotId*(OPTION_MARGIN*square.size)*/ square.receiveNumber(square, atom.value, slotId, {expanded: atom.expanded}); deleteAtom(atom); } else { @@ -6420,7 +6419,7 @@ registerRule(; atom.attached = true; - }; + } } else if (atom.highlightPaddle !== undefined) { const paddle = atom.highlightedPaddle; atom.attached = true; @@ -6431,7 +6430,7 @@ registerRule(; atom.dx = 0; atom.dy = 0; - }; + } // unlockMenuTool("hexagon"); //unlockMenuTool("tall_rectangle"); @@ -6445,32 +6444,32 @@ registerRule(; atom.colourTicker = Infinity; atom.needsColoursUpdate = true; atom.createOptions(atom); - }; + } else { atom.expanded = false; atom.deleteOptions(atom); atom.needsColoursUpdate = true; - }; + } }, deleteOptions: (atom) => { if (atom.options !== undefined) { atom.deletedOptions = atom.options; - }; + } for (const option of atom.options) { if (atom !== option) deleteChild(atom, option); - }; + } atom.needsColoursUpdate = true; atom.colourTicker = Infinity; atom.positionSelection(atom); }, updateColours: (atom) => { - let parentR = undefined; - let parentG = undefined; - let parentB = undefined; + let parentR; + let parentG; + let parentB; if (atom.parent.isSquare) { @@ -6485,13 +6484,13 @@ registerRule(; parentR = makeNumber({values: [...redNumber.values], channel: redNumber.channel}); parentG = makeNumber({values: [...greenNumber.values], channel: greenNumber.channel}); parentB = makeNumber({values: [...blueNumber.values], channel: blueNumber.channel}); - }; + } else { const values = [false, false, false, false, false, false, false, false, false, false]; parentR = makeNumber({values: [...values], channel: 0}); parentG = makeNumber({values: [...values], channel: 1}); parentB = makeNumber({values: [...values], channel: 2}); - }; + } const parentChannels = [parentR, parentG, parentB]; const mainParentChannel = parentChannels[CHANNEL_IDS[atom.channelSlot]]; @@ -6509,8 +6508,8 @@ registerRule(; for (const c of parentChannels) { c.values[9-i] = true; if (i > 0) c.values[9-i+1] = false; - }; - }; + } + } const baseArray = makeArray({channels: parentChannels}); @@ -6523,36 +6522,36 @@ registerRule(; option.needsColoursUpdate = false; //option.needsColoursUpdate = true; //option.updateColours(option); - }; - }; - }; + } + } + } }, getCenterId: (atom) => { - let startId = undefined; - let endId = undefined; + let startId; + let endId; for (let i = 0; i < atom.value.values.length; i++) { const value = atom.value.values[i]; if (value) { if (startId === undefined) startId = i; endId = i; - }; - }; + } + } return Math.round((endId + startId) / 2); }, getStartAndEndId: (atom) => { - let startId = undefined; - let endId = undefined; + let startId; + let endId; for (let i = 0; i < atom.value.values.length; i++) { const value = atom.value.values[i]; if (value) { if (startId === undefined) startId = i; endId = i; - }; - }; + } + } return [startId, endId]; }, @@ -6561,16 +6560,16 @@ registerRule(; const oldOptions = atom.parent.isSquare ? atom.deletedOptions : undefined; atom.options = []; - let startId = undefined; - let endId = undefined; + let startId; + let endId; for (let i = 0; i < atom.value.values.length; i++) { const value = atom.value.values[i]; if (value) { if (startId === undefined) startId = i; endId = i; - }; - }; + } + } if (startId === undefined) throw new Error("[ColourTode] Number cannot be NOTHING. Please let @TodePond know if you see this error!"); //const centerOptionId = 9 - Math.floor((endId + startId) / 2); @@ -6589,10 +6588,10 @@ registerRule(; if (oldOptions !== undefined) { //atom.isGradient = true; //atom.gradient = oldOptions[i].gradient; - }; + } atom.options.push(atom); continue; - }; + } const pityTop = i !== 9 - endId + 1; const pityBottom = i !== 9 - startId - 1; @@ -6601,7 +6600,7 @@ registerRule(; if (oldOptions !== undefined) { option.isGradient = true; option.gradient = oldOptions[i].gradient; - }; + } option.y = top + i * optionSpacing; option.value = 9 - i; @@ -6611,13 +6610,13 @@ registerRule(; option.needsColoursUpdate = true; //option.updateColours(option); atom.options.push(option); - }; + } atom.positionSelection(atom, start, end, top, bottom); atom.updateColours(atom); - }; + } }; // Ctrl+F: exdef; @@ -6652,7 +6651,7 @@ registerRule(; const next = points[nextId]; const mid = [0, 1].map(axis => (point[axis] + next[axis])/2); extraSegmentCorners.push(mid); - }; + } const center = [x+width/2, y+height/2]; const segmentPoints = points.map((p, i) => { @@ -6671,7 +6670,7 @@ registerRule(; path.moveTo(...head); for (const point of tail) { path.lineTo(...point); - }; + } path.closePath(); colourTodeContext.fillStyle = atom.colour; @@ -6685,15 +6684,15 @@ registerRule(; spath.moveTo(...shead); for (const point of stail) { spath.lineTo(...point); - }; + } spath.closePath(); colourTodeContext.fillStyle = Colour.Silver; colourTodeContext.fill(spath); colourTodeContext.lineWidth = 1 / CT_SCALE; colourTodeContext.strokeStyle = Colour.Silver; colourTodeContext.stroke(spath); - }; - }; + } + } if (atom.hasBorder) { colourTodeContext.lineWidth = BORDER_THICKNESS*1.5; @@ -6702,14 +6701,14 @@ registerRule(; if (atom.parent.isSquare) { SYMMETRY_TOGGLE_Y.drawY(atom, atom.size - 8, 4); - }; - }; + } + } }, getValue: (atom) => { let score = 0; for (const on of atom.ons) { if (on) score++; - }; + } return score; }, click: (atom) => { @@ -6717,16 +6716,16 @@ registerRule(; atom.unexpand(atom); } else { atom.expand(atom); - }; + } }, unexpand: (atom) => { atom.expanded = false; for (const thing of atom.handles) { deleteChild(atom, thing); - }; + } for (const thing of atom.buttons) { deleteChild(atom, thing); - }; + } atom.handles = []; atom.buttons = []; @@ -6765,7 +6764,7 @@ registerRule(; [sx, sy] = [tx * 2.2, ty * 2.2]; } else { [sx, sy] = [tx * 2, ty * 2]; - }; + } return [sx + atom.width/2, sy + atom.height/2]; }); @@ -6785,9 +6784,9 @@ registerRule(; if (atom.ons[i]) { button.inner.selected = true; button.inner.colour = Colour.Silver; - }; + } - }; + } }, construct: (atom) => { atom.ons = [false, false, false, false, false, false]; @@ -6822,7 +6821,7 @@ registerRule(; if (paddle.chance === undefined && paddle.expanded && left <= pright && right >= pright && ((top < pbottom && top > ptop) || (bottom > ptop && bottom < pbottom))) { if (atom.highlightPaddle !== undefined) { deleteChild(atom, atom.highlightPaddle); - }; + } atom.highlight = createChild(atom, HIGHLIGHT, {bottom: true}); atom.highlight.width = HIGHLIGHT_THICKNESS; @@ -6830,8 +6829,8 @@ registerRule(; atom.highlight.y = ptop; atom.highlight.x = pright - HIGHLIGHT_THICKNESS/2; return paddle; - }; - }; + } + } // let winningDistance = Infinity; // let winningSquare = undefined; @@ -6897,7 +6896,7 @@ registerRule(; atom.dx = 0; atom.dy = 0; - } ; + } // else if (paddle.isSquare) { // const square = paddle; // atom.variable = atom.highlightedSlot; @@ -6922,7 +6921,7 @@ registerRule(; square.receiveNumber(square, undefined, channelId); freeChild(square, atom); atom.attached = false; - }; + } return atom; }, @@ -6938,15 +6937,15 @@ registerRule(; const clone = makeAtom(COLOURTODE_HEXAGON); for (let i = 0; i < 6; i++) { clone.ons[i] = atom.ons[i]; - }; + } if (atom.expanded) { clone.expand(clone); - }; + } const {x, y} = getAtomPosition(atom); clone.x = x; clone.y = y; return clone; - }; + } }; const rotate = ([x, y], [ox, oy], radians) => { @@ -6982,7 +6981,7 @@ registerRule(; } else { atom.inner.selected = true; atom.inner.colour = Colour.Silver; - }; + } const hexagon = atom.parent; hexagon.ons[atom.id] = atom.inner.selected; @@ -6995,10 +6994,10 @@ registerRule(; hexagon.updateValue(hexagon); const slotId = CHANNEL_IDS[hexagon.variable]; square.receiveNumber(square, hexagon.value, slotId, {expanded: hexagon.expanded, numberAtom: hexagon}); - }; + } bringAtomToFront(atom.parent); - }; + } }; const HEXAGON_BUTTON_INNER = { @@ -7045,18 +7044,18 @@ registerRule(; if (atom.rotation > 0) { points = points.map(point => rotate(point, [x+width/2, y+height/2], atom.rotation * Math.PI/3)); - }; + } const [head, ...tail] = points; path.moveTo(...head); for (const point of tail) { path.lineTo(...point); - }; + } colourTodeContext.fillStyle = atom.colour; colourTodeContext.fill(path); - }; + } }; const COLOURTODE_CHANNEL_SELECTION_END = { @@ -7069,7 +7068,7 @@ registerRule(; const colours = getSplashesArrayFromArray(atom.parent.parent.value); colour = colours[Random.Uint32 % colours.length]; }; - }*/; + }*/ /*colourTodeContext.fillStyle = "#000000"; colourTodeContext.globalCompositeOperation = "lighten"; @@ -7084,7 +7083,7 @@ registerRule(; const H = Math.round(atom.height); colourTodeContext.drawImage(colourTodeCanvas, X, Y, W, H, X, Y, W, H); - colourTodeContext.filter = "none"*/; + colourTodeContext.filter = "none"*/ const X = Math.round(x); const Y = Math.round(y); @@ -7120,15 +7119,15 @@ registerRule(; if (atom.isTop) { endId = centerId - distanceFromMiddle; - }; + } if (!atom.isTop) { startId = centerId - (distanceFromMiddle-1); - }; + } const values = [false, false, false, false, false, false, false, false, false, false]; for (let i = startId; i <= endId; i++) { values[i] = true; - }; + } const number = makeNumber({channel: oldNumber.channel, values}); atom.parent.value = number; @@ -7142,12 +7141,12 @@ registerRule(; const square = atom.parent.parent; const channel = CHANNEL_IDS[atom.parent.channelSlot]; square.receiveNumber(square, number, channel); - }; + } if (atom.parent.parent.isPaddle) { const paddle = atom.parent.parent; updatePaddleSize(paddle); - }; + } }, dragLockX: true, @@ -7185,17 +7184,17 @@ registerRule(; atom.needsColoursUpdateCountdown--; if (atom.needsColoursUpdateCountdown < 0) { atom.needsColoursUpdate = true; - }; - }; + } + } if (atom.needsColoursUpdate) { atom.updateColours(atom); atom.needsColoursUpdateCountdown = -1; atom.needsColoursUpdate = false; - }; + } }, - getId: (atom) => { ; + getId: (atom) => { const parent = atom.parent; const centerId = parent.getCenterId(parent); const offset = atom.y / OPTION_SPACING; @@ -7208,7 +7207,7 @@ registerRule(; colours: atom.colours, width: atom.width * CT_SCALE, height: atom.height * CT_SCALE, - gradient: atom.gradient; + gradient: atom.gradient }); }, @@ -7233,12 +7232,12 @@ registerRule(; const square = parent.parent; const channel = CHANNEL_IDS[parent.channelSlot]; square.receiveNumber(square, number, channel); - }; + } if (parent.parent.isPaddle) { const paddle = parent.parent; updatePaddleSize(paddle); - }; + } }, construct: (atom) => { @@ -7246,15 +7245,15 @@ registerRule(; if (atom.pityTop) { const topPity = createChild(atom, COLOURTODE_OPTION_PADDING); topPity.y = -topPity.height; - }; + } if (atom.pityBottom) { const bottomPity = createChild(atom, COLOURTODE_OPTION_PADDING); bottomPity.y = atom.height; - }; + } //TODO: add cursor pity on the sides too; - }; + } }; const CHANNEL_VARIABLES = [ @@ -7278,7 +7277,7 @@ registerRule(; clone.variable = atom.variable; if (atom.expanded) { clone.expand(clone); - }; + } clone.updateAppearance(clone); return clone; }, @@ -7300,7 +7299,7 @@ registerRule(; if (atom.expanded) { atom.unexpand(atom); atom.expand(atom); - }; + } atom.attached = false; if (diamond.expanded) { diamond.unexpand(diamond); @@ -7311,8 +7310,8 @@ registerRule(; deleteChild(diamond, diamond[atom.highlightedSlot], {quiet: true}); diamond.expand(diamond); diamond.unexpand(diamond); - }; - }; + } + } return atom; }, hover: (atom) => { @@ -7324,8 +7323,8 @@ registerRule(; const bottom = y + atom.height; let winningDistance = Infinity; - let winningSquare = undefined; - let winningSlot = undefined; + let winningSquare; + let winningSlot; const atoms = getAllBaseAtoms(); for (const other of atoms) { @@ -7340,7 +7339,7 @@ registerRule(; while (endAtom.isTallRectangle && endAtom.operationAtoms[slotName] !== undefined) { endAtom = endAtom.operationAtoms[slotName]; - }; + } if (!endAtom.isTallRectangle) continue; if (!endAtom.expanded) continue; @@ -7360,9 +7359,9 @@ registerRule(; atom.highlightedSlot = slotName; return slot; - }; + } continue; - }; + } if (!other.isSquare) continue; if (!other.expanded) continue; @@ -7391,8 +7390,8 @@ registerRule(; winningDistance = distance; winningSlot = slot; winningSquare = other; - }; - }; + } + } if (winningSquare !== undefined) { @@ -7406,10 +7405,10 @@ registerRule(; atom.highlight.width = OPTION_MARGIN*2+winningSquare.size; atom.highlightedAtom = winningSquare; atom.highlightedSlot = winningSlot; - }; + } return; - }; + } }, place: (atom, highlightedAtom) => { @@ -7432,10 +7431,10 @@ registerRule(; if (atom.expanded) { atom.unexpand(atom); atom.expand(atom); - }; + } return; - }; + } const square = atom.highlightedAtom; const slotId = CHANNEL_IDS[atom.highlightedSlot]; @@ -7477,9 +7476,9 @@ registerRule(; if (atom.isTool) { colourTodeContext.lineWidth = BORDER_THICKNESS*1.5; colourTodeContext.strokeStyle = toolBorderColours[atom.colour.splash]; - }; + } colourTodeContext.stroke(path); - }; + } }, offscreen: COLOURTODE_RECTANGLE.offscreen, overlaps: COLOURTODE_RECTANGLE.overlaps, @@ -7496,7 +7495,7 @@ registerRule(; atom.width += BORDER_THICKNESS/2; atom.height += BORDER_THICKNESS/2; atom.size += BORDER_THICKNESS/2; - }; + } atom.operationAtoms = {padTop: undefined, padBottom: undefined}; }, @@ -7521,9 +7520,9 @@ registerRule(; operationAtom.y = atom.padTop.y + atom.padTop.height/2 - operationAtom.height/2; operationAtom.updateAppearance(operationAtom); atom.operationAtoms.padTop = operationAtom; - }; - }; - }; + } + } + } if (atom.value.subtract !== undefined) { @@ -7537,9 +7536,9 @@ registerRule(; operationAtom.highlightedSlot = "padBottom"; } else { - }; - }; - }; + } + } + } }, updateAppearance: (atom) => { if (atom.variable === "red") { @@ -7548,7 +7547,7 @@ registerRule(; atom.colour = Colour.Green; } else if (atom.variable === "blue") { atom.colour = Colour.Blue; - }; + } atom.borderColour = borderColours[atom.colour.splash]; }, @@ -7558,7 +7557,7 @@ registerRule(; atom.expand(atom); } else { atom.unexpand(atom); - }; + } }, expand: (atom) => { atom.expanded = true; @@ -7577,8 +7576,8 @@ registerRule(; atom.padTop.width = COLOURTODE_SQUARE.size + COLOURTODE_PICKER_PAD_MARGIN*2; atom.padTop.x = atom.width/2 - atom.padTop.width/2; atom.padTop.y = -atom.padTop.height - OPTION_MARGIN; - }; - }; + } + } if (atom.value.subtract === undefined) { if (atom.y > 0 || !(atom.parent.isTallRectangle && atom.parent.operationAtoms.padTop === atom)) { @@ -7594,8 +7593,8 @@ registerRule(; atom.padBottom.width = COLOURTODE_SQUARE.size + COLOURTODE_PICKER_PAD_MARGIN*2; atom.padBottom.x = atom.width/2 - atom.padBottom.width/2; atom.padBottom.y = atom.height + OPTION_MARGIN; - }; - }; + } + } atom.handleRight = createChild(atom, SYMMETRY_HANDLE); atom.handleRight.y = atom.height/2 - atom.handleRight.height/2; @@ -7618,7 +7617,7 @@ registerRule(; atom.padLeft.height = COLOURTODE_PICKER_PAD.height; atom.padLeft.width = OPTION_MARGIN + (atom.width+OPTION_MARGIN/1.5)*3; atom.padLeft.y = atom.height/2 - atom.padRight.height/2; - atom.padLeft.x = -atom.padLeft.width - OPTION_MARGIN*/; + atom.padLeft.x = -atom.padLeft.width - OPTION_MARGIN*/ atom.red = createChild(atom, DIAMOND_CHOICE); atom.red.x = atom.padRight.x + OPTION_MARGIN/Math.SQRT2; @@ -7649,15 +7648,15 @@ registerRule(; if (operationAtom === undefined) continue; registerAtom(operationAtom); giveChild(atom, operationAtom); - }; + } for (const child of atom.children) { if (!child.isTallRectangle) continue; if (child.expanded) { child.unexpand(child); child.expand(child); - }; - }; + } + } }, unexpand: (atom) => { @@ -7674,24 +7673,24 @@ registerRule(; if (atom.value.add === undefined) { deleteChild(atom, atom.padTop, {quiet: true}); deleteChild(atom, atom.handleTop, {quiet: true}); - }; + } if (atom.value.subtract === undefined) { deleteChild(atom, atom.padBottom, {quiet: true}); deleteChild(atom, atom.handleBottom, {quiet: true}); - }; + } /*deleteChild(atom, atom.padLeft); - deleteChild(atom, atom.handleLeft)*/; + deleteChild(atom, atom.handleLeft)*/ /*for (const opera; tion of ["padTop", "padBottom"]) { const operationAtom = atom.operationAtoms[operation]; if (operationAtom === undefined) continue; deleteChild(atom, operationAtom); - }*/; + }*/ - }; + } }; const DIAMOND_CHOICE = { @@ -7724,7 +7723,7 @@ registerRule(; if (top === COLOURTODE_BASE_PARENT) return; topDiamond = top; top = top.parent; - }; + } let channelNumber = 0; if (topDiamond.channelSlot === "green") channelNumber = 1; @@ -7733,7 +7732,7 @@ registerRule(; const topChannel = top.variableAtoms[channelNumber]; top.receiveNumber(top, topChannel.value, channelNumber, {expanded: topChannel.expanded, numberAtom: topChannel}); - }; + } }; const DIAMOND_PIN = { @@ -7827,7 +7826,7 @@ registerRule(; if (paddles.last === paddle) { createPaddle(); - }; + } } else { paddle.x = paddle.minX; @@ -7836,8 +7835,8 @@ registerRule(; if (paddles.last !== paddle) { deletePaddle(paddle); - }; - }; + } + } paddle.dx = 0; }, @@ -7861,7 +7860,7 @@ registerRule(; state.brush.colour = makeDiagram({left: [makeDiagramCell({content: diagram})]}); square.update(square); return square; - }; + } return paddle; }, @@ -7879,7 +7878,7 @@ registerRule(; } else if (cellAtoms.length === 1) { const leftClone = cloneDragonArray(cellAtoms[0].value); return leftClone; - }; + } const cells = makeDiagramCellsFromCellAtoms(cellAtoms); const diagram = makeDiagram({left: cells}); normaliseDiagram(diagram); @@ -7912,7 +7911,7 @@ registerRule(; square.value = leftClone; square.update(square); return square; - }; + } const square = makeAtom(COLOURTODE_SQUARE); hand.offset.x = -square.width/2; hand.offset.y = -square.height/2; @@ -7935,7 +7934,7 @@ registerRule(; path.moveTo(...head.map(n => Math.round(n))); for (const point of tail) { path.lineTo(...point.map(n => Math.round(n))); - }; + } path.closePath(); colourTodeContext.fillStyle = colour; @@ -7956,10 +7955,10 @@ registerRule(; const [x, y] = getAtomPosition(atom); - const left = x; - const right = x + atom.width; - const top = y; - const bottom = y + atom.height; + + + + /*const swidth = atom.width/10; const sheight = atom.height/10; @@ -7999,12 +7998,12 @@ registerRule(; for (const stripe of stripes) { fillPoints(Colour.Black, stripe); - }*/; + }*/ colourTodeContext.fillStyle = atom.colour; /*colourTodeContext.beginPath(); colourTodeContext.arc(x + atom.width/2, y+atom.height/2, atom.width / 5, 0, 2*Math.PI); - colourTodeContext.fill()*/; + colourTodeContext.fill()*/ const w = atom.width/3; const h = atom.width/3; @@ -8046,7 +8045,7 @@ registerRule(; if (cright > right) right = cright; if (ctop < top) top = ctop; if (cbottom > bottom) bottom = cbottom; - }; + } let topOffset = 0; let leftOffset = 0; @@ -8060,16 +8059,16 @@ registerRule(; if (top !== desiredTop) { topOffset = desiredTop - top; bottom += topOffset; - }; + } if (left !== desiredLeft) { leftOffset = desiredLeft - left; right += leftOffset; - }; + } for (const cellAtom of paddle.cellAtoms) { cellAtom.y += topOffset; cellAtom.x += leftOffset; - }; + } const desiredWidth = right + xPadding; const desiredHeight = bottom + yPadding; @@ -8077,17 +8076,17 @@ registerRule(; width = desiredWidth; height = desiredHeight; - }; + } if (paddle.rightTriangle !== undefined) { paddle.rightTriangle.x = width; paddle.rightTriangle.y = height/2 - paddle.rightTriangle.height/2; width = width+width + paddle.rightTriangle.width; - }; + } if (paddle.hasSymmetry || paddle.chance !== undefined) { width += SYMMETRY_CIRCLE.size/3; - }; + } paddle.width = width; paddle.height = height; @@ -8098,7 +8097,7 @@ registerRule(; //=============================//; for (const slot of paddle.slots) { deleteChild(paddle, slot); - }; + } paddle.slots = []; if (paddle.rightTriangle !== undefined) { @@ -8115,10 +8114,10 @@ registerRule(; cellAtom.slotted.x = cellAtom.x + paddle.rightTriangle.x + paddle.rightTriangle.width; cellAtom.slotted.y = cellAtom.y; slot.colour = Colour.Grey; - }; + } - }; - }; + } + } if (paddle.rightTriangle !== undefined) { @@ -8126,18 +8125,18 @@ registerRule(; paddle.offset = paddle.cellAtoms[0].slot.x - paddle.cellAtoms[0].x; } else { paddle.offset = 0; - }; - }; + } + } if (paddle.symmetryCircle !== undefined) { paddle.symmetryCircle.x = paddle.width - paddle.symmetryCircle.width/2; paddle.symmetryCircle.y = paddle.height/2 - paddle.symmetryCircle.height/2; - }; + } if (paddle.chance !== undefined) { paddle.chance.x = paddle.width - paddle.chance.width/2; paddle.chance.y = paddle.height/2 - paddle.chance.height/2; - }; + } if (paddle.chance !== undefined && paddle.symmetryCircle !== undefined) { paddle.symmetryCircle.y -= paddle.symmetryCircle.height/2; @@ -8145,8 +8144,8 @@ registerRule(; if (paddle.height > 100) { paddle.symmetryCircle.y -= OPTION_MARGIN/2; paddle.chance.y += OPTION_MARGIN/2; - }; - }; + } + } paddle.handle.y = paddle.height/2 - paddle.handle.height/2; @@ -8156,7 +8155,7 @@ registerRule(; paddle.dummyRight.x = paddle.width - PADDLE_MARGIN - paddle.dummyLeft.width; paddle.dummyRight.y = paddle.height/2 - paddle.dummyRight.height/2; - }; + } updatePaddleRule(paddle); positionPaddles(); @@ -8176,7 +8175,7 @@ registerRule(; if (achannel !== undefined && bchannel === undefined) return false; if (achannel === undefined && bchannel === undefined) continue; if (achannel.variable !== bchannel.variable) return false; - }; + } const asplashes = getSplashesArrayFromArray(a); const bsplashes = getSplashesArrayFromArray(b); @@ -8185,7 +8184,7 @@ registerRule(; const id = bsplashes.indexOf(asplash); if (id === -1) return false; bsplashes.splice(id, 1); - }; + } if (bsplashes.length > 0) return false; @@ -8194,29 +8193,29 @@ registerRule(; }; const applyRangeStamp = (stampeds, value) => { - if (value.stamp) return //already got a manual stamp; + if (value.stamp) return; //already got a manual stamp; const isSingle = isDragonArraySingleColour(value); if (!isSingle) { - let newStamp = undefined; + let newStamp; for (let i = 0; i < stampeds.length; i++) { const stamped = stampeds[i]; if (isDragonArrayEqual(stamped, value)) { newStamp = i; break; - }; - }; + } + } if (newStamp === undefined) { newStamp = stampeds.length; stampeds.push(value); - }; + } value.stamp = newStamp.toString(); - }; + } }; const getTopLeftOfCellAtoms = (cellAtoms) => { let smallestX = Infinity; let smallestY = Infinity; - let leader = undefined; + let leader; for (const cellAtom of cellAtoms) { if (cellAtom.x <= smallestX) { @@ -8224,9 +8223,9 @@ registerRule(; leader = cellAtom; smallestX = cellAtom.x; smallestY = cellAtom.y; - }; - }; - }; + } + } + } return leader; }; @@ -8252,11 +8251,11 @@ registerRule(; const x = (cellAtom.x - origin.x) / cellAtom.width; const y = (cellAtom.y - origin.y) / cellAtom.height; - const leftClone = cloneDragonArray(cellAtom.value) //TODO: should act different for multis; + const leftClone = cloneDragonArray(cellAtom.value); //TODO: should act different for multis const diagramCell = makeDiagramCell({x, y, content: leftClone}); diagramCells.push(diagramCell); - }; + } return diagramCells; @@ -8271,8 +8270,8 @@ registerRule(; paddle.rightTriangle.colour = Colour.splash(999); } else { paddle.rightTriangle.colour = Colour.splash(0); - }; - }; + } + } let transformations = DRAGON_TRANSFORMATIONS.NONE; if (paddle.hasSymmetry) { @@ -8287,7 +8286,7 @@ registerRule(; else if (key === "XR" || key === "YR") key = "XYR"; transformations = DRAGON_TRANSFORMATIONS[key]; - }; + } const orderedCellAtoms = getOrderedCellAtoms(paddle.cellAtoms); const origin = orderedCellAtoms[0]; @@ -8328,7 +8327,7 @@ registerRule(; content: miniClone, }); left.push(diagramCell); - }; + } } else { @@ -8337,7 +8336,7 @@ registerRule(; applyRangeStamp(stampeds, leftClone); const diagramCell = makeDiagramCell({x, y, content: leftClone}); left.push(diagramCell); - }; + } //=======//; // RIGHT //; @@ -8357,7 +8356,7 @@ registerRule(; }); right.push(mergeCell); - }; + } const rightContent = cellAtom.slotted === undefined? undefined : cellAtom.slotted.value; @@ -8402,7 +8401,7 @@ registerRule(; instruction: DRAGON_INSTRUCTION.recolour, }); right.push(diagramCell); - }; + } } else { @@ -8411,8 +8410,8 @@ registerRule(; applyRangeStamp(stampeds, rightClone); const rightDiagramCell = makeDiagramCell({x, y, content: rightClone}); right.push(rightDiagramCell); - }; - }; + } + } const diagram = makeMaximisedDiagram(makeDiagram({left, right})); @@ -8422,19 +8421,19 @@ registerRule(; paddle.rule = rule; if (paddle.registry !== undefined) { unregisterRegistry(paddle.registry); - }; + } if (locked && paddle.rightTriangle !== undefined) { //debugRule(rule); paddle.registry = registerRule(rule); //debugRegistry(paddle.registry, {redundants: false}); - }; + } }; const getAllAtoms = (pool = state.colourTode.atoms) => { const atoms = [...pool]; for (const atom of atoms) { atoms.push(...getAllAtoms(atom.children)); - }; + } return atoms; }; @@ -8445,11 +8444,11 @@ registerRule(; if (child.isPinhole) continue; if (child.isPaddleHandle) continue; atoms.push(child); - }; - }; + } + } for (const atom of atoms) { if (atom.isSquare && atom.expanded) atoms.push(...atom.children); - }; + } return atoms; }; @@ -8457,39 +8456,39 @@ registerRule(; if (paddles.length > 1) { unlockMenuTool("triangle"); - }; + } if (paddles.length > 2) { let ruleCount = 0; for (const paddle of paddles) { if (paddle.rightTriangle !== undefined) { ruleCount++; - }; - }; + } + } if (ruleCount >= 2) { unlockMenuTool("hexagon"); - }; - }; + } + } - let previous = undefined; + let previous; for (const paddle of paddles) { if (previous === undefined) { paddle.y = PADDLE.y + PADDLE.scroll; previous = paddle; continue; - }; + } paddle.y = previous.y + previous.height + PADDLE_MARGIN; previous = paddle; //bringAtomToBack(paddle) //causes bug where circle tool disappears but really shouldn't :(; - }; + } }; const deletePaddle = (paddle, id = paddles.indexOf(paddle)) => { paddles.splice(id, 1); if (paddle.registry !== undefined) { unregisterRegistry(paddle.registry); - }; + } deleteAtom(paddle); positionPaddles(); }; @@ -8527,16 +8526,16 @@ registerRule(; borderScale: 1/2, borderColour: Colour.Black, draw: (atom) => { - return; if (atom.locked) { atom.hasBorder = true; - atom.colour = Colour.Grey ; - }; + atom.colour = Colour.Grey; + } else { atom.hasBorder = false; atom.colour = Colour.Black; - }; + } CIRCLE.draw(atom); + return; }, overlaps: CIRCLE.overlaps, offscreen: CIRCLE.offscreen, @@ -8545,7 +8544,6 @@ registerRule(; y: OPTION_MARGIN/2/2, x: OPTION_MARGIN/2/2, click: (atom) => { - return; const handle = atom.parent; const paddle = handle.parent; if (atom.locked) { @@ -8557,7 +8555,7 @@ registerRule(; atom.draggable = true; //paddle.dragOnly = true; updatePaddleRule(paddle); - } ; + } else { atom.locked = true; @@ -8570,34 +8568,35 @@ registerRule(; for (const cellAtom of paddle.cellAtoms) { if (cellAtom.expanded) { cellAtom.unexpand(cellAtom); - }; + } if (cellAtom.slotted !== undefined) { const slotted = cellAtom.slotted; if (slotted.expanded) { slotted.unexpand(slotted); - }; - }; + } + } if (cellAtom.joins.length > 0 && cellAtom.joinExpanded) { cellAtom.joinUnepxand(cellAtom); - }; - }; + } + } /*if (paddle.hasSymmetry) { if (paddle.symmetryCircle.expanded) { paddle.symmetryCircle.unexpand(paddle.symmetryCircle); }; - }*/; + }*/ if (paddle.cellAtoms.length === 0) { paddle.grabbable = false; paddle.draggable = false; - }; + } //paddle.dragOnly = false; updatePaddleRule(paddle); - }; + } + return; }, grab: (atom) => atom.parent.parent, - + }; const SYMMETRY_TOGGLINGS = new Map(); @@ -8634,12 +8633,12 @@ registerRule(; if (atom.expanded) { atom.unexpand(atom); - }; + } else { atom.expand(atom); - }; + } }, expand: (atom) => { @@ -8690,7 +8689,7 @@ registerRule(; if (!paddle.hasSymmetry && paddle.expanded && id > pid && left <= pright && right >= pright && ((top < pbottom && top > ptop) || (bottom > ptop && bottom < pbottom))) { if (atom.highlightPaddle !== undefined) { deleteChild(atom, atom.highlightPaddle); - }; + } atom.highlightPaddle = createChild(atom, HIGHLIGHT, {bottom: true}); atom.highlightPaddle.width = HIGHLIGHT_THICKNESS; @@ -8699,15 +8698,15 @@ registerRule(; atom.highlightPaddle.x = pright - HIGHLIGHT_THICKNESS/2; atom.highlightedPaddle = paddle; return; - }; + } - }; + } if (atom.highlightPaddle !== undefined) { deleteChild(atom, atom.highlightPaddle); atom.highlightPaddle = undefined; atom.highlightedPaddle = undefined; - }; + } }, drop: (atom) => { @@ -8725,14 +8724,14 @@ registerRule(; atom.dy = 0; /*atom.x = paddle.width -atom.width/2; - atom.y = paddle.height/2 - atom.height/2*/; + atom.y = paddle.height/2 - atom.height/2*/ /*if (paddle.pinhole.locked && atom.expanded) { atom.unexpand(atom); - }*/; + }*/ - }; - }; + } + } }, @@ -8751,14 +8750,14 @@ registerRule(; clone.y = y; registerAtom(clone); return clone; - }*/; + }*/ atom.attached = false; freeChild(paddle, atom); paddle.hasSymmetry = false; paddle.symmetryCircle = undefined; updatePaddleSize(paddle); - }; + } return atom; }, @@ -8852,7 +8851,7 @@ registerRule(; case "down": return clockwise ? "left" : "right"; case "left": return clockwise ? "up" : "down"; case "up": return clockwise ? "right" : "left"; - }; + } throw new Error("Invalid rotation or clockwiseness"); }; @@ -8885,7 +8884,7 @@ registerRule(; const parent = triangle.parent; if (parent.isSquare) { parent.receiveNumber(parent, triangle.value, triangle.channelId, {expanded: triangle.expanded, numberAtom: triangle}); - }; + } }, offscreen: TRIANGLE_UP.offscreen, overlaps: TRIANGLE_UP.overlaps, @@ -8926,7 +8925,7 @@ registerRule(; const parent = triangle.parent; if (parent.isSquare) { parent.receiveNumber(parent, triangle.value, triangle.channelId, {expanded: triangle.expanded, numberAtom: triangle}); - }; + } }, offscreen: TRIANGLE_DOWN.offscreen, @@ -8971,7 +8970,7 @@ registerRule(; if (circle.parent !== COLOURTODE_BASE_PARENT) { const paddle = circle.parent; updatePaddleRule(paddle); - }; + } }, value: false, size: COLOURTODE_SQUARE.size - OPTION_MARGIN, @@ -9012,7 +9011,7 @@ registerRule(; if (circle.parent !== COLOURTODE_BASE_PARENT) { const paddle = circle.parent; updatePaddleRule(paddle); - }; + } }, value: false, size: COLOURTODE_SQUARE.size - OPTION_MARGIN, @@ -9060,7 +9059,7 @@ registerRule(; if (circle.parent !== COLOURTODE_BASE_PARENT) { const paddle = circle.parent; updatePaddleRule(paddle); - }; + } }, value: false, size: COLOURTODE_SQUARE.size - OPTION_MARGIN, @@ -9082,11 +9081,11 @@ registerRule(; for (const j of newAtom.value.joins) { const joinAtom = makeSquareFromValue(j); newAtom.joins.push(joinAtom); - }; - }; + } + } newAtom.stamp = newAtom.value.stamp; - }; + } if (!newAtom.value.isDiagram) { @@ -9116,13 +9115,13 @@ registerRule(; // const {add, subtract} = channel; // hexagon.ons = [add.values[2], add.values[1], subtract.values[1], subtract.values[2], subtract.values[3], add.values[3]]; // hexagon.updateValue(hexagon); - }; + } - }; + } if (newAtom.value !== undefined && newAtom.value.isDiagram) { newAtom.update(newAtom); - }; + } return newAtom; }; @@ -9134,10 +9133,10 @@ registerRule(; draw: (atom) => { if ((atom.previousBrushColour !== state.brush.colour) || atom.toolbarNeedsColourUpdate) { atom.update(atom); - }; + } if (atom.unlocked) { atom.element.draw(atom); - }; + } }, overlaps: (atom, x, y) => atom.element.overlaps(atom, x, y), grab: (atom, x, y) => { @@ -9149,7 +9148,7 @@ registerRule(; const newAtom = makeSquareFromValue(atom.value); registerAtom(newAtom); return newAtom; - }; + } const newAtom = makeAtom({...atom.element, x: atom.x, y: atom.y}); registerAtom(newAtom); @@ -9163,8 +9162,8 @@ registerRule(; newAtom.joins.push(joinAtom); }; }; - */; - }; + */ + } return newAtom; }, @@ -9178,7 +9177,7 @@ registerRule(; let y = COLOURTODE_PICKER_PAD_MARGIN; if (height < COLOURTODE_SQUARE.size) { y += (COLOURTODE_SQUARE.size - height)/2; - }; + } y += BORDER_THICKNESS; const atom = makeAtom({...COLOURTODE_TOOL, width, height, size, x: Math.round(menuRight), y, element}); @@ -9203,7 +9202,7 @@ registerRule(; atom.grabbable = false; unlocks[unlockName] = atom; if (UNLOCK_MODE) unlockMenuTool(unlockName); - }; + } return atom; }; @@ -9217,7 +9216,7 @@ registerRule(; /*registerAtom(unlock); menuRight += unlock.width; - menuRight += OPTION_MARGIN*/; + menuRight += OPTION_MARGIN*/ }; @@ -9231,7 +9230,7 @@ registerRule(; const hexagonTool = addMenuTool(COLOURTODE_HEXAGON, "hexagon"); // const wideRectangleTool = addMenuTool(COLOURTODE_PICKER_CHANNEL, "wide_rectangle"); //menuRight += BORDER_THICKNESS; - const tallRectangleTool = {} //addMenuTool(COLOURTODE_TALL_RECTANGLE, "tall_rectangle"); + const tallRectangleTool = {}; //addMenuTool(COLOURTODE_TALL_RECTANGLE, "tall_rectangle"); createPaddle(); squareTool.value = makeArrayFromSplash(state.brush.colour); @@ -9243,7 +9242,7 @@ registerRule(; if (atom.joinDrawId === undefined) { atom.joinDrawId = -1; atom.joinDrawTimer = 0; - }; + } /*; if (typeof state.brush.colour === "number") { @@ -9254,7 +9253,7 @@ registerRule(; if (atom === squareTool) { atom.stamp = atom.value.stamp; }; - }*/; + }*/ if (atom.value !== undefined && atom === squareTool) { @@ -9262,10 +9261,10 @@ registerRule(; atom.previousBrushColour = state.brush.colour; if (atom.multiAtoms === undefined) { atom.multiAtoms = []; - }; + } for (const multiAtom of atom.multiAtoms) { deleteChild(atom, multiAtom); - }; + } atom.multiAtoms = []; @@ -9283,17 +9282,17 @@ registerRule(; multiAtom.value = diagramCell.content; multiAtom.update(multiAtom); atom.multiAtoms.push(multiAtom); - }; - }; - }; - }; + } + } + } + } const valueClone = cloneDragonArray(atom.value); atom.colours = getSplashesArrayFromArray(valueClone); if (atom.colourId >= atom.colours.length) { atom.colourId = 0; - }; + } //atom.colour = Colour.splash(atom.colours[atom.colourId]); if (atom.toolbarNeedsColourUpdate && atom === squareTool) { atom.toolbarNeedsColourUpdate = false; @@ -9302,12 +9301,12 @@ registerRule(; for (const joinValue of atom.value.joins) { const joinSquare = makeSquareFromValue(joinValue); atom.joins.push(joinSquare); - }; + } COLOURTODE_SQUARE.updateGradient(atom); } else { atom.colour = Colour.splash(999); atom.borderColour = Colour.splash(999); - }; + } }; triangleTool.update = squareTool.update; @@ -9329,7 +9328,7 @@ registerRule(; } else if ((e.ctrlKey || e.metaKey) && e.key === 'c') { e.preventDefault(); copyPaddles(); - }; + } }, {passive: false}); on.paste(async e => { @@ -9337,7 +9336,7 @@ registerRule(; if (pack !== "") { unpackPaddles(pack); return; - }; + } const item = e.clipboardData.items[0]; const file = item.getAsFile(); @@ -9372,7 +9371,7 @@ registerRule(; y: atom.y, slotted: atom.slotted ? atom.slotted.value : undefined, }); - }; + } return cellAtoms; }; @@ -9383,9 +9382,9 @@ registerRule(; if (!loadedColour) { if (!v.isLeftSlot) { setBrushColour(v.value); - }; + } loadedColour = true; - }; + } const square = v.isLeftSlot ? makeAtom(SLOT) : makeSquareFromValue(v.value); square.isLeftSlot = v.isLeftSlot; registerAtom(square); @@ -9405,8 +9404,8 @@ registerRule(; slotted.highlightedSide = "slot"; slotted.slottee = true; square.slotted = slotted; - }; - }; + } + } return atoms; }; @@ -9476,10 +9475,10 @@ registerRule(; const v = packer(paddle, paddle[key]); if (v !== undefined) { packedPaddle[key] = v; - }; - }; + } + } packedPaddles.push(packedPaddle); - }; + } return JSON.stringify(packedPaddles) ; }; @@ -9487,7 +9486,7 @@ registerRule(; if (middleClicked) { middleClicked = false; return; - }; + } loadedColour = false; unlockMenuTool("triangle"); @@ -9497,7 +9496,7 @@ registerRule(; try { while (paddles.length > 0) { deletePaddle(paddles[paddles.length-1]); - }; + } for (const packed of JSON.parse(pack)) { const paddle = createPaddle(); for (const key in packed) { @@ -9506,16 +9505,16 @@ registerRule(; const v = unpacker(paddle, packed[key]); if (v !== undefined) { paddle[key] = v; - }; - }; + } + } updatePaddleSize(paddle); updatePaddleRule(paddle); - }; + } positionPaddles(); } catch(e) { console.error(e); alert("Error loading rules... Sorry! Please contact @todepond :)"); - }; + } }; const download = (content, fileName, contentType) => { @@ -9538,7 +9537,7 @@ registerRule(; startIn: 'downloads', types: [{ description: 'JSON', - accept: {'application/json': [".json"]}; + accept: {'application/json': [".json"]} }], }); const writable = await result.createWritable(); @@ -9546,7 +9545,7 @@ registerRule(; await writable.close(); } catch (err) { console.error('Failed to save file:', err); - }; + } } else { // Fallback to the Blob and link method; const blob = new Blob([pack], {type: 'application/json'}); @@ -9557,7 +9556,7 @@ registerRule(; link.download = 'spell.json'; link.click(); URL.revokeObjectURL(url); - }; + } }; const openPaddles = () => { From b91fa498b198dc799e4d19ab3beb6aa8498b608b Mon Sep 17 00:00:00 2001 From: inyourface34456 <62214409+inyourface34456@users.noreply.github.com> Date: Thu, 16 May 2024 13:36:40 -0400 Subject: [PATCH 3/7] Create the-one-true-todey-file-of-cellpond.min.js empty --- the-one-true-todey-file-of-cellpond.min.js | 1 + 1 file changed, 1 insertion(+) create mode 100644 the-one-true-todey-file-of-cellpond.min.js diff --git a/the-one-true-todey-file-of-cellpond.min.js b/the-one-true-todey-file-of-cellpond.min.js new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/the-one-true-todey-file-of-cellpond.min.js @@ -0,0 +1 @@ + From e2f20598101d8c17b6386fb2293963e8e79a7ae5 Mon Sep 17 00:00:00 2001 From: inyourface34456 <62214409+inyourface34456@users.noreply.github.com> Date: Thu, 16 May 2024 13:36:54 -0400 Subject: [PATCH 4/7] Update the-one-true-todey-file-of-cellpond.min.js --- the-one-true-todey-file-of-cellpond.min.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/the-one-true-todey-file-of-cellpond.min.js b/the-one-true-todey-file-of-cellpond.min.js index 8b13789..d53f266 100644 --- a/the-one-true-todey-file-of-cellpond.min.js +++ b/the-one-true-todey-file-of-cellpond.min.js @@ -1 +1 @@ - +var middleClicked=!1;document.addEventListener("mousedown",function(e){1===e.button&&(middleClicked=!0)});const urlParams=new URLSearchParams(window.location.search),NO_SECRET_MODE=urlParams.has("nosecret"),UNLOCK_MODE=urlParams.has("unlock"),SCALE=urlParams.get("scale")??1,DPR=urlParams.get("dpr")??devicePixelRatio;print("DPR:",DPR),NO_SECRET_MODE&&localStorage.setItem("secretHasAlreadyBeenRevealed","true");const TODEPOND_COLOURS=[Colour.Green.splash,Colour.Red.splash,Colour.Blue.splash,Colour.Yellow.splash,Colour.Orange.splash,Colour.Pink.splash,Colour.Rose.splash,Colour.Cyan.splash,Colour.Purple.splash,Colour.Black.splash,Colour.Grey.splash,Colour.Silver.splash,Colour.White.splash,],getRGB=e=>{let t=e%100,l=t%10,i=t-l;return[e-t,i,l]},clamp=(e,t,l)=>el?l:e,wrap=(e,t,l)=>{let i=l-t+1;return el?wrap(e-i,t,l):e};let brushColourCycleIndex=0;const brushColourCycle=[999,Colour.Green.splash,Colour.Blue.splash,Colour.Red.splash,Colour.Yellow.splash,Colour.Black.splash,Colour.Rose.splash,Colour.Cyan.splash,Colour.Orange.splash,Colour.Purple.splash,Colour.Pink.splash,Colour.Grey.splash,Colour.Silver.splash,],makeCell=({x:e=0,y:t=0,width:l=1,height:i=1,colour:o=112}={})=>{let a=e,r=e+l,n=t,d=t+i,h=l*i,s=a+l/2,c=n+i/2,u=[],g=void 0,$={x:e,y:t,width:l,height:i,colour:o,left:a,right:r,top:n,bottom:d,centerX:s,centerY:c,sections:u,size:h,lastDraw:g,lastDrawRepeat:0};return $};let edgeMode=0;const pickCell=(e,t)=>{if(e>=1||t>=1||e<0||t<0)return;let l=Math.floor(e*GRID_SIZE),i=Math.floor(t*GRID_SIZE),o=l*GRID_SIZE+i,a=state.grid[o];if(void 0===a)return;let r=1,n=a.size,d=a.values();for(let h of d)if(r===n||(r++,!(h.left>e)&&!(h.top>t)&&!(h.right<=e)&&!(h.bottom<=t)))return h},pickNeighbour=(e,t,l)=>{let i=e.left+e.width/2,o=e.top+e.height/2,a=i+t*e.width,r=o+l*e.height,n=pickCell(a,r);return n},pickRandomCell=()=>{let e=Random.Uint32/4294967295,t=Random.Uint32/4294967295,l=pickCell(e,t);return l},pickRandomVisibleCell=()=>{if(!state.view.visible)return;if(state.view.fullyVisible)return pickRandomCell();let e=state.region.left+Random.Uint32/4294967295*state.region.width,t=state.region.top+Random.Uint32/4294967295*state.region.height,l=pickCell(e,t);return l},state={grid:[],cellCount:0,ticker(){},time:0,maxTime:9999999,speed:{count:1638.4,dynamic:!1,redraw:2.5,redrawRepeatScore:.5,redrawRepeatPenalty:0},image:{data:void 0,size:void 0,baseSize:void 0},view:{height:void 0,width:void 0,iheight:void 0,iwidth:void 0,left:void 0,right:void 0,top:void 0,bottom:void 0,visible:!0,fullyVisible:!0},region:{left:0,right:1,top:0,bottom:1,width:1,height:1},camera:{x:0,y:0,dx:0,dy:0,dxTarget:0,dyTarget:0,dsControl:1,dsTargetSpeed:.05,underScale:.9,scale:.9,mscale:1,dmscale:.002,mscaleTarget:1,mscaleTargetControl:.001,mscaleTargetSpeed:.05},brush:{colour:Colour.Purple.splash,colour:Colour.Rose.splash,colour:Colour.Yellow.splash,colour:Colour.Grey.splash,colour:Colour.Green.splash,colour:999,size:3},cursor:{previous:{x:void 0,y:void 0}},dragon:{behaves:[]}};let WORLD_SIZE,WORLD_CELL_COUNT,WORLD_DIMENSION,WORLD_CELL_SIZE;const setWorldSize=e=>{WORLD_CELL_COUNT=2**(2*(WORLD_SIZE=e)),WORLD_CELL_SIZE=1/(WORLD_DIMENSION=2**WORLD_SIZE)};setWorldSize(6);const addCell=e=>{cacheCell(e),state.cellCount++},deleteCell=e=>{uncacheCell(e),e.isDeleted=!0,state.cellCount--},getCells=()=>{let e=new Set;for(let t of state.grid)for(let l of t.values())e.add(l);return e},GRID_SIZE=128;for(let x=0;x{let t=Math.floor(e.left*GRID_SIZE),l=Math.floor(e.top*GRID_SIZE),i=Math.ceil(e.right*GRID_SIZE),o=Math.ceil(e.bottom*GRID_SIZE);for(let a=t;a{for(let t of e.sections)t.delete(e)},world=makeCell({colour:111*WORLD_SIZE});addCell(world),on.load(()=>{let e=Show.start({paused:!1,scale:DPR}),{context:t,canvas:l}=e;l.style.position="absolute";let i=()=>{state.image.baseSize=Math.min(l.width,l.height),state.image.size=state.image.baseSize*state.camera.scale,state.image.left=state.camera.x*state.camera.scale,state.image.top=state.camera.y*state.camera.scale,state.image.right=state.image.left+state.image.size,state.image.bottom=state.image.top+state.image.size,state.view.left=clamp(state.image.left,0,l.width),state.view.top=clamp(state.image.top,0,l.height),state.view.right=clamp(state.image.right,0,l.width),state.view.bottom=clamp(state.image.bottom,0,l.height),state.view.width=state.view.right-state.view.left,state.view.height=state.view.bottom-state.view.top,state.view.visible=state.view.width>0&&state.view.height>0,state.view.fullyVisible=state.view.left===state.image.left&&state.view.right===state.image.right&&state.view.top===state.image.top&&state.view.bottom===state.image.bottom,state.view.iwidth=Math.ceil(state.view.width),state.view.iheight=Math.ceil(state.view.height),state.region.left=(state.view.left-state.image.left)/state.image.size,state.region.right=1+(state.view.right-state.image.right)/state.image.size,state.region.top=(state.view.top-state.image.top)/state.image.size,state.region.bottom=1+(state.view.bottom-state.image.bottom)/state.image.size,state.region.width=state.region.right-state.region.left,state.region.height=state.region.bottom-state.region.top,drawQueueNeedsReset=!0},o=()=>{state.image.data=t.getImageData(0,0,l.width,l.height)};t.fillStyle=Colour.Void,t.fillRect(0,0,l.width,l.height),i(),o(),state.camera.x+=(l.width-state.image.size)/2,state.camera.y+=(l.height-state.image.size)/2,e.resize=()=>{t.fillStyle=Colour.Void,t.fillRect(0,0,l.width,l.height),i(),o()};let a=e=>{i()},r=()=>{let e=getCells();for(let t of e.values())c(t,t.colour)},n=(e,t)=>c(e,e.colour,t),d=e=>!(e.right<=state.region.left)&&!(e.left>=state.region.right)&&!(e.bottom<=state.region.top)&&!(e.top>=state.region.bottom),h=e=>!(e.right<=state.region.left)&&!(e.left>=state.region.right)&&!(e.bottom<=state.region.top)&&!(e.top>=state.region.bottom),s=(e,t)=>(e.colour=t,h(e))?(O.add(e),D.delete(e),.01):0,c=(e,t,i=!1)=>{if(e.isDeleted||(e.colour=t,!h(e)))return 0;let o=state.image.size,a=l.width,r=state.camera.x*state.camera.scale,n=state.camera.y*state.camera.scale,d=Math.round(o*e.left+r);if(d>l.width)return 0;d<0&&(d=0);let s=Math.round(o*e.top+n);if(s>l.height)return 0;s<0&&(s=0);let c=Math.round(o*e.right+r);if(c<0)return 0;c>l.width&&(c=l.width);let u=Math.round(o*e.bottom+n);if(u<0)return 0;u>l.height&&(u=l.height);let g=Colour.splash(e.colour),$=g[0],p=g[1],f=g[2],v=4*a,m=c-d,_=4*m,w=(s*a+d)*4,b=state.image.data.data,S=Colour.Void.red,R=Colour.Void.green,C=Colour.Void.blue;if(!gridMode||m<=3||u-s<=3){S=$,R=p,C=f;for(let T=s;T{$(),R();let[e,t]=Mouse.position;state.cursor.previous.x=e,state.cursor.previous.y=t},g=!1,$=()=>{if(!state.worldBuilt||(Mouse.Middle||(g=!1),state.colourTode.hand.state!==to.BRUSHING&&state.colourTode.hand.state!==to.PENCILLING))return;if(Mouse.Middle&&!g){let[e,t]=Mouse.position;f(...p(e,t),{single:!0}),g=!0}if(!Mouse.Left)return;let[l,i]=p(...Mouse.position);if(void 0===l||void 0===i)return;let[o,a]=p(state.cursor.previous.x,state.cursor.previous.y),r=state.brush.size*WORLD_CELL_SIZE,n=l-o,d=i-a,h=Math.sign(n),s=Math.sign(d),c=Math.abs(n),u=Math.abs(d),$=Math.max(c,u),v=0,m=0;c===$?(m=WORLD_CELL_SIZE*s*(u/c),v=WORLD_CELL_SIZE*h):(v=WORLD_CELL_SIZE*h*(c/u),m=WORLD_CELL_SIZE*s);let _=new Set,w=$/WORLD_CELL_SIZE;if(0===n&&0===d)for(let b=-r/2;b<=r/2;b+=WORLD_CELL_SIZE)for(let S=-r/2;S<=r/2;S+=WORLD_CELL_SIZE)_.add([l+b,i+S]);else for(let R=0;R<=w;R++){let C=o+v*R,T=a+m*R;for(let k=-r/2;k<=r/2;k+=WORLD_CELL_SIZE)for(let E=-r/2;E<=r/2;E+=WORLD_CELL_SIZE)_.add([C+k,T+E])}for(let z of _.values())f(z[0],z[1])},p=(e,t)=>(e-=state.camera.x*state.camera.scale/DPR,t-=state.camera.y*state.camera.scale/DPR,e/=state.image.size,t/=state.image.size,[e*=DPR,t*=DPR]),f=(e,t,{single:l=!1}={})=>{let i=pickCell(e,t);if(void 0===i)return;if(!l&&(i.width!==WORLD_CELL_SIZE||i.height!=WORLD_CELL_SIZE)){let o=v(e,t);if(void 0!==o){let a=U([...o]);i=a}}if("number"==typeof state.brush.colour){i.colour=state.brush.colour,n(i);return}let r=[];for(let d of r=state.brush.colour.left[0].content.isDiagram?N(i,state.brush.colour.left[0].content):N(i,state.brush.colour))n(d)},v=(e,t)=>{let l=m(e,t),i=_(l,e,t);return i},m=(e,t)=>{let l=Math.floor(e*WORLD_DIMENSION)/WORLD_DIMENSION,i=Math.floor(t*WORLD_DIMENSION)/WORLD_DIMENSION,o=GRID_SIZE/WORLD_DIMENSION,a=new Set;for(let r=0;r{let i=new Set;for(let o of e.values())for(let a of o.values())if(!i.has(a)){for(let r of a.sections)if(!e.has(r))return;i.add(a)}return i},w,b,S;state.brush.hoverColour=Colour.Void;let R=()=>{let[e,t]=Mouse.position;if(e5.state===to.BRUSH||e5.state===to.BRUSHING||e5.state===to.PENCILLING){let l=pickCell(...p(e,t));void 0!==l&&(state.brush.hoverColour=l.colour)}else{let o=ts(e/C,t/C);if(void 0!==o){if(o.isSquare||o===squareTool){if(state.brush.hoverColour=o.value,o.joinExpanded){let a=Q(o.value);a.joins=[],state.brush.hoverColour=a}}else o.isTallRectangle||(o.isPaddle?state.brush.hoverColour=o.getColour(o):o.isSlot?state.brush.hoverColour=o.parent.getColour(o.parent):state.brush.hoverColour=o.colour.splash)}else state.brush.hoverColour=Colour.Void}if(!Mouse.Right){if(void 0!==w){let r=Math.hypot(e-w,t-b),n=Date.now()-S;(n<100||r<=0)&&(state.brush.hoverColour===Colour.Void?(++brushColourCycleIndex>=brushColourCycle.length&&(brushColourCycleIndex=0),tE(brushColourCycle[brushColourCycleIndex])):tE(state.brush.hoverColour),squareTool.toolbarNeedsColourUpdate=!0),drawQueueNeedsReset=!0}w=void 0,b=void 0;return}if(drawQueueNeedsReset=!0,void 0===w&&(w=e,b=t,S=Date.now(),dropperMovement=0),e5.state===to.FREE||e5.state==to.VOIDING||e5.state===to.BRUSH||e5.state===to.BRUSHING||e5.state===to.PENCILLING){let{x:d,y:h}=state.cursor.previous;if(void 0===d||void 0===h||void 0===e||void 0===t)return;let[s,c]=[e-d,t-h];state.camera.x+=s/state.camera.scale,state.camera.y+=c/state.camera.scale,i()}},C=DPR*SCALE;on.wheel(e=>{e.preventDefault();let t=e.deltaY/100;if(e.altKey)lt.scroll-=50*t,l$();else if(e.ctrlKey||e.metaKey){C-=.1*t;let l=lu();for(let i of l)i.needsColoursUpdate=!0;squareTool.toolbarNeedsColourUpdate=!0}else e.shiftKey?(0===t&&(t=e.deltaX/100),state.brush.size-=Math.sign(t),state.brush.size<0&&(state.brush.size=0)):k(t,...Mouse.position)},{passive:!1}),on.keydown(e=>{"Alt"===e.key&&e.preventDefault()},{passive:!1}),on.keydown(e=>{"f"===e.key&&(state.camera.x=640,state.camera.y=30,state.camera.scale=.95,i())});let T=e=>e,k=(e,t,l)=>{t*=DPR,l*=DPR;let o=-Math.sign(e),a=Math.abs(e);for(let r=0;r{e.preventDefault()}),on.keydown(e=>{let t=E[e.key];void 0!==t&&t(e)});let E={};E.e=()=>state.camera.mscaleTarget+=state.camera.mscaleTargetControl,E.q=()=>state.camera.mscaleTarget-=state.camera.mscaleTargetControl,E.w=()=>state.camera.dyTarget+=state.camera.dsControl,E.s=e=>{e.ctrlKey||e.metaKey||(state.camera.dyTarget-=state.camera.dsControl)},E.a=()=>state.camera.dxTarget+=state.camera.dsControl,E.d=()=>state.camera.dxTarget-=state.camera.dsControl,E[0]=()=>setWorldSize(0),E[1]=()=>setWorldSize(1),E[2]=()=>setWorldSize(2),E[3]=()=>setWorldSize(3),E[4]=()=>setWorldSize(4),E[5]=()=>setWorldSize(5),E[6]=()=>setWorldSize(6),E[7]=()=>setWorldSize(7),E[8]=()=>setWorldSize(8),E[9]=()=>setWorldSize(9),E.r=()=>{state.camera.mscaleTarget=1,state.camera.dxTarget=0,state.camera.dyTarget=0},E["="]=()=>edgeMode=1,E["-"]=()=>edgeMode=0,E.o=()=>edgeMode=0===edgeMode?1:0,gridMode=!0,E.g=()=>{gridMode=!gridMode,drawQueueNeedsReset=!0};let z=()=>{if(state.camera.mscale!==state.camera.mscaleTarget){let e=state.camera.mscaleTarget-state.camera.mscale;state.camera.mscale+=e*state.camera.mscaleTargetSpeed;let t=Math.sign(e),o=state.camera.mscaleTarget*state.camera.mscaleTargetControl*state.camera.mscaleTargetSpeed;1===t&&state.camera.mscale>state.camera.mscaleTarget-o&&(state.camera.mscale=state.camera.mscaleTarget),-1===t&&state.camera.mscalestate.camera.dxTarget-n&&(state.camera.dx=state.camera.dxTarget),-1===r&&state.camera.dxstate.camera.dyTarget-s&&(state.camera.dy=state.camera.dyTarget),-1===h&&state.camera.dy{eJ(),u(),z(),drawQueueNeedsReset&&(A(),drawQueueNeedsReset=!1),e.paused?B():I(),t.putImageData(state.image.data,0,0),t.clearRect(0,0,state.view.left,state.view.bottom),t.clearRect(state.view.left,0,l.width,state.view.top),t.clearRect(state.view.right,state.view.top,l.width,l.height),t.clearRect(0,state.view.bottom,l.width,l.height),state.time++,state.time>state.maxTime&&(state.time=0)};let D=new Set,O=new Set;drawQueueNeedsReset=!1;let P=e=>{for(let t=e.length-1;t>0;t--){let l=Random.Uint32%(t+1);[e[t],e[l]]=[e[l],e[t]]}return e},A=()=>{for(let e of(D.clear(),P([...state.grid])))if(d(e))for(let t of e.values())D.add(t)},I=()=>{let e=state.speed.dynamic?state.speed.aer*state.cellCount:state.speed.count;e=Math.min(e,state.cellCount),e*=state.worldBuilt?1:.1;let t=e*state.speed.redraw,l=!0;state.worldBuilt||(l=!1);let i=0;for(let o=0;o=t&&(l=!1);let r=L(a,l);i+=r}for(let d of O)if(i+=n(d),O.delete(d),i>=t)break;for(let h of D)if(i+=n(h),D.delete(h),i>=t)break},B=()=>{if(!state.view.visible)return;let e=state.speed.dynamic?state.speed.aer*state.cellCount:state.speed.count,t=e*state.speed.redraw;state.worldBuilt||(t=1);let l=0;for(let i of D)if(l+=n(i),D.delete(i),l>=t)break},L=(e,t)=>{if(void 0!==Y(e,t))return 1;for(let l of(state.dragon.behaves.shuffle(),state.dragon.behaves)){let i=l(e,t);if(void 0!==i)return i}return 0},G=(e,t,l)=>{let i=e.width/t,o=e.height/l,a=[],r=e.x,n=e.y,d=i,h=o;for(let s=0;s{let[l,i]=eo(t),o=e.width/l,a=e.height/i,r=[];for(let n of t.left){let d=M(n.content,{source:e.colour}),h=d[Random.Uint32%d.length],s=makeCell({x:e.x+n.x*o,y:e.y+n.y*a,width:n.width*o,height:n.height*a,colour:h});r.push(s)}for(let c of(deleteCell(e),r))addCell(c);return r},U=e=>{let t=1,l=1,i=0,o=0;for(let a of e)a.lefti&&(i=a.right),a.bottom>o&&(o=a.bottom),deleteCell(a);let r=makeCell({x:t,y:l,width:i-t,height:o-l,colour:e[0].colour,lastDraw:e[0].lastDraw});return addCell(r),r},Y=(e,t)=>{if(state.worldBuilt)return;if(state.cellCount>=WORLD_CELL_COUNT){state.worldBuilt=!0;return}if(e.colour<111)return 0;e.colour-=111;let l=2,i=2,o=G(e,l,i);for(let a of o)n(a);return 1},X=({values:e,channel:t=0,variable:l,add:i,subtract:o}={})=>{let a;return{values:a=void 0!==l?[!0,!0,!0,!0,!0,!0,!0,!0,!0,!0]:e,variable:l,channel:t,add:i,subtract:o}},H=e=>{let t=[...e.values],l=e.variable,i=e.channel,o=void 0===e.add?void 0:H(e.add),a=void 0===e.subtract?void 0:H(e.subtract),r=X({values:t,variable:l,channel:i,add:o,subtract:a});return r},V=e=>{let t=[!1,!1,!1,!1,!1,!1,!1,!1,!1,!1];return t[e]=!0,t},j=e=>{let t=V(e);return X({values:t})},W={};W.red=(e,{source:t}={})=>{if(void 0===t)return[!0,!0,!0,!0,!0,!0,!0,!0,!0,!0];let[l,i,o]=getRGB(t),a=V(l/100);return a},W.green=(e,{source:t}={})=>{if(void 0===t)return[!0,!0,!0,!0,!0,!0,!0,!0,!0,!0];let[l,i,o]=getRGB(t),a=V(i/10);return a},W.blue=(e,{source:t}={})=>{if(void 0===t)return[!0,!0,!0,!0,!0,!0,!0,!0,!0,!0];let[l,i,o]=getRGB(t),a=V(o);return a};let q=(e,t={})=>{if(void 0===e.variable)return e.values;let l=W[e.variable](e,t),i="red"===e.variable;return void 0!==e.add&&(l=e$(l,e.add,{source:t.source,multiplier:1,isHue:i})),void 0!==e.subtract&&(l=e$(l,e.subtract,{source:t.source,multiplier:-1,isHue:i})),l},Z=({channels:e,stamp:t,joins:l=[]}={})=>(void 0===e&&(e=[void 0,void 0,void 0]),{channels:e,stamp:t,joins:l});makeArrayFromSplash=e=>{let[t,l,i]=getRGB(e);t/=100,l/=10;let o=[!1,!1,!1,!1,!1,!1,!1,!1,!1,!1],a=[!1,!1,!1,!1,!1,!1,!1,!1,!1,!1],r=[!1,!1,!1,!1,!1,!1,!1,!1,!1,!1];o[t]=!0,a[l]=!0,r[i]=!0;let n=X({values:o,channel:0}),d=X({values:a,channel:1}),h=X({values:r,channel:2}),s=Z({channels:[n,d,h]});return s};let F=(e,t)=>{let l=M(e,t),i=new Set(l);return i},M=(e,t={})=>{let l=[];for(let i of e.joins){let o=M(i);l.push(...o)}if(e.isDiagram)return l.push(900),l;let[a,r,n]=e.channels;void 0===a&&(a=X({channel:0,variable:"red"})),void 0===r&&(r=X({channel:1,variable:"green"})),void 0===n&&(n=X({channel:2,variable:"blue"}));let d=q(a,t),h=q(r,t),s=q(n,t);for(let c=0;c{for(let t=0;t<3;t++)void 0===e.channels[t]&&(e.channels[t]=X({channel:t,variable:tK[t]}))},J=e=>{for(let t of e.channels){if(void 0===t)break;if(void 0!==t.variable)return!0}return!1},Q=e=>{if(void 0===e){let t=X({values:[!0,!0,!0,!0,!0,!0,!0,!0,!0,!0],channel:0}),l=X({values:[!0,!0,!0,!0,!0,!0,!0,!0,!0,!0],channel:1}),i=X({values:[!0,!0,!0,!0,!0,!0,!0,!0,!0,!0],channel:2}),o=Z({channels:[t,l,i]});return o}if(e.isDiagram)return ee(e);let a,r,n,d=e.stamp;void 0!==e.channels[0]&&null!==e.channels[0]&&(a=H(e.channels[0])),void 0!==e.channels[1]&&null!==e.channels[1]&&(r=H(e.channels[1])),void 0!==e.channels[2]&&null!==e.channels[2]&&(n=H(e.channels[2]));let h=[];for(let s of e.joins){let c=Q(s);h.push(c)}let u=Z({stamp:d,channels:[a,r,n],joins:h});return u};makeDiagram=({left:e=[],right:t,joins:l=[]}={})=>({left:e,right:t,isDiagram:!0,joins:l});let ee=e=>{let t=makeDiagram();for(let l of["left","right"]){if(void 0===e[l])continue;let i=[];for(let o of e[l]){let a=et(o);i.push(a)}t[l]=i}return t};makeDiagramCell=({x:e=0,y:t=0,width:l=1,height:i=1,content:o=Z(),instruction:a=DRAGON_INSTRUCTION.recolour,splitX:r=1,splitY:n=1}={})=>({x:e,y:t,width:l,height:i,content:o,instruction:a,splitX:r,splitY:n});let et=e=>{let t=Q(e.content);return{...e,content:t}},el=e=>{let[t,l]=eo(e);for(let i of["left","right"]){let o=e[i];if(void 0!==o)for(let a of o)a.width/=t,a.height/=l,a.x/=t,a.y/=l}return e},ei=e=>{let t=1/0,l=1/0;for(let i of["left","right"]){let o=e[i];if(void 0!==o)for(let a of o)a.width{let t=1/0,l=-1/0,i=1/0,o=-1/0;for(let a of e.left){let r=a.x,n=a.x+a.width,d=a.y,h=a.y+a.height;rl&&(l=n),h>o&&(o=h)}let s=l-t,c=o-i;return[s,c]};makeRule=({steps:e=[],transformations:t=DRAGON_TRANSFORMATIONS.NONE,locked:l=!0,chance:i}={})=>({steps:e,transformations:t,locked:l,chance:i}),DRAGON_TRANSFORMATIONS={NONE:[(e,t,l,i,o,a)=>[e,t],],X:[(e,t,l,i,o,a)=>[e,t],(e,t,l,i,o,a)=>[-e-l+o,t],],Y:[(e,t,l,i,o,a)=>[e,t],(e,t,l,i,o,a)=>[e,-t-i+a],],XY:[(e,t,l,i,o,a)=>[e,t],(e,t,l,i,o,a)=>[-e-l+o,t],(e,t,l,i,o,a)=>[e,-t-i+a],(e,t,l,i,o,a)=>[-e-l+o,-t-i+a],],R:[(e,t,l,i,o,a)=>[e,t],(e,t,l,i,o,a)=>[-t-i+a,e],(e,t,l,i,o,a)=>[-e-l+o,-t-i+a],(e,t,l,i,o,a)=>[t,-e-l+o],],XYR:[(e,t,l,i,o,a)=>[e,t],(e,t,l,i,o,a)=>[-t-i+a,e],(e,t,l,i,o,a)=>[-e-l+o,-t-i+a],(e,t,l,i,o,a)=>[t,-e-l+o],(e,t,l,i,o,a)=>[-e-l+o,t],(e,t,l,i,o,a)=>[-t-i+a,-e-l+o],(e,t,l,i,o,a)=>[e,-t-i+a],(e,t,l,i,o,a)=>[t,e],]};let ea=(e,t,l)=>{let[i,o]=eo(e.steps[0]),a=e.steps.map(e=>er(e,t,l,i,o)),r=makeRule({steps:a,transformations:e.transformations,locked:e.locked,chance:e.chance});return r},er=(e,t,l,i,o)=>{let{left:a,right:r}=e,[n,d]=[i,o],h=[],s=void 0===r?void 0:[];for(let c=0;c{let[a,r,n,d]=t(e.x,e.y,e.width,e.height,l,i),{splitX:h,splitY:s}=e;if(!o){let[c,u]=t(e.splitX,e.splitY,1,1,1,1).map(e=>Math.abs(e));h=c,s=u;let[g,$]=t(e.width,e.height,1,1,1,1).map(e=>Math.abs(e));n=g,d=$}void 0===a&&(a=e.x),void 0===r&&(r=e.y),void 0===n&&(n=e.width),void 0===d&&(d=e.height);let p=e.content;return makeDiagramCell({x:a,y:r,splitX:h,splitY:s,width:n,height:d,content:p,instruction:e.instruction})};registerRule=e=>{let t=[];for(let l of e.transformations){let i=ea(e,l);t.push(i)}let o=[];for(let a of t){let r=eh(a);o.push(...r)}let n=[];for(let d of o){let h=ec(d);n.push(h),state.dragon.behaves.push(h)}return{redundantRules:o,transformedRules:t,behaveFunctions:n}};let ed=({behaveFunctions:e})=>{state.dragon.behaves=state.dragon.behaves.filter(t=>!e.includes(t))},eh=e=>{let t=[],[l]=e.steps;for(let i of l.left){let o=(e,t,l=1,o=1)=>{let a=l/i.width,r=o/i.height,n=(e-i.x)*1/i.width,d=(t-i.y)*1/i.height;return[n,d,a,r]},a=ea(e,o,!0);t.push(a)}return t},es=e=>{let t=new Set,l=[e.left,e.right];for(let i of l)for(let o of i){let a=o.content.stamp;void 0!==a&&t.add(a)}return[...t.values()]},ec=e=>{let t=[];for(let l of e.steps){let i=es(l),o=eu(l,i,e.chance),a=eg(l,i),r=(e,t)=>{let[l,i]=o(e);if(void 0!==l)return a(l,t,i)};t.push(r)}let n=(e,l)=>{for(let i of t){let o=i(e,l);if(void 0!==o)return o}};return n},eu=(e,t,l=6)=>{let i=[];for(let o of e.left){let a=F(o.content),r=e=>{let t=o.width*e.width,l=o.height*e.height,i=e.x+o.x*e.width,r=e.y+o.y*e.height;if(1===edgeMode){for(;i>=1;)i-=1;for(;r>=1;)r-=1;for(;i<0;)i+=1;for(;r<0;)r+=1}let n=i+t/2,d=r+l/2,h=pickCell(n,d);return void 0!==h&&h.left===i&&h.top===r&&h.width===t&&h.height===l&&a.has(h.colour)?[h,o.content.stamp]:[void 0,void 0]};i.push(r)}let n=Math.round(10**(3-l/2)),d=()=>oneIn(n),h=e=>{if(void 0!==l&&!d())return[void 0,void 0];let o=[],a={};for(let r of t)a[r]=[];for(let n of i){let[h,s]=n(e);if(void 0===h)return[void 0,void 0];void 0!==s&&a[s].push(h.colour),o.push(h)}return[o,a]};return h},eg=(e,t)=>{let l=[];for(let i of e.right){let o=i.instruction(i);l.push(o)}let a=(e,t,l)=>{for(let i of l)r(e,t,i)},r=(e,t,l)=>{e[l]=[...t[l]]};return(e,i,o)=>{let n=0,d=0,h=0,s=[],c={};for(let u of(a(c,o,t),l)){let g=s.length>0?s.pop():e[d],$=u(g,i,e,d,c);void 0!==$.stampNameTakenFrom&&0===c[$.stampNameTakenFrom].length&&r(c,o,$.stampNameTakenFrom);let{drawn:p,bonusTargets:f,skip:v}=$;void 0!==v&&(h+=v),n+=p,void 0!==f&&s.push(...f),0===s.length&&(d++,h>0&&(d++,h--))}return n}};(DRAGON_INSTRUCTION={}).nothing=e=>()=>({drawn:0}),DRAGON_INSTRUCTION.nothing.type="NOTHING",DRAGON_INSTRUCTION.recolour=e=>{K(e.content);let t=M(e.content),l=J(e.content),i=(i,o,a,r,n)=>{let d,h=i.colour,c;if(void 0===e.content.stamp)d=t[Random.Uint32%t.length];else{let u=n[e.content.stamp];if(0===u.length)d=t[Random.Uint32%t.length];else{let g=Random.Uint32%u.length;d=h=u[g],u.splice(g,1),c=e.content.stamp}}if(l){let $=getRGB(d);for(let p=0;p!0===e&&t).filter(e=>!1!==e),w=_[Random.Uint8%_.length];d-=$[p];let b=10**(2-p);d+=w*b}}let S=0;return o?S+=s(i,d,!0):i.colour=d,{drawn:S,stampNameTakenFrom:c}};return i},DRAGON_INSTRUCTION.recolour.type="RECOLOUR";let e$=(e,t,{source:l,multiplier:i=1,isHue:o=!1})=>{let a=t.values;void 0!==t.variable&&(a=W[t.variable](t,{source:l}));let r=a.map((e,t)=>!0===e&&t).filter(e=>!1!==e),n=e.map((e,t)=>!0===e&&t).filter(e=>!1!==e),d=new Set;for(let h of n)for(let s of r){let c=clamp(h+s*i,0,9);d.add(c)}let u=[!1,!1,!1,!1,!1,!1,!1,!1,!1,!1];for(let g of d)u[g]=!0;return void 0!==t.add&&(u=e$(u,t.add,{source:l,multiplier:1,isHue:o})),void 0!==t.subtract&&(u=e$(u,t.add,{source:l,multiplier:-1,isHue:o})),u};DRAGON_INSTRUCTION.split=e=>{let t=(t,l,i,o,a)=>{let r=G(t,e.splitX,e.splitY);return{drawn:0,bonusTargets:r.reverse()}};return t},DRAGON_INSTRUCTION.split.type="SPLIT",DRAGON_INSTRUCTION.merge=e=>{let t=Math.abs(e.splitX)*Math.abs(e.splitY),l=(e,l,i,o,a)=>{let r=i.slice(o,o+t),n=U(r);return{drawn:0,skip:t-1,bonusTargets:[n]}};return l},DRAGON_INSTRUCTION.merge.type="MERGE";debugRegistry=(e,{transforms:t=!0,redundants:l=!0}={})=>{let{redundantRules:i,transformedRules:o}=e;if(print(""),print("================================================================="),t)for(let a of(print(""),print("TRANSFORMED RULES"),o))debugRule(a);if(l)for(let r of(print(""),print("REDUNDANT RULES"),i))debugRule(r)},debugRule=e=>{for(let t of e.steps){for(let l of(print(""),print("=== LEFT ==="),t.left))debugDiagramCell(l,{read:!0});for(let i of(print("=== RIGHT ==="),t.right))debugDiagramCell(i)}},debugDiagramCell=(e,{read:t=!1}={})=>{t?print("CHECK","at",e.x,e.y,"with size",e.width,e.height,"for",M(e.content)):"NOTHING"===e.instruction.type?print(e.instruction.type,"at",e.x,e.y,"with size",e.width,e.height):"RECOLOUR"===e.instruction.type?print(e.instruction.type,"at",e.x,e.y,"with size",e.width,e.height,"to",M(e.content)):print(e.instruction.type,e.splitX,e.splitY,"at",e.x,e.y,"with size",e.width,e.height)};let ep=makeArrayFromSplash(Colour.Grey.splash),ef=makeArrayFromSplash(Colour.Black.splash),ev=makeArrayFromSplash(Colour.Cyan.splash),em=makeArrayFromSplash(Colour.Blue.splash),ey=makeArrayFromSplash(Colour.Yellow.splash),e_=makeArrayFromSplash(Colour.Cyan.splash-111),[ex,ew]=getRGB(Colour.Red.splash);ex/=100,ew/=10;let eb=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:ep}),makeDiagramCell({x:0,y:1,content:ef}),],right:[makeDiagramCell({x:0,y:0,content:ef}),makeDiagramCell({x:0,y:1,content:ep}),]}),e0=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:ey}),makeDiagramCell({x:0,y:1,content:ef}),],right:[makeDiagramCell({x:0,y:0,content:ef}),makeDiagramCell({x:0,y:1,content:ey}),]}),eS=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:ey}),makeDiagramCell({x:1,y:1,content:ef}),],right:[makeDiagramCell({x:0,y:0,content:ef}),makeDiagramCell({x:1,y:1,content:ey}),]}),eR=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:ev}),makeDiagramCell({x:1,y:0,content:em}),]}),eC=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:em}),makeDiagramCell({x:0,y:1,content:ef}),],right:[makeDiagramCell({x:0,y:0,content:ef}),makeDiagramCell({x:0,y:1,content:em}),]}),eT=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:e_}),makeDiagramCell({x:1,y:0,content:ev}),],right:[makeDiagramCell({x:0,y:0,content:ev}),makeDiagramCell({x:1,y:0,content:e_}),]}),ek=makeDiagram({left:[makeDiagramCell({x:0,y:0,width:1,content:em}),],right:[makeDiagramCell({x:0,y:0,width:.5,content:e_,instruction:DRAGON_INSTRUCTION.split,splitX:2,splitY:1}),makeDiagramCell({x:.5,y:0,width:.5,content:ev}),]}),eE=makeDiagram({left:[makeDiagramCell({x:0,y:0,width:1,content:e_}),makeDiagramCell({x:1,y:0,width:1,content:ev}),makeDiagramCell({x:0,y:1,width:2,content:ef}),],right:[makeDiagramCell({x:0,y:0,width:2,content:em,instruction:DRAGON_INSTRUCTION.merge,splitX:2,splitY:1}),makeDiagramCell({x:0,y:1,width:2,content:ef}),]}),ez=makeDiagram({left:[makeDiagramCell({x:0,y:0,width:1,content:e_}),makeDiagramCell({x:1,y:0,width:1,content:e_}),],right:[makeDiagramCell({x:0,y:0,width:2,content:em,instruction:DRAGON_INSTRUCTION.merge,splitX:2,splitY:1}),]}),eD=makeDiagram({left:[makeDiagramCell({x:0,y:0,width:1,content:ev}),makeDiagramCell({x:1,y:0,width:1,content:ev}),],right:[makeDiagramCell({x:0,y:0,width:2,content:em,instruction:DRAGON_INSTRUCTION.merge,splitX:2,splitY:1}),]}),eO=makeDiagram({left:[makeDiagramCell({x:0,y:0,width:1,content:e_}),makeDiagramCell({x:1,y:0,width:1,content:ev}),makeDiagramCell({x:2,y:0,width:2,content:ef}),],right:[makeDiagramCell({x:0,y:0,width:2,content:ef,instruction:DRAGON_INSTRUCTION.merge,splitX:2,splitY:1}),makeDiagramCell({x:2,y:0,width:1,content:e_,instruction:DRAGON_INSTRUCTION.split,splitX:2,splitY:1}),makeDiagramCell({x:3,y:0,width:1,content:ev}),]}),eP=makeDiagram({left:[makeDiagramCell({x:0,y:0,width:.5,content:em}),makeDiagramCell({x:.5,y:0,width:.5,content:em}),makeDiagramCell({x:0,y:1,content:ef}),],right:[makeDiagramCell({x:0,y:0,width:1,content:ef,instruction:DRAGON_INSTRUCTION.merge,splitX:2,splitY:1}),makeDiagramCell({x:0,y:1,width:.5,content:em,instruction:DRAGON_INSTRUCTION.split,splitX:2,splitY:1}),makeDiagramCell({x:.5,y:1,width:.5,content:em}),]}),eA=makeDiagram({left:[makeDiagramCell({x:0,y:0,width:.5,content:ev}),makeDiagramCell({x:.5,y:0,width:.5,content:ev}),makeDiagramCell({x:0,y:1,content:ef}),],right:[makeDiagramCell({x:0,y:0,width:1,content:ef,instruction:DRAGON_INSTRUCTION.merge,splitX:2,splitY:1}),makeDiagramCell({x:0,y:1,width:.5,content:ev,instruction:DRAGON_INSTRUCTION.split,splitX:2,splitY:1}),makeDiagramCell({x:.5,y:1,width:.5,content:ev}),]}),eI=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:em}),makeDiagramCell({x:1,y:0,content:ev}),],right:[makeDiagramCell({x:0,y:0,content:ev}),makeDiagramCell({x:1,y:0,content:em}),]}),eB=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:e_}),],right:[makeDiagramCell({x:0,y:0,width:.5,content:em,instruction:DRAGON_INSTRUCTION.split,splitX:2,splitY:1}),makeDiagramCell({x:.5,y:0,width:.5,content:ev,instruction:DRAGON_INSTRUCTION.recolour}),]}),eL=makeDiagram({left:[makeDiagramCell({x:0,y:0,width:1,content:em}),makeDiagramCell({x:1,y:0,width:1,content:em}),],right:[makeDiagramCell({x:0,y:0,width:1,content:em}),makeDiagramCell({x:1,y:0,width:1,content:ev}),]}),eG=makeDiagram({left:[makeDiagramCell({x:0,y:0,width:1,content:ev}),makeDiagramCell({x:1,y:0,width:1,content:ev}),],right:[makeDiagramCell({x:0,y:0,width:1,content:em}),makeDiagramCell({x:1,y:0,width:1,content:ev}),]}),eN=makeDiagram({left:[makeDiagramCell({x:0,y:0,width:1,content:ep}),makeDiagramCell({x:0,y:1,width:1,content:ef}),],right:[makeDiagramCell({x:0,y:0,width:1,content:ef}),makeDiagramCell({x:0,y:1,width:1,content:ep}),]}),eU=makeDiagram({left:[makeDiagramCell({x:0,y:0,width:1,content:ep}),makeDiagramCell({x:1,y:0,width:1,content:ef}),],right:[makeDiagramCell({x:0,y:0,width:1,content:ef}),makeDiagramCell({x:1,y:0,width:1,content:ep}),]}),e7=Z();e7.channels=[X(),X(),X()];for(let eY=0;eY<3;eY++){let eX=e7.channels[eY];for(let e3=0;e3<10;e3++)0===eY&e3>0||(eX.values[e3]=!0)}RAINBOW_DIAGRAM=makeDiagram({left:[makeDiagramCell({content:e7})]});let e1=Z();e1.channels=[X(),X(),X()];for(let eH=0;eH<3;eH++){let e8=e1.channels[eH];for(let eV=0;eV<10;eV++)1===eH&eV>0||(e8.values[eV]=!0)}RAINBOW_DIAGRAM_2=makeDiagram({left:[makeDiagramCell({content:e1})]});let ej=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:e_}),],right:[makeDiagramCell({x:0,y:0,content:ev}),]}),eW=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:e_}),],right:[makeDiagramCell({x:0,y:0,content:em}),]}),eq=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:ev}),makeDiagramCell({x:0,y:1,content:ef}),],right:[makeDiagramCell({x:0,y:0,content:ef}),makeDiagramCell({x:0,y:1,content:ev}),]}),e2=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:em}),makeDiagramCell({x:0,y:1,content:ef}),],right:[makeDiagramCell({x:0,y:0,content:ef}),makeDiagramCell({x:0,y:1,content:em}),]}),eZ=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:em}),makeDiagramCell({x:1,y:0,content:ef}),],right:[makeDiagramCell({x:0,y:0,content:ef}),makeDiagramCell({x:1,y:0,content:em}),]}),eF=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:ev}),makeDiagramCell({x:-1,y:0,content:ef}),],right:[makeDiagramCell({x:0,y:0,content:ef}),makeDiagramCell({x:-1,y:0,content:ev}),]}),e4=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:em}),],right:[makeDiagramCell({x:0,y:0,content:ev}),]}),eM=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:ev}),],right:[makeDiagramCell({x:0,y:0,content:em}),]});state.colourTode={atoms:[],hand:{state:void 0,content:void 0,offset:{x:0,y:0},velocity:{x:0,y:0},velocityHistory:[],velocityMemory:5,previous:{x:0,y:0}}};let e5=state.colourTode.hand,eK=document.createElement("canvas"),e6=eK.getContext("2d");eK.style.position="absolute",eK.style.top="0px",document.body.append(eK),on.resize(()=>{eK.width=innerWidth*DPR,eK.height=innerHeight*DPR,eK.style.width=innerWidth,eK.style.height=innerHeight}),trigger("resize");let e9=()=>{te(),ti(),requestAnimationFrame(e9)},eJ=()=>{if(e5.velocityHistory.length>=e5.velocityMemory&&e5.velocityHistory.shift(),void 0!==Mouse.position&&void 0!==Mouse.position[0]&&void 0!==e5.previous.x){let[e,t]=Mouse.position.map(e=>e/C),l=(e-e5.previous.x)*DPR,i=(t-e5.previous.y)*DPR,o={x:l,y:i};e5.velocityHistory.push(o);let a=e5.velocityHistory.reduce((e,t)=>({x:e.x+t.x,y:e.y+t.y}),{x:0,y:0}),r={x:a.x/e5.velocityHistory.length,y:a.y/e5.velocityHistory.length};e5.velocity.x=r.x,e5.velocity.y=r.y,e5.previous.x=e,e5.previous.y=t}},eQ=.9,te=()=>{for(let e of state.colourTode.atoms)tt(e)},tt=(e,t=!0)=>{for(let l of e.children)tt(l,!1);if(void 0!==e.hover&&tl(e),e.update(e),e5.content===e||0===e.dx&&0===e.dy)return;if(e.x+=e.dx,e.y+=e.dy,e.x=clamp(e.x,e.minX,e.maxX),e.y=clamp(e.y,e.minY,e.maxY),e.dx*=eQ,e.dy*=eQ,t&&t$(e)){tu(e);return}let[i,o]=Mouse.position.map(e=>e/C);e5.state.atommove&&e5.state.atommove(e,i,o)},tl=e=>{if(e.highlightedAtom=void 0,e5.content!==e||e5.state!==to.DRAGGING)return;void 0!==e.highlight&&(tx(e,e.highlight),e.highlight=void 0);let t=e.hover(e);if(void 0!==t){if(void 0===e.highlight){let l=t_(e,lb,{bottom:!0});l.hasBorder=!0,l.colour=Colour.Grey;let{x:i,y:o}=ty(t);l.x=i,l.y=o,l.width=t.width,l.height=t.height,e.highlight=l}e.highlightedAtom=t}},ti=()=>{for(let e of(e6.clearRect(0,0,eK.width,eK.height),e6.scale(C,C),state.colourTode.atoms))tc(e);e6.scale(1/C,1/C)};requestAnimationFrame(e9);let to={};HAND_RELEASE=.5,to.FREE={cursor:"auto",mousemove(e){let t=e.clientX/C,l=e.clientY/C,i=ts(t,l);if(void 0!==i){i.grabbable&&(Mouse.Left?i.grabbable&&i.draggable&&(tf(i,t,l),e5.pityStartX=e.clientX,e5.pityStartY=e.clientY,e5.pityStartT=Date.now(),tn(to.TOUCHING),to.TOUCHING.mousemove(e)):void 0!==i.cursor?tn(to.HOVER,i.cursor(i,to.HOVER)):i.dragOnly?tn(to.HOVER,"move"):tn(to.HOVER));return}let[o,a]=Mouse.position;o*=DPR,a*=DPR,!(ostate.view.right)&&!(astate.view.bottom)&&(Mouse.Left?tn(to.BRUSHING):Mouse.Middle?tn(to.PENCILLING):tn(to.BRUSH))},mousedown(e){state.worldBuilt&&(e5.voidingStart=[e.clientX,e.clientY],tn(to.VOIDING))},atommove(e,t,l){if(e.grabbable&&tp(e,t,l)){if(Mouse.Left){tf(e,t,l),tn(to.DRAGGING),e5.content=e5.content.drag(e5.content,t,l);return}void 0!==e.cursor?tn(to.HOVER,e.cursor(e,to.HOVER)):e.dragOnly?tn(to.HOVER,"move"):tn(to.HOVER)}},camerapan(){let[e,t]=Mouse.position;if(e*=DPR,t*=DPR,e>=state.view.left&&e<=state.view.right&&t>=state.view.top&&t<=state.view.bottom){tn(to.BRUSH);return}}};let ta=!0;to.VOIDING={cursor:"auto",mousemove(e){let t=e5.voidingStart,[l,i]=t,o=[e.clientX-l,e.clientY-i];Math.hypot(...o)>10&&(tn(to.FREE),to.FREE.mousemove(e))},mouseup(t){let i=WORLD_SIZE;if(setWorldSize(0),ta)f(.5,.5);else{let o=state.brush.colour;state.brush.colour=111*i,f(.5,.5),state.brush.colour=o,state.worldBuilt=!1,e.paused=!1,l.style["background-color"]=Colour.Void}ta=!ta,setWorldSize(i),tn(to.FREE)}},to.BRUSH={cursor:"crosshair",mousemove(e){let t=e.clientX/C,l=e.clientY/C,i=ts(t,l);if(void 0!==i){i.grabbable?void 0!==i.cursor?tn(to.HOVER,i.cursor(i,to.HOVER)):i.dragOnly?tn(to.HOVER,"move"):tn(to.HOVER):tn(to.FREE);return}let o=e.clientX*DPR,a=e.clientY*DPR;(!(o>=state.view.left)||!(o<=state.view.right)||!(a>=state.view.top)||!(a<=state.view.bottom))&&tn(to.FREE)},mousedown(e){tn(to.BRUSHING)},middlemousedown(e){tn(to.PENCILLING)},atommove(e,t,l){tp(e,t,l)&&(e.grabbable?tn(to.HOVER):tn(to.FREE))},camerapan(){let[e,t]=Mouse.position;e*=DPR,t*=DPR,(!(e>=state.view.left)||!(e<=state.view.right)||!(t>=state.view.top)||!(t<=state.view.bottom))&&tn(to.FREE)}},to.BRUSHING={cursor:"crosshair",mousemove(e){let t=e.clientX*DPR,l=e.clientY*DPR;(!(t>=state.view.left)||!(t<=state.view.right)||!(l>=state.view.top)||!(l<=state.view.bottom))&&tn(to.FREE)},mouseup(e){tn(to.BRUSH)},camerapan(){let[e,t]=Mouse.position;if(e*=DPR,t*=DPR,e>=state.view.left&&e<=state.view.right&&t>=state.view.top&&t<=state.view.bottom)return;let[l,i]=Mouse.position.map(e=>e/C),o=ts(l,i);if(void 0!==o){o.grabbable?void 0!==o.cursor?tn(to.HOVER,o.cursor(o,to.HOVER)):o.dragOnly?tn(to.HOVER,"move"):tn(to.HOVER):tn(to.FREE);return}tn(to.FREE)}},to.PENCILLING={cursor:"crosshair",mousemove:to.BRUSHING.mousemove,middlemouseup:to.BRUSHING.mouseup,camerapan:to.BRUSHING.camerapan},to.HOVER={cursor:"pointer",mousedown(e){let t=ts(e.clientX/C,e.clientY/C);void 0!==t&&t.grabbable&&(tf(t,e.clientX/C,e.clientY/C),t.dragOnly?(e5.pityStartX=e.clientX,e5.pityStartY=e.clientY,e5.pityStartT=Date.now(),e5.hasStartedDragging=!1,e5.touchButton=0,tn(to.TOUCHING,"move")):(e5.pityStartX=e.clientX,e5.pityStartY=e.clientY,e5.pityStartT=Date.now(),e5.hasStartedDragging=!1,e5.touchButton=0,tn(to.TOUCHING)))},mousemove(e){let t=e.clientX/C,l=e.clientY/C,i=ts(t,l);if(void 0!==i){i.grabbable?void 0!==i.cursor?tn(to.HOVER,i.cursor(i,to.HOVER)):i.dragOnly?tn(to.HOVER,"move"):tn(to.HOVER):tn(to.FREE);return}let o=e.clientX,a=e.clientY;if(o>=state.view.left&&o<=state.view.right&&a>=state.view.top&&a<=state.view.bottom){tn(to.BRUSH);return}tn(to.FREE)},atommove(e,t,l){if(tp(e,t,l))return;let i=ts(t,l);if(void 0!==i)return;let[o,a]=Mouse.position;if(o*=DPR,a*=DPR,o>=state.view.left&&o<=state.view.right&&a>=state.view.top&&a<=state.view.bottom){tn(to.BRUSH);return}tn(to.FREE)},rightmousedown(e){let t=ts(e.clientX/C,e.clientY/C);void 0!==t&&t.grabbable&&(tf(t,e.clientX/C,e.clientY/C),t.dragOnly?(e5.pityStartX=e.clientX,e5.pityStartY=e.clientY,e5.pityStartT=Date.now(),e5.hasStartedDragging=!1,e5.touchButton=2,tn(to.TOUCHING,"move")):(e5.pityStartX=e.clientX,e5.pityStartY=e.clientY,e5.pityStartT=Date.now(),e5.hasStartedDragging=!1,e5.touchButton=2,tn(to.TOUCHING)))}};let tr=(e,t)=>t?.6*e:e;to.TOUCHING={cursor:"pointer",mousemove(e){if(0===e.movementX&&0===e.movementY||2===e5.touchButton&&!e5.content.rightDraggable)return;let t=Math.hypot(e.clientX-e5.pityStartX,e.clientY-e5.pityStartY),l=e.clientX-e5.pityStartX,i=e.clientY-e5.pityStartY;if(e5.content.dragLockX||(e5.content.x=(e5.pityStartX+tr(l,e5.content.attached&&!e5.content.noDampen))/C*DPR+e5.offset.x),e5.content.dragLockY||(e5.content.y=(e5.pityStartY+tr(i,e5.content.attached&&!e5.content.noDampen))/C*DPR+e5.offset.y),e5.content.x=clamp(e5.content.x,e5.content.minX,e5.content.maxX),e5.content.y=clamp(e5.content.y,e5.content.minY,e5.content.maxY),t<15)return;let o=Date.now()-e5.pityStartT;if(o<100){let a=Math.hypot(e5.velocity.x,e5.velocity.y);if(a<=10)return}e5.content.dragLockX||(e5.content.x=e5.pityStartX/C*DPR+e5.offset.x),e5.content.dragLockY||(e5.content.y=e5.pityStartY/C*DPR+e5.offset.y),e5.content.x=clamp(e5.content.x,e5.content.minX,e5.content.maxX),e5.content.y=clamp(e5.content.y,e5.content.minY,e5.content.maxY);let r=e.clientX/C,n=e.clientY/C;if(0===e5.touchButton&&e5.content.draggable){tn(to.DRAGGING);let d=e5.content.attached&&!e5.content.dragOnly&&!e5.content.noDampen;e5.content=e5.content.drag(e5.content,r,n),e5.content.dragLockX||(e5.content.x=(e5.pityStartX+tr(l,d))/C+e5.offset.x),e5.content.dragLockY||(e5.content.y=(e5.pityStartY+tr(i,d))/C+e5.offset.y),e5.content.x=clamp(e5.content.x,e5.content.minX,e5.content.maxX),e5.content.y=clamp(e5.content.y,e5.content.minY,e5.content.maxY),to.DRAGGING.mousemove(e);return}if(2===e5.touchButton&&e5.content.rightDraggable){tn(to.DRAGGING);let h=e5.content.attached&&!e5.content.dragOnly&&!e5.content.noDampen;e5.content=e5.content.rightDrag(e5.content,r,n),e5.content.dragLockX||(e5.content.x=(e5.pityStartX+tr(l,h))/C+e5.offset.x),e5.content.dragLockY||(e5.content.y=(e5.pityStartY+tr(i,h))/C+e5.offset.y),e5.content.x=clamp(e5.content.x,e5.content.minX,e5.content.maxX),e5.content.y=clamp(e5.content.y,e5.content.minY,e5.content.maxY),to.DRAGGING.mousemove(e);return}let s=ts(r,n);if(void 0!==s){s.grabbable?void 0!==s.cursor?tn(to.HOVER,s.cursor(s,to.HOVER)):s.dragOnly?tn(to.HOVER,"move"):tn(to.HOVER):tn(to.FREE);return}let c=e.clientX*DPR,u=e.clientY*DPR;if(c>=state.view.left&&c<=state.view.right&&u>=state.view.top&&u<=state.view.bottom){tn(to.BRUSH);return}tn(to.FREE)},mouseup(e){if(0!==e5.touchButton)return;e5.clickContent.click(e5.clickContent),e5.clickContent.dx=0,e5.clickContent.dy=0,e5.clickContent=void 0,e5.content.attached&&(e5.content.x=e5.pityStartX/C*DPR+e5.offset.x,e5.content.y=e5.pityStartY/C*DPR+e5.offset.y),e5.content.dx=0,e5.content.dy=0,e5.content=void 0;let t=e.clientX/C,l=e.clientY/C,i=ts(t,l);void 0!==i?void 0!==i.cursor?tn(to.HOVER,i.cursor(i,to.HOVER)):i.dragOnly?tn(to.HOVER,"move"):tn(to.HOVER):tn(to.FREE)},rightmouseup(e){if(2!==e5.touchButton)return;e5.clickContent.rightClick(e5.clickContent),e5.clickContent.dx=0,e5.clickContent.dy=0,e5.clickContent=void 0,e5.content.attached&&(e5.content.x=e5.pityStartX/C*DPR+e5.offset.x,e5.content.y=e5.pityStartY/C*DPR+e5.offset.y),e5.content.dx=0,e5.content.dy=0,e5.content=void 0;let t=e.clientX/C,l=e.clientY/C,i=ts(t,l);void 0!==i?void 0!==i.cursor?tn(to.HOVER,i.cursor(i,to.HOVER)):i.dragOnly?tn(to.HOVER,"move"):tn(to.HOVER):tn(to.FREE)}},to.DRAGGING={cursor:"move",mousemove(e){e5.hasStartedDragging||(e5.hasStartedDragging=!0,e5.content=e5.content.drag(e5.content,e.clientX/C*DPR,e.clientY/C*DPR));let t=e5.content.x,l=e5.content.y;e5.content.dragLockX||(e5.content.x=e.clientX/C*DPR+e5.offset.x),e5.content.dragLockY||(e5.content.y=e.clientY/C*DPR+e5.offset.y),e5.content.x=clamp(e5.content.x,e5.content.minX,e5.content.maxX),e5.content.y=clamp(e5.content.y,e5.content.minY,e5.content.maxY);let i=e5.content.x-t,o=e5.content.y-l;e5.content.move(e5.content,i,o)},mouseup(e){if(0!==e5.touchButton)return;e5.hasStartedDragging=!0,e5.content.dragLockX||(e5.content.dx=e5.velocity.x*HAND_RELEASE),e5.content.dragLockY||(e5.content.dy=e5.velocity.y*HAND_RELEASE),e5.content.drop(e5.content),void 0!==e5.content.highlightedAtom&&(e5.content.place(e5.content,e5.content.highlightedAtom),void 0!==e5.content.highlight&&(tx(e5.content,e5.content.highlight),e5.content.highlight=void 0)),e5.content=void 0;let t=e.clientX/C,l=e.clientY/C,i=ts(t,l);void 0!==i&&i.grabbable?void 0!==i.cursor?tn(to.HOVER,i.cursor(i,to.HOVER)):i.dragOnly?tn(to.HOVER,"move"):tn(to.HOVER):tn(to.FREE)},rightmouseup(e){if(2!==e5.touchButton)return;e5.hasStartedDragging=!0,e5.content.dragLockX||(e5.content.dx=e5.velocity.x*HAND_RELEASE),e5.content.dragLockY||(e5.content.dy=e5.velocity.y*HAND_RELEASE),e5.content.drop(e5.content),void 0!==e5.content.highlightedAtom&&(e5.content.place(e5.content,e5.content.highlightedAtom),void 0!==e5.content.highlight&&(tx(e5.content,e5.content.highlight),e5.content.highlight=void 0)),e5.content=void 0;let t=e.clientX/C,l=e.clientY/C,i=ts(t,l);void 0!==i&&i.grabbable?void 0!==i.cursor?tn(to.HOVER,i.cursor(i,to.HOVER)):i.dragOnly?tn(to.HOVER,"move"):tn(to.HOVER):tn(to.FREE)}};let tn=(e,t=e.cursor)=>{void 0!==e5.content&&void 0!==e5.content.cursor&&(t=e5.content.cursor(e5.content,e)),eK.style.cursor=t,e5.state=e};on.mousemove(e=>e5.state.mousemove?e5.state.mousemove(e):void 0),on.mousedown(e=>{0===e.button&&e5.state.mousedown&&e5.state.mousedown(e),1===e.button&&e5.state.middlemousedown&&e5.state.middlemousedown(e),2===e.button&&e5.state.rightmousedown&&e5.state.rightmousedown(e)}),on.mouseup(e=>{0===e.button&&e5.state.mouseup&&e5.state.mouseup(e),1===e.button&&e5.state.middlemouseup&&e5.state.middlemouseup(e),2===e.button&&e5.state.rightmouseup&&e5.state.rightmouseup(e)}),e5.state=to.FREE;let td={x:0,y:0,grab:(e,t,l,i=e)=>i,touch:(e,t=e)=>t},th=({grabbable:e=!0,draggable:t=!0,click:l=()=>{},rightClick:i=()=>{},drag:o=e=>e,rightDrag:a=e=>e,move:r=()=>{},drop:n=()=>{},draw:d=()=>{},update:h=()=>{},offscreen:s=()=>!1,overlaps:c=()=>!1,grab:u=e=>e,touch:g=e=>e,highlighter:$=!1,hover:p=()=>{},place:f=()=>{},x:v=0,y:m=0,dx:_=0,dy:w=0,maxX:b=1/0,minX:S=-1/0,maxY:R=1/0,minY:C=-1/0,size:T=40,colour:k=Colour.splash(999),children:E=[],parent:z=td,width:D=T,height:O=T,construct:P=()=>{},hasInner:A=!0,...I}={},...B)=>{let L={highlighter:$,place:f,hover:p,hasInner:A,move:r,drop:n,maxX:b,minX:S,maxY:R,minY:C,update:h,construct:P,draggable:t,width:D,height:O,touch:g,parent:z,children:E,draw:d,grabbable:e,click:l,drag:o,overlaps:c,offscreen:s,grab:u,x:v,y:m,dx:_,dy:w,size:T,colour:k,rightClick:i,rightDrag:a,...I};return L.construct(L,...B),L},ts=(e,t)=>{e*=DPR,t*=DPR;for(let l=state.colourTode.atoms.length-1;l>=0;l--){let i=state.colourTode.atoms[l];if(i.justVisual)continue;let o=tp(i,e,t);if(void 0!==o)return o}},tc=e=>{for(let t of e.children)t.behindParent&&tc(t);for(let l of(e.behindChildren&&e.draw(e),e.children))l.behindParent||tc(l);e.behindChildren||e.draw(e)},tu=e=>{let t=state.colourTode.atoms.indexOf(e);state.colourTode.atoms.splice(t,1)},tg=e=>{state.colourTode.atoms.push(e)},t$=e=>{for(let t of e.children)if(!t$(t))return!1;return e.offscreen(e)},tp=(e,t,l)=>{if(!e.behindChildren&&e.overlaps(e,t,l))return e;for(let i=e.children.length-1;i>=0;i--){let o=e.children[i];if(o.behindParent)continue;let a=tp(o,t,l);if(a)return a}if(e.behindChildren&&e.overlaps(e,t,l))return e;for(let r=e.children.length-1;r>=0;r--){let n=e.children[r];if(!n.behindParent)continue;let d=tp(n,t,l);if(d)return d}},tf=(e,t,l)=>{let i=e,o=e.touch(e);if(o!==i){let a=o.touch(o,t,l,i);i=o,o=a}e5.clickContent=o;let r=e,n=e.grab(e,t,l);if(void 0===n)return;if(n!==r){let d=n.grab(n,t,l,r);r=n,n=d}e5.content=n;let{x:h,y:s}=ty(n,{forceAbsolute:!0});return e5.offset.x=h-t*DPR,e5.offset.y=s-l*DPR,n.dx=0,n.dy=0,e.stayAtBack?tm(n):tv(n),n},tv=e=>{if(e.parent===td)tu(e),tg(e);else{let t=e.parent.children.indexOf(e);e.parent.children.splice(t,1),e.parent.children.push(e),e.parent.stayAtBack?tm(e.parent):tv(e.parent)}},tm=e=>{if(e.parent===td){let t=state.colourTode.atoms.indexOf(e);state.colourTode.atoms.splice(t,1),state.colourTode.atoms.unshift(e)}else{let l=e.parent.children.indexOf(e);e.parent.children.splice(l,1),e.parent.children.unshift(e),e.parent.stayAtBack?tm(e.parent):tv(e.parent)}},ty=(e,{forceAbsolute:t=!1}={})=>{let{x:l,y:i}=e;if(t||void 0===e.parent||e.hasAbsolutePosition)return{x:l,y:i};let{x:o,y:a}=ty(e.parent);return{x:l+o,y:i+a}},t_=(e,t,{bottom:l=!1}={})=>{let i=th(t);return l?e.children.unshift(i):e.children.push(i),i.parent=e,i},tx=(e,t,{quiet:l=!1}={})=>{let i=e.children.indexOf(t);if(-1===i){if(l)return;throw Error("Can't delete child of atom because I can't find it!")}e.children.splice(i,1),t.parent=td},tw=(e,t)=>{if(void 0===t)throw Error("Can't give child because child is undefined");if(void 0===e)throw Error("Can't give child because parent is undefined");tu(t),t.stayAtBack||t.behindOtherChildren?e.children.unshift(t):e.children.push(t),t.parent=e},tb=(e,t)=>{if(e5.content===t){let{x:l,y:i}=ty(e);e5.offset.x+=l,e5.offset.y+=i}tx(e,t),tg(t)},t0=3;borderColours=PREBUILT_BORDER_COLOURS;let tS=borderColours.clone;tS[999]=Colour.splash(999);let tR={draw(e){let{x:t,y:l}=ty(e),i=Math.round(t),o=Math.round(l),a=Math.round(e.width),r=Math.round(e.height),n=Math.round(e.width/2);if(e.hasBorder){if(e.hasInner){let d=t0;void 0===e.borderColour?(e6.fillStyle=Colour.splash(e.colour.splash),e.isTool?(e6.fillStyle=Colour.splash(e.colour.splash),d*=1.5):e.width===e.height&&(d*=1.5)):e6.fillStyle=e.borderColour,e6.beginPath(),e6.rect(i,o,a,r),void 0!==e.stamp&&e6.arc(i+n,o+n,Math.round((lv.size-t7/2)/2),0,2*Math.PI),e.isGradient?e6.putImageData(e.gradient,i*C,o*C):(e6.fill("evenodd"),e6.beginPath(),e6.fillStyle=e.colour,e6.rect(i+d,o+d,a-2*d,r-2*d),void 0!==e.stamp&&e6.arc(i+n,o+n,Math.round((lv.size-t7/2)/2)+d,0,2*Math.PI),e6.fill("evenodd"))}else void 0===e.borderColour?e6.strokeStyle=borderColours[e.colour.splash]:e6.strokeStyle=e.borderColour,i=Math.round(t+.5)-.5,o=Math.round(l+.5)-.5,e6.lineWidth=t0,e6.strokeRect(i,o,a,r)}else e6.fillStyle=e.colour,e6.fillRect(i,o,a,r)},offscreen(e){let{x:t,y:i}=ty(e),o=t+e.width,a=i+e.height;return!!(o<0)||!!(a<0)||!!(t>l.width)||!!(i>l.height)},overlaps(e,t,l){let{x:i,y:o}=ty(e),a=t0;(e.isTool||e.isSquare||e.isTallRectangle)&&(a*=1.5);let r=i+e.width,n=o+e.height;return!(tr)&&!(l>n)}},tC={draw(e){let{x:t,y:l}=ty(e),i=t+e.width/2,o=l+e.height/2,a=e.width/2;if(e.hasBorder){e.isTool&&(e.borderColour=tS[e.colour.splash]),e6.fillStyle=void 0!==e.borderColour?e.borderColour:Colour.Void,e6.beginPath(),e6.arc(i,o,a,0,2*Math.PI),e6.fill();let r=void 0!==e.borderScale?e.borderScale:1;a=e.width/2-1.5*t0*r}e6.fillStyle=e.colour,e6.beginPath(),e6.arc(i,o,a,0,2*Math.PI),e6.fill()},offscreen:tR.offscreen,overlaps:tR.overlaps},tT=(e,[t,l],i=!1)=>{for(let o of e.cellAtoms){i&&(o=o.slot);let{x:a,y:r}=ty(o);if(a===t&&r===l)return!0}return!1},tk=(e,[t,l],i=!1)=>{for(let o of e.cellAtoms){i&&(o=o.slot);let{x:a,y:r}=ty(o);if(a===t&&r===l&&(o.isLeftSlot||o.isSlot))return!0}return!1},tE=e=>{if("number"==typeof e)state.brush.colour=e,squareTool.toolbarNeedsColourUpdate=!0,squareTool.value=makeArrayFromSplash(e);else{let t=makeDiagramCell({content:e});state.brush.colour=makeDiagram({left:[t]}),squareTool.value=t.content,squareTool.toolbarNeedsColourUpdate=!0}},tz={isSquare:!0,hasBorder:!0,draw(e){!e.value.isDiagram&&tR.draw(e)},overlaps:tR.overlaps,offscreen:tR.offscreen,touch:e=>(tE(e.value),e),click(e){e.joins.length>0?e.parent!==td&&e.parent.isPaddle||(e.joinExpanded?e.joinUnepxand(e):e.joinExpand(e)):e.value.isDiagram||(e.expanded?e.unexpand(e):e.parent!==td&&e.parent.isPaddle||e.expand(e)),tE(e.value)},expand(e){e.expanded=!0,e.createPicker(e),e.value.channels.some(e=>void 0===e)&&lG("triangle")},unexpand(e){e.expanded=!1,e.redExpanded=e.red&&e.red.expanded,e.greenExpanded=e.green&&e.green.expanded,e.blueExpanded=e.blue&&e.blue.expanded,e.deletePicker(e)},createPicker(e){let t=t_(e,lC);t.width+=t7,e.pickerHandle=t,e.pickerHandle.behindParent=!0;let l=t_(e,t1);if(e.pickerPad=l,void 0!==e.value.channels[2]){if(void 0===e.value.channels[2].variable){let i=t_(e,tV);i.channelSlot="blue",i.x+=t3+3*(tz.size+t3),i.value=e.value.channels[2],i.needsColoursUpdate=!0,e.blue=i,i.deletedOptions=e.deletedBlueOptions,e.blueExpanded&&e.blue.click(e.blue),e.blue.attached=!0}else{let o=e.variableAtoms[2];o.behindOtherChildren=!1,tg(o),tw(e,o),o.variable="blue",o.x=(t3+tz.size)*3+(tz.size+t3)/2-o.width/3,o.y=e.height/2-o.height/2,o.attached=!0,e.blue=o}}if(void 0!==e.value.channels[1]){if(void 0===e.value.channels[1].variable){let a=t_(e,tV);a.channelSlot="green",a.x+=t3+2*(tz.size+t3),a.value=e.value.channels[1],a.needsColoursUpdate=!0,e.green=a,a.deletedOptions=e.deletedGreenOptions,e.greenExpanded&&e.green.click(e.green),e.green.attached=!0}else{let r=e.variableAtoms[1];r.behindOtherChildren=!1,tg(r),tw(e,r),r.variable="green",r.x=(t3+tz.size)*2+(tz.size+t3)/2-r.width/3,r.y=e.height/2-r.height/2,r.attached=!0,e.green=r}}if(void 0!==e.value.channels[0]){if(void 0===e.value.channels[0].variable){let n=t_(e,tV);n.channelSlot="red",n.x+=t3+tz.size+t3,n.value=e.value.channels[0],n.needsColoursUpdate=!0,e.red=n,n.deletedOptions=e.deletedRedOptions,e.redExpanded&&e.red.click(e.red),e.red.attached=!0}else{let d=e.variableAtoms[0];d.behindOtherChildren=!1,tg(d),tw(e,d),d.x=t3+tz.size+(tz.size+t3)/2-d.width/3,d.y=e.height/2-d.height/2,d.attached=!0,e.red=d}}},deletePicker(e){tx(e,e.pickerPad),tx(e,e.pickerHandle),e.red&&(e.deletedRedOptions=e.red.options,tx(e,e.red)),e.green&&(e.deletedGreenOptions=e.green.options,tx(e,e.green)),e.blue&&(e.deletedBlueOptions=e.blue.options,tx(e,e.blue))},receiveNumber(e,t,l=t.channel,{expanded:i,numberAtom:o}={}){if(e.redExpanded=e.red&&e.red.expanded,e.greenExpanded=e.green&&e.green.expanded,e.blueExpanded=e.blue&&e.blue.expanded,void 0===e.variableAtoms&&(e.variableAtoms=[void 0,void 0,void 0]),void 0!==t&&void 0!==t.variable?e.variableAtoms[l]=o:e.variableAtoms[l]=void 0,void 0!==i){let a=t8[l];e[`${a}Expanded`]=i}if(e.value.channels[l]=t,e.deletePicker(e),e.createPicker(e),e.needsColoursUpdate=!0,e.colourTicker=1/0,e.parent!==td){let r=e.parent;lc(r)}let n=makeDiagramCell({content:e.value});state.brush.colour=makeDiagram({left:[n]}),squareTool.toolbarNeedsColourUpdate=!0,lN.toolbarNeedsColourUpdate=!0,lU.toolbarNeedsColourUpdate=!0,lY.toolbarNeedsColourUpdate=!0},construct(e){e.needsColoursUpdate=!0,"number"==typeof state.brush.colour?e.value=makeArrayFromSplash(state.brush.colour):e.value=Q(state.brush.colour.left[0].content),e.colourId=0,e.dcolourId=1,e.colourTicker=1/0,e.joins=[],e.joinColourIds=[],e.variableAtoms=[],e.gradient=new ImageData(e.width*C,e.height*C),e.headGradient=new ImageData(e.width*C,e.height*C)},updateGradient(e){let t=Q(e.value);if(t.joins=[],e.colours=M(t),e.isGradient=!0,e.joins.length>0&&!e.joinExpanded){let l=[];for(let i of e.joins)i.updateGradient(i),l.push(i.gradient);e.headGradient=tI({colours:e.colours,width:e.width*C,height:e.height*C,stamp:e.value.stamp,gradient:e.headGradient});let o=[e.headGradient,...l];e.gradient=tA({gradients:o,width:e.width*C,height:e.height*C,stamp:e.value.stamp,mergedGradient:e.gradient})}else e.gradient=tI({colours:e.colours,width:e.width*C,height:e.height*C,gradient:e.gradient,stamp:e.value.stamp})},update(e){if(e.value.isDiagram){if(void 0===e.multiAtoms||0===e.multiAtoms.length){e.multiAtoms=[];let t=e.value,[l,i]=eo(t),o=e.width/l,a=e.height/i;for(let r of t.left){let n=t_(e,tz);n.x=r.x*o,n.y=r.y*a,n.width=r.width*o,n.height=r.height*a,n.value=r.content,e.multiAtoms.push(n)}}}else e.needsColoursUpdate&&(e.updateGradient(e),e.needsColoursUpdate=!1);let{x:d,y:h}=ty(e);if(e.highlightedAtom=void 0,e5.content===e&&e5.state===to.DRAGGING){let s=d,c=h,u=d+e.width,g=h+e.height;if(void 0!==e.highlight&&(tx(e,e.highlight),e.highlight=void 0),void 0===e.highlightedAtom){let $=lg();for(let p of $){if(p===e||!p.isSquare)continue;p.joins.length>0&&p.joinExpanded&&(p=p.pickerPad);let{x:f,y:v}=ty(p),m=f,_=f+p.width,w=v,b=v+p.height;if(!(s>_)&&!(ub)){if(p.isPicker)e.highlightedAtom=p.parent;else{if(p.parent!==td)continue;e.highlightedAtom=p}e.highlight=t_(e,lb,{bottom:!0}),e.highlight.hasBorder=!0,e.highlight.hasInner=!1,e.highlight.width=p.width,e.highlight.height=p.height,e.highlight.x=f,e.highlight.y=v;break}}}if(void 0===e.highlightedAtom)for(let S of paddles){if(!S.expanded)continue;let{x:R,y:C}=ty(S),T=R,k=R+S.width,E=C,z=C+S.height;if(!(s>k)&&!(uz)&&!(gT+S.rightTriangle.x?(e.highlight=t_(e,lb,{bottom:!0}),e.highlight.hasBorder=!0,e.highlight.colour=Colour.Grey,e.highlight.x=P,e.highlight.y=A,e.highlight.width=S.dummyRight.width,e.highlight.height=S.dummyRight.height,e.highlightedSide="right",e.highlightedAtom=S):(e.highlight=t_(e,lb,{bottom:!0}),e.highlight.hasBorder=!0,e.highlight.colour=Colour.Grey,e.highlight.x=D,e.highlight.y=O,e.highlight.width=S.dummyLeft.width,e.highlight.height=S.dummyLeft.height,e.highlightedSide="left",e.highlightedAtom=S);break}if(void 0!==S.rightTriangle&&s>T+S.rightTriangle.x){let I=1/0,B,L;for(let G of S.cellAtoms){let N=G.slot,{x:U,y:Y}=ty(N),X=U,H=U+N.width,V=Y,j=Y+N.height,W=[X,V],q=[X-N.width,V],Z=[X,V-N.height],F=[H,V],M=[X,j],K=Math.hypot(d-W[0],h-W[1]);void 0===G.slotted&&tk(S,W,!0)&&Ke,t.grab=e=>e.parent,t.dragOnly=!0;let l=t_(e,t1);e.pickerHandle=l,l.width=lC.height,l.x=e.width/2-l.width/2,l.height=lC.width,l.y=e.height,l.touch=e=>e,l.grab=e=>e.parent,l.dragOnly=!0;for(let i=0;ie.parent}if(e.needsColoursUpdate=!0,e.colourTicker=1/0,void 0!==e.multiAtoms)for(let a of e.multiAtoms)tv(a);e.attached=!1},joinUnepxand(e){e.joinExpanded=!1,tx(e,e.pickerPad),tx(e,e.pickerHandle);for(let t=0;t0&&e.joinExpanded)return e;if(e.isJoiner){let t=e.parent.joins.indexOf(e);e.parent.joins.splice(t,1),e.parent.value.joins.splice(t,1),e.parent.joinUnepxand(e.parent),e.parent.joins.length>0&&e.parent.joinExpand(e.parent),tb(e.parent,e),e.isJoiner=!1,e.touch=tz.touch}if(e.attached){let l=e.parent;if(e.slottee){if(e.attached=!1,e.slottee=!1,tb(l,e),e.cellAtom.slotted=void 0,e.cellAtom.isLeftSlot){tx(l,e.cellAtom);let i=l.cellAtoms.indexOf(e.cellAtom);l.cellAtoms.splice(i,1)}return e.cellAtom=void 0,la(l),e}let{x:o,y:a}=e;e.attached=!1,tb(l,e);let r=l.cellAtoms.indexOf(e);if(l.cellAtoms.splice(r,1),e.slot=void 0,void 0!==l.rightTriangle&&void 0!==e.slotted){let n=t_(l,li,{bottom:!0});n.x=o,n.y=a,n.isLeftSlot=!0,l.cellAtoms.push(n),n.isSlot=!1,n.slotted=e.slotted,n.slotted.cellAtom=n,e.slotted=void 0}la(l)}return e},size:40,expanded:!1},tD=(e,t)=>{let l=.5,i=.5;return[[1,0],[1,.5],[1,1],[.5,0],[.5,.5],[.5,1],[0,0],[0,.5],[0,1],]},tO=(e,t,l)=>{let i=[];for(let[o,a]of l){let r=[o-e,a-t],n=Math.hypot(...r);i.push(n)}return i},tP=e=>{let t=[];for(let l of e)t.push(l**2);return t},tA=({gradients:e,width:t,height:l,mergedGradient:i=new ImageData(t,l),stamp:o})=>{[t,l]=[t,l].map(e=>Math.round(e));let a=t*l*4;i.data.length!==a&&(i=new ImageData(t,l));let r=e.length,n=2*Math.PI/r,d=-n/2-Math.PI/2;2===r&&(d-=Math.PI/4);let h=e.map((e,t)=>t*n+n),s=0;for(let c=0;c2*Math.PI;)p-=2*Math.PI;let f=0,v=!1,m=0;for(;p>h[f];)if(++f>=e.length){f=0;break}let _=h[f]-p,w=f-1<0?h.length-1:f-1,b=h[w],S=b-p,R=f+1>=h.length?0:f+1,C;.05>Math.abs(S)?(v=!0,m=-S/.05/2+.5,C=w):.05>Math.abs(_)?(v=!0,m=_/.05/2+.5,C=R):p<.05&&(v=!0,m=p/.05/2+.5,C=w),v?(i.data[s]=e[f].data[s]*m+e[C].data[s]*(1-m),i.data[s+1]=e[f].data[s+1]*m+e[C].data[s+1]*(1-m),i.data[s+2]=e[f].data[s+2]*m+e[C].data[s+2]*(1-m),i.data[s+3]=e[f].data[s+3]*m+e[C].data[s+3]*(1-m)):(i.data[s]=e[f].data[s],i.data[s+1]=e[f].data[s+1],i.data[s+2]=e[f].data[s+2],i.data[s+3]=e[f].data[s+3]),s+=4}return i},tI=({colours:e,width:t,height:l,gradient:i=new ImageData(t,l),stamp:o})=>{[t,l]=[t,l].map(e=>Math.round(e));let a=t*l*4;i.data.length!==a&&(i=new ImageData(t,l));let r=1/0,n=-1/0,d=1/0,h=-1/0,s=1/0,c=-1/0;for(let u of e){let[g,$,p]=getRGB(u);gn&&(n=g),$h&&(h=$),pc&&(c=p)}let f=(e,t,l)=>Colour.splash((1===e?n:r)+(1===t?h:d)+(1===l?c:s)),v=[f(0,0,1),f(0,0,1),f(0,0,1),f(0,1,1),f(1,0,0),f(1,0,0),f(0,1,0),f(0,1,0),f(1,0,0)],m=tD(t,l),_=0;for(let w=0;we+t);for(let k=0;k<9;k++){let E=R[k],z=v[k];[0,1,2].forEach(e=>C[e]+=E*z[e])}let D=C.map(e=>e/T);if("circle"===o&&b>=t/4&&b<3*t/4&&w>=l/4&&w<3*l/4?i.data[_+3]=0:(i.data[_]=D[0],i.data[_+1]=D[1],i.data[_+2]=D[2],i.data[_+3]=255),(_+=4)>=i.data.length)break}return i},tB={size:tz.size,width:tz.size*Math.sqrt(3)/2,draw(e){let{x:t,y:l}=ty(e),i=e.size;e.isTool&&(i-=2.5*t0),e.isTool||(i-=2);let o=i,a=i*Math.sqrt(3)/2,r=t,n=l+1;e.isTool&&(n+=1.25*t0);let d=n+o,h=n+o/2;e6.fillStyle=e.colour;let s=new Path2D;s.moveTo(r,n),s.lineTo(r+a,h),s.lineTo(r,d),s.closePath(),e6.fillStyle=e.colour,e6.fill(s),e.hasBorder&&(e6.lineWidth=1.5*t0,e6.strokeStyle=e.borderColour,e.isTool&&(e6.strokeStyle=tS[e.colour.splash]),e6.stroke(s))},overlaps(e,t,l){let{x:i,y:o}=ty(e),a=e.size,r=e.size*Math.sqrt(3)/2,n=i,d=o;return!(tn+r)&&!(l>d+a)},offscreen(e){let{x:t,y:i}=ty(e),o=e.size,a=e.size*Math.sqrt(3)/2,r=t,n=i;return!!(r+a<0)||!!(n+o<0)||!!(r>l.width)||!!(n>l.height)}},tL={size:tz.size,draw(e){let{x:t,y:l}=ty(e),i=e.size,o=e.size*Math.sqrt(3)/2,a=e.size-o,r=t,n=l+a/2,d=n+o;e6.fillStyle=e.colour;let h=new Path2D;h.moveTo(r,d),h.lineTo(r+i/2,n),h.lineTo(r+i,d),h.closePath(),e6.fillStyle=e.colour,e6.fill(h),e.hasBorder&&(e6.lineWidth=1.5*t0,e6.strokeStyle=e.borderColour,e6.stroke(h))},overlaps(e,t,l){let{x:i,y:o}=ty(e),a=e.size,r=e.size*Math.sqrt(3)/2,n=i,d=o;return!(tn+a)&&!(l>d+r)},offscreen(e){let{x:t,y:i}=ty(e),o=e.size,a=e.size*Math.sqrt(3)/2,r=t,n=i;return!!(r+o<0)||!!(n+a<0)||!!(r>l.width)||!!(n>l.height)}},tG={size:tz.size,draw(e){let{x:t,y:l}=ty(e),i=e.size,o=e.size*Math.sqrt(3)/2,a=e.size-o,r=t,n=l+a/2;e6.fillStyle=e.colour;let d=new Path2D;d.moveTo(r,n),d.lineTo(r+i/2,n+o),d.lineTo(r+i,n),d.closePath(),e6.fillStyle=e.colour,e6.fill(d),e.hasBorder&&(e6.lineWidth=1.5*t0,e6.strokeStyle=e.borderColour,e6.stroke(d))},overlaps(e,t,l){let{x:i,y:o}=ty(e),a=e.size,r=e.size*Math.sqrt(3)/2,n=i,d=o;return!(tn+a)&&!(l>d+r)},offscreen(e){let{x:t,y:i}=ty(e),o=e.size,a=e.size*Math.sqrt(3)/2,r=t,n=i;return!!(r+o<0)||!!(n+a<0)||!!(r>l.width)||!!(n>l.height)}},tN={size:tz.size,width:tz.size*Math.sqrt(3)/2,draw(e){let{x:t,y:l}=ty(e),i=e.size;e.isTool&&(i-=2.5*t0),e.isTool||(i-=2);let o=i,a=i*Math.sqrt(3)/2,r=t,n=r+a,d=l+1;e.isTool&&(d+=1.25*t0);let h=d+o,s=d+o/2;e6.fillStyle=e.colour;let c=new Path2D;c.moveTo(n,d),c.lineTo(r,s),c.lineTo(n,h),c.closePath(),e6.fillStyle=e.colour,e6.fill(c),e.hasBorder&&(e6.lineWidth=1.5*t0,e6.strokeStyle=e.borderColour,e.isTool&&(e6.strokeStyle=tS[e.colour.splash]),e6.stroke(c))},overlaps(e,t,l){let{x:i,y:o}=ty(e),a=e.size,r=e.size*Math.sqrt(3)/2,n=i,d=o;return!(tn+r)&&!(l>d+a)},offscreen(e){let{x:t,y:i}=ty(e),o=e.size,a=e.size*Math.sqrt(3)/2,r=t,n=i;return!!(r+a<0)||!!(n+o<0)||!!(r>l.width)||!!(n>l.height)}},tU={behindOtherChildren:!0,expanded:!1,draw(e){"right"===e.direction?tB.draw(e):"down"===e.direction?tG.draw(e):"up"===e.direction?tL.draw(e):"left"===e.direction?tN.draw(e):tB.draw(e)},colour:Colour.splash(999),overlaps:tB.overlaps,offscreen:tB.offscreen,size:tz.size,width:tB.width,direction:"right",click(e){if(e.parent.isPaddle){e.parent.pinhole.locked=!e.parent.pinhole.locked,lc(e.parent);return}e.expanded?e.unexpand(e):e.expand(e)},expand(e){e.pad=t_(e,l0),e.handle=t_(e,lS),e.expanded=!0,e.upPick=t_(e,lk),e.downPick=t_(e,lE),"up"===e.direction&&(e.upPick.value=!0),"down"===e.direction&&(e.downPick.value=!0)},unexpand(e){tx(e,e.pad),tx(e,e.handle),tx(e,e.upPick),tx(e,e.downPick),e.expanded=!1},highlighter:!0,hover(e){if(e.highlightedSlot=void 0,"right"===e.direction){let{x:t,y:l}=ty(e),i=t,o=l,a=t+e.width,r=l+e.height;for(let n of paddles){if(!n.expanded||n.pinhole.locked||void 0!==n.rightTriangle)continue;let{x:d,y:h}=ty(n),s=d,c=d+n.width,u=h,g=h+n.height;if(!(i>c)&&!(ag)&&!(rT)&&!(mE)&&!(_G||mU)continue;let Y=["red","green","blue"].filter(e=>void 0===A[e]);if(0===Y.length)continue;let{x:X,y:H}=ty(A);for(let V of Y){let j=tH[V],W=X+A.size+2*t7+j*(tz.size+t3),q=H+t7,Z=Math.hypot($-W,p-q);Ze.parent,colour:Colour.Grey,width:t3+3*(tz.size+t3),height:tz.size,y:0,x:tz.size+t3,dragOnly:!0,isPicker:!0},tH={red:0,green:1,blue:2},t8=["red","green","blue",],tV={hasBorder:!0,draw:tR.draw,overlaps:tR.overlaps,offscreen:tR.offscreen,width:tz.size,y:(tz.size-tY)/2,height:tY,grab:e=>e,rightDraggable:!0,rightDrag(e){let t=th(tV);tg(t);let{x:l,y:i}=ty(e);return e5.offset.x-=e.x-l,e5.offset.y-=e.y-i,t.value=H(e.value),e.expanded&&(t.createOptions(t),t.expanded=!0),t},drag(e){if(e.parent.isSquare){let t=e.parent;t[e.channelSlot]=void 0;let l=tH[e.channelSlot];t.receiveNumber(t,void 0,l),tb(t,e),e.channelSlot=t8[e.value.channel],e.updateColours(e),e.attached=!1,e.needsColoursUpdate=!0,e.colourTicker=1/0}else if(e.parent.isTallRectangle){let i=e.parent;tb(i,e),i.operationAtoms[e.highlightedSlot]=void 0;let o="padTop"===e.highlightedSlot?"add":"subtract";if(i.value[o]=void 0,e.attached=!1,i.expanded)i.unexpand(i),i.expand(i);else{let a="padTop"===e.highlightedSlot?"handleTop":"handleBottom";tx(i,i[a],{quiet:!0}),tx(i,i[e.highlightedSlot],{quiet:!0}),i.expand(i),i.unexpand(i)}}else if(e.parent.isPaddle){let r=e.parent;r.chance=void 0,tb(r,e),la(r)}return e},construct(e){let t=Random.Uint8%3;e.value=X({values:[!1,!1,!1,!1,!1,!1,!1,!1,!1,!0],channel:t}),e.needsColoursUpdate=!0,e.colourId=0,e.dcolourId=1,e.colourTicker=1/0,e.selectionBack=t_(e,tM);let l=t_(e,t4);e.selectionTop=l,e.selectionTop.isTop=!0,l.dragOnly=!1;let i=t_(e,t4);e.selectionBottom=i,e.selectionBottom.isTop=!1,i.dragOnly=!1,e.positionSelection(e)},positionSelection(e,t,l,i,o){if(e.expanded){let a=tX;e.selectionTop.y=l-e.selectionTop.height,e.selectionBottom.y=t+a-e.selectionBottom.height,e.selectionTop.minY=i-e.selectionTop.height,e.selectionTop.maxY=e.selectionBottom.y-a,e.selectionBottom.minY=e.selectionTop.y+a,e.selectionBottom.maxY=o-e.selectionBottom.height+a}else e.selectionTop.y=-e.selectionTop.height,e.selectionBottom.y=e.height;e.positionSelectionBack(e);let r=e.children.indexOf(e.selectionTop);e.children.splice(r,1),e.children.push(e.selectionTop);let n=e.children.indexOf(e.selectionBottom);if(e.children.splice(n,1),e.children.push(e.selectionBottom),e.parent.isTallRectangle){let d="padTop"===e.highlightedSlot?"add":"subtract";e.parent.value[d]=e.value}},positionSelectionBack(e){e.selectionBack.x=-t4.height,e.selectionBack.y=e.selectionTop.y,e.selectionBack.height=e.selectionBottom.y-e.selectionTop.y+e.selectionTop.height,e.selectionBack.width=e.width+2*t4.height},update(e){if(e.expanded&&e.needsColoursUpdate&&(e.needsColoursUpdate=!1,e.isGradient=!0,e.gradient=tI({colours:e.colours,width:e.width*C,height:e.height*C,gradient:e.gradient}),e.updateColours(e)),!e.expanded&&e.needsColoursUpdate){if(e.needsColoursUpdate=!1,e.parent.isSquare){let t=[];for(let l=0;l<3;l++)if(l===e.value.channel)t[l]=e.value;else{let i=[!0,!1,!1,!1,!1,!1,!1,!1,!1,!1];t[l]=X({values:i,channel:l})}let o=Z({channels:t});e.colours=M(o)}else{let a;for(let r=0;r<10;r++){let n=e.value.values[r];if(!1===n)continue;let d=makeArrayFromSplash(`${r}${r}${r}`);void 0===a?a=d:a.joins.push(d)}e.colours=M(a)}e.isGradient=!0,e.gradient=tI({colours:e.colours,width:e.width*C,height:e.height*C,gradient:e.gradient})}if(e.highlightedAtom=void 0,e5.content===e&&e5.state===to.DRAGGING){let{x:h,y:s}=ty(e),c=h,u=s,g=h+e.width,$=s+e.height;void 0!==e.highlight&&(tx(e,e.highlight),e.highlight=void 0);let p=1/0,f,v,m=lg();for(let _ of m){let w=_;if(w.isTallRectangle){if(!w.expanded)continue;let b=["padTop","padBottom"];for(let S of b){let R=w;for(;R.isTallRectangle&&void 0!==R.operationAtoms[S];)R=R.operationAtoms[S];if(!R.isTallRectangle||!R.expanded)continue;let T=R[S],{x:k,y:E}=ty(T),z=k,D=k+T.width,O=E,P=E+T.height;if(!(c>D)&&!(gP)){e.highlightedSlot=S,e.highlightedAtom=T;break}}}else{if(!_.isSquare||!_.expanded)continue;let{x:A,y:I}=ty(_.pickerPad),B=A,L=A+_.pickerPad.width,G=I,N=I+_.pickerPad.height;if(c>L||gN)continue;let U=["red","green","blue"].filter(e=>void 0===_[e]);if(0===U.length)continue;let{x:Y,y:H}=ty(_);for(let V of U){let j=tH[V],W=Y+_.size+2*t7+j*(tz.size+t3),q=H+t7,F=Math.hypot(h-W,s-q);F0)for(let s=0;s<10;s++){let c=e.options[s];if(e.parent.isSquare)h.values[9-s]=!0,s>0&&(h.values[9-s+1]=!1);else for(let u of d)u.values[9-s]=!0,s>0&&(u.values[9-s+1]=!1);let g=Z({channels:d}),$=M(g);c.colours=$,c.colourTicker=1/0,c!==e&&(c.needsColoursUpdateCountdown=s,c.needsColoursUpdate=!1)}},getCenterId(e){let t,l;for(let i=0;i[e,t+.13397460000000005/2*o]);let r=[];for(let n=0;n<6;n++){let d=wrap(n+1,0,5),h=a[n],s=a[d],c=[0,1].map(e=>(h[e]+s[e])/2);r.push(c)}let u=[t+i/2,l+o/2],g=a.map((e,t)=>{let l=wrap(t+1,0,5),i=a[wrap(t+1+1,0,5)],o=wrap(t+1+1,0,5),n=r[o],d=r[l];return[u,d,i,n]}),[$,...p]=a,f=new Path2D;for(let v of(f.moveTo(...$),p))f.lineTo(...v);if(f.closePath(),e6.fillStyle=e.colour,e6.fill(f),void 0!==e.ons)for(let m=0;m<6;m++){if(!e.ons[m])continue;let[_,...w]=g[m],b=new Path2D;for(let S of(b.moveTo(..._),w))b.lineTo(...S);b.closePath(),e6.fillStyle=Colour.Silver,e6.fill(b),e6.lineWidth=1/C,e6.strokeStyle=Colour.Silver,e6.stroke(b)}e.hasBorder&&(e6.lineWidth=1.5*t0,e6.strokeStyle=e.borderColour,e6.stroke(f),e.parent.isSquare&&lD.drawY(e,e.size-8,4))},getValue(e){let t=0;for(let l of e.ons)l&&t++;return t},click(e){e.expanded?e.unexpand(e):e.expand(e)},unexpand(e){for(let t of(e.expanded=!1,e.handles))tx(e,t);for(let l of e.buttons)tx(e,l);e.handles=[],e.buttons=[]},expand(e){e.expanded=!0,e.handles=[],e.buttons=[];let{width:t,height:l}=e,i=.22373758200000007*t,o=[[t,l/2-tF.height/2],[t-i,l-tF.height/2],[i,l-tF.height/2],[0,l/2-tF.height/2],[i,0-tF.height/2],[t-i,0-tF.height/2],],a=[[t,l/2],[t-i,l],[i,l],[0,l/2],[i,0],[t-i,0],];a=a.map(([t,l],i)=>{let[o,a]=[t-e.width/2,l-e.height/2],[r,n]=[];return i%3==0?[r,n]=[2.2*o,2.2*a]:[r,n]=[2*o,2*a],[r+e.width/2,n+e.height/2]});for(let r=0;r<6;r++){let n=t_(e,tF);n.rotation=r,n.x=o[r][0]-tF.width/2,n.y=o[r][1],e.handles.push(n);let d=t_(e,t2);d.x=a[r][0]-t2.size/2,d.y=a[r][1]-t2.size/2,e.buttons.push(d),d.id=r,e.ons[r]&&(d.inner.selected=!0,d.inner.colour=Colour.Silver)}},construct(e){e.ons=[!1,!1,!1,!1,!1,!1]},updateValue(e){let t=tH[e.variable],l=!e.ons[1]&&!e.ons[0]&&!e.ons[5],i=!e.ons[2]&&!e.ons[3]&&!e.ons[4],o=!l&&!i,a=[l||o,e.ons[1],e.ons[0],e.ons[5],!1,!1,!1,!1,!1,!1],r=[i||o,e.ons[2],e.ons[3],e.ons[4],!1,!1,!1,!1,!1,!1],n=X({values:a}),d=X({values:r}),h=X({channel:t,variable:e.variable,add:n,subtract:d});e.value=h},hover(e){let{x:t,y:l}=ty(e),i=t,o=l,a=t+e.width,r=l+e.height;for(let n of paddles){let{x:d,y:h}=ty(n),s=d+n.width,c=h,u=h+n.height;if(void 0===n.chance&&n.expanded&&i<=s&&a>=s&&(oc||r>c&&r{let[a,r]=[e-l,t-i],n=Math.sqrt(a**2+r**2),d=Math.atan2(r,a),[h,s]=[n*Math.cos(o+d),n*Math.sin(o+d),];return[l+h,i+s]},t2={size:tz.size,offscreen:tC.offscreen,overlaps:tC.overlaps,colour:Colour.Grey,grab:e=>e.parent,behindChildren:!0,draw(e){tC.draw(e)},construct(e){e.inner=t_(e,tZ,{bottom:!1}),e.inner.x=e.width/2-e.inner.width/2,e.inner.y=e.height/2-e.inner.height/2},click(e){e.inner.selected?(e.inner.selected=!1,e.inner.colour=Colour.Grey):(e.inner.selected=!0,e.inner.colour=Colour.Silver);let t=e.parent;if(t.ons[e.id]=e.inner.selected,t.parent.isPaddle){let l=t.parent;la(l)}else if(t.parent.isSquare){let i=t.parent;t.updateValue(t);let o=tH[t.variable];i.receiveNumber(i,t.value,o,{expanded:t.expanded,numberAtom:t})}tv(e.parent)}},tZ={size:2*tz.size/3,offscreen:tC.offscreen,overlaps:tC.overlaps,grab:e=>e.parent,touch:e=>e.parent,draw:tC.draw,hasBorder:!0,borderColour:Colour.Black,colour:Colour.Grey},tF={offscreen:tR.offscreen,overlaps(e,t,l){e.y-=e.height/2,e.height*=2;let i=tR.overlaps(e,t,l);return e.height/=2,e.y+=e.height/2,i},colour:Colour.Grey,rotation:0,touch:e=>e.parent,grab:e=>e.parent,x:50,width:tz.size/2+tz.size/4,height:tz.size/3,draw(e){let{x:t,y:l}=ty(e),{width:i,height:o}=e,a=new Path2D,r=[[t,l],[t+i,l],[t+i,l+o],[t,l+o],];e.rotation>0&&(r=r.map(a=>tq(a,[t+i/2,l+o/2],e.rotation*Math.PI/3)));let[n,...d]=r;for(let h of(a.moveTo(...n),d))a.lineTo(...h);e6.fillStyle=e.colour,e6.fill(a)}},t4={draw(e){let{x:t,y:l}=ty(e),i=Math.round(e.width),o=Math.round(e.height);e6.fillStyle=Colour.Grey,e6.fillRect(Math.round(t),Math.round(l),i,o)},overlaps:tR.overlaps,offscreen:tR.offscreen,height:tX-tY,width:tz.size+2*t7,x:-t7,dragOnly:!0,grab:e=>e.parent.expanded?e:e.parent,touch:e=>e.parent.expanded?e:e.parent,cursor:e=>e.parent.expanded?"ns-resize":"pointer",move(e){e.parent.positionSelectionBack(e.parent)},drop(e){let t=Math.round((e.y+tY/2)/tX),l=e.parent.value,[i,o]=e.parent.getStartAndEndId(e.parent),a=e.parent.getCenterId(e.parent);e.isTop&&(o=a-t),e.isTop||(i=a-(t-1));let r=[!1,!1,!1,!1,!1,!1,!1,!1,!1,!1];for(let n=i;n<=o;n++)r[n]=!0;let d=X({channel:l.channel,values:r});if(e.parent.value=d,e.parent.deleteOptions(e.parent),e.parent.createOptions(e.parent),e.dx=0,e.dy=0,e.parent.parent.isSquare){let h=e.parent.parent,s=tH[e.parent.channelSlot];h.receiveNumber(h,d,s)}if(e.parent.parent.isPaddle){let c=e.parent.parent;la(c)}},dragLockX:!0},tM={overlaps:tR.overlaps,offscreen:tR.offscreen,width:(tz.size-tY)/2,height:tz.size,grab:e=>e.parent,touch:e=>e.parent,dragLockX:!0,draw:tR.draw,colour:Colour.Grey},t5={draw:tR.draw,overlaps:tR.overlaps,offscreen:tR.offscreen,height:tY,width:tz.size,grab:e=>e.parent,hasBorder:!0,colourTicker:1/0,colours:[999],colourId:0,dcolourId:1,update(e){e.needsColoursUpdateCountdown>=0&&(e.needsColoursUpdateCountdown--,e.needsColoursUpdateCountdown<0&&(e.needsColoursUpdate=!0)),e.needsColoursUpdate&&(e.updateColours(e),e.needsColoursUpdateCountdown=-1,e.needsColoursUpdate=!1)},getId(e){let t=e.parent,l=t.getCenterId(t),i=e.y/tX;return l-i},updateColours(e){e.isGradient=!0,e.gradient=tI({colours:e.colours,width:e.width*C,height:e.height*C,gradient:e.gradient})},touch(e){let t=e.getId(e);return e.parent.value.values[t]?e.parent:e},click(e){let t=[!1,!1,!1,!1,!1,!1,!1,!1,!1,!1];t[e.value]=!0;let l=X({values:t,channel:e.parent.value.channel}),i=e.parent;if(i.value=l,i.deleteOptions(i),i.createOptions(i),i.needsColoursUpdate=!0,i.parent.isSquare){let o=i.parent,a=tH[i.channelSlot];o.receiveNumber(o,l,a)}if(i.parent.isPaddle){let r=i.parent;la(r)}},construct(e){if(e.pityTop){let t=t_(e,tQ);t.y=-t.height}if(e.pityBottom){let l=t_(e,tQ);l.y=e.height}}},tK=["red","green","blue",],t6={behindChildren:!0,highlighter:!0,rightDraggable:!0,rightDrag(e){let t=th(t6);tg(t);let{x:l,y:i}=ty(e);return e5.offset.x-=e.x-l,e5.offset.y-=e.y-i,t.variable=e.variable,e.expanded&&t.expand(t),t.updateAppearance(t),t},drag(e){if(e.parent.isSquare){let t=e.parent;t[e.channelSlot]=void 0;let l=tH[e.channelSlot];t.receiveNumber(t,void 0,l),tb(t,e),e.updateAppearance(e),e.attached=!1}else if(e.parent.isTallRectangle){let i=e.parent;tb(i,e),i.operationAtoms[e.highlightedSlot]=void 0;let o="padTop"===e.highlightedSlot?"add":"subtract";if(i.value[o]=void 0,e.expanded&&(e.unexpand(e),e.expand(e)),e.attached=!1,i.expanded)i.unexpand(i),i.expand(i);else{let a="padTop"===e.highlightedSlot?"handleTop":"handleBottom";tx(i,i[a],{quiet:!0}),tx(i,i[e.highlightedSlot],{quiet:!0}),i.expand(i),i.unexpand(i)}}return e},hover(e){let{x:t,y:l}=ty(e),i=t,o=l,a=t+e.width,r=l+e.height,n=1/0,d,h,s=lg();for(let c of s){if(c===e)continue;if(c.isTallRectangle){if(!c.expanded)continue;let u=["padTop","padBottom"];for(let g of u){let $=c;for(;$.isTallRectangle&&void 0!==$.operationAtoms[g];)$=$.operationAtoms[g];if(!$.isTallRectangle||!$.expanded)continue;let p=$[g],{x:f,y:v}=ty(p),m=f,_=f+p.width,w=v,b=v+p.height;if(!(i>_)&&!(ab))return e.highlightedSlot=g,p}continue}if(!c.isSquare||!c.expanded)continue;let{x:S,y:R}=ty(c.pickerPad),C=S,T=S+c.pickerPad.width,k=R,E=R+c.pickerPad.height;if(i>T||aE)continue;let z=["red","green","blue"].filter(e=>void 0===c[e]);if(0===z.length)continue;let{x:D,y:O}=ty(c);for(let P of z){let A=tH[P],I=D+c.size+2*t7+A*(tz.size+t3),B=O+t7,L=Math.hypot(t-I,l-B);Le)),s.lineTo(...[r+a,d].map(e=>e)),s.lineTo(...[h,n+o].map(e=>e)),s.lineTo(...[r,d].map(e=>e)),s.closePath(),e6.fillStyle=e.colour,e6.fill(s),e.hasBorder&&(e6.lineWidth=t0,e6.strokeStyle=e.borderColour,e.isTool&&(e6.lineWidth=1.5*t0,e6.strokeStyle=tS[e.colour.splash]),e6.stroke(s))},offscreen:tR.offscreen,overlaps:tR.overlaps,hasBorder:!0,isTallRectangle:!0,size:tY+t7/3*2,height:tY+t7/3*2,width:tY+t7/3*2,construct(e){e.variable=tK[Random.Uint8%3],e.value=X({variable:e.variable}),e.updateAppearance(e),e.isTool||(e.width+=t0/2,e.height+=t0/2,e.size+=t0/2),e.operationAtoms={padTop:void 0,padBottom:void 0}},makeOperationAtoms(e){if(void 0!==e.value.add&&void 0===e.operationAtoms.padtop){if(void 0===e.value.add.variable){let t=t_(e,tV);t.value=e.value.add,e.operationAtoms.padTop=t,t.x=e.padTop.x+t7,t.y=e.padTop.y+e.padTop.height/2-t.height/2,t.highlightedSlot="padTop"}else{let l=t_(e,t6);l.value=e.value.add,l.variable=e.value.add.variable,l.makeOperationAtoms(l),l.highlightedSlot="padTop",l.x=0,l.y=e.padTop.y+e.padTop.height/2-l.height/2,l.updateAppearance(l),e.operationAtoms.padTop=l}}if(void 0!==e.value.subtract&&void 0===e.operationAtoms.padBottom&&void 0===e.value.subtract.variable){let i=t_(e,tV);i.value=e.value.subtract,e.operationAtoms.padBottom=i,i.x=e.padBottom.x+t7,i.y=e.padBottom.y+e.padBottom.height/2-i.height/2,i.highlightedSlot="padBottom"}},updateAppearance(e){"red"===e.variable?e.colour=Colour.Red:"green"===e.variable?e.colour=Colour.Green:"blue"===e.variable&&(e.colour=Colour.Blue),e.borderColour=borderColours[e.colour.splash]},expanded:!1,click(e){e.expanded?e.unexpand(e):e.expand(e)},expand(e){for(let t of(e.expanded=!0,void 0===e.value.add&&(e.y<0||!(e.parent.isTallRectangle&&e.parent.operationAtoms.padBottom===e))&&(e.handleTop=t_(e,lC),e.handleTop.width=e.handleTop.height,e.handleTop.height*=2,e.handleTop.y=e.height/2-e.handleTop.height,e.handleTop.x=e.width/2-e.handleTop.width/2,e.handleTop.behindParent=!0,e.padTop=t_(e,lR),e.padTop.height=t1.height,e.padTop.width=tz.size+2*t3,e.padTop.x=e.width/2-e.padTop.width/2,e.padTop.y=-e.padTop.height-t7),void 0===e.value.subtract&&(e.y>0||!(e.parent.isTallRectangle&&e.parent.operationAtoms.padTop===e))&&(e.handleBottom=t_(e,lC),e.handleBottom.width=e.handleBottom.height,e.handleBottom.height*=2,e.handleBottom.y=e.height/2,e.handleBottom.x=e.width/2-e.handleBottom.width/2,e.handleBottom.behindParent=!0,e.padBottom=t_(e,lR),e.padBottom.height=t1.height,e.padBottom.width=tz.size+2*t3,e.padBottom.x=e.width/2-e.padBottom.width/2,e.padBottom.y=e.height+t7),e.handleRight=t_(e,lC),e.handleRight.y=e.height/2-e.handleRight.height/2,e.handleRight.x=e.width/2,e.handleRight.width*=2.5,e.handleRight.behindParent=!0,e.padRight=t_(e,lR),e.padRight.height=t1.height,e.padRight.width=t7+(e.width+t7/1.5)*3,e.padRight.y=e.height/2-e.padRight.height/2,e.padRight.x=e.width/2+(tz.size+2*t3)/2+t7,e.red=t_(e,t9),e.red.x=e.padRight.x+t7/Math.SQRT2,e.red.borderColour=Colour.Red,e.red.colour=Colour.Black,e.red.value="red",e.green=t_(e,t9),e.green.x=e.padRight.x+t7/Math.SQRT2+(e.green.width+t7)*1,e.green.borderColour=Colour.Green,e.green.colour=Colour.Black,e.green.value="green",e.blue=t_(e,t9),e.blue.x=e.padRight.x+t7/Math.SQRT2+(e.blue.width+t7)*2,e.blue.borderColour=Colour.Blue,e.blue.colour=Colour.Black,e.blue.value="blue",e.winnerPin=t_(e,tJ),e.winnerPin.x=e[e.variable].x+e.winnerPin.width/2,e.winnerPin.y=e.winnerPin.height/2,e.winnerPin.colour=e[e.variable].borderColour,e.winnerPin.borderColour=e.winnerPin.colour,["padTop","padBottom"])){let l=e.operationAtoms[t];void 0!==l&&(tg(l),tw(e,l))}for(let i of e.children)i.isTallRectangle&&i.expanded&&(i.unexpand(i),i.expand(i))},unexpand(e){e.expanded=!1,tx(e,e.red),tx(e,e.green),tx(e,e.blue),tx(e,e.padRight),tx(e,e.handleRight),tx(e,e.winnerPin),void 0===e.value.add&&(tx(e,e.padTop,{quiet:!0}),tx(e,e.handleTop,{quiet:!0})),void 0===e.value.subtract&&(tx(e,e.padBottom,{quiet:!0}),tx(e,e.handleBottom,{quiet:!0}))}},t9={draw(e){t6.draw(e)},offscreen:tR.offscreen,overlaps:tR.overlaps,hasBorder:!0,size:tY+t7/3*2,height:tY+t7/3*2,width:tY+t7/3*2,grab:e=>e.parent,click(e){if(e.value===e.parent.variable)return;e.parent.variable=e.value,e.parent.value.variable=e.value,e.parent.winnerPin.x=e.x+e.parent.winnerPin.width/2,e.parent.winnerPin.colour=e.borderColour,e.parent.winnerPin.borderColour=e.borderColour,e.parent.updateAppearance(e.parent);let t=e.parent,l=t,i=t.parent;for(;!i.isSquare;){if(i===td)return;l=i,i=i.parent}let o=0;"green"===l.channelSlot&&(o=1),"blue"===l.channelSlot&&(o=2);let a=i.variableAtoms[o];i.receiveNumber(i,a.value,o,{expanded:a.expanded,numberAtom:a})}},tJ={draw(e){t6.draw(e)},offscreen:tR.offscreen,overlaps:tR.overlaps,hasBorder:!0,size:(tY+t7/3*2)/2,height:(tY+t7/3*2)/2,width:(tY+t7/3*2)/2,grab:e=>e.parent,touch:e=>e.parent},tQ={draw(){},overlaps:tR.overlaps,offscreen:tR.offscreen,grab:e=>e.parent.parent,touch:e=>e.parent,colour:Colour.Grey,width:tz.size,height:tX-tY,y:0,x:0};paddles=[];let le=tz.size/2,lt={stayAtBack:!0,attached:!0,noDampen:!0,isPaddle:!0,behindChildren:!0,draw:tR.draw,overlaps:tR.overlaps,offscreen:tR.offscreen,colour:Colour.Grey,size:tz.size+4*t7,width:tz.size+4*t7,height:tz.size+4*t7,dragOnly:!0,dragLockY:!0,scroll:0,rightTriangle:void 0,x:Math.round(le),y:tz.size+t7+le,construct(e){e.cellAtoms=[],e.slots=[];let t=t_(e,lv);e.handle=t,e.setLimits(e),e.x=e.minX,e.expanded=!1,e.pinhole=t_(t,lm),e.dummyLeft=t_(e,li),e.dummyLeft.visible=!1,e.dummyRight=t_(e,li),e.dummyRight.visible=!1,la(e)},setLimits(e){e.maxX=e.handle.width,e.minX=e.handle.width-e.width},drop(e){let t=e.maxX-e.x,l=e.x-e.minX;te,rightDraggable:!0,getColour(e){let t=e.cellAtoms;if(0===t.length){let l=Z({channels:[void 0,void 0,void 0]});return l}if(1===t.length){let i=Q(t[0].value);return i}let o=ls(t),a=makeDiagram({left:o});return el(a),a},rightDrag(e){let t=e.cellAtoms;if(0===t.length){let l=th(tz);e5.offset.x=-l.width/2,e5.offset.y=-l.height/2;let i=Z({channels:[void 0,void 0,void 0]});return tE(i),tg(l),l.value=i,l.update(l),l}if(1===t.length){let o=Q(t[0].value),a=t[0].clone(t[0]);return e5.offset.x=-a.width/2,e5.offset.y=-a.height/2,tE(o),tg(a),a.value=o,a.update(a),a}let r=th(tz);e5.offset.x=-r.width/2,e5.offset.y=-r.height/2;let n=ls(t),d=makeDiagram({left:n});return el(d),r.value=d,tg(r),tE(d),r.update(r),r}},ll=(e,t)=>{let l=new Path2D,[i,...o]=t;for(let a of(l.moveTo(...i.map(e=>Math.round(e))),o))l.lineTo(...a.map(e=>Math.round(e)));l.closePath(),e6.fillStyle=e,e6.fill(l)},li={visible:!0,isSlot:!0,behindChildren:!0,draw(e){if(!e.visible)return;let[t,l]=ty(e);e6.fillStyle=e.colour;let i=e.width/3,o=e.width/3,a=t+e.width/2-i/2,r=l+e.height/2-o/2;e6.fillRect(...[a,r,i,o].map(e=>Math.round(e)))},offscreen:tR.offscreen,overlaps:tR.overlaps,colour:Colour.Black,size:tz.size,grab:e=>e.parent,dragOnly:!0},lo=tz.size,la=e=>{let t=lt.width,l=lt.size;if(e.cellAtoms.length>0){let i=1/0,o=-1/0,a=-1/0,r=1/0;for(let n of e.cellAtoms){let d=n.x,h=n.y,s=d,c=d+lo,u=h,g=h+lo;sa&&(a=c),uo&&(o=g)}let $=0,p=0,f=lt.height/2-tz.size/2,v=lt.width/2-tz.size/2,m=f,_=v;for(let w of(i!==m&&(o+=$=m-i),r!==_&&(a+=p=_-r),e.cellAtoms))w.y+=$,w.x+=p;let b=a+v,S=o+f;t=b,l=S}for(let R of(void 0!==e.rightTriangle&&(e.rightTriangle.x=t,e.rightTriangle.y=l/2-e.rightTriangle.height/2,t=t+t+e.rightTriangle.width),(e.hasSymmetry||void 0!==e.chance)&&(t+=lx.size/3),e.width=t,e.height=l,e.setLimits(e),e.slots))tx(e,R);if(e.slots=[],void 0!==e.rightTriangle)for(let C of e.cellAtoms){let T=t_(e,li,{bottom:!0});C.slot=T,e.slots.push(T),T.x=C.x+e.rightTriangle.x+e.rightTriangle.width,T.y=C.y,T.cellAtom=C,void 0!==C.slotted&&(C.slotted.x=C.x+e.rightTriangle.x+e.rightTriangle.width,C.slotted.y=C.y,T.colour=Colour.Grey)}void 0!==e.rightTriangle&&(void 0!==e.cellAtoms[0]&&void 0!==e.cellAtoms[0].slot?e.offset=e.cellAtoms[0].slot.x-e.cellAtoms[0].x:e.offset=0),void 0!==e.symmetryCircle&&(e.symmetryCircle.x=e.width-e.symmetryCircle.width/2,e.symmetryCircle.y=e.height/2-e.symmetryCircle.height/2),void 0!==e.chance&&(e.chance.x=e.width-e.chance.width/2,e.chance.y=e.height/2-e.chance.height/2),void 0!==e.chance&&void 0!==e.symmetryCircle&&(e.symmetryCircle.y-=e.symmetryCircle.height/2,e.chance.y+=e.symmetryCircle.height/2,e.height>100&&(e.symmetryCircle.y-=t7/2,e.chance.y+=t7/2)),e.handle.y=e.height/2-e.handle.height/2,0===e.cellAtoms.length&&(e.dummyLeft.x=le,e.dummyLeft.y=e.height/2-e.dummyLeft.height/2,e.dummyRight.x=e.width-le-e.dummyLeft.width,e.dummyRight.y=e.height/2-e.dummyRight.height/2),lc(e),l$()},lr=e=>{let t=F(e);return 1===t.size},ln=(e,t)=>{for(let l=0;l<3;l++){let i=e.channels[l],o=t.channels[l];if(void 0===i&&void 0!==o||void 0!==i&&void 0===o||(void 0!==i||void 0!==o)&&i.variable!==o.variable)return!1}let a=M(e),r=M(t);for(let n of a){let d=r.indexOf(n);if(-1===d)return!1;r.splice(d,1)}return!(r.length>0)},ld=(e,t)=>{if(t.stamp)return;let l=lr(t);if(!l){let i;for(let o=0;o{let t=[...e].sort((e,t)=>e.xt.x?1:e.yt.y?1:0);return t},ls=e=>{let t=lh(e),l=t[0],i=[];for(let o of e){let a=(o.x-l.x)/o.width,r=(o.y-l.y)/o.height,n=Q(o.value),d=makeDiagramCell({x:a,y:r,content:n});i.push(d)}return i},lc=e=>{if(!e.expanded)return;void 0!==e.rightTriangle&&(e.pinhole.locked?e.rightTriangle.colour=Colour.splash(999):e.rightTriangle.colour=Colour.splash(0));let t=DRAGON_TRANSFORMATIONS.NONE;if(e.hasSymmetry){let[l,i,o]=l_(e.symmetryCircle.value),a=l>0,r=i>0,n=o>0,d=`${r?"X":""}${a?"Y":""}${n?"R":""}`;""===d?d="NONE":("XR"===d||"YR"===d)&&(d="XYR"),t=DRAGON_TRANSFORMATIONS[d]}let h=lh(e.cellAtoms),s=h[0],c=[],u=[],g=[];for(let $ of h){let p=($.x-s.x)/$.width,f=($.y-s.y)/$.height;if($.isLeftSlot){let v=X({values:[!0,!0,!0,!0,!0,!0,!0,!0,!0,!0],channel:0}),m=X({values:[!0,!0,!0,!0,!0,!0,!0,!0,!0,!0],channel:1}),_=X({values:[!0,!0,!0,!0,!0,!0,!0,!0,!0,!0],channel:2}),w=Z({channels:[v,m,_]});ld(g,w);let b=makeDiagramCell({x:p,y:f,content:w});c.push(b)}else if($.value.isDiagram){let S=lh($.value.left);for(let R of S){let C=Q(R.content);ld(g,C);let T=p+R.x,k=f+R.y,E=makeDiagramCell({x:T,y:k,width:R.width,height:R.height,content:C});c.push(E)}}else{let z=Q($.value);ld(g,z);let D=makeDiagramCell({x:p,y:f,content:z});c.push(D)}if(!$.isLeftSlot&&$.value.isDiagram){let O=ei($.value),[P,A]=eo(O),I=makeDiagramCell({x:p,y:f,instruction:DRAGON_INSTRUCTION.merge,splitX:P,splitY:A});u.push(I)}let B=void 0===$.slotted?void 0:$.slotted.value;if(void 0===B){let L=makeDiagramCell({x:p,y:f,instruction:DRAGON_INSTRUCTION.nothing});u.push(L)}else if(B.isDiagram){let G=ei(B),[N,U]=eo(G),Y=makeDiagramCell({x:p,y:f,instruction:DRAGON_INSTRUCTION.split,splitX:N,splitY:U});u.push(Y);let H=lh(B.left);for(let V of H){let j=Q(V.content);ld(g,j);let W=p+V.x,q=f+V.y,F=makeDiagramCell({x:W,y:q,width:V.width,height:V.height,content:j,instruction:DRAGON_INSTRUCTION.recolour});u.push(F)}}else{let M=Q(B);ld(g,M);let K=makeDiagramCell({x:p,y:f,content:M});u.push(K)}}let J=ei(makeDiagram({left:c,right:u})),ee=e.pinhole.locked,et=void 0===e.chance?void 0:e.chance.getValue(e.chance),el=makeRule({steps:[J],transformations:t,locked:ee,chance:et});e.rule=el,void 0!==e.registry&&ed(e.registry),ee&&void 0!==e.rightTriangle&&(e.registry=registerRule(el))},lu=(e=state.colourTode.atoms)=>{let t=[...e];for(let l of t)t.push(...lu(l.children));return t},lg=()=>{let e=[...state.colourTode.atoms];for(let t of paddles)for(let l of t.children)!l.isPinhole&&(l.isPaddleHandle||e.push(l));for(let i of e)i.isSquare&&i.expanded&&e.push(...i.children);return e},l$=()=>{if(paddles.length>1&&lG("triangle"),paddles.length>2){let e=0;for(let t of paddles)void 0!==t.rightTriangle&&e++;e>=2&&lG("hexagon")}let l;for(let i of paddles){if(void 0===l){i.y=lt.y+lt.scroll,l=i;continue}i.y=l.y+l.height+le,l=i}},lp=(e,t=paddles.indexOf(e))=>{paddles.splice(t,1),void 0!==e.registry&&ed(e.registry),tu(e),l$()},lf=()=>{let e=th(lt);return paddles.push(e),l$(),tg(e),e},lv={isPaddleHandle:!0,attached:!0,behindChildren:!0,draw:tR.draw,overlaps:tR.overlaps,offscreen:tR.offscreen,colour:Colour.Grey,size:lt.x,x:-lt.x,y:lt.size/2-lt.x/2,touch:e=>e.parent.pinhole,grab:e=>e.parent.pinhole},lm={isPinhole:!0,attached:!0,locked:!1,borderScale:.5,borderColour:Colour.Black,draw(e){e.locked?(e.hasBorder=!0,e.colour=Colour.Grey):(e.hasBorder=!1,e.colour=Colour.Black),tC.draw(e)},overlaps:tC.overlaps,offscreen:tC.offscreen,colour:Colour.Black,size:lv.size-t7/2,y:t7/2/2,x:t7/2/2,click(e){let t=e.parent,l=t.parent;if(e.locked)e.locked=!1,l.grabbable=!0,t.draggable=!0,l.draggable=!0,e.draggable=!0,lc(l);else{for(let i of(e.locked=!0,t.draggable=!1,e.draggable=!1,l.cellAtoms)){if(i.expanded&&i.unexpand(i),void 0!==i.slotted){let o=i.slotted;o.expanded&&o.unexpand(o)}i.joins.length>0&&i.joinExpanded&&i.joinUnepxand(i)}0===l.cellAtoms.length&&(l.grabbable=!1,l.draggable=!1),lc(l)}},grab:e=>e.parent.parent},ly=new Map;ly.set(0,DRAGON_TRANSFORMATIONS.NONE),ly.set(100,DRAGON_TRANSFORMATIONS.X),ly.set(10,DRAGON_TRANSFORMATIONS.Y),ly.set(110,DRAGON_TRANSFORMATIONS.XY),ly.set(1,DRAGON_TRANSFORMATIONS.R),ly.set(111,DRAGON_TRANSFORMATIONS.XYR),ly.set(101,DRAGON_TRANSFORMATIONS.XYR),ly.set(11,DRAGON_TRANSFORMATIONS.XYR);let l_=getRGB,lx={hasBorder:!0,draw(e){if(tC.draw(e),void 0===e.value)return;let[t,l,i]=l_(e.value);t>0&&lz.drawX(e),l>0&&lD.drawY(e),i>0&&lO.drawR(e)},offscreen:tC.offscreen,overlaps:tC.overlaps,expanded:!1,borderColour:Colour.Grey,colour:Colour.Black,value:0,click(e){e.expanded?e.unexpand(e):e.expand(e)},expand(e){e.pad=t_(e,lR),e.handle=t_(e,lC),e.handle.width+=t7,e.expanded=!0;let[t,l,i]=l_(e.value);e.xToggle=t_(e,lz),e.yToggle=t_(e,lD),e.rToggle=t_(e,lO),t>0&&(e.xToggle.value=!0),l>0&&(e.yToggle.value=!0),i>0&&(e.rToggle.value=!0)},unexpand(e){tx(e,e.pad),tx(e,e.handle),tx(e,e.xToggle),tx(e,e.yToggle),tx(e,e.rToggle),e.expanded=!1},size:tz.size,update(e){let{x:t,y:l}=ty(e),i=state.colourTode.atoms.indexOf(e),o=t,a=l,r=t+e.width,n=l+e.height;if(e5.content===e)for(let d of paddles){let h=state.colourTode.atoms.indexOf(d),{x:s,y:c}=ty(d),u=s+d.width,g=c,$=c+d.height;if(!d.hasSymmetry&&d.expanded&&i>h&&o<=u&&r>=u&&(a<$&&a>g||n>g&&n<$)){void 0!==e.highlightPaddle&&tx(e,e.highlightPaddle),e.highlightPaddle=t_(e,lb,{bottom:!0}),e.highlightPaddle.width=lw,e.highlightPaddle.height=d.height,e.highlightPaddle.y=g,e.highlightPaddle.x=u-lw/2,e.highlightedPaddle=d;return}}void 0!==e.highlightPaddle&&(tx(e,e.highlightPaddle),e.highlightPaddle=void 0,e.highlightedPaddle=void 0)},drop(e){if(!e.attached&&void 0!==e.highlightedPaddle){let t=e.highlightedPaddle;e.attached=!0,tw(t,e),t.hasSymmetry=!0,t.symmetryCircle=e,la(t),e.dx=0,e.dy=0}},drag(e){if(e.attached){let t=e.parent;e.attached=!1,tb(t,e),t.hasSymmetry=!1,t.symmetryCircle=void 0,la(t)}return e},rightDraggable:!0,rightDrag(e){let t=th(lx);t.value=e.value;let{x:l,y:i}=ty(e);return e5.offset.x-=e.x-l,e5.offset.y-=e.y-i,t.x=l,t.y=i,tg(t),t}},lw=t0,lb={behindParent:!0,draw:tR.draw,offscreen:tR.offscreen,overlaps:tR.overlaps,draggable:!1,grabbable:!1,justVisual:!0,colour:Colour.splash(999),borderColour:Colour.splash(999),hasAbsolutePosition:!0,hasInner:!1},l0={draw:tR.draw,offscreen:tR.offscreen,overlaps:tR.overlaps,dragOnly:!0,width:lx.size,x:lx.size*Math.sqrt(3)/2+t7,height:2*lx.size-t7,y:-lx.size/2+t7/2,colour:Colour.Grey,grab:e=>e.parent},lS={draw:tR.draw,offscreen:tR.offscreen,overlaps:tR.overlaps,dragOnly:!0,width:lx.size/2+t7,x:lx.size/2,height:lx.size/3,y:lx.size/2-lx.size/3/2,colour:Colour.Grey,grab:e=>e.parent},lR={draw:tR.draw,offscreen:tR.offscreen,overlaps:tR.overlaps,dragOnly:!0,width:lx.size,x:lx.size+t7,height:3*lx.size-t7,y:-(3*lx.size)/3+t7/2,colour:Colour.Grey,grab:e=>e.parent},lC={draw:tR.draw,offscreen:tR.offscreen,overlaps:tR.overlaps,dragOnly:!0,width:lx.size/2,x:lx.size/2+lx.size/4,height:lx.size/3,y:lx.size/2-lx.size/3/2,colour:Colour.Grey,grab:e=>e.parent},lT=(e,t)=>{switch(t=!t,e){case"right":return t?"down":"up";case"down":return t?"left":"right";case"left":return t?"up":"down";case"up":return t?"right":"left"}throw Error("Invalid rotation or clockwiseness")},lk={hasBorder:!0,colour:Colour.Black,borderColour:Colour.Black,draw(e){tL.draw(e)},touch:e=>(e.colour=Colour.Silver,e),click(e){let t=e.parent;e.colour=Colour.Black,t.direction=lT(t.direction,!0),e.value=!0,t.updateValue(t);let l=t.parent;l.isSquare&&l.receiveNumber(l,t.value,t.channelId,{expanded:t.expanded,numberAtom:t})},offscreen:tL.offscreen,overlaps:tL.overlaps,value:!1,size:tz.size-1.5*t7,grab:e=>e.parent,x:l0.x+l0.width/2-(tz.size-1.5*t7)/2,y:l0.y+1.5*t7/2},lE={hasBorder:!0,colour:Colour.Black,borderColour:Colour.Black,draw(e){tG.draw(e)},touch:e=>(e.colour=Colour.Silver,e),click(e){let t=e.parent;e.colour=Colour.Black,t.direction=lT(t.direction,!1),e.value=!0,t.updateValue(t);let l=t.parent;l.isSquare&&l.receiveNumber(l,t.value,t.channelId,{expanded:t.expanded,numberAtom:t})},offscreen:tG.offscreen,overlaps:tG.overlaps,value:!1,size:tz.size-1.5*t7,grab:e=>e.parent,x:l0.x+l0.width/2-(tz.size-1.5*t7)/2,y:l0.y+l0.height-(tz.size-1.5*t7)-t7/2},lz={hasBorder:!0,borderColour:Colour.Black,colour:Colour.Grey,draw(e){e.colour=e.value?Colour.Silver:Colour.Grey,tC.draw(e),e.drawX(e)},drawX(e){let{x:t,y:l}=ty(e),i=e.size,o=l+e.size/2-1*t0/2;e6.fillStyle=e.borderColour,e6.fillRect(t,o,i,1*t0)},offscreen:tC.offscreen,overlaps:tC.overlaps,expanded:!1,click(e){e.value=!e.value;let[t,l,i]=l_(e.parent.value);t=e.value?100:0,e.parent.value=t+l+i;let o=e.parent;if(o.parent!==td){let a=o.parent;lc(a)}},value:!1,size:tz.size-t7,grab:e=>e.parent,x:lR.x+lR.width/2-(tz.size-t7)/2,y:lR.y+t7/2},lD={hasBorder:!0,borderColour:Colour.Black,colour:Colour.Grey,draw(e){e.colour=e.value?Colour.Silver:Colour.Grey,tC.draw(e),e.drawY(e)},drawY(e,t=e.size,l=0){let{x:i,y:o}=ty(e),a=i+e.size/2-1*t0/2;e6.fillStyle=e.borderColour,e6.fillRect(a,o+l,1*t0,t)},offscreen:tC.offscreen,overlaps:tC.overlaps,expanded:!1,click(e){e.value=!e.value;let[t,l,i]=l_(e.parent.value);l=e.value?10:0,e.parent.value=t+l+i;let o=e.parent;if(o.parent!==td){let a=o.parent;lc(a)}},value:!1,size:tz.size-t7,grab:e=>e.parent,x:lR.x+lR.width/2-(tz.size-t7)/2,y:t7/2},lO={hasBorder:!0,borderColour:Colour.Black,colour:Colour.Grey,draw(e){e.colour=e.value?Colour.Silver:Colour.Grey,tC.draw(e),e.drawR(e)},drawR(e){let{x:t,y:l}=ty(e),i=t+e.size/2,o=l+e.size/2,a=e.size/2-3*t0;e6.fillStyle=e.borderColour,e6.beginPath(),e6.arc(i,o,a,0,2*Math.PI),e6.fill(),a-=t0,e6.fillStyle=e.colour,e6.beginPath(),e6.arc(i,o,a,0,2*Math.PI),e6.fill()},offscreen:tC.offscreen,overlaps:tC.overlaps,expanded:!1,click(e){e.value=!e.value;let[t,l,i]=l_(e.parent.value);i=e.value?1:0,e.parent.value=t+l+i;let o=e.parent;if(o.parent!==td){let a=o.parent;lc(a)}},value:!1,size:tz.size-t7,grab:e=>e.parent,x:lR.x+lR.width/2-(tz.size-t7)/2,y:lR.y+lR.height-(tz.size-t7)-t7/2},lP=e=>{let t=th({...tz});if(t.value=Q(e),void 0!==t.value){if(void 0!==t.value.joins)for(let l of t.value.joins){let i=lP(l);t.joins.push(i)}t.stamp=t.value.stamp}if(!t.value.isDiagram)for(let o=0;o<3;o++){let a=t.value.channels[o];if(void 0===a||void 0===a.variable)continue;let r=th(tU);t.variableAtoms[o]=r,r.highlightedSlot=t8[o],r.channelId=o;let n=o-1<0?t8[2]:t8[o-1],d=o+1>2?t8[0]:t8[o+1];a.subtract?r.direction="down":a.add?r.direction="up":a.variable===n?r.direction="left":a.variable===d&&(r.direction="right"),r.updateValue(r)}return void 0!==t.value&&t.value.isDiagram&&t.update(t),t},lA=10,lI={element:tz,draw(e){(e.previousBrushColour!==state.brush.colour||e.toolbarNeedsColourUpdate)&&e.update(e),e.unlocked&&e.element.draw(e)},overlaps:(e,t,l)=>e.element.overlaps(e,t,l),grab:(e,t,l)=>e,drag(e){if(e===squareTool){let t=lP(e.value);return tg(t),t}let l=th({...e.element,x:e.x,y:e.y});return tg(l),l.value,l},cursor:()=>"move"},lB=0,lL=(e,t)=>{let{width:l=tz.size,height:i=tz.size,size:o}=e,a=t3;i{let t=unlocks[e];t.unlocked||(t.unlocked=!0,t.grabbable=!0)};squareTool=lL(tz),lA+=t0;let lN=lL(tU,"triangle");lA-=t0;let lU=lL(lx,"circle"),l7=lL(tW,"hexagon"),lY={};lf(),squareTool.value=makeArrayFromSplash(state.brush.colour),lU.borderScale=1,squareTool.update=e=>{if(void 0===e.joinDrawId&&(e.joinDrawId=-1,e.joinDrawTimer=0),void 0!==e.value&&e===squareTool&&(e.previousBrushColour!==state.brush.colour||e.toolbarNeedsColourUpdate)){for(let t of(e.previousBrushColour=state.brush.colour,void 0===e.multiAtoms&&(e.multiAtoms=[]),e.multiAtoms))tx(e,t);if(e.multiAtoms=[],e.value.isDiagram){let l=e.value,[i,o]=eo(l),a=e.width/i,r=e.height/o;for(let n of l.left){let d=t_(e,tz);d.x=n.x*a,d.y=n.y*r,d.width=n.width*a,d.height=n.height*r,d.value=n.content,d.update(d),e.multiAtoms.push(d)}}}let h=Q(e.value);if(e.colours=M(h),e.colourId>=e.colours.length&&(e.colourId=0),e.toolbarNeedsColourUpdate&&e===squareTool){for(let s of(e.toolbarNeedsColourUpdate=!1,e.isGradient=!0,e.joins=[],e.value.joins)){let c=lP(s);e.joins.push(c)}tz.updateGradient(e)}else e.colour=Colour.splash(999),e.borderColour=Colour.splash(999)},lN.update=squareTool.update,lU.update=squareTool.update,lY.update=squareTool.update,l7.update=squareTool.update,on.keydown(e=>{(e.ctrlKey||e.metaKey)&&"s"===e.key?(e.preventDefault(),lV()):(e.ctrlKey||e.metaKey)&&"o"===e.key?(e.preventDefault(),lj()):(e.ctrlKey||e.metaKey)&&"c"===e.key&&(e.preventDefault(),lW())},{passive:!1}),on.paste(async e=>{let t=e.clipboardData.getData("text");if(""!==t){l8(t);return}let l=e.clipboardData.items[0],i=l.getAsFile(),o=await i.text();l8(o)}),on.dragover(e=>{e.stopPropagation(),e.preventDefault()},{passive:!1}),on.drop(async e=>{e.stopPropagation(),e.preventDefault();let t=e.dataTransfer.items[0],l=t.getAsFile(),i=await l.text();l8(i)},{passive:!1});let lX={},l3={};lX.cellAtoms=(e,t)=>{let l=[];for(let i of t)l.push({isLeftSlot:i.isLeftSlot,value:i.value,x:i.x,y:i.y,slotted:i.slotted?i.slotted.value:void 0});return l};let l1=!1;l3.cellAtoms=(e,t)=>{let l=[];for(let i of t){l1||(i.isLeftSlot||tE(i.value),l1=!0);let o=i.isLeftSlot?th(li):lP(i.value);if(o.isLeftSlot=i.isLeftSlot,tg(o),tw(e,o),o.attached=!0,o.x=i.x,o.y=i.y,o.highlightedSide="left",l.push(o),void 0!==i.slotted){let a=lP(i.slotted);tg(a),tw(e,a),a.attached=!0,a.cellAtom=o,a.highlightedSide="slot",a.slottee=!0,o.slotted=a}}return l},lX.symmetryCircle=(e,t)=>{if(void 0!==t)return t.value},l3.symmetryCircle=(e,t)=>{let l=t_(e,lx);return l.value=t,l},lX.chance=(e,t)=>{if(void 0!==t)return t.ons},l3.chance=(e,t)=>{let l=t_(e,tW);return l.ons=t,l};lX.expanded=(e,t)=>t,lX.x=(e,t)=>t,lX.y=(e,t)=>t,lX.width=(e,t)=>t,lX.height=(e,t)=>t,lX.hasSymmetry=(e,t)=>t,l3.expanded=(e,t)=>t,l3.x=(e,t)=>t,l3.y=(e,t)=>t,l3.width=(e,t)=>t,l3.height=(e,t)=>t,l3.hasSymmetry=(e,t)=>t,lX.pinhole=(e,t)=>t.locked,l3.pinhole=(e,t)=>(e.pinhole.locked=t,e.pinhole),lX.rightTriangle=(e,t)=>void 0!==t,l3.rightTriangle=(e,t)=>{if(!t)return;let l=t_(e,tU);return l};let lH=()=>{let e=[];for(let t of paddles){let l={};for(let i in t){let o=lX[i];if(void 0===o)continue;let a=o(t,t[i]);void 0!==a&&(l[i]=a)}e.push(l)}return JSON.stringify(e)},l8=e=>{if(middleClicked){middleClicked=!1;return}l1=!1,lG("triangle"),lG("circle"),lG("hexagon");try{for(;paddles.length>0;)lp(paddles[paddles.length-1]);for(let t of JSON.parse(e)){let l=lf();for(let i in t){let o=l3[i];if(void 0===o)continue;let a=o(l,t[i]);void 0!==a&&(l[i]=a)}la(l),lc(l)}l$()}catch(r){console.error(r),alert("Error loading rules... Sorry! Please contact @todepond :)")}},lV=async()=>{let e=lH(paddles);if(window.showSaveFilePicker)try{let t=await showSaveFilePicker({excludeAcceptAllOption:!0,suggestedName:"spell",startIn:"downloads",types:[{description:"JSON",accept:{"application/json":[".json"]}}]}),l=await t.createWritable();await l.write(e),await l.close()}catch(i){console.error("Failed to save file:",i)}else{let o=new Blob([e],{type:"application/json"}),a=URL.createObjectURL(o),r=document.createElement("a");r.href=a,r.download="spell.json",r.click(),URL.revokeObjectURL(a)}},lj=()=>{let e=document.createElement("input");e.type="file",e.onchange=async t=>{let l=e.files[0],i=await l.text();l8(i),Keyboard.Control=!1},e.click(),Keyboard.Control=!1},lW=()=>{let e=lH(paddles);print(e),navigator.clipboard.writeText(e)}}); From 7a0064ce038291fe2ae44d8ca602742e6824c8ee Mon Sep 17 00:00:00 2001 From: inyourface34456 <62214409+inyourface34456@users.noreply.github.com> Date: Thu, 16 May 2024 13:37:15 -0400 Subject: [PATCH 5/7] faster load --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index f7a0827..beae69e 100644 --- a/index.html +++ b/index.html @@ -14,4 +14,4 @@ - + From 4ed505797838d742a92e3e133250a9fe636ddd55 Mon Sep 17 00:00:00 2001 From: inyourface34456 <62214409+inyourface34456@users.noreply.github.com> Date: Thu, 16 May 2024 13:38:20 -0400 Subject: [PATCH 6/7] Update the-one-true-todey-file-of-cellpond.js fixed? --- the-one-true-todey-file-of-cellpond.js | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/the-one-true-todey-file-of-cellpond.js b/the-one-true-todey-file-of-cellpond.js index 736e2d3..2037617 100644 --- a/the-one-true-todey-file-of-cellpond.js +++ b/the-one-true-todey-file-of-cellpond.js @@ -64,7 +64,7 @@ document.addEventListener('mousedown', function(event) { const urlParams = new URLSearchParams(window.location.search); const NO_SECRET_MODE = urlParams.has("nosecret"); - +const NO_FOOLS_MODE = urlParams.has("nofools"); const UNLOCK_MODE = urlParams.has("unlock"); const SCALE = urlParams.get("scale") ?? 1; const DPR = urlParams.get("dpr") ?? devicePixelRatio; @@ -73,7 +73,7 @@ if (NO_SECRET_MODE) { localStorage.setItem("secretHasAlreadyBeenRevealed", "true"); } - +const secretHasAlreadyBeenRevealed = localStorage.getItem("secretHasAlreadyBeenRevealed"); //========//; @@ -96,7 +96,7 @@ const TODEPOND_COLOURS = [ Colour.White.splash, ]; - +const TODEPOND_RAINBOW_COLOURS = TODEPOND_COLOURS.slice(0, -4); const getRGB = (splash) => { const gb = splash % 100; @@ -2121,6 +2121,7 @@ on.load(() => { const behaveFunction = (origin, redraw) => { + let count = 1; for (const stepFunction of stepFunctions) { const drawn = stepFunction(origin, redraw); if (drawn !== undefined) return drawn; @@ -2518,8 +2519,8 @@ on.load(() => { const BLUE = makeArrayFromSplash(Colour.Blue.splash); const YELLOW = makeArrayFromSplash(Colour.Yellow.splash); const PURPLE = makeArrayFromSplash(Colour.Cyan.splash - 111); - - let [RED_R, RED_G] = getRGB(Colour.Red.splash); + const RED = makeArrayFromSplash(Colour.Red.splash); + let [RED_R, RED_G, RED_B] = getRGB(Colour.Red.splash); RED_R /= 100; RED_G /= 10; /*BLACK.channels[0].values[RED_R] = true; @@ -7955,10 +7956,10 @@ registerRule(; const [x, y] = getAtomPosition(atom); - - - - + const left = x; + const right = x + atom.width; + const top = y; + const bottom = y + atom.height; /*const swidth = atom.width/10; const sheight = atom.height/10; From 49ef75740a3dfa6f14f79f588ad07cc6da2e4458 Mon Sep 17 00:00:00 2001 From: inyourface34456 <62214409+inyourface34456@users.noreply.github.com> Date: Thu, 16 May 2024 13:38:55 -0400 Subject: [PATCH 7/7] Update the-one-true-todey-file-of-cellpond.min.js --- the-one-true-todey-file-of-cellpond.min.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/the-one-true-todey-file-of-cellpond.min.js b/the-one-true-todey-file-of-cellpond.min.js index d53f266..d81a7ba 100644 --- a/the-one-true-todey-file-of-cellpond.min.js +++ b/the-one-true-todey-file-of-cellpond.min.js @@ -1 +1 @@ -var middleClicked=!1;document.addEventListener("mousedown",function(e){1===e.button&&(middleClicked=!0)});const urlParams=new URLSearchParams(window.location.search),NO_SECRET_MODE=urlParams.has("nosecret"),UNLOCK_MODE=urlParams.has("unlock"),SCALE=urlParams.get("scale")??1,DPR=urlParams.get("dpr")??devicePixelRatio;print("DPR:",DPR),NO_SECRET_MODE&&localStorage.setItem("secretHasAlreadyBeenRevealed","true");const TODEPOND_COLOURS=[Colour.Green.splash,Colour.Red.splash,Colour.Blue.splash,Colour.Yellow.splash,Colour.Orange.splash,Colour.Pink.splash,Colour.Rose.splash,Colour.Cyan.splash,Colour.Purple.splash,Colour.Black.splash,Colour.Grey.splash,Colour.Silver.splash,Colour.White.splash,],getRGB=e=>{let t=e%100,l=t%10,i=t-l;return[e-t,i,l]},clamp=(e,t,l)=>el?l:e,wrap=(e,t,l)=>{let i=l-t+1;return el?wrap(e-i,t,l):e};let brushColourCycleIndex=0;const brushColourCycle=[999,Colour.Green.splash,Colour.Blue.splash,Colour.Red.splash,Colour.Yellow.splash,Colour.Black.splash,Colour.Rose.splash,Colour.Cyan.splash,Colour.Orange.splash,Colour.Purple.splash,Colour.Pink.splash,Colour.Grey.splash,Colour.Silver.splash,],makeCell=({x:e=0,y:t=0,width:l=1,height:i=1,colour:o=112}={})=>{let a=e,r=e+l,n=t,d=t+i,h=l*i,s=a+l/2,c=n+i/2,u=[],g=void 0,$={x:e,y:t,width:l,height:i,colour:o,left:a,right:r,top:n,bottom:d,centerX:s,centerY:c,sections:u,size:h,lastDraw:g,lastDrawRepeat:0};return $};let edgeMode=0;const pickCell=(e,t)=>{if(e>=1||t>=1||e<0||t<0)return;let l=Math.floor(e*GRID_SIZE),i=Math.floor(t*GRID_SIZE),o=l*GRID_SIZE+i,a=state.grid[o];if(void 0===a)return;let r=1,n=a.size,d=a.values();for(let h of d)if(r===n||(r++,!(h.left>e)&&!(h.top>t)&&!(h.right<=e)&&!(h.bottom<=t)))return h},pickNeighbour=(e,t,l)=>{let i=e.left+e.width/2,o=e.top+e.height/2,a=i+t*e.width,r=o+l*e.height,n=pickCell(a,r);return n},pickRandomCell=()=>{let e=Random.Uint32/4294967295,t=Random.Uint32/4294967295,l=pickCell(e,t);return l},pickRandomVisibleCell=()=>{if(!state.view.visible)return;if(state.view.fullyVisible)return pickRandomCell();let e=state.region.left+Random.Uint32/4294967295*state.region.width,t=state.region.top+Random.Uint32/4294967295*state.region.height,l=pickCell(e,t);return l},state={grid:[],cellCount:0,ticker(){},time:0,maxTime:9999999,speed:{count:1638.4,dynamic:!1,redraw:2.5,redrawRepeatScore:.5,redrawRepeatPenalty:0},image:{data:void 0,size:void 0,baseSize:void 0},view:{height:void 0,width:void 0,iheight:void 0,iwidth:void 0,left:void 0,right:void 0,top:void 0,bottom:void 0,visible:!0,fullyVisible:!0},region:{left:0,right:1,top:0,bottom:1,width:1,height:1},camera:{x:0,y:0,dx:0,dy:0,dxTarget:0,dyTarget:0,dsControl:1,dsTargetSpeed:.05,underScale:.9,scale:.9,mscale:1,dmscale:.002,mscaleTarget:1,mscaleTargetControl:.001,mscaleTargetSpeed:.05},brush:{colour:Colour.Purple.splash,colour:Colour.Rose.splash,colour:Colour.Yellow.splash,colour:Colour.Grey.splash,colour:Colour.Green.splash,colour:999,size:3},cursor:{previous:{x:void 0,y:void 0}},dragon:{behaves:[]}};let WORLD_SIZE,WORLD_CELL_COUNT,WORLD_DIMENSION,WORLD_CELL_SIZE;const setWorldSize=e=>{WORLD_CELL_COUNT=2**(2*(WORLD_SIZE=e)),WORLD_CELL_SIZE=1/(WORLD_DIMENSION=2**WORLD_SIZE)};setWorldSize(6);const addCell=e=>{cacheCell(e),state.cellCount++},deleteCell=e=>{uncacheCell(e),e.isDeleted=!0,state.cellCount--},getCells=()=>{let e=new Set;for(let t of state.grid)for(let l of t.values())e.add(l);return e},GRID_SIZE=128;for(let x=0;x{let t=Math.floor(e.left*GRID_SIZE),l=Math.floor(e.top*GRID_SIZE),i=Math.ceil(e.right*GRID_SIZE),o=Math.ceil(e.bottom*GRID_SIZE);for(let a=t;a{for(let t of e.sections)t.delete(e)},world=makeCell({colour:111*WORLD_SIZE});addCell(world),on.load(()=>{let e=Show.start({paused:!1,scale:DPR}),{context:t,canvas:l}=e;l.style.position="absolute";let i=()=>{state.image.baseSize=Math.min(l.width,l.height),state.image.size=state.image.baseSize*state.camera.scale,state.image.left=state.camera.x*state.camera.scale,state.image.top=state.camera.y*state.camera.scale,state.image.right=state.image.left+state.image.size,state.image.bottom=state.image.top+state.image.size,state.view.left=clamp(state.image.left,0,l.width),state.view.top=clamp(state.image.top,0,l.height),state.view.right=clamp(state.image.right,0,l.width),state.view.bottom=clamp(state.image.bottom,0,l.height),state.view.width=state.view.right-state.view.left,state.view.height=state.view.bottom-state.view.top,state.view.visible=state.view.width>0&&state.view.height>0,state.view.fullyVisible=state.view.left===state.image.left&&state.view.right===state.image.right&&state.view.top===state.image.top&&state.view.bottom===state.image.bottom,state.view.iwidth=Math.ceil(state.view.width),state.view.iheight=Math.ceil(state.view.height),state.region.left=(state.view.left-state.image.left)/state.image.size,state.region.right=1+(state.view.right-state.image.right)/state.image.size,state.region.top=(state.view.top-state.image.top)/state.image.size,state.region.bottom=1+(state.view.bottom-state.image.bottom)/state.image.size,state.region.width=state.region.right-state.region.left,state.region.height=state.region.bottom-state.region.top,drawQueueNeedsReset=!0},o=()=>{state.image.data=t.getImageData(0,0,l.width,l.height)};t.fillStyle=Colour.Void,t.fillRect(0,0,l.width,l.height),i(),o(),state.camera.x+=(l.width-state.image.size)/2,state.camera.y+=(l.height-state.image.size)/2,e.resize=()=>{t.fillStyle=Colour.Void,t.fillRect(0,0,l.width,l.height),i(),o()};let a=e=>{i()},r=()=>{let e=getCells();for(let t of e.values())c(t,t.colour)},n=(e,t)=>c(e,e.colour,t),d=e=>!(e.right<=state.region.left)&&!(e.left>=state.region.right)&&!(e.bottom<=state.region.top)&&!(e.top>=state.region.bottom),h=e=>!(e.right<=state.region.left)&&!(e.left>=state.region.right)&&!(e.bottom<=state.region.top)&&!(e.top>=state.region.bottom),s=(e,t)=>(e.colour=t,h(e))?(O.add(e),D.delete(e),.01):0,c=(e,t,i=!1)=>{if(e.isDeleted||(e.colour=t,!h(e)))return 0;let o=state.image.size,a=l.width,r=state.camera.x*state.camera.scale,n=state.camera.y*state.camera.scale,d=Math.round(o*e.left+r);if(d>l.width)return 0;d<0&&(d=0);let s=Math.round(o*e.top+n);if(s>l.height)return 0;s<0&&(s=0);let c=Math.round(o*e.right+r);if(c<0)return 0;c>l.width&&(c=l.width);let u=Math.round(o*e.bottom+n);if(u<0)return 0;u>l.height&&(u=l.height);let g=Colour.splash(e.colour),$=g[0],p=g[1],f=g[2],v=4*a,m=c-d,_=4*m,w=(s*a+d)*4,b=state.image.data.data,S=Colour.Void.red,R=Colour.Void.green,C=Colour.Void.blue;if(!gridMode||m<=3||u-s<=3){S=$,R=p,C=f;for(let T=s;T{$(),R();let[e,t]=Mouse.position;state.cursor.previous.x=e,state.cursor.previous.y=t},g=!1,$=()=>{if(!state.worldBuilt||(Mouse.Middle||(g=!1),state.colourTode.hand.state!==to.BRUSHING&&state.colourTode.hand.state!==to.PENCILLING))return;if(Mouse.Middle&&!g){let[e,t]=Mouse.position;f(...p(e,t),{single:!0}),g=!0}if(!Mouse.Left)return;let[l,i]=p(...Mouse.position);if(void 0===l||void 0===i)return;let[o,a]=p(state.cursor.previous.x,state.cursor.previous.y),r=state.brush.size*WORLD_CELL_SIZE,n=l-o,d=i-a,h=Math.sign(n),s=Math.sign(d),c=Math.abs(n),u=Math.abs(d),$=Math.max(c,u),v=0,m=0;c===$?(m=WORLD_CELL_SIZE*s*(u/c),v=WORLD_CELL_SIZE*h):(v=WORLD_CELL_SIZE*h*(c/u),m=WORLD_CELL_SIZE*s);let _=new Set,w=$/WORLD_CELL_SIZE;if(0===n&&0===d)for(let b=-r/2;b<=r/2;b+=WORLD_CELL_SIZE)for(let S=-r/2;S<=r/2;S+=WORLD_CELL_SIZE)_.add([l+b,i+S]);else for(let R=0;R<=w;R++){let C=o+v*R,T=a+m*R;for(let k=-r/2;k<=r/2;k+=WORLD_CELL_SIZE)for(let E=-r/2;E<=r/2;E+=WORLD_CELL_SIZE)_.add([C+k,T+E])}for(let z of _.values())f(z[0],z[1])},p=(e,t)=>(e-=state.camera.x*state.camera.scale/DPR,t-=state.camera.y*state.camera.scale/DPR,e/=state.image.size,t/=state.image.size,[e*=DPR,t*=DPR]),f=(e,t,{single:l=!1}={})=>{let i=pickCell(e,t);if(void 0===i)return;if(!l&&(i.width!==WORLD_CELL_SIZE||i.height!=WORLD_CELL_SIZE)){let o=v(e,t);if(void 0!==o){let a=U([...o]);i=a}}if("number"==typeof state.brush.colour){i.colour=state.brush.colour,n(i);return}let r=[];for(let d of r=state.brush.colour.left[0].content.isDiagram?N(i,state.brush.colour.left[0].content):N(i,state.brush.colour))n(d)},v=(e,t)=>{let l=m(e,t),i=_(l,e,t);return i},m=(e,t)=>{let l=Math.floor(e*WORLD_DIMENSION)/WORLD_DIMENSION,i=Math.floor(t*WORLD_DIMENSION)/WORLD_DIMENSION,o=GRID_SIZE/WORLD_DIMENSION,a=new Set;for(let r=0;r{let i=new Set;for(let o of e.values())for(let a of o.values())if(!i.has(a)){for(let r of a.sections)if(!e.has(r))return;i.add(a)}return i},w,b,S;state.brush.hoverColour=Colour.Void;let R=()=>{let[e,t]=Mouse.position;if(e5.state===to.BRUSH||e5.state===to.BRUSHING||e5.state===to.PENCILLING){let l=pickCell(...p(e,t));void 0!==l&&(state.brush.hoverColour=l.colour)}else{let o=ts(e/C,t/C);if(void 0!==o){if(o.isSquare||o===squareTool){if(state.brush.hoverColour=o.value,o.joinExpanded){let a=Q(o.value);a.joins=[],state.brush.hoverColour=a}}else o.isTallRectangle||(o.isPaddle?state.brush.hoverColour=o.getColour(o):o.isSlot?state.brush.hoverColour=o.parent.getColour(o.parent):state.brush.hoverColour=o.colour.splash)}else state.brush.hoverColour=Colour.Void}if(!Mouse.Right){if(void 0!==w){let r=Math.hypot(e-w,t-b),n=Date.now()-S;(n<100||r<=0)&&(state.brush.hoverColour===Colour.Void?(++brushColourCycleIndex>=brushColourCycle.length&&(brushColourCycleIndex=0),tE(brushColourCycle[brushColourCycleIndex])):tE(state.brush.hoverColour),squareTool.toolbarNeedsColourUpdate=!0),drawQueueNeedsReset=!0}w=void 0,b=void 0;return}if(drawQueueNeedsReset=!0,void 0===w&&(w=e,b=t,S=Date.now(),dropperMovement=0),e5.state===to.FREE||e5.state==to.VOIDING||e5.state===to.BRUSH||e5.state===to.BRUSHING||e5.state===to.PENCILLING){let{x:d,y:h}=state.cursor.previous;if(void 0===d||void 0===h||void 0===e||void 0===t)return;let[s,c]=[e-d,t-h];state.camera.x+=s/state.camera.scale,state.camera.y+=c/state.camera.scale,i()}},C=DPR*SCALE;on.wheel(e=>{e.preventDefault();let t=e.deltaY/100;if(e.altKey)lt.scroll-=50*t,l$();else if(e.ctrlKey||e.metaKey){C-=.1*t;let l=lu();for(let i of l)i.needsColoursUpdate=!0;squareTool.toolbarNeedsColourUpdate=!0}else e.shiftKey?(0===t&&(t=e.deltaX/100),state.brush.size-=Math.sign(t),state.brush.size<0&&(state.brush.size=0)):k(t,...Mouse.position)},{passive:!1}),on.keydown(e=>{"Alt"===e.key&&e.preventDefault()},{passive:!1}),on.keydown(e=>{"f"===e.key&&(state.camera.x=640,state.camera.y=30,state.camera.scale=.95,i())});let T=e=>e,k=(e,t,l)=>{t*=DPR,l*=DPR;let o=-Math.sign(e),a=Math.abs(e);for(let r=0;r{e.preventDefault()}),on.keydown(e=>{let t=E[e.key];void 0!==t&&t(e)});let E={};E.e=()=>state.camera.mscaleTarget+=state.camera.mscaleTargetControl,E.q=()=>state.camera.mscaleTarget-=state.camera.mscaleTargetControl,E.w=()=>state.camera.dyTarget+=state.camera.dsControl,E.s=e=>{e.ctrlKey||e.metaKey||(state.camera.dyTarget-=state.camera.dsControl)},E.a=()=>state.camera.dxTarget+=state.camera.dsControl,E.d=()=>state.camera.dxTarget-=state.camera.dsControl,E[0]=()=>setWorldSize(0),E[1]=()=>setWorldSize(1),E[2]=()=>setWorldSize(2),E[3]=()=>setWorldSize(3),E[4]=()=>setWorldSize(4),E[5]=()=>setWorldSize(5),E[6]=()=>setWorldSize(6),E[7]=()=>setWorldSize(7),E[8]=()=>setWorldSize(8),E[9]=()=>setWorldSize(9),E.r=()=>{state.camera.mscaleTarget=1,state.camera.dxTarget=0,state.camera.dyTarget=0},E["="]=()=>edgeMode=1,E["-"]=()=>edgeMode=0,E.o=()=>edgeMode=0===edgeMode?1:0,gridMode=!0,E.g=()=>{gridMode=!gridMode,drawQueueNeedsReset=!0};let z=()=>{if(state.camera.mscale!==state.camera.mscaleTarget){let e=state.camera.mscaleTarget-state.camera.mscale;state.camera.mscale+=e*state.camera.mscaleTargetSpeed;let t=Math.sign(e),o=state.camera.mscaleTarget*state.camera.mscaleTargetControl*state.camera.mscaleTargetSpeed;1===t&&state.camera.mscale>state.camera.mscaleTarget-o&&(state.camera.mscale=state.camera.mscaleTarget),-1===t&&state.camera.mscalestate.camera.dxTarget-n&&(state.camera.dx=state.camera.dxTarget),-1===r&&state.camera.dxstate.camera.dyTarget-s&&(state.camera.dy=state.camera.dyTarget),-1===h&&state.camera.dy{eJ(),u(),z(),drawQueueNeedsReset&&(A(),drawQueueNeedsReset=!1),e.paused?B():I(),t.putImageData(state.image.data,0,0),t.clearRect(0,0,state.view.left,state.view.bottom),t.clearRect(state.view.left,0,l.width,state.view.top),t.clearRect(state.view.right,state.view.top,l.width,l.height),t.clearRect(0,state.view.bottom,l.width,l.height),state.time++,state.time>state.maxTime&&(state.time=0)};let D=new Set,O=new Set;drawQueueNeedsReset=!1;let P=e=>{for(let t=e.length-1;t>0;t--){let l=Random.Uint32%(t+1);[e[t],e[l]]=[e[l],e[t]]}return e},A=()=>{for(let e of(D.clear(),P([...state.grid])))if(d(e))for(let t of e.values())D.add(t)},I=()=>{let e=state.speed.dynamic?state.speed.aer*state.cellCount:state.speed.count;e=Math.min(e,state.cellCount),e*=state.worldBuilt?1:.1;let t=e*state.speed.redraw,l=!0;state.worldBuilt||(l=!1);let i=0;for(let o=0;o=t&&(l=!1);let r=L(a,l);i+=r}for(let d of O)if(i+=n(d),O.delete(d),i>=t)break;for(let h of D)if(i+=n(h),D.delete(h),i>=t)break},B=()=>{if(!state.view.visible)return;let e=state.speed.dynamic?state.speed.aer*state.cellCount:state.speed.count,t=e*state.speed.redraw;state.worldBuilt||(t=1);let l=0;for(let i of D)if(l+=n(i),D.delete(i),l>=t)break},L=(e,t)=>{if(void 0!==Y(e,t))return 1;for(let l of(state.dragon.behaves.shuffle(),state.dragon.behaves)){let i=l(e,t);if(void 0!==i)return i}return 0},G=(e,t,l)=>{let i=e.width/t,o=e.height/l,a=[],r=e.x,n=e.y,d=i,h=o;for(let s=0;s{let[l,i]=eo(t),o=e.width/l,a=e.height/i,r=[];for(let n of t.left){let d=M(n.content,{source:e.colour}),h=d[Random.Uint32%d.length],s=makeCell({x:e.x+n.x*o,y:e.y+n.y*a,width:n.width*o,height:n.height*a,colour:h});r.push(s)}for(let c of(deleteCell(e),r))addCell(c);return r},U=e=>{let t=1,l=1,i=0,o=0;for(let a of e)a.lefti&&(i=a.right),a.bottom>o&&(o=a.bottom),deleteCell(a);let r=makeCell({x:t,y:l,width:i-t,height:o-l,colour:e[0].colour,lastDraw:e[0].lastDraw});return addCell(r),r},Y=(e,t)=>{if(state.worldBuilt)return;if(state.cellCount>=WORLD_CELL_COUNT){state.worldBuilt=!0;return}if(e.colour<111)return 0;e.colour-=111;let l=2,i=2,o=G(e,l,i);for(let a of o)n(a);return 1},X=({values:e,channel:t=0,variable:l,add:i,subtract:o}={})=>{let a;return{values:a=void 0!==l?[!0,!0,!0,!0,!0,!0,!0,!0,!0,!0]:e,variable:l,channel:t,add:i,subtract:o}},H=e=>{let t=[...e.values],l=e.variable,i=e.channel,o=void 0===e.add?void 0:H(e.add),a=void 0===e.subtract?void 0:H(e.subtract),r=X({values:t,variable:l,channel:i,add:o,subtract:a});return r},V=e=>{let t=[!1,!1,!1,!1,!1,!1,!1,!1,!1,!1];return t[e]=!0,t},j=e=>{let t=V(e);return X({values:t})},W={};W.red=(e,{source:t}={})=>{if(void 0===t)return[!0,!0,!0,!0,!0,!0,!0,!0,!0,!0];let[l,i,o]=getRGB(t),a=V(l/100);return a},W.green=(e,{source:t}={})=>{if(void 0===t)return[!0,!0,!0,!0,!0,!0,!0,!0,!0,!0];let[l,i,o]=getRGB(t),a=V(i/10);return a},W.blue=(e,{source:t}={})=>{if(void 0===t)return[!0,!0,!0,!0,!0,!0,!0,!0,!0,!0];let[l,i,o]=getRGB(t),a=V(o);return a};let q=(e,t={})=>{if(void 0===e.variable)return e.values;let l=W[e.variable](e,t),i="red"===e.variable;return void 0!==e.add&&(l=e$(l,e.add,{source:t.source,multiplier:1,isHue:i})),void 0!==e.subtract&&(l=e$(l,e.subtract,{source:t.source,multiplier:-1,isHue:i})),l},Z=({channels:e,stamp:t,joins:l=[]}={})=>(void 0===e&&(e=[void 0,void 0,void 0]),{channels:e,stamp:t,joins:l});makeArrayFromSplash=e=>{let[t,l,i]=getRGB(e);t/=100,l/=10;let o=[!1,!1,!1,!1,!1,!1,!1,!1,!1,!1],a=[!1,!1,!1,!1,!1,!1,!1,!1,!1,!1],r=[!1,!1,!1,!1,!1,!1,!1,!1,!1,!1];o[t]=!0,a[l]=!0,r[i]=!0;let n=X({values:o,channel:0}),d=X({values:a,channel:1}),h=X({values:r,channel:2}),s=Z({channels:[n,d,h]});return s};let F=(e,t)=>{let l=M(e,t),i=new Set(l);return i},M=(e,t={})=>{let l=[];for(let i of e.joins){let o=M(i);l.push(...o)}if(e.isDiagram)return l.push(900),l;let[a,r,n]=e.channels;void 0===a&&(a=X({channel:0,variable:"red"})),void 0===r&&(r=X({channel:1,variable:"green"})),void 0===n&&(n=X({channel:2,variable:"blue"}));let d=q(a,t),h=q(r,t),s=q(n,t);for(let c=0;c{for(let t=0;t<3;t++)void 0===e.channels[t]&&(e.channels[t]=X({channel:t,variable:tK[t]}))},J=e=>{for(let t of e.channels){if(void 0===t)break;if(void 0!==t.variable)return!0}return!1},Q=e=>{if(void 0===e){let t=X({values:[!0,!0,!0,!0,!0,!0,!0,!0,!0,!0],channel:0}),l=X({values:[!0,!0,!0,!0,!0,!0,!0,!0,!0,!0],channel:1}),i=X({values:[!0,!0,!0,!0,!0,!0,!0,!0,!0,!0],channel:2}),o=Z({channels:[t,l,i]});return o}if(e.isDiagram)return ee(e);let a,r,n,d=e.stamp;void 0!==e.channels[0]&&null!==e.channels[0]&&(a=H(e.channels[0])),void 0!==e.channels[1]&&null!==e.channels[1]&&(r=H(e.channels[1])),void 0!==e.channels[2]&&null!==e.channels[2]&&(n=H(e.channels[2]));let h=[];for(let s of e.joins){let c=Q(s);h.push(c)}let u=Z({stamp:d,channels:[a,r,n],joins:h});return u};makeDiagram=({left:e=[],right:t,joins:l=[]}={})=>({left:e,right:t,isDiagram:!0,joins:l});let ee=e=>{let t=makeDiagram();for(let l of["left","right"]){if(void 0===e[l])continue;let i=[];for(let o of e[l]){let a=et(o);i.push(a)}t[l]=i}return t};makeDiagramCell=({x:e=0,y:t=0,width:l=1,height:i=1,content:o=Z(),instruction:a=DRAGON_INSTRUCTION.recolour,splitX:r=1,splitY:n=1}={})=>({x:e,y:t,width:l,height:i,content:o,instruction:a,splitX:r,splitY:n});let et=e=>{let t=Q(e.content);return{...e,content:t}},el=e=>{let[t,l]=eo(e);for(let i of["left","right"]){let o=e[i];if(void 0!==o)for(let a of o)a.width/=t,a.height/=l,a.x/=t,a.y/=l}return e},ei=e=>{let t=1/0,l=1/0;for(let i of["left","right"]){let o=e[i];if(void 0!==o)for(let a of o)a.width{let t=1/0,l=-1/0,i=1/0,o=-1/0;for(let a of e.left){let r=a.x,n=a.x+a.width,d=a.y,h=a.y+a.height;rl&&(l=n),h>o&&(o=h)}let s=l-t,c=o-i;return[s,c]};makeRule=({steps:e=[],transformations:t=DRAGON_TRANSFORMATIONS.NONE,locked:l=!0,chance:i}={})=>({steps:e,transformations:t,locked:l,chance:i}),DRAGON_TRANSFORMATIONS={NONE:[(e,t,l,i,o,a)=>[e,t],],X:[(e,t,l,i,o,a)=>[e,t],(e,t,l,i,o,a)=>[-e-l+o,t],],Y:[(e,t,l,i,o,a)=>[e,t],(e,t,l,i,o,a)=>[e,-t-i+a],],XY:[(e,t,l,i,o,a)=>[e,t],(e,t,l,i,o,a)=>[-e-l+o,t],(e,t,l,i,o,a)=>[e,-t-i+a],(e,t,l,i,o,a)=>[-e-l+o,-t-i+a],],R:[(e,t,l,i,o,a)=>[e,t],(e,t,l,i,o,a)=>[-t-i+a,e],(e,t,l,i,o,a)=>[-e-l+o,-t-i+a],(e,t,l,i,o,a)=>[t,-e-l+o],],XYR:[(e,t,l,i,o,a)=>[e,t],(e,t,l,i,o,a)=>[-t-i+a,e],(e,t,l,i,o,a)=>[-e-l+o,-t-i+a],(e,t,l,i,o,a)=>[t,-e-l+o],(e,t,l,i,o,a)=>[-e-l+o,t],(e,t,l,i,o,a)=>[-t-i+a,-e-l+o],(e,t,l,i,o,a)=>[e,-t-i+a],(e,t,l,i,o,a)=>[t,e],]};let ea=(e,t,l)=>{let[i,o]=eo(e.steps[0]),a=e.steps.map(e=>er(e,t,l,i,o)),r=makeRule({steps:a,transformations:e.transformations,locked:e.locked,chance:e.chance});return r},er=(e,t,l,i,o)=>{let{left:a,right:r}=e,[n,d]=[i,o],h=[],s=void 0===r?void 0:[];for(let c=0;c{let[a,r,n,d]=t(e.x,e.y,e.width,e.height,l,i),{splitX:h,splitY:s}=e;if(!o){let[c,u]=t(e.splitX,e.splitY,1,1,1,1).map(e=>Math.abs(e));h=c,s=u;let[g,$]=t(e.width,e.height,1,1,1,1).map(e=>Math.abs(e));n=g,d=$}void 0===a&&(a=e.x),void 0===r&&(r=e.y),void 0===n&&(n=e.width),void 0===d&&(d=e.height);let p=e.content;return makeDiagramCell({x:a,y:r,splitX:h,splitY:s,width:n,height:d,content:p,instruction:e.instruction})};registerRule=e=>{let t=[];for(let l of e.transformations){let i=ea(e,l);t.push(i)}let o=[];for(let a of t){let r=eh(a);o.push(...r)}let n=[];for(let d of o){let h=ec(d);n.push(h),state.dragon.behaves.push(h)}return{redundantRules:o,transformedRules:t,behaveFunctions:n}};let ed=({behaveFunctions:e})=>{state.dragon.behaves=state.dragon.behaves.filter(t=>!e.includes(t))},eh=e=>{let t=[],[l]=e.steps;for(let i of l.left){let o=(e,t,l=1,o=1)=>{let a=l/i.width,r=o/i.height,n=(e-i.x)*1/i.width,d=(t-i.y)*1/i.height;return[n,d,a,r]},a=ea(e,o,!0);t.push(a)}return t},es=e=>{let t=new Set,l=[e.left,e.right];for(let i of l)for(let o of i){let a=o.content.stamp;void 0!==a&&t.add(a)}return[...t.values()]},ec=e=>{let t=[];for(let l of e.steps){let i=es(l),o=eu(l,i,e.chance),a=eg(l,i),r=(e,t)=>{let[l,i]=o(e);if(void 0!==l)return a(l,t,i)};t.push(r)}let n=(e,l)=>{for(let i of t){let o=i(e,l);if(void 0!==o)return o}};return n},eu=(e,t,l=6)=>{let i=[];for(let o of e.left){let a=F(o.content),r=e=>{let t=o.width*e.width,l=o.height*e.height,i=e.x+o.x*e.width,r=e.y+o.y*e.height;if(1===edgeMode){for(;i>=1;)i-=1;for(;r>=1;)r-=1;for(;i<0;)i+=1;for(;r<0;)r+=1}let n=i+t/2,d=r+l/2,h=pickCell(n,d);return void 0!==h&&h.left===i&&h.top===r&&h.width===t&&h.height===l&&a.has(h.colour)?[h,o.content.stamp]:[void 0,void 0]};i.push(r)}let n=Math.round(10**(3-l/2)),d=()=>oneIn(n),h=e=>{if(void 0!==l&&!d())return[void 0,void 0];let o=[],a={};for(let r of t)a[r]=[];for(let n of i){let[h,s]=n(e);if(void 0===h)return[void 0,void 0];void 0!==s&&a[s].push(h.colour),o.push(h)}return[o,a]};return h},eg=(e,t)=>{let l=[];for(let i of e.right){let o=i.instruction(i);l.push(o)}let a=(e,t,l)=>{for(let i of l)r(e,t,i)},r=(e,t,l)=>{e[l]=[...t[l]]};return(e,i,o)=>{let n=0,d=0,h=0,s=[],c={};for(let u of(a(c,o,t),l)){let g=s.length>0?s.pop():e[d],$=u(g,i,e,d,c);void 0!==$.stampNameTakenFrom&&0===c[$.stampNameTakenFrom].length&&r(c,o,$.stampNameTakenFrom);let{drawn:p,bonusTargets:f,skip:v}=$;void 0!==v&&(h+=v),n+=p,void 0!==f&&s.push(...f),0===s.length&&(d++,h>0&&(d++,h--))}return n}};(DRAGON_INSTRUCTION={}).nothing=e=>()=>({drawn:0}),DRAGON_INSTRUCTION.nothing.type="NOTHING",DRAGON_INSTRUCTION.recolour=e=>{K(e.content);let t=M(e.content),l=J(e.content),i=(i,o,a,r,n)=>{let d,h=i.colour,c;if(void 0===e.content.stamp)d=t[Random.Uint32%t.length];else{let u=n[e.content.stamp];if(0===u.length)d=t[Random.Uint32%t.length];else{let g=Random.Uint32%u.length;d=h=u[g],u.splice(g,1),c=e.content.stamp}}if(l){let $=getRGB(d);for(let p=0;p!0===e&&t).filter(e=>!1!==e),w=_[Random.Uint8%_.length];d-=$[p];let b=10**(2-p);d+=w*b}}let S=0;return o?S+=s(i,d,!0):i.colour=d,{drawn:S,stampNameTakenFrom:c}};return i},DRAGON_INSTRUCTION.recolour.type="RECOLOUR";let e$=(e,t,{source:l,multiplier:i=1,isHue:o=!1})=>{let a=t.values;void 0!==t.variable&&(a=W[t.variable](t,{source:l}));let r=a.map((e,t)=>!0===e&&t).filter(e=>!1!==e),n=e.map((e,t)=>!0===e&&t).filter(e=>!1!==e),d=new Set;for(let h of n)for(let s of r){let c=clamp(h+s*i,0,9);d.add(c)}let u=[!1,!1,!1,!1,!1,!1,!1,!1,!1,!1];for(let g of d)u[g]=!0;return void 0!==t.add&&(u=e$(u,t.add,{source:l,multiplier:1,isHue:o})),void 0!==t.subtract&&(u=e$(u,t.add,{source:l,multiplier:-1,isHue:o})),u};DRAGON_INSTRUCTION.split=e=>{let t=(t,l,i,o,a)=>{let r=G(t,e.splitX,e.splitY);return{drawn:0,bonusTargets:r.reverse()}};return t},DRAGON_INSTRUCTION.split.type="SPLIT",DRAGON_INSTRUCTION.merge=e=>{let t=Math.abs(e.splitX)*Math.abs(e.splitY),l=(e,l,i,o,a)=>{let r=i.slice(o,o+t),n=U(r);return{drawn:0,skip:t-1,bonusTargets:[n]}};return l},DRAGON_INSTRUCTION.merge.type="MERGE";debugRegistry=(e,{transforms:t=!0,redundants:l=!0}={})=>{let{redundantRules:i,transformedRules:o}=e;if(print(""),print("================================================================="),t)for(let a of(print(""),print("TRANSFORMED RULES"),o))debugRule(a);if(l)for(let r of(print(""),print("REDUNDANT RULES"),i))debugRule(r)},debugRule=e=>{for(let t of e.steps){for(let l of(print(""),print("=== LEFT ==="),t.left))debugDiagramCell(l,{read:!0});for(let i of(print("=== RIGHT ==="),t.right))debugDiagramCell(i)}},debugDiagramCell=(e,{read:t=!1}={})=>{t?print("CHECK","at",e.x,e.y,"with size",e.width,e.height,"for",M(e.content)):"NOTHING"===e.instruction.type?print(e.instruction.type,"at",e.x,e.y,"with size",e.width,e.height):"RECOLOUR"===e.instruction.type?print(e.instruction.type,"at",e.x,e.y,"with size",e.width,e.height,"to",M(e.content)):print(e.instruction.type,e.splitX,e.splitY,"at",e.x,e.y,"with size",e.width,e.height)};let ep=makeArrayFromSplash(Colour.Grey.splash),ef=makeArrayFromSplash(Colour.Black.splash),ev=makeArrayFromSplash(Colour.Cyan.splash),em=makeArrayFromSplash(Colour.Blue.splash),ey=makeArrayFromSplash(Colour.Yellow.splash),e_=makeArrayFromSplash(Colour.Cyan.splash-111),[ex,ew]=getRGB(Colour.Red.splash);ex/=100,ew/=10;let eb=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:ep}),makeDiagramCell({x:0,y:1,content:ef}),],right:[makeDiagramCell({x:0,y:0,content:ef}),makeDiagramCell({x:0,y:1,content:ep}),]}),e0=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:ey}),makeDiagramCell({x:0,y:1,content:ef}),],right:[makeDiagramCell({x:0,y:0,content:ef}),makeDiagramCell({x:0,y:1,content:ey}),]}),eS=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:ey}),makeDiagramCell({x:1,y:1,content:ef}),],right:[makeDiagramCell({x:0,y:0,content:ef}),makeDiagramCell({x:1,y:1,content:ey}),]}),eR=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:ev}),makeDiagramCell({x:1,y:0,content:em}),]}),eC=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:em}),makeDiagramCell({x:0,y:1,content:ef}),],right:[makeDiagramCell({x:0,y:0,content:ef}),makeDiagramCell({x:0,y:1,content:em}),]}),eT=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:e_}),makeDiagramCell({x:1,y:0,content:ev}),],right:[makeDiagramCell({x:0,y:0,content:ev}),makeDiagramCell({x:1,y:0,content:e_}),]}),ek=makeDiagram({left:[makeDiagramCell({x:0,y:0,width:1,content:em}),],right:[makeDiagramCell({x:0,y:0,width:.5,content:e_,instruction:DRAGON_INSTRUCTION.split,splitX:2,splitY:1}),makeDiagramCell({x:.5,y:0,width:.5,content:ev}),]}),eE=makeDiagram({left:[makeDiagramCell({x:0,y:0,width:1,content:e_}),makeDiagramCell({x:1,y:0,width:1,content:ev}),makeDiagramCell({x:0,y:1,width:2,content:ef}),],right:[makeDiagramCell({x:0,y:0,width:2,content:em,instruction:DRAGON_INSTRUCTION.merge,splitX:2,splitY:1}),makeDiagramCell({x:0,y:1,width:2,content:ef}),]}),ez=makeDiagram({left:[makeDiagramCell({x:0,y:0,width:1,content:e_}),makeDiagramCell({x:1,y:0,width:1,content:e_}),],right:[makeDiagramCell({x:0,y:0,width:2,content:em,instruction:DRAGON_INSTRUCTION.merge,splitX:2,splitY:1}),]}),eD=makeDiagram({left:[makeDiagramCell({x:0,y:0,width:1,content:ev}),makeDiagramCell({x:1,y:0,width:1,content:ev}),],right:[makeDiagramCell({x:0,y:0,width:2,content:em,instruction:DRAGON_INSTRUCTION.merge,splitX:2,splitY:1}),]}),eO=makeDiagram({left:[makeDiagramCell({x:0,y:0,width:1,content:e_}),makeDiagramCell({x:1,y:0,width:1,content:ev}),makeDiagramCell({x:2,y:0,width:2,content:ef}),],right:[makeDiagramCell({x:0,y:0,width:2,content:ef,instruction:DRAGON_INSTRUCTION.merge,splitX:2,splitY:1}),makeDiagramCell({x:2,y:0,width:1,content:e_,instruction:DRAGON_INSTRUCTION.split,splitX:2,splitY:1}),makeDiagramCell({x:3,y:0,width:1,content:ev}),]}),eP=makeDiagram({left:[makeDiagramCell({x:0,y:0,width:.5,content:em}),makeDiagramCell({x:.5,y:0,width:.5,content:em}),makeDiagramCell({x:0,y:1,content:ef}),],right:[makeDiagramCell({x:0,y:0,width:1,content:ef,instruction:DRAGON_INSTRUCTION.merge,splitX:2,splitY:1}),makeDiagramCell({x:0,y:1,width:.5,content:em,instruction:DRAGON_INSTRUCTION.split,splitX:2,splitY:1}),makeDiagramCell({x:.5,y:1,width:.5,content:em}),]}),eA=makeDiagram({left:[makeDiagramCell({x:0,y:0,width:.5,content:ev}),makeDiagramCell({x:.5,y:0,width:.5,content:ev}),makeDiagramCell({x:0,y:1,content:ef}),],right:[makeDiagramCell({x:0,y:0,width:1,content:ef,instruction:DRAGON_INSTRUCTION.merge,splitX:2,splitY:1}),makeDiagramCell({x:0,y:1,width:.5,content:ev,instruction:DRAGON_INSTRUCTION.split,splitX:2,splitY:1}),makeDiagramCell({x:.5,y:1,width:.5,content:ev}),]}),eI=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:em}),makeDiagramCell({x:1,y:0,content:ev}),],right:[makeDiagramCell({x:0,y:0,content:ev}),makeDiagramCell({x:1,y:0,content:em}),]}),eB=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:e_}),],right:[makeDiagramCell({x:0,y:0,width:.5,content:em,instruction:DRAGON_INSTRUCTION.split,splitX:2,splitY:1}),makeDiagramCell({x:.5,y:0,width:.5,content:ev,instruction:DRAGON_INSTRUCTION.recolour}),]}),eL=makeDiagram({left:[makeDiagramCell({x:0,y:0,width:1,content:em}),makeDiagramCell({x:1,y:0,width:1,content:em}),],right:[makeDiagramCell({x:0,y:0,width:1,content:em}),makeDiagramCell({x:1,y:0,width:1,content:ev}),]}),eG=makeDiagram({left:[makeDiagramCell({x:0,y:0,width:1,content:ev}),makeDiagramCell({x:1,y:0,width:1,content:ev}),],right:[makeDiagramCell({x:0,y:0,width:1,content:em}),makeDiagramCell({x:1,y:0,width:1,content:ev}),]}),eN=makeDiagram({left:[makeDiagramCell({x:0,y:0,width:1,content:ep}),makeDiagramCell({x:0,y:1,width:1,content:ef}),],right:[makeDiagramCell({x:0,y:0,width:1,content:ef}),makeDiagramCell({x:0,y:1,width:1,content:ep}),]}),eU=makeDiagram({left:[makeDiagramCell({x:0,y:0,width:1,content:ep}),makeDiagramCell({x:1,y:0,width:1,content:ef}),],right:[makeDiagramCell({x:0,y:0,width:1,content:ef}),makeDiagramCell({x:1,y:0,width:1,content:ep}),]}),e7=Z();e7.channels=[X(),X(),X()];for(let eY=0;eY<3;eY++){let eX=e7.channels[eY];for(let e3=0;e3<10;e3++)0===eY&e3>0||(eX.values[e3]=!0)}RAINBOW_DIAGRAM=makeDiagram({left:[makeDiagramCell({content:e7})]});let e1=Z();e1.channels=[X(),X(),X()];for(let eH=0;eH<3;eH++){let e8=e1.channels[eH];for(let eV=0;eV<10;eV++)1===eH&eV>0||(e8.values[eV]=!0)}RAINBOW_DIAGRAM_2=makeDiagram({left:[makeDiagramCell({content:e1})]});let ej=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:e_}),],right:[makeDiagramCell({x:0,y:0,content:ev}),]}),eW=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:e_}),],right:[makeDiagramCell({x:0,y:0,content:em}),]}),eq=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:ev}),makeDiagramCell({x:0,y:1,content:ef}),],right:[makeDiagramCell({x:0,y:0,content:ef}),makeDiagramCell({x:0,y:1,content:ev}),]}),e2=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:em}),makeDiagramCell({x:0,y:1,content:ef}),],right:[makeDiagramCell({x:0,y:0,content:ef}),makeDiagramCell({x:0,y:1,content:em}),]}),eZ=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:em}),makeDiagramCell({x:1,y:0,content:ef}),],right:[makeDiagramCell({x:0,y:0,content:ef}),makeDiagramCell({x:1,y:0,content:em}),]}),eF=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:ev}),makeDiagramCell({x:-1,y:0,content:ef}),],right:[makeDiagramCell({x:0,y:0,content:ef}),makeDiagramCell({x:-1,y:0,content:ev}),]}),e4=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:em}),],right:[makeDiagramCell({x:0,y:0,content:ev}),]}),eM=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:ev}),],right:[makeDiagramCell({x:0,y:0,content:em}),]});state.colourTode={atoms:[],hand:{state:void 0,content:void 0,offset:{x:0,y:0},velocity:{x:0,y:0},velocityHistory:[],velocityMemory:5,previous:{x:0,y:0}}};let e5=state.colourTode.hand,eK=document.createElement("canvas"),e6=eK.getContext("2d");eK.style.position="absolute",eK.style.top="0px",document.body.append(eK),on.resize(()=>{eK.width=innerWidth*DPR,eK.height=innerHeight*DPR,eK.style.width=innerWidth,eK.style.height=innerHeight}),trigger("resize");let e9=()=>{te(),ti(),requestAnimationFrame(e9)},eJ=()=>{if(e5.velocityHistory.length>=e5.velocityMemory&&e5.velocityHistory.shift(),void 0!==Mouse.position&&void 0!==Mouse.position[0]&&void 0!==e5.previous.x){let[e,t]=Mouse.position.map(e=>e/C),l=(e-e5.previous.x)*DPR,i=(t-e5.previous.y)*DPR,o={x:l,y:i};e5.velocityHistory.push(o);let a=e5.velocityHistory.reduce((e,t)=>({x:e.x+t.x,y:e.y+t.y}),{x:0,y:0}),r={x:a.x/e5.velocityHistory.length,y:a.y/e5.velocityHistory.length};e5.velocity.x=r.x,e5.velocity.y=r.y,e5.previous.x=e,e5.previous.y=t}},eQ=.9,te=()=>{for(let e of state.colourTode.atoms)tt(e)},tt=(e,t=!0)=>{for(let l of e.children)tt(l,!1);if(void 0!==e.hover&&tl(e),e.update(e),e5.content===e||0===e.dx&&0===e.dy)return;if(e.x+=e.dx,e.y+=e.dy,e.x=clamp(e.x,e.minX,e.maxX),e.y=clamp(e.y,e.minY,e.maxY),e.dx*=eQ,e.dy*=eQ,t&&t$(e)){tu(e);return}let[i,o]=Mouse.position.map(e=>e/C);e5.state.atommove&&e5.state.atommove(e,i,o)},tl=e=>{if(e.highlightedAtom=void 0,e5.content!==e||e5.state!==to.DRAGGING)return;void 0!==e.highlight&&(tx(e,e.highlight),e.highlight=void 0);let t=e.hover(e);if(void 0!==t){if(void 0===e.highlight){let l=t_(e,lb,{bottom:!0});l.hasBorder=!0,l.colour=Colour.Grey;let{x:i,y:o}=ty(t);l.x=i,l.y=o,l.width=t.width,l.height=t.height,e.highlight=l}e.highlightedAtom=t}},ti=()=>{for(let e of(e6.clearRect(0,0,eK.width,eK.height),e6.scale(C,C),state.colourTode.atoms))tc(e);e6.scale(1/C,1/C)};requestAnimationFrame(e9);let to={};HAND_RELEASE=.5,to.FREE={cursor:"auto",mousemove(e){let t=e.clientX/C,l=e.clientY/C,i=ts(t,l);if(void 0!==i){i.grabbable&&(Mouse.Left?i.grabbable&&i.draggable&&(tf(i,t,l),e5.pityStartX=e.clientX,e5.pityStartY=e.clientY,e5.pityStartT=Date.now(),tn(to.TOUCHING),to.TOUCHING.mousemove(e)):void 0!==i.cursor?tn(to.HOVER,i.cursor(i,to.HOVER)):i.dragOnly?tn(to.HOVER,"move"):tn(to.HOVER));return}let[o,a]=Mouse.position;o*=DPR,a*=DPR,!(ostate.view.right)&&!(astate.view.bottom)&&(Mouse.Left?tn(to.BRUSHING):Mouse.Middle?tn(to.PENCILLING):tn(to.BRUSH))},mousedown(e){state.worldBuilt&&(e5.voidingStart=[e.clientX,e.clientY],tn(to.VOIDING))},atommove(e,t,l){if(e.grabbable&&tp(e,t,l)){if(Mouse.Left){tf(e,t,l),tn(to.DRAGGING),e5.content=e5.content.drag(e5.content,t,l);return}void 0!==e.cursor?tn(to.HOVER,e.cursor(e,to.HOVER)):e.dragOnly?tn(to.HOVER,"move"):tn(to.HOVER)}},camerapan(){let[e,t]=Mouse.position;if(e*=DPR,t*=DPR,e>=state.view.left&&e<=state.view.right&&t>=state.view.top&&t<=state.view.bottom){tn(to.BRUSH);return}}};let ta=!0;to.VOIDING={cursor:"auto",mousemove(e){let t=e5.voidingStart,[l,i]=t,o=[e.clientX-l,e.clientY-i];Math.hypot(...o)>10&&(tn(to.FREE),to.FREE.mousemove(e))},mouseup(t){let i=WORLD_SIZE;if(setWorldSize(0),ta)f(.5,.5);else{let o=state.brush.colour;state.brush.colour=111*i,f(.5,.5),state.brush.colour=o,state.worldBuilt=!1,e.paused=!1,l.style["background-color"]=Colour.Void}ta=!ta,setWorldSize(i),tn(to.FREE)}},to.BRUSH={cursor:"crosshair",mousemove(e){let t=e.clientX/C,l=e.clientY/C,i=ts(t,l);if(void 0!==i){i.grabbable?void 0!==i.cursor?tn(to.HOVER,i.cursor(i,to.HOVER)):i.dragOnly?tn(to.HOVER,"move"):tn(to.HOVER):tn(to.FREE);return}let o=e.clientX*DPR,a=e.clientY*DPR;(!(o>=state.view.left)||!(o<=state.view.right)||!(a>=state.view.top)||!(a<=state.view.bottom))&&tn(to.FREE)},mousedown(e){tn(to.BRUSHING)},middlemousedown(e){tn(to.PENCILLING)},atommove(e,t,l){tp(e,t,l)&&(e.grabbable?tn(to.HOVER):tn(to.FREE))},camerapan(){let[e,t]=Mouse.position;e*=DPR,t*=DPR,(!(e>=state.view.left)||!(e<=state.view.right)||!(t>=state.view.top)||!(t<=state.view.bottom))&&tn(to.FREE)}},to.BRUSHING={cursor:"crosshair",mousemove(e){let t=e.clientX*DPR,l=e.clientY*DPR;(!(t>=state.view.left)||!(t<=state.view.right)||!(l>=state.view.top)||!(l<=state.view.bottom))&&tn(to.FREE)},mouseup(e){tn(to.BRUSH)},camerapan(){let[e,t]=Mouse.position;if(e*=DPR,t*=DPR,e>=state.view.left&&e<=state.view.right&&t>=state.view.top&&t<=state.view.bottom)return;let[l,i]=Mouse.position.map(e=>e/C),o=ts(l,i);if(void 0!==o){o.grabbable?void 0!==o.cursor?tn(to.HOVER,o.cursor(o,to.HOVER)):o.dragOnly?tn(to.HOVER,"move"):tn(to.HOVER):tn(to.FREE);return}tn(to.FREE)}},to.PENCILLING={cursor:"crosshair",mousemove:to.BRUSHING.mousemove,middlemouseup:to.BRUSHING.mouseup,camerapan:to.BRUSHING.camerapan},to.HOVER={cursor:"pointer",mousedown(e){let t=ts(e.clientX/C,e.clientY/C);void 0!==t&&t.grabbable&&(tf(t,e.clientX/C,e.clientY/C),t.dragOnly?(e5.pityStartX=e.clientX,e5.pityStartY=e.clientY,e5.pityStartT=Date.now(),e5.hasStartedDragging=!1,e5.touchButton=0,tn(to.TOUCHING,"move")):(e5.pityStartX=e.clientX,e5.pityStartY=e.clientY,e5.pityStartT=Date.now(),e5.hasStartedDragging=!1,e5.touchButton=0,tn(to.TOUCHING)))},mousemove(e){let t=e.clientX/C,l=e.clientY/C,i=ts(t,l);if(void 0!==i){i.grabbable?void 0!==i.cursor?tn(to.HOVER,i.cursor(i,to.HOVER)):i.dragOnly?tn(to.HOVER,"move"):tn(to.HOVER):tn(to.FREE);return}let o=e.clientX,a=e.clientY;if(o>=state.view.left&&o<=state.view.right&&a>=state.view.top&&a<=state.view.bottom){tn(to.BRUSH);return}tn(to.FREE)},atommove(e,t,l){if(tp(e,t,l))return;let i=ts(t,l);if(void 0!==i)return;let[o,a]=Mouse.position;if(o*=DPR,a*=DPR,o>=state.view.left&&o<=state.view.right&&a>=state.view.top&&a<=state.view.bottom){tn(to.BRUSH);return}tn(to.FREE)},rightmousedown(e){let t=ts(e.clientX/C,e.clientY/C);void 0!==t&&t.grabbable&&(tf(t,e.clientX/C,e.clientY/C),t.dragOnly?(e5.pityStartX=e.clientX,e5.pityStartY=e.clientY,e5.pityStartT=Date.now(),e5.hasStartedDragging=!1,e5.touchButton=2,tn(to.TOUCHING,"move")):(e5.pityStartX=e.clientX,e5.pityStartY=e.clientY,e5.pityStartT=Date.now(),e5.hasStartedDragging=!1,e5.touchButton=2,tn(to.TOUCHING)))}};let tr=(e,t)=>t?.6*e:e;to.TOUCHING={cursor:"pointer",mousemove(e){if(0===e.movementX&&0===e.movementY||2===e5.touchButton&&!e5.content.rightDraggable)return;let t=Math.hypot(e.clientX-e5.pityStartX,e.clientY-e5.pityStartY),l=e.clientX-e5.pityStartX,i=e.clientY-e5.pityStartY;if(e5.content.dragLockX||(e5.content.x=(e5.pityStartX+tr(l,e5.content.attached&&!e5.content.noDampen))/C*DPR+e5.offset.x),e5.content.dragLockY||(e5.content.y=(e5.pityStartY+tr(i,e5.content.attached&&!e5.content.noDampen))/C*DPR+e5.offset.y),e5.content.x=clamp(e5.content.x,e5.content.minX,e5.content.maxX),e5.content.y=clamp(e5.content.y,e5.content.minY,e5.content.maxY),t<15)return;let o=Date.now()-e5.pityStartT;if(o<100){let a=Math.hypot(e5.velocity.x,e5.velocity.y);if(a<=10)return}e5.content.dragLockX||(e5.content.x=e5.pityStartX/C*DPR+e5.offset.x),e5.content.dragLockY||(e5.content.y=e5.pityStartY/C*DPR+e5.offset.y),e5.content.x=clamp(e5.content.x,e5.content.minX,e5.content.maxX),e5.content.y=clamp(e5.content.y,e5.content.minY,e5.content.maxY);let r=e.clientX/C,n=e.clientY/C;if(0===e5.touchButton&&e5.content.draggable){tn(to.DRAGGING);let d=e5.content.attached&&!e5.content.dragOnly&&!e5.content.noDampen;e5.content=e5.content.drag(e5.content,r,n),e5.content.dragLockX||(e5.content.x=(e5.pityStartX+tr(l,d))/C+e5.offset.x),e5.content.dragLockY||(e5.content.y=(e5.pityStartY+tr(i,d))/C+e5.offset.y),e5.content.x=clamp(e5.content.x,e5.content.minX,e5.content.maxX),e5.content.y=clamp(e5.content.y,e5.content.minY,e5.content.maxY),to.DRAGGING.mousemove(e);return}if(2===e5.touchButton&&e5.content.rightDraggable){tn(to.DRAGGING);let h=e5.content.attached&&!e5.content.dragOnly&&!e5.content.noDampen;e5.content=e5.content.rightDrag(e5.content,r,n),e5.content.dragLockX||(e5.content.x=(e5.pityStartX+tr(l,h))/C+e5.offset.x),e5.content.dragLockY||(e5.content.y=(e5.pityStartY+tr(i,h))/C+e5.offset.y),e5.content.x=clamp(e5.content.x,e5.content.minX,e5.content.maxX),e5.content.y=clamp(e5.content.y,e5.content.minY,e5.content.maxY),to.DRAGGING.mousemove(e);return}let s=ts(r,n);if(void 0!==s){s.grabbable?void 0!==s.cursor?tn(to.HOVER,s.cursor(s,to.HOVER)):s.dragOnly?tn(to.HOVER,"move"):tn(to.HOVER):tn(to.FREE);return}let c=e.clientX*DPR,u=e.clientY*DPR;if(c>=state.view.left&&c<=state.view.right&&u>=state.view.top&&u<=state.view.bottom){tn(to.BRUSH);return}tn(to.FREE)},mouseup(e){if(0!==e5.touchButton)return;e5.clickContent.click(e5.clickContent),e5.clickContent.dx=0,e5.clickContent.dy=0,e5.clickContent=void 0,e5.content.attached&&(e5.content.x=e5.pityStartX/C*DPR+e5.offset.x,e5.content.y=e5.pityStartY/C*DPR+e5.offset.y),e5.content.dx=0,e5.content.dy=0,e5.content=void 0;let t=e.clientX/C,l=e.clientY/C,i=ts(t,l);void 0!==i?void 0!==i.cursor?tn(to.HOVER,i.cursor(i,to.HOVER)):i.dragOnly?tn(to.HOVER,"move"):tn(to.HOVER):tn(to.FREE)},rightmouseup(e){if(2!==e5.touchButton)return;e5.clickContent.rightClick(e5.clickContent),e5.clickContent.dx=0,e5.clickContent.dy=0,e5.clickContent=void 0,e5.content.attached&&(e5.content.x=e5.pityStartX/C*DPR+e5.offset.x,e5.content.y=e5.pityStartY/C*DPR+e5.offset.y),e5.content.dx=0,e5.content.dy=0,e5.content=void 0;let t=e.clientX/C,l=e.clientY/C,i=ts(t,l);void 0!==i?void 0!==i.cursor?tn(to.HOVER,i.cursor(i,to.HOVER)):i.dragOnly?tn(to.HOVER,"move"):tn(to.HOVER):tn(to.FREE)}},to.DRAGGING={cursor:"move",mousemove(e){e5.hasStartedDragging||(e5.hasStartedDragging=!0,e5.content=e5.content.drag(e5.content,e.clientX/C*DPR,e.clientY/C*DPR));let t=e5.content.x,l=e5.content.y;e5.content.dragLockX||(e5.content.x=e.clientX/C*DPR+e5.offset.x),e5.content.dragLockY||(e5.content.y=e.clientY/C*DPR+e5.offset.y),e5.content.x=clamp(e5.content.x,e5.content.minX,e5.content.maxX),e5.content.y=clamp(e5.content.y,e5.content.minY,e5.content.maxY);let i=e5.content.x-t,o=e5.content.y-l;e5.content.move(e5.content,i,o)},mouseup(e){if(0!==e5.touchButton)return;e5.hasStartedDragging=!0,e5.content.dragLockX||(e5.content.dx=e5.velocity.x*HAND_RELEASE),e5.content.dragLockY||(e5.content.dy=e5.velocity.y*HAND_RELEASE),e5.content.drop(e5.content),void 0!==e5.content.highlightedAtom&&(e5.content.place(e5.content,e5.content.highlightedAtom),void 0!==e5.content.highlight&&(tx(e5.content,e5.content.highlight),e5.content.highlight=void 0)),e5.content=void 0;let t=e.clientX/C,l=e.clientY/C,i=ts(t,l);void 0!==i&&i.grabbable?void 0!==i.cursor?tn(to.HOVER,i.cursor(i,to.HOVER)):i.dragOnly?tn(to.HOVER,"move"):tn(to.HOVER):tn(to.FREE)},rightmouseup(e){if(2!==e5.touchButton)return;e5.hasStartedDragging=!0,e5.content.dragLockX||(e5.content.dx=e5.velocity.x*HAND_RELEASE),e5.content.dragLockY||(e5.content.dy=e5.velocity.y*HAND_RELEASE),e5.content.drop(e5.content),void 0!==e5.content.highlightedAtom&&(e5.content.place(e5.content,e5.content.highlightedAtom),void 0!==e5.content.highlight&&(tx(e5.content,e5.content.highlight),e5.content.highlight=void 0)),e5.content=void 0;let t=e.clientX/C,l=e.clientY/C,i=ts(t,l);void 0!==i&&i.grabbable?void 0!==i.cursor?tn(to.HOVER,i.cursor(i,to.HOVER)):i.dragOnly?tn(to.HOVER,"move"):tn(to.HOVER):tn(to.FREE)}};let tn=(e,t=e.cursor)=>{void 0!==e5.content&&void 0!==e5.content.cursor&&(t=e5.content.cursor(e5.content,e)),eK.style.cursor=t,e5.state=e};on.mousemove(e=>e5.state.mousemove?e5.state.mousemove(e):void 0),on.mousedown(e=>{0===e.button&&e5.state.mousedown&&e5.state.mousedown(e),1===e.button&&e5.state.middlemousedown&&e5.state.middlemousedown(e),2===e.button&&e5.state.rightmousedown&&e5.state.rightmousedown(e)}),on.mouseup(e=>{0===e.button&&e5.state.mouseup&&e5.state.mouseup(e),1===e.button&&e5.state.middlemouseup&&e5.state.middlemouseup(e),2===e.button&&e5.state.rightmouseup&&e5.state.rightmouseup(e)}),e5.state=to.FREE;let td={x:0,y:0,grab:(e,t,l,i=e)=>i,touch:(e,t=e)=>t},th=({grabbable:e=!0,draggable:t=!0,click:l=()=>{},rightClick:i=()=>{},drag:o=e=>e,rightDrag:a=e=>e,move:r=()=>{},drop:n=()=>{},draw:d=()=>{},update:h=()=>{},offscreen:s=()=>!1,overlaps:c=()=>!1,grab:u=e=>e,touch:g=e=>e,highlighter:$=!1,hover:p=()=>{},place:f=()=>{},x:v=0,y:m=0,dx:_=0,dy:w=0,maxX:b=1/0,minX:S=-1/0,maxY:R=1/0,minY:C=-1/0,size:T=40,colour:k=Colour.splash(999),children:E=[],parent:z=td,width:D=T,height:O=T,construct:P=()=>{},hasInner:A=!0,...I}={},...B)=>{let L={highlighter:$,place:f,hover:p,hasInner:A,move:r,drop:n,maxX:b,minX:S,maxY:R,minY:C,update:h,construct:P,draggable:t,width:D,height:O,touch:g,parent:z,children:E,draw:d,grabbable:e,click:l,drag:o,overlaps:c,offscreen:s,grab:u,x:v,y:m,dx:_,dy:w,size:T,colour:k,rightClick:i,rightDrag:a,...I};return L.construct(L,...B),L},ts=(e,t)=>{e*=DPR,t*=DPR;for(let l=state.colourTode.atoms.length-1;l>=0;l--){let i=state.colourTode.atoms[l];if(i.justVisual)continue;let o=tp(i,e,t);if(void 0!==o)return o}},tc=e=>{for(let t of e.children)t.behindParent&&tc(t);for(let l of(e.behindChildren&&e.draw(e),e.children))l.behindParent||tc(l);e.behindChildren||e.draw(e)},tu=e=>{let t=state.colourTode.atoms.indexOf(e);state.colourTode.atoms.splice(t,1)},tg=e=>{state.colourTode.atoms.push(e)},t$=e=>{for(let t of e.children)if(!t$(t))return!1;return e.offscreen(e)},tp=(e,t,l)=>{if(!e.behindChildren&&e.overlaps(e,t,l))return e;for(let i=e.children.length-1;i>=0;i--){let o=e.children[i];if(o.behindParent)continue;let a=tp(o,t,l);if(a)return a}if(e.behindChildren&&e.overlaps(e,t,l))return e;for(let r=e.children.length-1;r>=0;r--){let n=e.children[r];if(!n.behindParent)continue;let d=tp(n,t,l);if(d)return d}},tf=(e,t,l)=>{let i=e,o=e.touch(e);if(o!==i){let a=o.touch(o,t,l,i);i=o,o=a}e5.clickContent=o;let r=e,n=e.grab(e,t,l);if(void 0===n)return;if(n!==r){let d=n.grab(n,t,l,r);r=n,n=d}e5.content=n;let{x:h,y:s}=ty(n,{forceAbsolute:!0});return e5.offset.x=h-t*DPR,e5.offset.y=s-l*DPR,n.dx=0,n.dy=0,e.stayAtBack?tm(n):tv(n),n},tv=e=>{if(e.parent===td)tu(e),tg(e);else{let t=e.parent.children.indexOf(e);e.parent.children.splice(t,1),e.parent.children.push(e),e.parent.stayAtBack?tm(e.parent):tv(e.parent)}},tm=e=>{if(e.parent===td){let t=state.colourTode.atoms.indexOf(e);state.colourTode.atoms.splice(t,1),state.colourTode.atoms.unshift(e)}else{let l=e.parent.children.indexOf(e);e.parent.children.splice(l,1),e.parent.children.unshift(e),e.parent.stayAtBack?tm(e.parent):tv(e.parent)}},ty=(e,{forceAbsolute:t=!1}={})=>{let{x:l,y:i}=e;if(t||void 0===e.parent||e.hasAbsolutePosition)return{x:l,y:i};let{x:o,y:a}=ty(e.parent);return{x:l+o,y:i+a}},t_=(e,t,{bottom:l=!1}={})=>{let i=th(t);return l?e.children.unshift(i):e.children.push(i),i.parent=e,i},tx=(e,t,{quiet:l=!1}={})=>{let i=e.children.indexOf(t);if(-1===i){if(l)return;throw Error("Can't delete child of atom because I can't find it!")}e.children.splice(i,1),t.parent=td},tw=(e,t)=>{if(void 0===t)throw Error("Can't give child because child is undefined");if(void 0===e)throw Error("Can't give child because parent is undefined");tu(t),t.stayAtBack||t.behindOtherChildren?e.children.unshift(t):e.children.push(t),t.parent=e},tb=(e,t)=>{if(e5.content===t){let{x:l,y:i}=ty(e);e5.offset.x+=l,e5.offset.y+=i}tx(e,t),tg(t)},t0=3;borderColours=PREBUILT_BORDER_COLOURS;let tS=borderColours.clone;tS[999]=Colour.splash(999);let tR={draw(e){let{x:t,y:l}=ty(e),i=Math.round(t),o=Math.round(l),a=Math.round(e.width),r=Math.round(e.height),n=Math.round(e.width/2);if(e.hasBorder){if(e.hasInner){let d=t0;void 0===e.borderColour?(e6.fillStyle=Colour.splash(e.colour.splash),e.isTool?(e6.fillStyle=Colour.splash(e.colour.splash),d*=1.5):e.width===e.height&&(d*=1.5)):e6.fillStyle=e.borderColour,e6.beginPath(),e6.rect(i,o,a,r),void 0!==e.stamp&&e6.arc(i+n,o+n,Math.round((lv.size-t7/2)/2),0,2*Math.PI),e.isGradient?e6.putImageData(e.gradient,i*C,o*C):(e6.fill("evenodd"),e6.beginPath(),e6.fillStyle=e.colour,e6.rect(i+d,o+d,a-2*d,r-2*d),void 0!==e.stamp&&e6.arc(i+n,o+n,Math.round((lv.size-t7/2)/2)+d,0,2*Math.PI),e6.fill("evenodd"))}else void 0===e.borderColour?e6.strokeStyle=borderColours[e.colour.splash]:e6.strokeStyle=e.borderColour,i=Math.round(t+.5)-.5,o=Math.round(l+.5)-.5,e6.lineWidth=t0,e6.strokeRect(i,o,a,r)}else e6.fillStyle=e.colour,e6.fillRect(i,o,a,r)},offscreen(e){let{x:t,y:i}=ty(e),o=t+e.width,a=i+e.height;return!!(o<0)||!!(a<0)||!!(t>l.width)||!!(i>l.height)},overlaps(e,t,l){let{x:i,y:o}=ty(e),a=t0;(e.isTool||e.isSquare||e.isTallRectangle)&&(a*=1.5);let r=i+e.width,n=o+e.height;return!(tr)&&!(l>n)}},tC={draw(e){let{x:t,y:l}=ty(e),i=t+e.width/2,o=l+e.height/2,a=e.width/2;if(e.hasBorder){e.isTool&&(e.borderColour=tS[e.colour.splash]),e6.fillStyle=void 0!==e.borderColour?e.borderColour:Colour.Void,e6.beginPath(),e6.arc(i,o,a,0,2*Math.PI),e6.fill();let r=void 0!==e.borderScale?e.borderScale:1;a=e.width/2-1.5*t0*r}e6.fillStyle=e.colour,e6.beginPath(),e6.arc(i,o,a,0,2*Math.PI),e6.fill()},offscreen:tR.offscreen,overlaps:tR.overlaps},tT=(e,[t,l],i=!1)=>{for(let o of e.cellAtoms){i&&(o=o.slot);let{x:a,y:r}=ty(o);if(a===t&&r===l)return!0}return!1},tk=(e,[t,l],i=!1)=>{for(let o of e.cellAtoms){i&&(o=o.slot);let{x:a,y:r}=ty(o);if(a===t&&r===l&&(o.isLeftSlot||o.isSlot))return!0}return!1},tE=e=>{if("number"==typeof e)state.brush.colour=e,squareTool.toolbarNeedsColourUpdate=!0,squareTool.value=makeArrayFromSplash(e);else{let t=makeDiagramCell({content:e});state.brush.colour=makeDiagram({left:[t]}),squareTool.value=t.content,squareTool.toolbarNeedsColourUpdate=!0}},tz={isSquare:!0,hasBorder:!0,draw(e){!e.value.isDiagram&&tR.draw(e)},overlaps:tR.overlaps,offscreen:tR.offscreen,touch:e=>(tE(e.value),e),click(e){e.joins.length>0?e.parent!==td&&e.parent.isPaddle||(e.joinExpanded?e.joinUnepxand(e):e.joinExpand(e)):e.value.isDiagram||(e.expanded?e.unexpand(e):e.parent!==td&&e.parent.isPaddle||e.expand(e)),tE(e.value)},expand(e){e.expanded=!0,e.createPicker(e),e.value.channels.some(e=>void 0===e)&&lG("triangle")},unexpand(e){e.expanded=!1,e.redExpanded=e.red&&e.red.expanded,e.greenExpanded=e.green&&e.green.expanded,e.blueExpanded=e.blue&&e.blue.expanded,e.deletePicker(e)},createPicker(e){let t=t_(e,lC);t.width+=t7,e.pickerHandle=t,e.pickerHandle.behindParent=!0;let l=t_(e,t1);if(e.pickerPad=l,void 0!==e.value.channels[2]){if(void 0===e.value.channels[2].variable){let i=t_(e,tV);i.channelSlot="blue",i.x+=t3+3*(tz.size+t3),i.value=e.value.channels[2],i.needsColoursUpdate=!0,e.blue=i,i.deletedOptions=e.deletedBlueOptions,e.blueExpanded&&e.blue.click(e.blue),e.blue.attached=!0}else{let o=e.variableAtoms[2];o.behindOtherChildren=!1,tg(o),tw(e,o),o.variable="blue",o.x=(t3+tz.size)*3+(tz.size+t3)/2-o.width/3,o.y=e.height/2-o.height/2,o.attached=!0,e.blue=o}}if(void 0!==e.value.channels[1]){if(void 0===e.value.channels[1].variable){let a=t_(e,tV);a.channelSlot="green",a.x+=t3+2*(tz.size+t3),a.value=e.value.channels[1],a.needsColoursUpdate=!0,e.green=a,a.deletedOptions=e.deletedGreenOptions,e.greenExpanded&&e.green.click(e.green),e.green.attached=!0}else{let r=e.variableAtoms[1];r.behindOtherChildren=!1,tg(r),tw(e,r),r.variable="green",r.x=(t3+tz.size)*2+(tz.size+t3)/2-r.width/3,r.y=e.height/2-r.height/2,r.attached=!0,e.green=r}}if(void 0!==e.value.channels[0]){if(void 0===e.value.channels[0].variable){let n=t_(e,tV);n.channelSlot="red",n.x+=t3+tz.size+t3,n.value=e.value.channels[0],n.needsColoursUpdate=!0,e.red=n,n.deletedOptions=e.deletedRedOptions,e.redExpanded&&e.red.click(e.red),e.red.attached=!0}else{let d=e.variableAtoms[0];d.behindOtherChildren=!1,tg(d),tw(e,d),d.x=t3+tz.size+(tz.size+t3)/2-d.width/3,d.y=e.height/2-d.height/2,d.attached=!0,e.red=d}}},deletePicker(e){tx(e,e.pickerPad),tx(e,e.pickerHandle),e.red&&(e.deletedRedOptions=e.red.options,tx(e,e.red)),e.green&&(e.deletedGreenOptions=e.green.options,tx(e,e.green)),e.blue&&(e.deletedBlueOptions=e.blue.options,tx(e,e.blue))},receiveNumber(e,t,l=t.channel,{expanded:i,numberAtom:o}={}){if(e.redExpanded=e.red&&e.red.expanded,e.greenExpanded=e.green&&e.green.expanded,e.blueExpanded=e.blue&&e.blue.expanded,void 0===e.variableAtoms&&(e.variableAtoms=[void 0,void 0,void 0]),void 0!==t&&void 0!==t.variable?e.variableAtoms[l]=o:e.variableAtoms[l]=void 0,void 0!==i){let a=t8[l];e[`${a}Expanded`]=i}if(e.value.channels[l]=t,e.deletePicker(e),e.createPicker(e),e.needsColoursUpdate=!0,e.colourTicker=1/0,e.parent!==td){let r=e.parent;lc(r)}let n=makeDiagramCell({content:e.value});state.brush.colour=makeDiagram({left:[n]}),squareTool.toolbarNeedsColourUpdate=!0,lN.toolbarNeedsColourUpdate=!0,lU.toolbarNeedsColourUpdate=!0,lY.toolbarNeedsColourUpdate=!0},construct(e){e.needsColoursUpdate=!0,"number"==typeof state.brush.colour?e.value=makeArrayFromSplash(state.brush.colour):e.value=Q(state.brush.colour.left[0].content),e.colourId=0,e.dcolourId=1,e.colourTicker=1/0,e.joins=[],e.joinColourIds=[],e.variableAtoms=[],e.gradient=new ImageData(e.width*C,e.height*C),e.headGradient=new ImageData(e.width*C,e.height*C)},updateGradient(e){let t=Q(e.value);if(t.joins=[],e.colours=M(t),e.isGradient=!0,e.joins.length>0&&!e.joinExpanded){let l=[];for(let i of e.joins)i.updateGradient(i),l.push(i.gradient);e.headGradient=tI({colours:e.colours,width:e.width*C,height:e.height*C,stamp:e.value.stamp,gradient:e.headGradient});let o=[e.headGradient,...l];e.gradient=tA({gradients:o,width:e.width*C,height:e.height*C,stamp:e.value.stamp,mergedGradient:e.gradient})}else e.gradient=tI({colours:e.colours,width:e.width*C,height:e.height*C,gradient:e.gradient,stamp:e.value.stamp})},update(e){if(e.value.isDiagram){if(void 0===e.multiAtoms||0===e.multiAtoms.length){e.multiAtoms=[];let t=e.value,[l,i]=eo(t),o=e.width/l,a=e.height/i;for(let r of t.left){let n=t_(e,tz);n.x=r.x*o,n.y=r.y*a,n.width=r.width*o,n.height=r.height*a,n.value=r.content,e.multiAtoms.push(n)}}}else e.needsColoursUpdate&&(e.updateGradient(e),e.needsColoursUpdate=!1);let{x:d,y:h}=ty(e);if(e.highlightedAtom=void 0,e5.content===e&&e5.state===to.DRAGGING){let s=d,c=h,u=d+e.width,g=h+e.height;if(void 0!==e.highlight&&(tx(e,e.highlight),e.highlight=void 0),void 0===e.highlightedAtom){let $=lg();for(let p of $){if(p===e||!p.isSquare)continue;p.joins.length>0&&p.joinExpanded&&(p=p.pickerPad);let{x:f,y:v}=ty(p),m=f,_=f+p.width,w=v,b=v+p.height;if(!(s>_)&&!(ub)){if(p.isPicker)e.highlightedAtom=p.parent;else{if(p.parent!==td)continue;e.highlightedAtom=p}e.highlight=t_(e,lb,{bottom:!0}),e.highlight.hasBorder=!0,e.highlight.hasInner=!1,e.highlight.width=p.width,e.highlight.height=p.height,e.highlight.x=f,e.highlight.y=v;break}}}if(void 0===e.highlightedAtom)for(let S of paddles){if(!S.expanded)continue;let{x:R,y:C}=ty(S),T=R,k=R+S.width,E=C,z=C+S.height;if(!(s>k)&&!(uz)&&!(gT+S.rightTriangle.x?(e.highlight=t_(e,lb,{bottom:!0}),e.highlight.hasBorder=!0,e.highlight.colour=Colour.Grey,e.highlight.x=P,e.highlight.y=A,e.highlight.width=S.dummyRight.width,e.highlight.height=S.dummyRight.height,e.highlightedSide="right",e.highlightedAtom=S):(e.highlight=t_(e,lb,{bottom:!0}),e.highlight.hasBorder=!0,e.highlight.colour=Colour.Grey,e.highlight.x=D,e.highlight.y=O,e.highlight.width=S.dummyLeft.width,e.highlight.height=S.dummyLeft.height,e.highlightedSide="left",e.highlightedAtom=S);break}if(void 0!==S.rightTriangle&&s>T+S.rightTriangle.x){let I=1/0,B,L;for(let G of S.cellAtoms){let N=G.slot,{x:U,y:Y}=ty(N),X=U,H=U+N.width,V=Y,j=Y+N.height,W=[X,V],q=[X-N.width,V],Z=[X,V-N.height],F=[H,V],M=[X,j],K=Math.hypot(d-W[0],h-W[1]);void 0===G.slotted&&tk(S,W,!0)&&Ke,t.grab=e=>e.parent,t.dragOnly=!0;let l=t_(e,t1);e.pickerHandle=l,l.width=lC.height,l.x=e.width/2-l.width/2,l.height=lC.width,l.y=e.height,l.touch=e=>e,l.grab=e=>e.parent,l.dragOnly=!0;for(let i=0;ie.parent}if(e.needsColoursUpdate=!0,e.colourTicker=1/0,void 0!==e.multiAtoms)for(let a of e.multiAtoms)tv(a);e.attached=!1},joinUnepxand(e){e.joinExpanded=!1,tx(e,e.pickerPad),tx(e,e.pickerHandle);for(let t=0;t0&&e.joinExpanded)return e;if(e.isJoiner){let t=e.parent.joins.indexOf(e);e.parent.joins.splice(t,1),e.parent.value.joins.splice(t,1),e.parent.joinUnepxand(e.parent),e.parent.joins.length>0&&e.parent.joinExpand(e.parent),tb(e.parent,e),e.isJoiner=!1,e.touch=tz.touch}if(e.attached){let l=e.parent;if(e.slottee){if(e.attached=!1,e.slottee=!1,tb(l,e),e.cellAtom.slotted=void 0,e.cellAtom.isLeftSlot){tx(l,e.cellAtom);let i=l.cellAtoms.indexOf(e.cellAtom);l.cellAtoms.splice(i,1)}return e.cellAtom=void 0,la(l),e}let{x:o,y:a}=e;e.attached=!1,tb(l,e);let r=l.cellAtoms.indexOf(e);if(l.cellAtoms.splice(r,1),e.slot=void 0,void 0!==l.rightTriangle&&void 0!==e.slotted){let n=t_(l,li,{bottom:!0});n.x=o,n.y=a,n.isLeftSlot=!0,l.cellAtoms.push(n),n.isSlot=!1,n.slotted=e.slotted,n.slotted.cellAtom=n,e.slotted=void 0}la(l)}return e},size:40,expanded:!1},tD=(e,t)=>{let l=.5,i=.5;return[[1,0],[1,.5],[1,1],[.5,0],[.5,.5],[.5,1],[0,0],[0,.5],[0,1],]},tO=(e,t,l)=>{let i=[];for(let[o,a]of l){let r=[o-e,a-t],n=Math.hypot(...r);i.push(n)}return i},tP=e=>{let t=[];for(let l of e)t.push(l**2);return t},tA=({gradients:e,width:t,height:l,mergedGradient:i=new ImageData(t,l),stamp:o})=>{[t,l]=[t,l].map(e=>Math.round(e));let a=t*l*4;i.data.length!==a&&(i=new ImageData(t,l));let r=e.length,n=2*Math.PI/r,d=-n/2-Math.PI/2;2===r&&(d-=Math.PI/4);let h=e.map((e,t)=>t*n+n),s=0;for(let c=0;c2*Math.PI;)p-=2*Math.PI;let f=0,v=!1,m=0;for(;p>h[f];)if(++f>=e.length){f=0;break}let _=h[f]-p,w=f-1<0?h.length-1:f-1,b=h[w],S=b-p,R=f+1>=h.length?0:f+1,C;.05>Math.abs(S)?(v=!0,m=-S/.05/2+.5,C=w):.05>Math.abs(_)?(v=!0,m=_/.05/2+.5,C=R):p<.05&&(v=!0,m=p/.05/2+.5,C=w),v?(i.data[s]=e[f].data[s]*m+e[C].data[s]*(1-m),i.data[s+1]=e[f].data[s+1]*m+e[C].data[s+1]*(1-m),i.data[s+2]=e[f].data[s+2]*m+e[C].data[s+2]*(1-m),i.data[s+3]=e[f].data[s+3]*m+e[C].data[s+3]*(1-m)):(i.data[s]=e[f].data[s],i.data[s+1]=e[f].data[s+1],i.data[s+2]=e[f].data[s+2],i.data[s+3]=e[f].data[s+3]),s+=4}return i},tI=({colours:e,width:t,height:l,gradient:i=new ImageData(t,l),stamp:o})=>{[t,l]=[t,l].map(e=>Math.round(e));let a=t*l*4;i.data.length!==a&&(i=new ImageData(t,l));let r=1/0,n=-1/0,d=1/0,h=-1/0,s=1/0,c=-1/0;for(let u of e){let[g,$,p]=getRGB(u);gn&&(n=g),$h&&(h=$),pc&&(c=p)}let f=(e,t,l)=>Colour.splash((1===e?n:r)+(1===t?h:d)+(1===l?c:s)),v=[f(0,0,1),f(0,0,1),f(0,0,1),f(0,1,1),f(1,0,0),f(1,0,0),f(0,1,0),f(0,1,0),f(1,0,0)],m=tD(t,l),_=0;for(let w=0;we+t);for(let k=0;k<9;k++){let E=R[k],z=v[k];[0,1,2].forEach(e=>C[e]+=E*z[e])}let D=C.map(e=>e/T);if("circle"===o&&b>=t/4&&b<3*t/4&&w>=l/4&&w<3*l/4?i.data[_+3]=0:(i.data[_]=D[0],i.data[_+1]=D[1],i.data[_+2]=D[2],i.data[_+3]=255),(_+=4)>=i.data.length)break}return i},tB={size:tz.size,width:tz.size*Math.sqrt(3)/2,draw(e){let{x:t,y:l}=ty(e),i=e.size;e.isTool&&(i-=2.5*t0),e.isTool||(i-=2);let o=i,a=i*Math.sqrt(3)/2,r=t,n=l+1;e.isTool&&(n+=1.25*t0);let d=n+o,h=n+o/2;e6.fillStyle=e.colour;let s=new Path2D;s.moveTo(r,n),s.lineTo(r+a,h),s.lineTo(r,d),s.closePath(),e6.fillStyle=e.colour,e6.fill(s),e.hasBorder&&(e6.lineWidth=1.5*t0,e6.strokeStyle=e.borderColour,e.isTool&&(e6.strokeStyle=tS[e.colour.splash]),e6.stroke(s))},overlaps(e,t,l){let{x:i,y:o}=ty(e),a=e.size,r=e.size*Math.sqrt(3)/2,n=i,d=o;return!(tn+r)&&!(l>d+a)},offscreen(e){let{x:t,y:i}=ty(e),o=e.size,a=e.size*Math.sqrt(3)/2,r=t,n=i;return!!(r+a<0)||!!(n+o<0)||!!(r>l.width)||!!(n>l.height)}},tL={size:tz.size,draw(e){let{x:t,y:l}=ty(e),i=e.size,o=e.size*Math.sqrt(3)/2,a=e.size-o,r=t,n=l+a/2,d=n+o;e6.fillStyle=e.colour;let h=new Path2D;h.moveTo(r,d),h.lineTo(r+i/2,n),h.lineTo(r+i,d),h.closePath(),e6.fillStyle=e.colour,e6.fill(h),e.hasBorder&&(e6.lineWidth=1.5*t0,e6.strokeStyle=e.borderColour,e6.stroke(h))},overlaps(e,t,l){let{x:i,y:o}=ty(e),a=e.size,r=e.size*Math.sqrt(3)/2,n=i,d=o;return!(tn+a)&&!(l>d+r)},offscreen(e){let{x:t,y:i}=ty(e),o=e.size,a=e.size*Math.sqrt(3)/2,r=t,n=i;return!!(r+o<0)||!!(n+a<0)||!!(r>l.width)||!!(n>l.height)}},tG={size:tz.size,draw(e){let{x:t,y:l}=ty(e),i=e.size,o=e.size*Math.sqrt(3)/2,a=e.size-o,r=t,n=l+a/2;e6.fillStyle=e.colour;let d=new Path2D;d.moveTo(r,n),d.lineTo(r+i/2,n+o),d.lineTo(r+i,n),d.closePath(),e6.fillStyle=e.colour,e6.fill(d),e.hasBorder&&(e6.lineWidth=1.5*t0,e6.strokeStyle=e.borderColour,e6.stroke(d))},overlaps(e,t,l){let{x:i,y:o}=ty(e),a=e.size,r=e.size*Math.sqrt(3)/2,n=i,d=o;return!(tn+a)&&!(l>d+r)},offscreen(e){let{x:t,y:i}=ty(e),o=e.size,a=e.size*Math.sqrt(3)/2,r=t,n=i;return!!(r+o<0)||!!(n+a<0)||!!(r>l.width)||!!(n>l.height)}},tN={size:tz.size,width:tz.size*Math.sqrt(3)/2,draw(e){let{x:t,y:l}=ty(e),i=e.size;e.isTool&&(i-=2.5*t0),e.isTool||(i-=2);let o=i,a=i*Math.sqrt(3)/2,r=t,n=r+a,d=l+1;e.isTool&&(d+=1.25*t0);let h=d+o,s=d+o/2;e6.fillStyle=e.colour;let c=new Path2D;c.moveTo(n,d),c.lineTo(r,s),c.lineTo(n,h),c.closePath(),e6.fillStyle=e.colour,e6.fill(c),e.hasBorder&&(e6.lineWidth=1.5*t0,e6.strokeStyle=e.borderColour,e.isTool&&(e6.strokeStyle=tS[e.colour.splash]),e6.stroke(c))},overlaps(e,t,l){let{x:i,y:o}=ty(e),a=e.size,r=e.size*Math.sqrt(3)/2,n=i,d=o;return!(tn+r)&&!(l>d+a)},offscreen(e){let{x:t,y:i}=ty(e),o=e.size,a=e.size*Math.sqrt(3)/2,r=t,n=i;return!!(r+a<0)||!!(n+o<0)||!!(r>l.width)||!!(n>l.height)}},tU={behindOtherChildren:!0,expanded:!1,draw(e){"right"===e.direction?tB.draw(e):"down"===e.direction?tG.draw(e):"up"===e.direction?tL.draw(e):"left"===e.direction?tN.draw(e):tB.draw(e)},colour:Colour.splash(999),overlaps:tB.overlaps,offscreen:tB.offscreen,size:tz.size,width:tB.width,direction:"right",click(e){if(e.parent.isPaddle){e.parent.pinhole.locked=!e.parent.pinhole.locked,lc(e.parent);return}e.expanded?e.unexpand(e):e.expand(e)},expand(e){e.pad=t_(e,l0),e.handle=t_(e,lS),e.expanded=!0,e.upPick=t_(e,lk),e.downPick=t_(e,lE),"up"===e.direction&&(e.upPick.value=!0),"down"===e.direction&&(e.downPick.value=!0)},unexpand(e){tx(e,e.pad),tx(e,e.handle),tx(e,e.upPick),tx(e,e.downPick),e.expanded=!1},highlighter:!0,hover(e){if(e.highlightedSlot=void 0,"right"===e.direction){let{x:t,y:l}=ty(e),i=t,o=l,a=t+e.width,r=l+e.height;for(let n of paddles){if(!n.expanded||n.pinhole.locked||void 0!==n.rightTriangle)continue;let{x:d,y:h}=ty(n),s=d,c=d+n.width,u=h,g=h+n.height;if(!(i>c)&&!(ag)&&!(rT)&&!(mE)&&!(_G||mU)continue;let Y=["red","green","blue"].filter(e=>void 0===A[e]);if(0===Y.length)continue;let{x:X,y:H}=ty(A);for(let V of Y){let j=tH[V],W=X+A.size+2*t7+j*(tz.size+t3),q=H+t7,Z=Math.hypot($-W,p-q);Ze.parent,colour:Colour.Grey,width:t3+3*(tz.size+t3),height:tz.size,y:0,x:tz.size+t3,dragOnly:!0,isPicker:!0},tH={red:0,green:1,blue:2},t8=["red","green","blue",],tV={hasBorder:!0,draw:tR.draw,overlaps:tR.overlaps,offscreen:tR.offscreen,width:tz.size,y:(tz.size-tY)/2,height:tY,grab:e=>e,rightDraggable:!0,rightDrag(e){let t=th(tV);tg(t);let{x:l,y:i}=ty(e);return e5.offset.x-=e.x-l,e5.offset.y-=e.y-i,t.value=H(e.value),e.expanded&&(t.createOptions(t),t.expanded=!0),t},drag(e){if(e.parent.isSquare){let t=e.parent;t[e.channelSlot]=void 0;let l=tH[e.channelSlot];t.receiveNumber(t,void 0,l),tb(t,e),e.channelSlot=t8[e.value.channel],e.updateColours(e),e.attached=!1,e.needsColoursUpdate=!0,e.colourTicker=1/0}else if(e.parent.isTallRectangle){let i=e.parent;tb(i,e),i.operationAtoms[e.highlightedSlot]=void 0;let o="padTop"===e.highlightedSlot?"add":"subtract";if(i.value[o]=void 0,e.attached=!1,i.expanded)i.unexpand(i),i.expand(i);else{let a="padTop"===e.highlightedSlot?"handleTop":"handleBottom";tx(i,i[a],{quiet:!0}),tx(i,i[e.highlightedSlot],{quiet:!0}),i.expand(i),i.unexpand(i)}}else if(e.parent.isPaddle){let r=e.parent;r.chance=void 0,tb(r,e),la(r)}return e},construct(e){let t=Random.Uint8%3;e.value=X({values:[!1,!1,!1,!1,!1,!1,!1,!1,!1,!0],channel:t}),e.needsColoursUpdate=!0,e.colourId=0,e.dcolourId=1,e.colourTicker=1/0,e.selectionBack=t_(e,tM);let l=t_(e,t4);e.selectionTop=l,e.selectionTop.isTop=!0,l.dragOnly=!1;let i=t_(e,t4);e.selectionBottom=i,e.selectionBottom.isTop=!1,i.dragOnly=!1,e.positionSelection(e)},positionSelection(e,t,l,i,o){if(e.expanded){let a=tX;e.selectionTop.y=l-e.selectionTop.height,e.selectionBottom.y=t+a-e.selectionBottom.height,e.selectionTop.minY=i-e.selectionTop.height,e.selectionTop.maxY=e.selectionBottom.y-a,e.selectionBottom.minY=e.selectionTop.y+a,e.selectionBottom.maxY=o-e.selectionBottom.height+a}else e.selectionTop.y=-e.selectionTop.height,e.selectionBottom.y=e.height;e.positionSelectionBack(e);let r=e.children.indexOf(e.selectionTop);e.children.splice(r,1),e.children.push(e.selectionTop);let n=e.children.indexOf(e.selectionBottom);if(e.children.splice(n,1),e.children.push(e.selectionBottom),e.parent.isTallRectangle){let d="padTop"===e.highlightedSlot?"add":"subtract";e.parent.value[d]=e.value}},positionSelectionBack(e){e.selectionBack.x=-t4.height,e.selectionBack.y=e.selectionTop.y,e.selectionBack.height=e.selectionBottom.y-e.selectionTop.y+e.selectionTop.height,e.selectionBack.width=e.width+2*t4.height},update(e){if(e.expanded&&e.needsColoursUpdate&&(e.needsColoursUpdate=!1,e.isGradient=!0,e.gradient=tI({colours:e.colours,width:e.width*C,height:e.height*C,gradient:e.gradient}),e.updateColours(e)),!e.expanded&&e.needsColoursUpdate){if(e.needsColoursUpdate=!1,e.parent.isSquare){let t=[];for(let l=0;l<3;l++)if(l===e.value.channel)t[l]=e.value;else{let i=[!0,!1,!1,!1,!1,!1,!1,!1,!1,!1];t[l]=X({values:i,channel:l})}let o=Z({channels:t});e.colours=M(o)}else{let a;for(let r=0;r<10;r++){let n=e.value.values[r];if(!1===n)continue;let d=makeArrayFromSplash(`${r}${r}${r}`);void 0===a?a=d:a.joins.push(d)}e.colours=M(a)}e.isGradient=!0,e.gradient=tI({colours:e.colours,width:e.width*C,height:e.height*C,gradient:e.gradient})}if(e.highlightedAtom=void 0,e5.content===e&&e5.state===to.DRAGGING){let{x:h,y:s}=ty(e),c=h,u=s,g=h+e.width,$=s+e.height;void 0!==e.highlight&&(tx(e,e.highlight),e.highlight=void 0);let p=1/0,f,v,m=lg();for(let _ of m){let w=_;if(w.isTallRectangle){if(!w.expanded)continue;let b=["padTop","padBottom"];for(let S of b){let R=w;for(;R.isTallRectangle&&void 0!==R.operationAtoms[S];)R=R.operationAtoms[S];if(!R.isTallRectangle||!R.expanded)continue;let T=R[S],{x:k,y:E}=ty(T),z=k,D=k+T.width,O=E,P=E+T.height;if(!(c>D)&&!(gP)){e.highlightedSlot=S,e.highlightedAtom=T;break}}}else{if(!_.isSquare||!_.expanded)continue;let{x:A,y:I}=ty(_.pickerPad),B=A,L=A+_.pickerPad.width,G=I,N=I+_.pickerPad.height;if(c>L||gN)continue;let U=["red","green","blue"].filter(e=>void 0===_[e]);if(0===U.length)continue;let{x:Y,y:H}=ty(_);for(let V of U){let j=tH[V],W=Y+_.size+2*t7+j*(tz.size+t3),q=H+t7,F=Math.hypot(h-W,s-q);F0)for(let s=0;s<10;s++){let c=e.options[s];if(e.parent.isSquare)h.values[9-s]=!0,s>0&&(h.values[9-s+1]=!1);else for(let u of d)u.values[9-s]=!0,s>0&&(u.values[9-s+1]=!1);let g=Z({channels:d}),$=M(g);c.colours=$,c.colourTicker=1/0,c!==e&&(c.needsColoursUpdateCountdown=s,c.needsColoursUpdate=!1)}},getCenterId(e){let t,l;for(let i=0;i[e,t+.13397460000000005/2*o]);let r=[];for(let n=0;n<6;n++){let d=wrap(n+1,0,5),h=a[n],s=a[d],c=[0,1].map(e=>(h[e]+s[e])/2);r.push(c)}let u=[t+i/2,l+o/2],g=a.map((e,t)=>{let l=wrap(t+1,0,5),i=a[wrap(t+1+1,0,5)],o=wrap(t+1+1,0,5),n=r[o],d=r[l];return[u,d,i,n]}),[$,...p]=a,f=new Path2D;for(let v of(f.moveTo(...$),p))f.lineTo(...v);if(f.closePath(),e6.fillStyle=e.colour,e6.fill(f),void 0!==e.ons)for(let m=0;m<6;m++){if(!e.ons[m])continue;let[_,...w]=g[m],b=new Path2D;for(let S of(b.moveTo(..._),w))b.lineTo(...S);b.closePath(),e6.fillStyle=Colour.Silver,e6.fill(b),e6.lineWidth=1/C,e6.strokeStyle=Colour.Silver,e6.stroke(b)}e.hasBorder&&(e6.lineWidth=1.5*t0,e6.strokeStyle=e.borderColour,e6.stroke(f),e.parent.isSquare&&lD.drawY(e,e.size-8,4))},getValue(e){let t=0;for(let l of e.ons)l&&t++;return t},click(e){e.expanded?e.unexpand(e):e.expand(e)},unexpand(e){for(let t of(e.expanded=!1,e.handles))tx(e,t);for(let l of e.buttons)tx(e,l);e.handles=[],e.buttons=[]},expand(e){e.expanded=!0,e.handles=[],e.buttons=[];let{width:t,height:l}=e,i=.22373758200000007*t,o=[[t,l/2-tF.height/2],[t-i,l-tF.height/2],[i,l-tF.height/2],[0,l/2-tF.height/2],[i,0-tF.height/2],[t-i,0-tF.height/2],],a=[[t,l/2],[t-i,l],[i,l],[0,l/2],[i,0],[t-i,0],];a=a.map(([t,l],i)=>{let[o,a]=[t-e.width/2,l-e.height/2],[r,n]=[];return i%3==0?[r,n]=[2.2*o,2.2*a]:[r,n]=[2*o,2*a],[r+e.width/2,n+e.height/2]});for(let r=0;r<6;r++){let n=t_(e,tF);n.rotation=r,n.x=o[r][0]-tF.width/2,n.y=o[r][1],e.handles.push(n);let d=t_(e,t2);d.x=a[r][0]-t2.size/2,d.y=a[r][1]-t2.size/2,e.buttons.push(d),d.id=r,e.ons[r]&&(d.inner.selected=!0,d.inner.colour=Colour.Silver)}},construct(e){e.ons=[!1,!1,!1,!1,!1,!1]},updateValue(e){let t=tH[e.variable],l=!e.ons[1]&&!e.ons[0]&&!e.ons[5],i=!e.ons[2]&&!e.ons[3]&&!e.ons[4],o=!l&&!i,a=[l||o,e.ons[1],e.ons[0],e.ons[5],!1,!1,!1,!1,!1,!1],r=[i||o,e.ons[2],e.ons[3],e.ons[4],!1,!1,!1,!1,!1,!1],n=X({values:a}),d=X({values:r}),h=X({channel:t,variable:e.variable,add:n,subtract:d});e.value=h},hover(e){let{x:t,y:l}=ty(e),i=t,o=l,a=t+e.width,r=l+e.height;for(let n of paddles){let{x:d,y:h}=ty(n),s=d+n.width,c=h,u=h+n.height;if(void 0===n.chance&&n.expanded&&i<=s&&a>=s&&(oc||r>c&&r{let[a,r]=[e-l,t-i],n=Math.sqrt(a**2+r**2),d=Math.atan2(r,a),[h,s]=[n*Math.cos(o+d),n*Math.sin(o+d),];return[l+h,i+s]},t2={size:tz.size,offscreen:tC.offscreen,overlaps:tC.overlaps,colour:Colour.Grey,grab:e=>e.parent,behindChildren:!0,draw(e){tC.draw(e)},construct(e){e.inner=t_(e,tZ,{bottom:!1}),e.inner.x=e.width/2-e.inner.width/2,e.inner.y=e.height/2-e.inner.height/2},click(e){e.inner.selected?(e.inner.selected=!1,e.inner.colour=Colour.Grey):(e.inner.selected=!0,e.inner.colour=Colour.Silver);let t=e.parent;if(t.ons[e.id]=e.inner.selected,t.parent.isPaddle){let l=t.parent;la(l)}else if(t.parent.isSquare){let i=t.parent;t.updateValue(t);let o=tH[t.variable];i.receiveNumber(i,t.value,o,{expanded:t.expanded,numberAtom:t})}tv(e.parent)}},tZ={size:2*tz.size/3,offscreen:tC.offscreen,overlaps:tC.overlaps,grab:e=>e.parent,touch:e=>e.parent,draw:tC.draw,hasBorder:!0,borderColour:Colour.Black,colour:Colour.Grey},tF={offscreen:tR.offscreen,overlaps(e,t,l){e.y-=e.height/2,e.height*=2;let i=tR.overlaps(e,t,l);return e.height/=2,e.y+=e.height/2,i},colour:Colour.Grey,rotation:0,touch:e=>e.parent,grab:e=>e.parent,x:50,width:tz.size/2+tz.size/4,height:tz.size/3,draw(e){let{x:t,y:l}=ty(e),{width:i,height:o}=e,a=new Path2D,r=[[t,l],[t+i,l],[t+i,l+o],[t,l+o],];e.rotation>0&&(r=r.map(a=>tq(a,[t+i/2,l+o/2],e.rotation*Math.PI/3)));let[n,...d]=r;for(let h of(a.moveTo(...n),d))a.lineTo(...h);e6.fillStyle=e.colour,e6.fill(a)}},t4={draw(e){let{x:t,y:l}=ty(e),i=Math.round(e.width),o=Math.round(e.height);e6.fillStyle=Colour.Grey,e6.fillRect(Math.round(t),Math.round(l),i,o)},overlaps:tR.overlaps,offscreen:tR.offscreen,height:tX-tY,width:tz.size+2*t7,x:-t7,dragOnly:!0,grab:e=>e.parent.expanded?e:e.parent,touch:e=>e.parent.expanded?e:e.parent,cursor:e=>e.parent.expanded?"ns-resize":"pointer",move(e){e.parent.positionSelectionBack(e.parent)},drop(e){let t=Math.round((e.y+tY/2)/tX),l=e.parent.value,[i,o]=e.parent.getStartAndEndId(e.parent),a=e.parent.getCenterId(e.parent);e.isTop&&(o=a-t),e.isTop||(i=a-(t-1));let r=[!1,!1,!1,!1,!1,!1,!1,!1,!1,!1];for(let n=i;n<=o;n++)r[n]=!0;let d=X({channel:l.channel,values:r});if(e.parent.value=d,e.parent.deleteOptions(e.parent),e.parent.createOptions(e.parent),e.dx=0,e.dy=0,e.parent.parent.isSquare){let h=e.parent.parent,s=tH[e.parent.channelSlot];h.receiveNumber(h,d,s)}if(e.parent.parent.isPaddle){let c=e.parent.parent;la(c)}},dragLockX:!0},tM={overlaps:tR.overlaps,offscreen:tR.offscreen,width:(tz.size-tY)/2,height:tz.size,grab:e=>e.parent,touch:e=>e.parent,dragLockX:!0,draw:tR.draw,colour:Colour.Grey},t5={draw:tR.draw,overlaps:tR.overlaps,offscreen:tR.offscreen,height:tY,width:tz.size,grab:e=>e.parent,hasBorder:!0,colourTicker:1/0,colours:[999],colourId:0,dcolourId:1,update(e){e.needsColoursUpdateCountdown>=0&&(e.needsColoursUpdateCountdown--,e.needsColoursUpdateCountdown<0&&(e.needsColoursUpdate=!0)),e.needsColoursUpdate&&(e.updateColours(e),e.needsColoursUpdateCountdown=-1,e.needsColoursUpdate=!1)},getId(e){let t=e.parent,l=t.getCenterId(t),i=e.y/tX;return l-i},updateColours(e){e.isGradient=!0,e.gradient=tI({colours:e.colours,width:e.width*C,height:e.height*C,gradient:e.gradient})},touch(e){let t=e.getId(e);return e.parent.value.values[t]?e.parent:e},click(e){let t=[!1,!1,!1,!1,!1,!1,!1,!1,!1,!1];t[e.value]=!0;let l=X({values:t,channel:e.parent.value.channel}),i=e.parent;if(i.value=l,i.deleteOptions(i),i.createOptions(i),i.needsColoursUpdate=!0,i.parent.isSquare){let o=i.parent,a=tH[i.channelSlot];o.receiveNumber(o,l,a)}if(i.parent.isPaddle){let r=i.parent;la(r)}},construct(e){if(e.pityTop){let t=t_(e,tQ);t.y=-t.height}if(e.pityBottom){let l=t_(e,tQ);l.y=e.height}}},tK=["red","green","blue",],t6={behindChildren:!0,highlighter:!0,rightDraggable:!0,rightDrag(e){let t=th(t6);tg(t);let{x:l,y:i}=ty(e);return e5.offset.x-=e.x-l,e5.offset.y-=e.y-i,t.variable=e.variable,e.expanded&&t.expand(t),t.updateAppearance(t),t},drag(e){if(e.parent.isSquare){let t=e.parent;t[e.channelSlot]=void 0;let l=tH[e.channelSlot];t.receiveNumber(t,void 0,l),tb(t,e),e.updateAppearance(e),e.attached=!1}else if(e.parent.isTallRectangle){let i=e.parent;tb(i,e),i.operationAtoms[e.highlightedSlot]=void 0;let o="padTop"===e.highlightedSlot?"add":"subtract";if(i.value[o]=void 0,e.expanded&&(e.unexpand(e),e.expand(e)),e.attached=!1,i.expanded)i.unexpand(i),i.expand(i);else{let a="padTop"===e.highlightedSlot?"handleTop":"handleBottom";tx(i,i[a],{quiet:!0}),tx(i,i[e.highlightedSlot],{quiet:!0}),i.expand(i),i.unexpand(i)}}return e},hover(e){let{x:t,y:l}=ty(e),i=t,o=l,a=t+e.width,r=l+e.height,n=1/0,d,h,s=lg();for(let c of s){if(c===e)continue;if(c.isTallRectangle){if(!c.expanded)continue;let u=["padTop","padBottom"];for(let g of u){let $=c;for(;$.isTallRectangle&&void 0!==$.operationAtoms[g];)$=$.operationAtoms[g];if(!$.isTallRectangle||!$.expanded)continue;let p=$[g],{x:f,y:v}=ty(p),m=f,_=f+p.width,w=v,b=v+p.height;if(!(i>_)&&!(ab))return e.highlightedSlot=g,p}continue}if(!c.isSquare||!c.expanded)continue;let{x:S,y:R}=ty(c.pickerPad),C=S,T=S+c.pickerPad.width,k=R,E=R+c.pickerPad.height;if(i>T||aE)continue;let z=["red","green","blue"].filter(e=>void 0===c[e]);if(0===z.length)continue;let{x:D,y:O}=ty(c);for(let P of z){let A=tH[P],I=D+c.size+2*t7+A*(tz.size+t3),B=O+t7,L=Math.hypot(t-I,l-B);Le)),s.lineTo(...[r+a,d].map(e=>e)),s.lineTo(...[h,n+o].map(e=>e)),s.lineTo(...[r,d].map(e=>e)),s.closePath(),e6.fillStyle=e.colour,e6.fill(s),e.hasBorder&&(e6.lineWidth=t0,e6.strokeStyle=e.borderColour,e.isTool&&(e6.lineWidth=1.5*t0,e6.strokeStyle=tS[e.colour.splash]),e6.stroke(s))},offscreen:tR.offscreen,overlaps:tR.overlaps,hasBorder:!0,isTallRectangle:!0,size:tY+t7/3*2,height:tY+t7/3*2,width:tY+t7/3*2,construct(e){e.variable=tK[Random.Uint8%3],e.value=X({variable:e.variable}),e.updateAppearance(e),e.isTool||(e.width+=t0/2,e.height+=t0/2,e.size+=t0/2),e.operationAtoms={padTop:void 0,padBottom:void 0}},makeOperationAtoms(e){if(void 0!==e.value.add&&void 0===e.operationAtoms.padtop){if(void 0===e.value.add.variable){let t=t_(e,tV);t.value=e.value.add,e.operationAtoms.padTop=t,t.x=e.padTop.x+t7,t.y=e.padTop.y+e.padTop.height/2-t.height/2,t.highlightedSlot="padTop"}else{let l=t_(e,t6);l.value=e.value.add,l.variable=e.value.add.variable,l.makeOperationAtoms(l),l.highlightedSlot="padTop",l.x=0,l.y=e.padTop.y+e.padTop.height/2-l.height/2,l.updateAppearance(l),e.operationAtoms.padTop=l}}if(void 0!==e.value.subtract&&void 0===e.operationAtoms.padBottom&&void 0===e.value.subtract.variable){let i=t_(e,tV);i.value=e.value.subtract,e.operationAtoms.padBottom=i,i.x=e.padBottom.x+t7,i.y=e.padBottom.y+e.padBottom.height/2-i.height/2,i.highlightedSlot="padBottom"}},updateAppearance(e){"red"===e.variable?e.colour=Colour.Red:"green"===e.variable?e.colour=Colour.Green:"blue"===e.variable&&(e.colour=Colour.Blue),e.borderColour=borderColours[e.colour.splash]},expanded:!1,click(e){e.expanded?e.unexpand(e):e.expand(e)},expand(e){for(let t of(e.expanded=!0,void 0===e.value.add&&(e.y<0||!(e.parent.isTallRectangle&&e.parent.operationAtoms.padBottom===e))&&(e.handleTop=t_(e,lC),e.handleTop.width=e.handleTop.height,e.handleTop.height*=2,e.handleTop.y=e.height/2-e.handleTop.height,e.handleTop.x=e.width/2-e.handleTop.width/2,e.handleTop.behindParent=!0,e.padTop=t_(e,lR),e.padTop.height=t1.height,e.padTop.width=tz.size+2*t3,e.padTop.x=e.width/2-e.padTop.width/2,e.padTop.y=-e.padTop.height-t7),void 0===e.value.subtract&&(e.y>0||!(e.parent.isTallRectangle&&e.parent.operationAtoms.padTop===e))&&(e.handleBottom=t_(e,lC),e.handleBottom.width=e.handleBottom.height,e.handleBottom.height*=2,e.handleBottom.y=e.height/2,e.handleBottom.x=e.width/2-e.handleBottom.width/2,e.handleBottom.behindParent=!0,e.padBottom=t_(e,lR),e.padBottom.height=t1.height,e.padBottom.width=tz.size+2*t3,e.padBottom.x=e.width/2-e.padBottom.width/2,e.padBottom.y=e.height+t7),e.handleRight=t_(e,lC),e.handleRight.y=e.height/2-e.handleRight.height/2,e.handleRight.x=e.width/2,e.handleRight.width*=2.5,e.handleRight.behindParent=!0,e.padRight=t_(e,lR),e.padRight.height=t1.height,e.padRight.width=t7+(e.width+t7/1.5)*3,e.padRight.y=e.height/2-e.padRight.height/2,e.padRight.x=e.width/2+(tz.size+2*t3)/2+t7,e.red=t_(e,t9),e.red.x=e.padRight.x+t7/Math.SQRT2,e.red.borderColour=Colour.Red,e.red.colour=Colour.Black,e.red.value="red",e.green=t_(e,t9),e.green.x=e.padRight.x+t7/Math.SQRT2+(e.green.width+t7)*1,e.green.borderColour=Colour.Green,e.green.colour=Colour.Black,e.green.value="green",e.blue=t_(e,t9),e.blue.x=e.padRight.x+t7/Math.SQRT2+(e.blue.width+t7)*2,e.blue.borderColour=Colour.Blue,e.blue.colour=Colour.Black,e.blue.value="blue",e.winnerPin=t_(e,tJ),e.winnerPin.x=e[e.variable].x+e.winnerPin.width/2,e.winnerPin.y=e.winnerPin.height/2,e.winnerPin.colour=e[e.variable].borderColour,e.winnerPin.borderColour=e.winnerPin.colour,["padTop","padBottom"])){let l=e.operationAtoms[t];void 0!==l&&(tg(l),tw(e,l))}for(let i of e.children)i.isTallRectangle&&i.expanded&&(i.unexpand(i),i.expand(i))},unexpand(e){e.expanded=!1,tx(e,e.red),tx(e,e.green),tx(e,e.blue),tx(e,e.padRight),tx(e,e.handleRight),tx(e,e.winnerPin),void 0===e.value.add&&(tx(e,e.padTop,{quiet:!0}),tx(e,e.handleTop,{quiet:!0})),void 0===e.value.subtract&&(tx(e,e.padBottom,{quiet:!0}),tx(e,e.handleBottom,{quiet:!0}))}},t9={draw(e){t6.draw(e)},offscreen:tR.offscreen,overlaps:tR.overlaps,hasBorder:!0,size:tY+t7/3*2,height:tY+t7/3*2,width:tY+t7/3*2,grab:e=>e.parent,click(e){if(e.value===e.parent.variable)return;e.parent.variable=e.value,e.parent.value.variable=e.value,e.parent.winnerPin.x=e.x+e.parent.winnerPin.width/2,e.parent.winnerPin.colour=e.borderColour,e.parent.winnerPin.borderColour=e.borderColour,e.parent.updateAppearance(e.parent);let t=e.parent,l=t,i=t.parent;for(;!i.isSquare;){if(i===td)return;l=i,i=i.parent}let o=0;"green"===l.channelSlot&&(o=1),"blue"===l.channelSlot&&(o=2);let a=i.variableAtoms[o];i.receiveNumber(i,a.value,o,{expanded:a.expanded,numberAtom:a})}},tJ={draw(e){t6.draw(e)},offscreen:tR.offscreen,overlaps:tR.overlaps,hasBorder:!0,size:(tY+t7/3*2)/2,height:(tY+t7/3*2)/2,width:(tY+t7/3*2)/2,grab:e=>e.parent,touch:e=>e.parent},tQ={draw(){},overlaps:tR.overlaps,offscreen:tR.offscreen,grab:e=>e.parent.parent,touch:e=>e.parent,colour:Colour.Grey,width:tz.size,height:tX-tY,y:0,x:0};paddles=[];let le=tz.size/2,lt={stayAtBack:!0,attached:!0,noDampen:!0,isPaddle:!0,behindChildren:!0,draw:tR.draw,overlaps:tR.overlaps,offscreen:tR.offscreen,colour:Colour.Grey,size:tz.size+4*t7,width:tz.size+4*t7,height:tz.size+4*t7,dragOnly:!0,dragLockY:!0,scroll:0,rightTriangle:void 0,x:Math.round(le),y:tz.size+t7+le,construct(e){e.cellAtoms=[],e.slots=[];let t=t_(e,lv);e.handle=t,e.setLimits(e),e.x=e.minX,e.expanded=!1,e.pinhole=t_(t,lm),e.dummyLeft=t_(e,li),e.dummyLeft.visible=!1,e.dummyRight=t_(e,li),e.dummyRight.visible=!1,la(e)},setLimits(e){e.maxX=e.handle.width,e.minX=e.handle.width-e.width},drop(e){let t=e.maxX-e.x,l=e.x-e.minX;te,rightDraggable:!0,getColour(e){let t=e.cellAtoms;if(0===t.length){let l=Z({channels:[void 0,void 0,void 0]});return l}if(1===t.length){let i=Q(t[0].value);return i}let o=ls(t),a=makeDiagram({left:o});return el(a),a},rightDrag(e){let t=e.cellAtoms;if(0===t.length){let l=th(tz);e5.offset.x=-l.width/2,e5.offset.y=-l.height/2;let i=Z({channels:[void 0,void 0,void 0]});return tE(i),tg(l),l.value=i,l.update(l),l}if(1===t.length){let o=Q(t[0].value),a=t[0].clone(t[0]);return e5.offset.x=-a.width/2,e5.offset.y=-a.height/2,tE(o),tg(a),a.value=o,a.update(a),a}let r=th(tz);e5.offset.x=-r.width/2,e5.offset.y=-r.height/2;let n=ls(t),d=makeDiagram({left:n});return el(d),r.value=d,tg(r),tE(d),r.update(r),r}},ll=(e,t)=>{let l=new Path2D,[i,...o]=t;for(let a of(l.moveTo(...i.map(e=>Math.round(e))),o))l.lineTo(...a.map(e=>Math.round(e)));l.closePath(),e6.fillStyle=e,e6.fill(l)},li={visible:!0,isSlot:!0,behindChildren:!0,draw(e){if(!e.visible)return;let[t,l]=ty(e);e6.fillStyle=e.colour;let i=e.width/3,o=e.width/3,a=t+e.width/2-i/2,r=l+e.height/2-o/2;e6.fillRect(...[a,r,i,o].map(e=>Math.round(e)))},offscreen:tR.offscreen,overlaps:tR.overlaps,colour:Colour.Black,size:tz.size,grab:e=>e.parent,dragOnly:!0},lo=tz.size,la=e=>{let t=lt.width,l=lt.size;if(e.cellAtoms.length>0){let i=1/0,o=-1/0,a=-1/0,r=1/0;for(let n of e.cellAtoms){let d=n.x,h=n.y,s=d,c=d+lo,u=h,g=h+lo;sa&&(a=c),uo&&(o=g)}let $=0,p=0,f=lt.height/2-tz.size/2,v=lt.width/2-tz.size/2,m=f,_=v;for(let w of(i!==m&&(o+=$=m-i),r!==_&&(a+=p=_-r),e.cellAtoms))w.y+=$,w.x+=p;let b=a+v,S=o+f;t=b,l=S}for(let R of(void 0!==e.rightTriangle&&(e.rightTriangle.x=t,e.rightTriangle.y=l/2-e.rightTriangle.height/2,t=t+t+e.rightTriangle.width),(e.hasSymmetry||void 0!==e.chance)&&(t+=lx.size/3),e.width=t,e.height=l,e.setLimits(e),e.slots))tx(e,R);if(e.slots=[],void 0!==e.rightTriangle)for(let C of e.cellAtoms){let T=t_(e,li,{bottom:!0});C.slot=T,e.slots.push(T),T.x=C.x+e.rightTriangle.x+e.rightTriangle.width,T.y=C.y,T.cellAtom=C,void 0!==C.slotted&&(C.slotted.x=C.x+e.rightTriangle.x+e.rightTriangle.width,C.slotted.y=C.y,T.colour=Colour.Grey)}void 0!==e.rightTriangle&&(void 0!==e.cellAtoms[0]&&void 0!==e.cellAtoms[0].slot?e.offset=e.cellAtoms[0].slot.x-e.cellAtoms[0].x:e.offset=0),void 0!==e.symmetryCircle&&(e.symmetryCircle.x=e.width-e.symmetryCircle.width/2,e.symmetryCircle.y=e.height/2-e.symmetryCircle.height/2),void 0!==e.chance&&(e.chance.x=e.width-e.chance.width/2,e.chance.y=e.height/2-e.chance.height/2),void 0!==e.chance&&void 0!==e.symmetryCircle&&(e.symmetryCircle.y-=e.symmetryCircle.height/2,e.chance.y+=e.symmetryCircle.height/2,e.height>100&&(e.symmetryCircle.y-=t7/2,e.chance.y+=t7/2)),e.handle.y=e.height/2-e.handle.height/2,0===e.cellAtoms.length&&(e.dummyLeft.x=le,e.dummyLeft.y=e.height/2-e.dummyLeft.height/2,e.dummyRight.x=e.width-le-e.dummyLeft.width,e.dummyRight.y=e.height/2-e.dummyRight.height/2),lc(e),l$()},lr=e=>{let t=F(e);return 1===t.size},ln=(e,t)=>{for(let l=0;l<3;l++){let i=e.channels[l],o=t.channels[l];if(void 0===i&&void 0!==o||void 0!==i&&void 0===o||(void 0!==i||void 0!==o)&&i.variable!==o.variable)return!1}let a=M(e),r=M(t);for(let n of a){let d=r.indexOf(n);if(-1===d)return!1;r.splice(d,1)}return!(r.length>0)},ld=(e,t)=>{if(t.stamp)return;let l=lr(t);if(!l){let i;for(let o=0;o{let t=[...e].sort((e,t)=>e.xt.x?1:e.yt.y?1:0);return t},ls=e=>{let t=lh(e),l=t[0],i=[];for(let o of e){let a=(o.x-l.x)/o.width,r=(o.y-l.y)/o.height,n=Q(o.value),d=makeDiagramCell({x:a,y:r,content:n});i.push(d)}return i},lc=e=>{if(!e.expanded)return;void 0!==e.rightTriangle&&(e.pinhole.locked?e.rightTriangle.colour=Colour.splash(999):e.rightTriangle.colour=Colour.splash(0));let t=DRAGON_TRANSFORMATIONS.NONE;if(e.hasSymmetry){let[l,i,o]=l_(e.symmetryCircle.value),a=l>0,r=i>0,n=o>0,d=`${r?"X":""}${a?"Y":""}${n?"R":""}`;""===d?d="NONE":("XR"===d||"YR"===d)&&(d="XYR"),t=DRAGON_TRANSFORMATIONS[d]}let h=lh(e.cellAtoms),s=h[0],c=[],u=[],g=[];for(let $ of h){let p=($.x-s.x)/$.width,f=($.y-s.y)/$.height;if($.isLeftSlot){let v=X({values:[!0,!0,!0,!0,!0,!0,!0,!0,!0,!0],channel:0}),m=X({values:[!0,!0,!0,!0,!0,!0,!0,!0,!0,!0],channel:1}),_=X({values:[!0,!0,!0,!0,!0,!0,!0,!0,!0,!0],channel:2}),w=Z({channels:[v,m,_]});ld(g,w);let b=makeDiagramCell({x:p,y:f,content:w});c.push(b)}else if($.value.isDiagram){let S=lh($.value.left);for(let R of S){let C=Q(R.content);ld(g,C);let T=p+R.x,k=f+R.y,E=makeDiagramCell({x:T,y:k,width:R.width,height:R.height,content:C});c.push(E)}}else{let z=Q($.value);ld(g,z);let D=makeDiagramCell({x:p,y:f,content:z});c.push(D)}if(!$.isLeftSlot&&$.value.isDiagram){let O=ei($.value),[P,A]=eo(O),I=makeDiagramCell({x:p,y:f,instruction:DRAGON_INSTRUCTION.merge,splitX:P,splitY:A});u.push(I)}let B=void 0===$.slotted?void 0:$.slotted.value;if(void 0===B){let L=makeDiagramCell({x:p,y:f,instruction:DRAGON_INSTRUCTION.nothing});u.push(L)}else if(B.isDiagram){let G=ei(B),[N,U]=eo(G),Y=makeDiagramCell({x:p,y:f,instruction:DRAGON_INSTRUCTION.split,splitX:N,splitY:U});u.push(Y);let H=lh(B.left);for(let V of H){let j=Q(V.content);ld(g,j);let W=p+V.x,q=f+V.y,F=makeDiagramCell({x:W,y:q,width:V.width,height:V.height,content:j,instruction:DRAGON_INSTRUCTION.recolour});u.push(F)}}else{let M=Q(B);ld(g,M);let K=makeDiagramCell({x:p,y:f,content:M});u.push(K)}}let J=ei(makeDiagram({left:c,right:u})),ee=e.pinhole.locked,et=void 0===e.chance?void 0:e.chance.getValue(e.chance),el=makeRule({steps:[J],transformations:t,locked:ee,chance:et});e.rule=el,void 0!==e.registry&&ed(e.registry),ee&&void 0!==e.rightTriangle&&(e.registry=registerRule(el))},lu=(e=state.colourTode.atoms)=>{let t=[...e];for(let l of t)t.push(...lu(l.children));return t},lg=()=>{let e=[...state.colourTode.atoms];for(let t of paddles)for(let l of t.children)!l.isPinhole&&(l.isPaddleHandle||e.push(l));for(let i of e)i.isSquare&&i.expanded&&e.push(...i.children);return e},l$=()=>{if(paddles.length>1&&lG("triangle"),paddles.length>2){let e=0;for(let t of paddles)void 0!==t.rightTriangle&&e++;e>=2&&lG("hexagon")}let l;for(let i of paddles){if(void 0===l){i.y=lt.y+lt.scroll,l=i;continue}i.y=l.y+l.height+le,l=i}},lp=(e,t=paddles.indexOf(e))=>{paddles.splice(t,1),void 0!==e.registry&&ed(e.registry),tu(e),l$()},lf=()=>{let e=th(lt);return paddles.push(e),l$(),tg(e),e},lv={isPaddleHandle:!0,attached:!0,behindChildren:!0,draw:tR.draw,overlaps:tR.overlaps,offscreen:tR.offscreen,colour:Colour.Grey,size:lt.x,x:-lt.x,y:lt.size/2-lt.x/2,touch:e=>e.parent.pinhole,grab:e=>e.parent.pinhole},lm={isPinhole:!0,attached:!0,locked:!1,borderScale:.5,borderColour:Colour.Black,draw(e){e.locked?(e.hasBorder=!0,e.colour=Colour.Grey):(e.hasBorder=!1,e.colour=Colour.Black),tC.draw(e)},overlaps:tC.overlaps,offscreen:tC.offscreen,colour:Colour.Black,size:lv.size-t7/2,y:t7/2/2,x:t7/2/2,click(e){let t=e.parent,l=t.parent;if(e.locked)e.locked=!1,l.grabbable=!0,t.draggable=!0,l.draggable=!0,e.draggable=!0,lc(l);else{for(let i of(e.locked=!0,t.draggable=!1,e.draggable=!1,l.cellAtoms)){if(i.expanded&&i.unexpand(i),void 0!==i.slotted){let o=i.slotted;o.expanded&&o.unexpand(o)}i.joins.length>0&&i.joinExpanded&&i.joinUnepxand(i)}0===l.cellAtoms.length&&(l.grabbable=!1,l.draggable=!1),lc(l)}},grab:e=>e.parent.parent},ly=new Map;ly.set(0,DRAGON_TRANSFORMATIONS.NONE),ly.set(100,DRAGON_TRANSFORMATIONS.X),ly.set(10,DRAGON_TRANSFORMATIONS.Y),ly.set(110,DRAGON_TRANSFORMATIONS.XY),ly.set(1,DRAGON_TRANSFORMATIONS.R),ly.set(111,DRAGON_TRANSFORMATIONS.XYR),ly.set(101,DRAGON_TRANSFORMATIONS.XYR),ly.set(11,DRAGON_TRANSFORMATIONS.XYR);let l_=getRGB,lx={hasBorder:!0,draw(e){if(tC.draw(e),void 0===e.value)return;let[t,l,i]=l_(e.value);t>0&&lz.drawX(e),l>0&&lD.drawY(e),i>0&&lO.drawR(e)},offscreen:tC.offscreen,overlaps:tC.overlaps,expanded:!1,borderColour:Colour.Grey,colour:Colour.Black,value:0,click(e){e.expanded?e.unexpand(e):e.expand(e)},expand(e){e.pad=t_(e,lR),e.handle=t_(e,lC),e.handle.width+=t7,e.expanded=!0;let[t,l,i]=l_(e.value);e.xToggle=t_(e,lz),e.yToggle=t_(e,lD),e.rToggle=t_(e,lO),t>0&&(e.xToggle.value=!0),l>0&&(e.yToggle.value=!0),i>0&&(e.rToggle.value=!0)},unexpand(e){tx(e,e.pad),tx(e,e.handle),tx(e,e.xToggle),tx(e,e.yToggle),tx(e,e.rToggle),e.expanded=!1},size:tz.size,update(e){let{x:t,y:l}=ty(e),i=state.colourTode.atoms.indexOf(e),o=t,a=l,r=t+e.width,n=l+e.height;if(e5.content===e)for(let d of paddles){let h=state.colourTode.atoms.indexOf(d),{x:s,y:c}=ty(d),u=s+d.width,g=c,$=c+d.height;if(!d.hasSymmetry&&d.expanded&&i>h&&o<=u&&r>=u&&(a<$&&a>g||n>g&&n<$)){void 0!==e.highlightPaddle&&tx(e,e.highlightPaddle),e.highlightPaddle=t_(e,lb,{bottom:!0}),e.highlightPaddle.width=lw,e.highlightPaddle.height=d.height,e.highlightPaddle.y=g,e.highlightPaddle.x=u-lw/2,e.highlightedPaddle=d;return}}void 0!==e.highlightPaddle&&(tx(e,e.highlightPaddle),e.highlightPaddle=void 0,e.highlightedPaddle=void 0)},drop(e){if(!e.attached&&void 0!==e.highlightedPaddle){let t=e.highlightedPaddle;e.attached=!0,tw(t,e),t.hasSymmetry=!0,t.symmetryCircle=e,la(t),e.dx=0,e.dy=0}},drag(e){if(e.attached){let t=e.parent;e.attached=!1,tb(t,e),t.hasSymmetry=!1,t.symmetryCircle=void 0,la(t)}return e},rightDraggable:!0,rightDrag(e){let t=th(lx);t.value=e.value;let{x:l,y:i}=ty(e);return e5.offset.x-=e.x-l,e5.offset.y-=e.y-i,t.x=l,t.y=i,tg(t),t}},lw=t0,lb={behindParent:!0,draw:tR.draw,offscreen:tR.offscreen,overlaps:tR.overlaps,draggable:!1,grabbable:!1,justVisual:!0,colour:Colour.splash(999),borderColour:Colour.splash(999),hasAbsolutePosition:!0,hasInner:!1},l0={draw:tR.draw,offscreen:tR.offscreen,overlaps:tR.overlaps,dragOnly:!0,width:lx.size,x:lx.size*Math.sqrt(3)/2+t7,height:2*lx.size-t7,y:-lx.size/2+t7/2,colour:Colour.Grey,grab:e=>e.parent},lS={draw:tR.draw,offscreen:tR.offscreen,overlaps:tR.overlaps,dragOnly:!0,width:lx.size/2+t7,x:lx.size/2,height:lx.size/3,y:lx.size/2-lx.size/3/2,colour:Colour.Grey,grab:e=>e.parent},lR={draw:tR.draw,offscreen:tR.offscreen,overlaps:tR.overlaps,dragOnly:!0,width:lx.size,x:lx.size+t7,height:3*lx.size-t7,y:-(3*lx.size)/3+t7/2,colour:Colour.Grey,grab:e=>e.parent},lC={draw:tR.draw,offscreen:tR.offscreen,overlaps:tR.overlaps,dragOnly:!0,width:lx.size/2,x:lx.size/2+lx.size/4,height:lx.size/3,y:lx.size/2-lx.size/3/2,colour:Colour.Grey,grab:e=>e.parent},lT=(e,t)=>{switch(t=!t,e){case"right":return t?"down":"up";case"down":return t?"left":"right";case"left":return t?"up":"down";case"up":return t?"right":"left"}throw Error("Invalid rotation or clockwiseness")},lk={hasBorder:!0,colour:Colour.Black,borderColour:Colour.Black,draw(e){tL.draw(e)},touch:e=>(e.colour=Colour.Silver,e),click(e){let t=e.parent;e.colour=Colour.Black,t.direction=lT(t.direction,!0),e.value=!0,t.updateValue(t);let l=t.parent;l.isSquare&&l.receiveNumber(l,t.value,t.channelId,{expanded:t.expanded,numberAtom:t})},offscreen:tL.offscreen,overlaps:tL.overlaps,value:!1,size:tz.size-1.5*t7,grab:e=>e.parent,x:l0.x+l0.width/2-(tz.size-1.5*t7)/2,y:l0.y+1.5*t7/2},lE={hasBorder:!0,colour:Colour.Black,borderColour:Colour.Black,draw(e){tG.draw(e)},touch:e=>(e.colour=Colour.Silver,e),click(e){let t=e.parent;e.colour=Colour.Black,t.direction=lT(t.direction,!1),e.value=!0,t.updateValue(t);let l=t.parent;l.isSquare&&l.receiveNumber(l,t.value,t.channelId,{expanded:t.expanded,numberAtom:t})},offscreen:tG.offscreen,overlaps:tG.overlaps,value:!1,size:tz.size-1.5*t7,grab:e=>e.parent,x:l0.x+l0.width/2-(tz.size-1.5*t7)/2,y:l0.y+l0.height-(tz.size-1.5*t7)-t7/2},lz={hasBorder:!0,borderColour:Colour.Black,colour:Colour.Grey,draw(e){e.colour=e.value?Colour.Silver:Colour.Grey,tC.draw(e),e.drawX(e)},drawX(e){let{x:t,y:l}=ty(e),i=e.size,o=l+e.size/2-1*t0/2;e6.fillStyle=e.borderColour,e6.fillRect(t,o,i,1*t0)},offscreen:tC.offscreen,overlaps:tC.overlaps,expanded:!1,click(e){e.value=!e.value;let[t,l,i]=l_(e.parent.value);t=e.value?100:0,e.parent.value=t+l+i;let o=e.parent;if(o.parent!==td){let a=o.parent;lc(a)}},value:!1,size:tz.size-t7,grab:e=>e.parent,x:lR.x+lR.width/2-(tz.size-t7)/2,y:lR.y+t7/2},lD={hasBorder:!0,borderColour:Colour.Black,colour:Colour.Grey,draw(e){e.colour=e.value?Colour.Silver:Colour.Grey,tC.draw(e),e.drawY(e)},drawY(e,t=e.size,l=0){let{x:i,y:o}=ty(e),a=i+e.size/2-1*t0/2;e6.fillStyle=e.borderColour,e6.fillRect(a,o+l,1*t0,t)},offscreen:tC.offscreen,overlaps:tC.overlaps,expanded:!1,click(e){e.value=!e.value;let[t,l,i]=l_(e.parent.value);l=e.value?10:0,e.parent.value=t+l+i;let o=e.parent;if(o.parent!==td){let a=o.parent;lc(a)}},value:!1,size:tz.size-t7,grab:e=>e.parent,x:lR.x+lR.width/2-(tz.size-t7)/2,y:t7/2},lO={hasBorder:!0,borderColour:Colour.Black,colour:Colour.Grey,draw(e){e.colour=e.value?Colour.Silver:Colour.Grey,tC.draw(e),e.drawR(e)},drawR(e){let{x:t,y:l}=ty(e),i=t+e.size/2,o=l+e.size/2,a=e.size/2-3*t0;e6.fillStyle=e.borderColour,e6.beginPath(),e6.arc(i,o,a,0,2*Math.PI),e6.fill(),a-=t0,e6.fillStyle=e.colour,e6.beginPath(),e6.arc(i,o,a,0,2*Math.PI),e6.fill()},offscreen:tC.offscreen,overlaps:tC.overlaps,expanded:!1,click(e){e.value=!e.value;let[t,l,i]=l_(e.parent.value);i=e.value?1:0,e.parent.value=t+l+i;let o=e.parent;if(o.parent!==td){let a=o.parent;lc(a)}},value:!1,size:tz.size-t7,grab:e=>e.parent,x:lR.x+lR.width/2-(tz.size-t7)/2,y:lR.y+lR.height-(tz.size-t7)-t7/2},lP=e=>{let t=th({...tz});if(t.value=Q(e),void 0!==t.value){if(void 0!==t.value.joins)for(let l of t.value.joins){let i=lP(l);t.joins.push(i)}t.stamp=t.value.stamp}if(!t.value.isDiagram)for(let o=0;o<3;o++){let a=t.value.channels[o];if(void 0===a||void 0===a.variable)continue;let r=th(tU);t.variableAtoms[o]=r,r.highlightedSlot=t8[o],r.channelId=o;let n=o-1<0?t8[2]:t8[o-1],d=o+1>2?t8[0]:t8[o+1];a.subtract?r.direction="down":a.add?r.direction="up":a.variable===n?r.direction="left":a.variable===d&&(r.direction="right"),r.updateValue(r)}return void 0!==t.value&&t.value.isDiagram&&t.update(t),t},lA=10,lI={element:tz,draw(e){(e.previousBrushColour!==state.brush.colour||e.toolbarNeedsColourUpdate)&&e.update(e),e.unlocked&&e.element.draw(e)},overlaps:(e,t,l)=>e.element.overlaps(e,t,l),grab:(e,t,l)=>e,drag(e){if(e===squareTool){let t=lP(e.value);return tg(t),t}let l=th({...e.element,x:e.x,y:e.y});return tg(l),l.value,l},cursor:()=>"move"},lB=0,lL=(e,t)=>{let{width:l=tz.size,height:i=tz.size,size:o}=e,a=t3;i{let t=unlocks[e];t.unlocked||(t.unlocked=!0,t.grabbable=!0)};squareTool=lL(tz),lA+=t0;let lN=lL(tU,"triangle");lA-=t0;let lU=lL(lx,"circle"),l7=lL(tW,"hexagon"),lY={};lf(),squareTool.value=makeArrayFromSplash(state.brush.colour),lU.borderScale=1,squareTool.update=e=>{if(void 0===e.joinDrawId&&(e.joinDrawId=-1,e.joinDrawTimer=0),void 0!==e.value&&e===squareTool&&(e.previousBrushColour!==state.brush.colour||e.toolbarNeedsColourUpdate)){for(let t of(e.previousBrushColour=state.brush.colour,void 0===e.multiAtoms&&(e.multiAtoms=[]),e.multiAtoms))tx(e,t);if(e.multiAtoms=[],e.value.isDiagram){let l=e.value,[i,o]=eo(l),a=e.width/i,r=e.height/o;for(let n of l.left){let d=t_(e,tz);d.x=n.x*a,d.y=n.y*r,d.width=n.width*a,d.height=n.height*r,d.value=n.content,d.update(d),e.multiAtoms.push(d)}}}let h=Q(e.value);if(e.colours=M(h),e.colourId>=e.colours.length&&(e.colourId=0),e.toolbarNeedsColourUpdate&&e===squareTool){for(let s of(e.toolbarNeedsColourUpdate=!1,e.isGradient=!0,e.joins=[],e.value.joins)){let c=lP(s);e.joins.push(c)}tz.updateGradient(e)}else e.colour=Colour.splash(999),e.borderColour=Colour.splash(999)},lN.update=squareTool.update,lU.update=squareTool.update,lY.update=squareTool.update,l7.update=squareTool.update,on.keydown(e=>{(e.ctrlKey||e.metaKey)&&"s"===e.key?(e.preventDefault(),lV()):(e.ctrlKey||e.metaKey)&&"o"===e.key?(e.preventDefault(),lj()):(e.ctrlKey||e.metaKey)&&"c"===e.key&&(e.preventDefault(),lW())},{passive:!1}),on.paste(async e=>{let t=e.clipboardData.getData("text");if(""!==t){l8(t);return}let l=e.clipboardData.items[0],i=l.getAsFile(),o=await i.text();l8(o)}),on.dragover(e=>{e.stopPropagation(),e.preventDefault()},{passive:!1}),on.drop(async e=>{e.stopPropagation(),e.preventDefault();let t=e.dataTransfer.items[0],l=t.getAsFile(),i=await l.text();l8(i)},{passive:!1});let lX={},l3={};lX.cellAtoms=(e,t)=>{let l=[];for(let i of t)l.push({isLeftSlot:i.isLeftSlot,value:i.value,x:i.x,y:i.y,slotted:i.slotted?i.slotted.value:void 0});return l};let l1=!1;l3.cellAtoms=(e,t)=>{let l=[];for(let i of t){l1||(i.isLeftSlot||tE(i.value),l1=!0);let o=i.isLeftSlot?th(li):lP(i.value);if(o.isLeftSlot=i.isLeftSlot,tg(o),tw(e,o),o.attached=!0,o.x=i.x,o.y=i.y,o.highlightedSide="left",l.push(o),void 0!==i.slotted){let a=lP(i.slotted);tg(a),tw(e,a),a.attached=!0,a.cellAtom=o,a.highlightedSide="slot",a.slottee=!0,o.slotted=a}}return l},lX.symmetryCircle=(e,t)=>{if(void 0!==t)return t.value},l3.symmetryCircle=(e,t)=>{let l=t_(e,lx);return l.value=t,l},lX.chance=(e,t)=>{if(void 0!==t)return t.ons},l3.chance=(e,t)=>{let l=t_(e,tW);return l.ons=t,l};lX.expanded=(e,t)=>t,lX.x=(e,t)=>t,lX.y=(e,t)=>t,lX.width=(e,t)=>t,lX.height=(e,t)=>t,lX.hasSymmetry=(e,t)=>t,l3.expanded=(e,t)=>t,l3.x=(e,t)=>t,l3.y=(e,t)=>t,l3.width=(e,t)=>t,l3.height=(e,t)=>t,l3.hasSymmetry=(e,t)=>t,lX.pinhole=(e,t)=>t.locked,l3.pinhole=(e,t)=>(e.pinhole.locked=t,e.pinhole),lX.rightTriangle=(e,t)=>void 0!==t,l3.rightTriangle=(e,t)=>{if(!t)return;let l=t_(e,tU);return l};let lH=()=>{let e=[];for(let t of paddles){let l={};for(let i in t){let o=lX[i];if(void 0===o)continue;let a=o(t,t[i]);void 0!==a&&(l[i]=a)}e.push(l)}return JSON.stringify(e)},l8=e=>{if(middleClicked){middleClicked=!1;return}l1=!1,lG("triangle"),lG("circle"),lG("hexagon");try{for(;paddles.length>0;)lp(paddles[paddles.length-1]);for(let t of JSON.parse(e)){let l=lf();for(let i in t){let o=l3[i];if(void 0===o)continue;let a=o(l,t[i]);void 0!==a&&(l[i]=a)}la(l),lc(l)}l$()}catch(r){console.error(r),alert("Error loading rules... Sorry! Please contact @todepond :)")}},lV=async()=>{let e=lH(paddles);if(window.showSaveFilePicker)try{let t=await showSaveFilePicker({excludeAcceptAllOption:!0,suggestedName:"spell",startIn:"downloads",types:[{description:"JSON",accept:{"application/json":[".json"]}}]}),l=await t.createWritable();await l.write(e),await l.close()}catch(i){console.error("Failed to save file:",i)}else{let o=new Blob([e],{type:"application/json"}),a=URL.createObjectURL(o),r=document.createElement("a");r.href=a,r.download="spell.json",r.click(),URL.revokeObjectURL(a)}},lj=()=>{let e=document.createElement("input");e.type="file",e.onchange=async t=>{let l=e.files[0],i=await l.text();l8(i),Keyboard.Control=!1},e.click(),Keyboard.Control=!1},lW=()=>{let e=lH(paddles);print(e),navigator.clipboard.writeText(e)}}); +var middleClicked=!1;document.addEventListener("mousedown",function(e){1===e.button&&(middleClicked=!0)});const urlParams=new URLSearchParams(window.location.search),NO_SECRET_MODE=urlParams.has("nosecret"),NO_FOOLS_MODE=urlParams.has("nofools"),UNLOCK_MODE=urlParams.has("unlock"),SCALE=urlParams.get("scale")??1,DPR=urlParams.get("dpr")??devicePixelRatio;print("DPR:",DPR),NO_SECRET_MODE&&localStorage.setItem("secretHasAlreadyBeenRevealed","true");const secretHasAlreadyBeenRevealed=localStorage.getItem("secretHasAlreadyBeenRevealed"),TODEPOND_COLOURS=[Colour.Green.splash,Colour.Red.splash,Colour.Blue.splash,Colour.Yellow.splash,Colour.Orange.splash,Colour.Pink.splash,Colour.Rose.splash,Colour.Cyan.splash,Colour.Purple.splash,Colour.Black.splash,Colour.Grey.splash,Colour.Silver.splash,Colour.White.splash,],TODEPOND_RAINBOW_COLOURS=TODEPOND_COLOURS.slice(0,-4),getRGB=e=>{let t=e%100,l=t%10,i=t-l;return[e-t,i,l]},clamp=(e,t,l)=>el?l:e,wrap=(e,t,l)=>{let i=l-t+1;return el?wrap(e-i,t,l):e};let brushColourCycleIndex=0;const brushColourCycle=[999,Colour.Green.splash,Colour.Blue.splash,Colour.Red.splash,Colour.Yellow.splash,Colour.Black.splash,Colour.Rose.splash,Colour.Cyan.splash,Colour.Orange.splash,Colour.Purple.splash,Colour.Pink.splash,Colour.Grey.splash,Colour.Silver.splash,],makeCell=({x:e=0,y:t=0,width:l=1,height:i=1,colour:o=112}={})=>{let a=e,r=e+l,n=t,d=t+i,h=l*i,s=a+l/2,c=n+i/2,u=[],g=void 0,$={x:e,y:t,width:l,height:i,colour:o,left:a,right:r,top:n,bottom:d,centerX:s,centerY:c,sections:u,size:h,lastDraw:g,lastDrawRepeat:0};return $};let edgeMode=0;const pickCell=(e,t)=>{if(e>=1||t>=1||e<0||t<0)return;let l=Math.floor(e*GRID_SIZE),i=Math.floor(t*GRID_SIZE),o=l*GRID_SIZE+i,a=state.grid[o];if(void 0===a)return;let r=1,n=a.size,d=a.values();for(let h of d)if(r===n||(r++,!(h.left>e)&&!(h.top>t)&&!(h.right<=e)&&!(h.bottom<=t)))return h},pickNeighbour=(e,t,l)=>{let i=e.left+e.width/2,o=e.top+e.height/2,a=i+t*e.width,r=o+l*e.height,n=pickCell(a,r);return n},pickRandomCell=()=>{let e=Random.Uint32/4294967295,t=Random.Uint32/4294967295,l=pickCell(e,t);return l},pickRandomVisibleCell=()=>{if(!state.view.visible)return;if(state.view.fullyVisible)return pickRandomCell();let e=state.region.left+Random.Uint32/4294967295*state.region.width,t=state.region.top+Random.Uint32/4294967295*state.region.height,l=pickCell(e,t);return l},state={grid:[],cellCount:0,ticker(){},time:0,maxTime:9999999,speed:{count:1638.4,dynamic:!1,redraw:2.5,redrawRepeatScore:.5,redrawRepeatPenalty:0},image:{data:void 0,size:void 0,baseSize:void 0},view:{height:void 0,width:void 0,iheight:void 0,iwidth:void 0,left:void 0,right:void 0,top:void 0,bottom:void 0,visible:!0,fullyVisible:!0},region:{left:0,right:1,top:0,bottom:1,width:1,height:1},camera:{x:0,y:0,dx:0,dy:0,dxTarget:0,dyTarget:0,dsControl:1,dsTargetSpeed:.05,underScale:.9,scale:.9,mscale:1,dmscale:.002,mscaleTarget:1,mscaleTargetControl:.001,mscaleTargetSpeed:.05},brush:{colour:Colour.Purple.splash,colour:Colour.Rose.splash,colour:Colour.Yellow.splash,colour:Colour.Grey.splash,colour:Colour.Green.splash,colour:999,size:3},cursor:{previous:{x:void 0,y:void 0}},dragon:{behaves:[]}};let WORLD_SIZE,WORLD_CELL_COUNT,WORLD_DIMENSION,WORLD_CELL_SIZE;const setWorldSize=e=>{WORLD_CELL_COUNT=2**(2*(WORLD_SIZE=e)),WORLD_CELL_SIZE=1/(WORLD_DIMENSION=2**WORLD_SIZE)};setWorldSize(6);const addCell=e=>{cacheCell(e),state.cellCount++},deleteCell=e=>{uncacheCell(e),e.isDeleted=!0,state.cellCount--},getCells=()=>{let e=new Set;for(let t of state.grid)for(let l of t.values())e.add(l);return e},GRID_SIZE=128;for(let x=0;x{let t=Math.floor(e.left*GRID_SIZE),l=Math.floor(e.top*GRID_SIZE),i=Math.ceil(e.right*GRID_SIZE),o=Math.ceil(e.bottom*GRID_SIZE);for(let a=t;a{for(let t of e.sections)t.delete(e)},world=makeCell({colour:111*WORLD_SIZE});addCell(world),on.load(()=>{let e=Show.start({paused:!1,scale:DPR}),{context:t,canvas:l}=e;l.style.position="absolute";let i=()=>{state.image.baseSize=Math.min(l.width,l.height),state.image.size=state.image.baseSize*state.camera.scale,state.image.left=state.camera.x*state.camera.scale,state.image.top=state.camera.y*state.camera.scale,state.image.right=state.image.left+state.image.size,state.image.bottom=state.image.top+state.image.size,state.view.left=clamp(state.image.left,0,l.width),state.view.top=clamp(state.image.top,0,l.height),state.view.right=clamp(state.image.right,0,l.width),state.view.bottom=clamp(state.image.bottom,0,l.height),state.view.width=state.view.right-state.view.left,state.view.height=state.view.bottom-state.view.top,state.view.visible=state.view.width>0&&state.view.height>0,state.view.fullyVisible=state.view.left===state.image.left&&state.view.right===state.image.right&&state.view.top===state.image.top&&state.view.bottom===state.image.bottom,state.view.iwidth=Math.ceil(state.view.width),state.view.iheight=Math.ceil(state.view.height),state.region.left=(state.view.left-state.image.left)/state.image.size,state.region.right=1+(state.view.right-state.image.right)/state.image.size,state.region.top=(state.view.top-state.image.top)/state.image.size,state.region.bottom=1+(state.view.bottom-state.image.bottom)/state.image.size,state.region.width=state.region.right-state.region.left,state.region.height=state.region.bottom-state.region.top,drawQueueNeedsReset=!0},o=()=>{state.image.data=t.getImageData(0,0,l.width,l.height)};t.fillStyle=Colour.Void,t.fillRect(0,0,l.width,l.height),i(),o(),state.camera.x+=(l.width-state.image.size)/2,state.camera.y+=(l.height-state.image.size)/2,e.resize=()=>{t.fillStyle=Colour.Void,t.fillRect(0,0,l.width,l.height),i(),o()};let a=e=>{i()},r=()=>{let e=getCells();for(let t of e.values())c(t,t.colour)},n=(e,t)=>c(e,e.colour,t),d=e=>!(e.right<=state.region.left)&&!(e.left>=state.region.right)&&!(e.bottom<=state.region.top)&&!(e.top>=state.region.bottom),h=e=>!(e.right<=state.region.left)&&!(e.left>=state.region.right)&&!(e.bottom<=state.region.top)&&!(e.top>=state.region.bottom),s=(e,t)=>(e.colour=t,h(e))?(D.add(e),O.delete(e),.01):0,c=(e,t,i=!1)=>{if(e.isDeleted||(e.colour=t,!h(e)))return 0;let o=state.image.size,a=l.width,r=state.camera.x*state.camera.scale,n=state.camera.y*state.camera.scale,d=Math.round(o*e.left+r);if(d>l.width)return 0;d<0&&(d=0);let s=Math.round(o*e.top+n);if(s>l.height)return 0;s<0&&(s=0);let c=Math.round(o*e.right+r);if(c<0)return 0;c>l.width&&(c=l.width);let u=Math.round(o*e.bottom+n);if(u<0)return 0;u>l.height&&(u=l.height);let g=Colour.splash(e.colour),$=g[0],p=g[1],f=g[2],v=4*a,m=c-d,_=4*m,w=(s*a+d)*4,b=state.image.data.data,S=Colour.Void.red,R=Colour.Void.green,C=Colour.Void.blue;if(!gridMode||m<=3||u-s<=3){S=$,R=p,C=f;for(let T=s;T{$(),R();let[e,t]=Mouse.position;state.cursor.previous.x=e,state.cursor.previous.y=t},g=!1,$=()=>{if(!state.worldBuilt||(Mouse.Middle||(g=!1),state.colourTode.hand.state!==tr.BRUSHING&&state.colourTode.hand.state!==tr.PENCILLING))return;if(Mouse.Middle&&!g){let[e,t]=Mouse.position;f(...p(e,t),{single:!0}),g=!0}if(!Mouse.Left)return;let[l,i]=p(...Mouse.position);if(void 0===l||void 0===i)return;let[o,a]=p(state.cursor.previous.x,state.cursor.previous.y),r=state.brush.size*WORLD_CELL_SIZE,n=l-o,d=i-a,h=Math.sign(n),s=Math.sign(d),c=Math.abs(n),u=Math.abs(d),$=Math.max(c,u),v=0,m=0;c===$?(m=WORLD_CELL_SIZE*s*(u/c),v=WORLD_CELL_SIZE*h):(v=WORLD_CELL_SIZE*h*(c/u),m=WORLD_CELL_SIZE*s);let _=new Set,w=$/WORLD_CELL_SIZE;if(0===n&&0===d)for(let b=-r/2;b<=r/2;b+=WORLD_CELL_SIZE)for(let S=-r/2;S<=r/2;S+=WORLD_CELL_SIZE)_.add([l+b,i+S]);else for(let R=0;R<=w;R++){let C=o+v*R,T=a+m*R;for(let k=-r/2;k<=r/2;k+=WORLD_CELL_SIZE)for(let E=-r/2;E<=r/2;E+=WORLD_CELL_SIZE)_.add([C+k,T+E])}for(let z of _.values())f(z[0],z[1])},p=(e,t)=>(e-=state.camera.x*state.camera.scale/DPR,t-=state.camera.y*state.camera.scale/DPR,e/=state.image.size,t/=state.image.size,[e*=DPR,t*=DPR]),f=(e,t,{single:l=!1}={})=>{let i=pickCell(e,t);if(void 0===i)return;if(!l&&(i.width!==WORLD_CELL_SIZE||i.height!=WORLD_CELL_SIZE)){let o=v(e,t);if(void 0!==o){let a=U([...o]);i=a}}if("number"==typeof state.brush.colour){i.colour=state.brush.colour,n(i);return}let r=[];for(let d of r=state.brush.colour.left[0].content.isDiagram?N(i,state.brush.colour.left[0].content):N(i,state.brush.colour))n(d)},v=(e,t)=>{let l=m(e,t),i=_(l,e,t);return i},m=(e,t)=>{let l=Math.floor(e*WORLD_DIMENSION)/WORLD_DIMENSION,i=Math.floor(t*WORLD_DIMENSION)/WORLD_DIMENSION,o=GRID_SIZE/WORLD_DIMENSION,a=new Set;for(let r=0;r{let i=new Set;for(let o of e.values())for(let a of o.values())if(!i.has(a)){for(let r of a.sections)if(!e.has(r))return;i.add(a)}return i},w,b,S;state.brush.hoverColour=Colour.Void;let R=()=>{let[e,t]=Mouse.position;if(e6.state===tr.BRUSH||e6.state===tr.BRUSHING||e6.state===tr.PENCILLING){let l=pickCell(...p(e,t));void 0!==l&&(state.brush.hoverColour=l.colour)}else{let o=tu(e/C,t/C);if(void 0!==o){if(o.isSquare||o===squareTool){if(state.brush.hoverColour=o.value,o.joinExpanded){let a=Q(o.value);a.joins=[],state.brush.hoverColour=a}}else o.isTallRectangle||(o.isPaddle?state.brush.hoverColour=o.getColour(o):o.isSlot?state.brush.hoverColour=o.parent.getColour(o.parent):state.brush.hoverColour=o.colour.splash)}else state.brush.hoverColour=Colour.Void}if(!Mouse.Right){if(void 0!==w){let r=Math.hypot(e-w,t-b),n=Date.now()-S;(n<100||r<=0)&&(state.brush.hoverColour===Colour.Void?(++brushColourCycleIndex>=brushColourCycle.length&&(brushColourCycleIndex=0),tO(brushColourCycle[brushColourCycleIndex])):tO(state.brush.hoverColour),squareTool.toolbarNeedsColourUpdate=!0),drawQueueNeedsReset=!0}w=void 0,b=void 0;return}if(drawQueueNeedsReset=!0,void 0===w&&(w=e,b=t,S=Date.now(),dropperMovement=0),e6.state===tr.FREE||e6.state==tr.VOIDING||e6.state===tr.BRUSH||e6.state===tr.BRUSHING||e6.state===tr.PENCILLING){let{x:d,y:h}=state.cursor.previous;if(void 0===d||void 0===h||void 0===e||void 0===t)return;let[s,c]=[e-d,t-h];state.camera.x+=s/state.camera.scale,state.camera.y+=c/state.camera.scale,i()}},C=DPR*SCALE;on.wheel(e=>{e.preventDefault();let t=e.deltaY/100;if(e.altKey)li.scroll-=50*t,lf();else if(e.ctrlKey||e.metaKey){C-=.1*t;let l=l$();for(let i of l)i.needsColoursUpdate=!0;squareTool.toolbarNeedsColourUpdate=!0}else e.shiftKey?(0===t&&(t=e.deltaX/100),state.brush.size-=Math.sign(t),state.brush.size<0&&(state.brush.size=0)):k(t,...Mouse.position)},{passive:!1}),on.keydown(e=>{"Alt"===e.key&&e.preventDefault()},{passive:!1}),on.keydown(e=>{"f"===e.key&&(state.camera.x=640,state.camera.y=30,state.camera.scale=.95,i())});let T=e=>e,k=(e,t,l)=>{t*=DPR,l*=DPR;let o=-Math.sign(e),a=Math.abs(e);for(let r=0;r{e.preventDefault()}),on.keydown(e=>{let t=E[e.key];void 0!==t&&t(e)});let E={};E.e=()=>state.camera.mscaleTarget+=state.camera.mscaleTargetControl,E.q=()=>state.camera.mscaleTarget-=state.camera.mscaleTargetControl,E.w=()=>state.camera.dyTarget+=state.camera.dsControl,E.s=e=>{e.ctrlKey||e.metaKey||(state.camera.dyTarget-=state.camera.dsControl)},E.a=()=>state.camera.dxTarget+=state.camera.dsControl,E.d=()=>state.camera.dxTarget-=state.camera.dsControl,E[0]=()=>setWorldSize(0),E[1]=()=>setWorldSize(1),E[2]=()=>setWorldSize(2),E[3]=()=>setWorldSize(3),E[4]=()=>setWorldSize(4),E[5]=()=>setWorldSize(5),E[6]=()=>setWorldSize(6),E[7]=()=>setWorldSize(7),E[8]=()=>setWorldSize(8),E[9]=()=>setWorldSize(9),E.r=()=>{state.camera.mscaleTarget=1,state.camera.dxTarget=0,state.camera.dyTarget=0},E["="]=()=>edgeMode=1,E["-"]=()=>edgeMode=0,E.o=()=>edgeMode=0===edgeMode?1:0,gridMode=!0,E.g=()=>{gridMode=!gridMode,drawQueueNeedsReset=!0};let z=()=>{if(state.camera.mscale!==state.camera.mscaleTarget){let e=state.camera.mscaleTarget-state.camera.mscale;state.camera.mscale+=e*state.camera.mscaleTargetSpeed;let t=Math.sign(e),o=state.camera.mscaleTarget*state.camera.mscaleTargetControl*state.camera.mscaleTargetSpeed;1===t&&state.camera.mscale>state.camera.mscaleTarget-o&&(state.camera.mscale=state.camera.mscaleTarget),-1===t&&state.camera.mscalestate.camera.dxTarget-n&&(state.camera.dx=state.camera.dxTarget),-1===r&&state.camera.dxstate.camera.dyTarget-s&&(state.camera.dy=state.camera.dyTarget),-1===h&&state.camera.dy{te(),u(),z(),drawQueueNeedsReset&&(A(),drawQueueNeedsReset=!1),e.paused?B():I(),t.putImageData(state.image.data,0,0),t.clearRect(0,0,state.view.left,state.view.bottom),t.clearRect(state.view.left,0,l.width,state.view.top),t.clearRect(state.view.right,state.view.top,l.width,l.height),t.clearRect(0,state.view.bottom,l.width,l.height),state.time++,state.time>state.maxTime&&(state.time=0)};let O=new Set,D=new Set;drawQueueNeedsReset=!1;let P=e=>{for(let t=e.length-1;t>0;t--){let l=Random.Uint32%(t+1);[e[t],e[l]]=[e[l],e[t]]}return e},A=()=>{for(let e of(O.clear(),P([...state.grid])))if(d(e))for(let t of e.values())O.add(t)},I=()=>{let e=state.speed.dynamic?state.speed.aer*state.cellCount:state.speed.count;e=Math.min(e,state.cellCount),e*=state.worldBuilt?1:.1;let t=e*state.speed.redraw,l=!0;state.worldBuilt||(l=!1);let i=0;for(let o=0;o=t&&(l=!1);let r=L(a,l);i+=r}for(let d of D)if(i+=n(d),D.delete(d),i>=t)break;for(let h of O)if(i+=n(h),O.delete(h),i>=t)break},B=()=>{if(!state.view.visible)return;let e=state.speed.dynamic?state.speed.aer*state.cellCount:state.speed.count,t=e*state.speed.redraw;state.worldBuilt||(t=1);let l=0;for(let i of O)if(l+=n(i),O.delete(i),l>=t)break},L=(e,t)=>{if(void 0!==Y(e,t))return 1;for(let l of(state.dragon.behaves.shuffle(),state.dragon.behaves)){let i=l(e,t);if(void 0!==i)return i}return 0},G=(e,t,l)=>{let i=e.width/t,o=e.height/l,a=[],r=e.x,n=e.y,d=i,h=o;for(let s=0;s{let[l,i]=eo(t),o=e.width/l,a=e.height/i,r=[];for(let n of t.left){let d=M(n.content,{source:e.colour}),h=d[Random.Uint32%d.length],s=makeCell({x:e.x+n.x*o,y:e.y+n.y*a,width:n.width*o,height:n.height*a,colour:h});r.push(s)}for(let c of(deleteCell(e),r))addCell(c);return r},U=e=>{let t=1,l=1,i=0,o=0;for(let a of e)a.lefti&&(i=a.right),a.bottom>o&&(o=a.bottom),deleteCell(a);let r=makeCell({x:t,y:l,width:i-t,height:o-l,colour:e[0].colour,lastDraw:e[0].lastDraw});return addCell(r),r},Y=(e,t)=>{if(state.worldBuilt)return;if(state.cellCount>=WORLD_CELL_COUNT){state.worldBuilt=!0;return}if(e.colour<111)return 0;e.colour-=111;let l=2,i=2,o=G(e,l,i);for(let a of o)n(a);return 1},X=({values:e,channel:t=0,variable:l,add:i,subtract:o}={})=>{let a;return{values:a=void 0!==l?[!0,!0,!0,!0,!0,!0,!0,!0,!0,!0]:e,variable:l,channel:t,add:i,subtract:o}},H=e=>{let t=[...e.values],l=e.variable,i=e.channel,o=void 0===e.add?void 0:H(e.add),a=void 0===e.subtract?void 0:H(e.subtract),r=X({values:t,variable:l,channel:i,add:o,subtract:a});return r},V=e=>{let t=[!1,!1,!1,!1,!1,!1,!1,!1,!1,!1];return t[e]=!0,t},j=e=>{let t=V(e);return X({values:t})},W={};W.red=(e,{source:t}={})=>{if(void 0===t)return[!0,!0,!0,!0,!0,!0,!0,!0,!0,!0];let[l,i,o]=getRGB(t),a=V(l/100);return a},W.green=(e,{source:t}={})=>{if(void 0===t)return[!0,!0,!0,!0,!0,!0,!0,!0,!0,!0];let[l,i,o]=getRGB(t),a=V(i/10);return a},W.blue=(e,{source:t}={})=>{if(void 0===t)return[!0,!0,!0,!0,!0,!0,!0,!0,!0,!0];let[l,i,o]=getRGB(t),a=V(o);return a};let q=(e,t={})=>{if(void 0===e.variable)return e.values;let l=W[e.variable](e,t),i="red"===e.variable;return void 0!==e.add&&(l=e$(l,e.add,{source:t.source,multiplier:1,isHue:i})),void 0!==e.subtract&&(l=e$(l,e.subtract,{source:t.source,multiplier:-1,isHue:i})),l},Z=({channels:e,stamp:t,joins:l=[]}={})=>(void 0===e&&(e=[void 0,void 0,void 0]),{channels:e,stamp:t,joins:l});makeArrayFromSplash=e=>{let[t,l,i]=getRGB(e);t/=100,l/=10;let o=[!1,!1,!1,!1,!1,!1,!1,!1,!1,!1],a=[!1,!1,!1,!1,!1,!1,!1,!1,!1,!1],r=[!1,!1,!1,!1,!1,!1,!1,!1,!1,!1];o[t]=!0,a[l]=!0,r[i]=!0;let n=X({values:o,channel:0}),d=X({values:a,channel:1}),h=X({values:r,channel:2}),s=Z({channels:[n,d,h]});return s};let F=(e,t)=>{let l=M(e,t),i=new Set(l);return i},M=(e,t={})=>{let l=[];for(let i of e.joins){let o=M(i);l.push(...o)}if(e.isDiagram)return l.push(900),l;let[a,r,n]=e.channels;void 0===a&&(a=X({channel:0,variable:"red"})),void 0===r&&(r=X({channel:1,variable:"green"})),void 0===n&&(n=X({channel:2,variable:"blue"}));let d=q(a,t),h=q(r,t),s=q(n,t);for(let c=0;c{for(let t=0;t<3;t++)void 0===e.channels[t]&&(e.channels[t]=X({channel:t,variable:t9[t]}))},J=e=>{for(let t of e.channels){if(void 0===t)break;if(void 0!==t.variable)return!0}return!1},Q=e=>{if(void 0===e){let t=X({values:[!0,!0,!0,!0,!0,!0,!0,!0,!0,!0],channel:0}),l=X({values:[!0,!0,!0,!0,!0,!0,!0,!0,!0,!0],channel:1}),i=X({values:[!0,!0,!0,!0,!0,!0,!0,!0,!0,!0],channel:2}),o=Z({channels:[t,l,i]});return o}if(e.isDiagram)return ee(e);let a,r,n,d=e.stamp;void 0!==e.channels[0]&&null!==e.channels[0]&&(a=H(e.channels[0])),void 0!==e.channels[1]&&null!==e.channels[1]&&(r=H(e.channels[1])),void 0!==e.channels[2]&&null!==e.channels[2]&&(n=H(e.channels[2]));let h=[];for(let s of e.joins){let c=Q(s);h.push(c)}let u=Z({stamp:d,channels:[a,r,n],joins:h});return u};makeDiagram=({left:e=[],right:t,joins:l=[]}={})=>({left:e,right:t,isDiagram:!0,joins:l});let ee=e=>{let t=makeDiagram();for(let l of["left","right"]){if(void 0===e[l])continue;let i=[];for(let o of e[l]){let a=et(o);i.push(a)}t[l]=i}return t};makeDiagramCell=({x:e=0,y:t=0,width:l=1,height:i=1,content:o=Z(),instruction:a=DRAGON_INSTRUCTION.recolour,splitX:r=1,splitY:n=1}={})=>({x:e,y:t,width:l,height:i,content:o,instruction:a,splitX:r,splitY:n});let et=e=>{let t=Q(e.content);return{...e,content:t}},el=e=>{let[t,l]=eo(e);for(let i of["left","right"]){let o=e[i];if(void 0!==o)for(let a of o)a.width/=t,a.height/=l,a.x/=t,a.y/=l}return e},ei=e=>{let t=1/0,l=1/0;for(let i of["left","right"]){let o=e[i];if(void 0!==o)for(let a of o)a.width{let t=1/0,l=-1/0,i=1/0,o=-1/0;for(let a of e.left){let r=a.x,n=a.x+a.width,d=a.y,h=a.y+a.height;rl&&(l=n),h>o&&(o=h)}let s=l-t,c=o-i;return[s,c]};makeRule=({steps:e=[],transformations:t=DRAGON_TRANSFORMATIONS.NONE,locked:l=!0,chance:i}={})=>({steps:e,transformations:t,locked:l,chance:i}),DRAGON_TRANSFORMATIONS={NONE:[(e,t,l,i,o,a)=>[e,t],],X:[(e,t,l,i,o,a)=>[e,t],(e,t,l,i,o,a)=>[-e-l+o,t],],Y:[(e,t,l,i,o,a)=>[e,t],(e,t,l,i,o,a)=>[e,-t-i+a],],XY:[(e,t,l,i,o,a)=>[e,t],(e,t,l,i,o,a)=>[-e-l+o,t],(e,t,l,i,o,a)=>[e,-t-i+a],(e,t,l,i,o,a)=>[-e-l+o,-t-i+a],],R:[(e,t,l,i,o,a)=>[e,t],(e,t,l,i,o,a)=>[-t-i+a,e],(e,t,l,i,o,a)=>[-e-l+o,-t-i+a],(e,t,l,i,o,a)=>[t,-e-l+o],],XYR:[(e,t,l,i,o,a)=>[e,t],(e,t,l,i,o,a)=>[-t-i+a,e],(e,t,l,i,o,a)=>[-e-l+o,-t-i+a],(e,t,l,i,o,a)=>[t,-e-l+o],(e,t,l,i,o,a)=>[-e-l+o,t],(e,t,l,i,o,a)=>[-t-i+a,-e-l+o],(e,t,l,i,o,a)=>[e,-t-i+a],(e,t,l,i,o,a)=>[t,e],]};let ea=(e,t,l)=>{let[i,o]=eo(e.steps[0]),a=e.steps.map(e=>er(e,t,l,i,o)),r=makeRule({steps:a,transformations:e.transformations,locked:e.locked,chance:e.chance});return r},er=(e,t,l,i,o)=>{let{left:a,right:r}=e,[n,d]=[i,o],h=[],s=void 0===r?void 0:[];for(let c=0;c{let[a,r,n,d]=t(e.x,e.y,e.width,e.height,l,i),{splitX:h,splitY:s}=e;if(!o){let[c,u]=t(e.splitX,e.splitY,1,1,1,1).map(e=>Math.abs(e));h=c,s=u;let[g,$]=t(e.width,e.height,1,1,1,1).map(e=>Math.abs(e));n=g,d=$}void 0===a&&(a=e.x),void 0===r&&(r=e.y),void 0===n&&(n=e.width),void 0===d&&(d=e.height);let p=e.content;return makeDiagramCell({x:a,y:r,splitX:h,splitY:s,width:n,height:d,content:p,instruction:e.instruction})};registerRule=e=>{let t=[];for(let l of e.transformations){let i=ea(e,l);t.push(i)}let o=[];for(let a of t){let r=eh(a);o.push(...r)}let n=[];for(let d of o){let h=ec(d);n.push(h),state.dragon.behaves.push(h)}return{redundantRules:o,transformedRules:t,behaveFunctions:n}};let ed=({behaveFunctions:e})=>{state.dragon.behaves=state.dragon.behaves.filter(t=>!e.includes(t))},eh=e=>{let t=[],[l]=e.steps;for(let i of l.left){let o=(e,t,l=1,o=1)=>{let a=l/i.width,r=o/i.height,n=(e-i.x)*1/i.width,d=(t-i.y)*1/i.height;return[n,d,a,r]},a=ea(e,o,!0);t.push(a)}return t},es=e=>{let t=new Set,l=[e.left,e.right];for(let i of l)for(let o of i){let a=o.content.stamp;void 0!==a&&t.add(a)}return[...t.values()]},ec=e=>{let t=[];for(let l of e.steps){let i=es(l),o=eu(l,i,e.chance),a=eg(l,i),r=(e,t)=>{let[l,i]=o(e);if(void 0!==l)return a(l,t,i)};t.push(r)}let n=(e,l)=>{for(let i of t){let o=i(e,l);if(void 0!==o)return o}};return n},eu=(e,t,l=6)=>{let i=[];for(let o of e.left){let a=F(o.content),r=e=>{let t=o.width*e.width,l=o.height*e.height,i=e.x+o.x*e.width,r=e.y+o.y*e.height;if(1===edgeMode){for(;i>=1;)i-=1;for(;r>=1;)r-=1;for(;i<0;)i+=1;for(;r<0;)r+=1}let n=i+t/2,d=r+l/2,h=pickCell(n,d);return void 0!==h&&h.left===i&&h.top===r&&h.width===t&&h.height===l&&a.has(h.colour)?[h,o.content.stamp]:[void 0,void 0]};i.push(r)}let n=Math.round(10**(3-l/2)),d=()=>oneIn(n),h=e=>{if(void 0!==l&&!d())return[void 0,void 0];let o=[],a={};for(let r of t)a[r]=[];for(let n of i){let[h,s]=n(e);if(void 0===h)return[void 0,void 0];void 0!==s&&a[s].push(h.colour),o.push(h)}return[o,a]};return h},eg=(e,t)=>{let l=[];for(let i of e.right){let o=i.instruction(i);l.push(o)}let a=(e,t,l)=>{for(let i of l)r(e,t,i)},r=(e,t,l)=>{e[l]=[...t[l]]};return(e,i,o)=>{let n=0,d=0,h=0,s=[],c={};for(let u of(a(c,o,t),l)){let g=s.length>0?s.pop():e[d],$=u(g,i,e,d,c);void 0!==$.stampNameTakenFrom&&0===c[$.stampNameTakenFrom].length&&r(c,o,$.stampNameTakenFrom);let{drawn:p,bonusTargets:f,skip:v}=$;void 0!==v&&(h+=v),n+=p,void 0!==f&&s.push(...f),0===s.length&&(d++,h>0&&(d++,h--))}return n}};(DRAGON_INSTRUCTION={}).nothing=e=>()=>({drawn:0}),DRAGON_INSTRUCTION.nothing.type="NOTHING",DRAGON_INSTRUCTION.recolour=e=>{K(e.content);let t=M(e.content),l=J(e.content),i=(i,o,a,r,n)=>{let d,h=i.colour,c;if(void 0===e.content.stamp)d=t[Random.Uint32%t.length];else{let u=n[e.content.stamp];if(0===u.length)d=t[Random.Uint32%t.length];else{let g=Random.Uint32%u.length;d=h=u[g],u.splice(g,1),c=e.content.stamp}}if(l){let $=getRGB(d);for(let p=0;p!0===e&&t).filter(e=>!1!==e),w=_[Random.Uint8%_.length];d-=$[p];let b=10**(2-p);d+=w*b}}let S=0;return o?S+=s(i,d,!0):i.colour=d,{drawn:S,stampNameTakenFrom:c}};return i},DRAGON_INSTRUCTION.recolour.type="RECOLOUR";let e$=(e,t,{source:l,multiplier:i=1,isHue:o=!1})=>{let a=t.values;void 0!==t.variable&&(a=W[t.variable](t,{source:l}));let r=a.map((e,t)=>!0===e&&t).filter(e=>!1!==e),n=e.map((e,t)=>!0===e&&t).filter(e=>!1!==e),d=new Set;for(let h of n)for(let s of r){let c=clamp(h+s*i,0,9);d.add(c)}let u=[!1,!1,!1,!1,!1,!1,!1,!1,!1,!1];for(let g of d)u[g]=!0;return void 0!==t.add&&(u=e$(u,t.add,{source:l,multiplier:1,isHue:o})),void 0!==t.subtract&&(u=e$(u,t.add,{source:l,multiplier:-1,isHue:o})),u};DRAGON_INSTRUCTION.split=e=>{let t=(t,l,i,o,a)=>{let r=G(t,e.splitX,e.splitY);return{drawn:0,bonusTargets:r.reverse()}};return t},DRAGON_INSTRUCTION.split.type="SPLIT",DRAGON_INSTRUCTION.merge=e=>{let t=Math.abs(e.splitX)*Math.abs(e.splitY),l=(e,l,i,o,a)=>{let r=i.slice(o,o+t),n=U(r);return{drawn:0,skip:t-1,bonusTargets:[n]}};return l},DRAGON_INSTRUCTION.merge.type="MERGE";debugRegistry=(e,{transforms:t=!0,redundants:l=!0}={})=>{let{redundantRules:i,transformedRules:o}=e;if(print(""),print("================================================================="),t)for(let a of(print(""),print("TRANSFORMED RULES"),o))debugRule(a);if(l)for(let r of(print(""),print("REDUNDANT RULES"),i))debugRule(r)},debugRule=e=>{for(let t of e.steps){for(let l of(print(""),print("=== LEFT ==="),t.left))debugDiagramCell(l,{read:!0});for(let i of(print("=== RIGHT ==="),t.right))debugDiagramCell(i)}},debugDiagramCell=(e,{read:t=!1}={})=>{t?print("CHECK","at",e.x,e.y,"with size",e.width,e.height,"for",M(e.content)):"NOTHING"===e.instruction.type?print(e.instruction.type,"at",e.x,e.y,"with size",e.width,e.height):"RECOLOUR"===e.instruction.type?print(e.instruction.type,"at",e.x,e.y,"with size",e.width,e.height,"to",M(e.content)):print(e.instruction.type,e.splitX,e.splitY,"at",e.x,e.y,"with size",e.width,e.height)};let ep=makeArrayFromSplash(Colour.Grey.splash),ef=makeArrayFromSplash(Colour.Black.splash),ev=makeArrayFromSplash(Colour.Cyan.splash),em=makeArrayFromSplash(Colour.Blue.splash),ey=makeArrayFromSplash(Colour.Yellow.splash),e_=makeArrayFromSplash(Colour.Cyan.splash-111),ex=makeArrayFromSplash(Colour.Red.splash),[ew,eb,e0]=getRGB(Colour.Red.splash);ew/=100,eb/=10;let eS=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:ep}),makeDiagramCell({x:0,y:1,content:ef}),],right:[makeDiagramCell({x:0,y:0,content:ef}),makeDiagramCell({x:0,y:1,content:ep}),]}),eR=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:ey}),makeDiagramCell({x:0,y:1,content:ef}),],right:[makeDiagramCell({x:0,y:0,content:ef}),makeDiagramCell({x:0,y:1,content:ey}),]}),eC=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:ey}),makeDiagramCell({x:1,y:1,content:ef}),],right:[makeDiagramCell({x:0,y:0,content:ef}),makeDiagramCell({x:1,y:1,content:ey}),]}),eT=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:ev}),makeDiagramCell({x:1,y:0,content:em}),]}),ek=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:em}),makeDiagramCell({x:0,y:1,content:ef}),],right:[makeDiagramCell({x:0,y:0,content:ef}),makeDiagramCell({x:0,y:1,content:em}),]}),eE=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:e_}),makeDiagramCell({x:1,y:0,content:ev}),],right:[makeDiagramCell({x:0,y:0,content:ev}),makeDiagramCell({x:1,y:0,content:e_}),]}),ez=makeDiagram({left:[makeDiagramCell({x:0,y:0,width:1,content:em}),],right:[makeDiagramCell({x:0,y:0,width:.5,content:e_,instruction:DRAGON_INSTRUCTION.split,splitX:2,splitY:1}),makeDiagramCell({x:.5,y:0,width:.5,content:ev}),]}),eO=makeDiagram({left:[makeDiagramCell({x:0,y:0,width:1,content:e_}),makeDiagramCell({x:1,y:0,width:1,content:ev}),makeDiagramCell({x:0,y:1,width:2,content:ef}),],right:[makeDiagramCell({x:0,y:0,width:2,content:em,instruction:DRAGON_INSTRUCTION.merge,splitX:2,splitY:1}),makeDiagramCell({x:0,y:1,width:2,content:ef}),]}),eD=makeDiagram({left:[makeDiagramCell({x:0,y:0,width:1,content:e_}),makeDiagramCell({x:1,y:0,width:1,content:e_}),],right:[makeDiagramCell({x:0,y:0,width:2,content:em,instruction:DRAGON_INSTRUCTION.merge,splitX:2,splitY:1}),]}),eP=makeDiagram({left:[makeDiagramCell({x:0,y:0,width:1,content:ev}),makeDiagramCell({x:1,y:0,width:1,content:ev}),],right:[makeDiagramCell({x:0,y:0,width:2,content:em,instruction:DRAGON_INSTRUCTION.merge,splitX:2,splitY:1}),]}),eA=makeDiagram({left:[makeDiagramCell({x:0,y:0,width:1,content:e_}),makeDiagramCell({x:1,y:0,width:1,content:ev}),makeDiagramCell({x:2,y:0,width:2,content:ef}),],right:[makeDiagramCell({x:0,y:0,width:2,content:ef,instruction:DRAGON_INSTRUCTION.merge,splitX:2,splitY:1}),makeDiagramCell({x:2,y:0,width:1,content:e_,instruction:DRAGON_INSTRUCTION.split,splitX:2,splitY:1}),makeDiagramCell({x:3,y:0,width:1,content:ev}),]}),eI=makeDiagram({left:[makeDiagramCell({x:0,y:0,width:.5,content:em}),makeDiagramCell({x:.5,y:0,width:.5,content:em}),makeDiagramCell({x:0,y:1,content:ef}),],right:[makeDiagramCell({x:0,y:0,width:1,content:ef,instruction:DRAGON_INSTRUCTION.merge,splitX:2,splitY:1}),makeDiagramCell({x:0,y:1,width:.5,content:em,instruction:DRAGON_INSTRUCTION.split,splitX:2,splitY:1}),makeDiagramCell({x:.5,y:1,width:.5,content:em}),]}),eB=makeDiagram({left:[makeDiagramCell({x:0,y:0,width:.5,content:ev}),makeDiagramCell({x:.5,y:0,width:.5,content:ev}),makeDiagramCell({x:0,y:1,content:ef}),],right:[makeDiagramCell({x:0,y:0,width:1,content:ef,instruction:DRAGON_INSTRUCTION.merge,splitX:2,splitY:1}),makeDiagramCell({x:0,y:1,width:.5,content:ev,instruction:DRAGON_INSTRUCTION.split,splitX:2,splitY:1}),makeDiagramCell({x:.5,y:1,width:.5,content:ev}),]}),eL=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:em}),makeDiagramCell({x:1,y:0,content:ev}),],right:[makeDiagramCell({x:0,y:0,content:ev}),makeDiagramCell({x:1,y:0,content:em}),]}),eG=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:e_}),],right:[makeDiagramCell({x:0,y:0,width:.5,content:em,instruction:DRAGON_INSTRUCTION.split,splitX:2,splitY:1}),makeDiagramCell({x:.5,y:0,width:.5,content:ev,instruction:DRAGON_INSTRUCTION.recolour}),]}),eN=makeDiagram({left:[makeDiagramCell({x:0,y:0,width:1,content:em}),makeDiagramCell({x:1,y:0,width:1,content:em}),],right:[makeDiagramCell({x:0,y:0,width:1,content:em}),makeDiagramCell({x:1,y:0,width:1,content:ev}),]}),eU=makeDiagram({left:[makeDiagramCell({x:0,y:0,width:1,content:ev}),makeDiagramCell({x:1,y:0,width:1,content:ev}),],right:[makeDiagramCell({x:0,y:0,width:1,content:em}),makeDiagramCell({x:1,y:0,width:1,content:ev}),]}),e7=makeDiagram({left:[makeDiagramCell({x:0,y:0,width:1,content:ep}),makeDiagramCell({x:0,y:1,width:1,content:ef}),],right:[makeDiagramCell({x:0,y:0,width:1,content:ef}),makeDiagramCell({x:0,y:1,width:1,content:ep}),]}),eY=makeDiagram({left:[makeDiagramCell({x:0,y:0,width:1,content:ep}),makeDiagramCell({x:1,y:0,width:1,content:ef}),],right:[makeDiagramCell({x:0,y:0,width:1,content:ef}),makeDiagramCell({x:1,y:0,width:1,content:ep}),]}),eX=Z();eX.channels=[X(),X(),X()];for(let e3=0;e3<3;e3++){let e1=eX.channels[e3];for(let eH=0;eH<10;eH++)0===e3&eH>0||(e1.values[eH]=!0)}RAINBOW_DIAGRAM=makeDiagram({left:[makeDiagramCell({content:eX})]});let e8=Z();e8.channels=[X(),X(),X()];for(let eV=0;eV<3;eV++){let ej=e8.channels[eV];for(let eW=0;eW<10;eW++)1===eV&eW>0||(ej.values[eW]=!0)}RAINBOW_DIAGRAM_2=makeDiagram({left:[makeDiagramCell({content:e8})]});let eq=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:e_}),],right:[makeDiagramCell({x:0,y:0,content:ev}),]}),e2=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:e_}),],right:[makeDiagramCell({x:0,y:0,content:em}),]}),eZ=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:ev}),makeDiagramCell({x:0,y:1,content:ef}),],right:[makeDiagramCell({x:0,y:0,content:ef}),makeDiagramCell({x:0,y:1,content:ev}),]}),eF=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:em}),makeDiagramCell({x:0,y:1,content:ef}),],right:[makeDiagramCell({x:0,y:0,content:ef}),makeDiagramCell({x:0,y:1,content:em}),]}),e4=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:em}),makeDiagramCell({x:1,y:0,content:ef}),],right:[makeDiagramCell({x:0,y:0,content:ef}),makeDiagramCell({x:1,y:0,content:em}),]}),eM=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:ev}),makeDiagramCell({x:-1,y:0,content:ef}),],right:[makeDiagramCell({x:0,y:0,content:ef}),makeDiagramCell({x:-1,y:0,content:ev}),]}),e5=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:em}),],right:[makeDiagramCell({x:0,y:0,content:ev}),]}),eK=makeDiagram({left:[makeDiagramCell({x:0,y:0,content:ev}),],right:[makeDiagramCell({x:0,y:0,content:em}),]});state.colourTode={atoms:[],hand:{state:void 0,content:void 0,offset:{x:0,y:0},velocity:{x:0,y:0},velocityHistory:[],velocityMemory:5,previous:{x:0,y:0}}};let e6=state.colourTode.hand,e9=document.createElement("canvas"),eJ=e9.getContext("2d");e9.style.position="absolute",e9.style.top="0px",document.body.append(e9),on.resize(()=>{e9.width=innerWidth*DPR,e9.height=innerHeight*DPR,e9.style.width=innerWidth,e9.style.height=innerHeight}),trigger("resize");let eQ=()=>{tl(),ta(),requestAnimationFrame(eQ)},te=()=>{if(e6.velocityHistory.length>=e6.velocityMemory&&e6.velocityHistory.shift(),void 0!==Mouse.position&&void 0!==Mouse.position[0]&&void 0!==e6.previous.x){let[e,t]=Mouse.position.map(e=>e/C),l=(e-e6.previous.x)*DPR,i=(t-e6.previous.y)*DPR,o={x:l,y:i};e6.velocityHistory.push(o);let a=e6.velocityHistory.reduce((e,t)=>({x:e.x+t.x,y:e.y+t.y}),{x:0,y:0}),r={x:a.x/e6.velocityHistory.length,y:a.y/e6.velocityHistory.length};e6.velocity.x=r.x,e6.velocity.y=r.y,e6.previous.x=e,e6.previous.y=t}},tt=.9,tl=()=>{for(let e of state.colourTode.atoms)ti(e)},ti=(e,t=!0)=>{for(let l of e.children)ti(l,!1);if(void 0!==e.hover&&to(e),e.update(e),e6.content===e||0===e.dx&&0===e.dy)return;if(e.x+=e.dx,e.y+=e.dy,e.x=clamp(e.x,e.minX,e.maxX),e.y=clamp(e.y,e.minY,e.maxY),e.dx*=tt,e.dy*=tt,t&&tf(e)){t$(e);return}let[i,o]=Mouse.position.map(e=>e/C);e6.state.atommove&&e6.state.atommove(e,i,o)},to=e=>{if(e.highlightedAtom=void 0,e6.content!==e||e6.state!==tr.DRAGGING)return;void 0!==e.highlight&&(tb(e,e.highlight),e.highlight=void 0);let t=e.hover(e);if(void 0!==t){if(void 0===e.highlight){let l=tw(e,lS,{bottom:!0});l.hasBorder=!0,l.colour=Colour.Grey;let{x:i,y:o}=tx(t);l.x=i,l.y=o,l.width=t.width,l.height=t.height,e.highlight=l}e.highlightedAtom=t}},ta=()=>{for(let e of(eJ.clearRect(0,0,e9.width,e9.height),eJ.scale(C,C),state.colourTode.atoms))tg(e);eJ.scale(1/C,1/C)};requestAnimationFrame(eQ);let tr={};HAND_RELEASE=.5,tr.FREE={cursor:"auto",mousemove(e){let t=e.clientX/C,l=e.clientY/C,i=tu(t,l);if(void 0!==i){i.grabbable&&(Mouse.Left?i.grabbable&&i.draggable&&(tm(i,t,l),e6.pityStartX=e.clientX,e6.pityStartY=e.clientY,e6.pityStartT=Date.now(),th(tr.TOUCHING),tr.TOUCHING.mousemove(e)):void 0!==i.cursor?th(tr.HOVER,i.cursor(i,tr.HOVER)):i.dragOnly?th(tr.HOVER,"move"):th(tr.HOVER));return}let[o,a]=Mouse.position;o*=DPR,a*=DPR,!(ostate.view.right)&&!(astate.view.bottom)&&(Mouse.Left?th(tr.BRUSHING):Mouse.Middle?th(tr.PENCILLING):th(tr.BRUSH))},mousedown(e){state.worldBuilt&&(e6.voidingStart=[e.clientX,e.clientY],th(tr.VOIDING))},atommove(e,t,l){if(e.grabbable&&tv(e,t,l)){if(Mouse.Left){tm(e,t,l),th(tr.DRAGGING),e6.content=e6.content.drag(e6.content,t,l);return}void 0!==e.cursor?th(tr.HOVER,e.cursor(e,tr.HOVER)):e.dragOnly?th(tr.HOVER,"move"):th(tr.HOVER)}},camerapan(){let[e,t]=Mouse.position;if(e*=DPR,t*=DPR,e>=state.view.left&&e<=state.view.right&&t>=state.view.top&&t<=state.view.bottom){th(tr.BRUSH);return}}};let tn=!0;tr.VOIDING={cursor:"auto",mousemove(e){let t=e6.voidingStart,[l,i]=t,o=[e.clientX-l,e.clientY-i];Math.hypot(...o)>10&&(th(tr.FREE),tr.FREE.mousemove(e))},mouseup(t){let i=WORLD_SIZE;if(setWorldSize(0),tn)f(.5,.5);else{let o=state.brush.colour;state.brush.colour=111*i,f(.5,.5),state.brush.colour=o,state.worldBuilt=!1,e.paused=!1,l.style["background-color"]=Colour.Void}tn=!tn,setWorldSize(i),th(tr.FREE)}},tr.BRUSH={cursor:"crosshair",mousemove(e){let t=e.clientX/C,l=e.clientY/C,i=tu(t,l);if(void 0!==i){i.grabbable?void 0!==i.cursor?th(tr.HOVER,i.cursor(i,tr.HOVER)):i.dragOnly?th(tr.HOVER,"move"):th(tr.HOVER):th(tr.FREE);return}let o=e.clientX*DPR,a=e.clientY*DPR;(!(o>=state.view.left)||!(o<=state.view.right)||!(a>=state.view.top)||!(a<=state.view.bottom))&&th(tr.FREE)},mousedown(e){th(tr.BRUSHING)},middlemousedown(e){th(tr.PENCILLING)},atommove(e,t,l){tv(e,t,l)&&(e.grabbable?th(tr.HOVER):th(tr.FREE))},camerapan(){let[e,t]=Mouse.position;e*=DPR,t*=DPR,(!(e>=state.view.left)||!(e<=state.view.right)||!(t>=state.view.top)||!(t<=state.view.bottom))&&th(tr.FREE)}},tr.BRUSHING={cursor:"crosshair",mousemove(e){let t=e.clientX*DPR,l=e.clientY*DPR;(!(t>=state.view.left)||!(t<=state.view.right)||!(l>=state.view.top)||!(l<=state.view.bottom))&&th(tr.FREE)},mouseup(e){th(tr.BRUSH)},camerapan(){let[e,t]=Mouse.position;if(e*=DPR,t*=DPR,e>=state.view.left&&e<=state.view.right&&t>=state.view.top&&t<=state.view.bottom)return;let[l,i]=Mouse.position.map(e=>e/C),o=tu(l,i);if(void 0!==o){o.grabbable?void 0!==o.cursor?th(tr.HOVER,o.cursor(o,tr.HOVER)):o.dragOnly?th(tr.HOVER,"move"):th(tr.HOVER):th(tr.FREE);return}th(tr.FREE)}},tr.PENCILLING={cursor:"crosshair",mousemove:tr.BRUSHING.mousemove,middlemouseup:tr.BRUSHING.mouseup,camerapan:tr.BRUSHING.camerapan},tr.HOVER={cursor:"pointer",mousedown(e){let t=tu(e.clientX/C,e.clientY/C);void 0!==t&&t.grabbable&&(tm(t,e.clientX/C,e.clientY/C),t.dragOnly?(e6.pityStartX=e.clientX,e6.pityStartY=e.clientY,e6.pityStartT=Date.now(),e6.hasStartedDragging=!1,e6.touchButton=0,th(tr.TOUCHING,"move")):(e6.pityStartX=e.clientX,e6.pityStartY=e.clientY,e6.pityStartT=Date.now(),e6.hasStartedDragging=!1,e6.touchButton=0,th(tr.TOUCHING)))},mousemove(e){let t=e.clientX/C,l=e.clientY/C,i=tu(t,l);if(void 0!==i){i.grabbable?void 0!==i.cursor?th(tr.HOVER,i.cursor(i,tr.HOVER)):i.dragOnly?th(tr.HOVER,"move"):th(tr.HOVER):th(tr.FREE);return}let o=e.clientX,a=e.clientY;if(o>=state.view.left&&o<=state.view.right&&a>=state.view.top&&a<=state.view.bottom){th(tr.BRUSH);return}th(tr.FREE)},atommove(e,t,l){if(tv(e,t,l))return;let i=tu(t,l);if(void 0!==i)return;let[o,a]=Mouse.position;if(o*=DPR,a*=DPR,o>=state.view.left&&o<=state.view.right&&a>=state.view.top&&a<=state.view.bottom){th(tr.BRUSH);return}th(tr.FREE)},rightmousedown(e){let t=tu(e.clientX/C,e.clientY/C);void 0!==t&&t.grabbable&&(tm(t,e.clientX/C,e.clientY/C),t.dragOnly?(e6.pityStartX=e.clientX,e6.pityStartY=e.clientY,e6.pityStartT=Date.now(),e6.hasStartedDragging=!1,e6.touchButton=2,th(tr.TOUCHING,"move")):(e6.pityStartX=e.clientX,e6.pityStartY=e.clientY,e6.pityStartT=Date.now(),e6.hasStartedDragging=!1,e6.touchButton=2,th(tr.TOUCHING)))}};let td=(e,t)=>t?.6*e:e;tr.TOUCHING={cursor:"pointer",mousemove(e){if(0===e.movementX&&0===e.movementY||2===e6.touchButton&&!e6.content.rightDraggable)return;let t=Math.hypot(e.clientX-e6.pityStartX,e.clientY-e6.pityStartY),l=e.clientX-e6.pityStartX,i=e.clientY-e6.pityStartY;if(e6.content.dragLockX||(e6.content.x=(e6.pityStartX+td(l,e6.content.attached&&!e6.content.noDampen))/C*DPR+e6.offset.x),e6.content.dragLockY||(e6.content.y=(e6.pityStartY+td(i,e6.content.attached&&!e6.content.noDampen))/C*DPR+e6.offset.y),e6.content.x=clamp(e6.content.x,e6.content.minX,e6.content.maxX),e6.content.y=clamp(e6.content.y,e6.content.minY,e6.content.maxY),t<15)return;let o=Date.now()-e6.pityStartT;if(o<100){let a=Math.hypot(e6.velocity.x,e6.velocity.y);if(a<=10)return}e6.content.dragLockX||(e6.content.x=e6.pityStartX/C*DPR+e6.offset.x),e6.content.dragLockY||(e6.content.y=e6.pityStartY/C*DPR+e6.offset.y),e6.content.x=clamp(e6.content.x,e6.content.minX,e6.content.maxX),e6.content.y=clamp(e6.content.y,e6.content.minY,e6.content.maxY);let r=e.clientX/C,n=e.clientY/C;if(0===e6.touchButton&&e6.content.draggable){th(tr.DRAGGING);let d=e6.content.attached&&!e6.content.dragOnly&&!e6.content.noDampen;e6.content=e6.content.drag(e6.content,r,n),e6.content.dragLockX||(e6.content.x=(e6.pityStartX+td(l,d))/C+e6.offset.x),e6.content.dragLockY||(e6.content.y=(e6.pityStartY+td(i,d))/C+e6.offset.y),e6.content.x=clamp(e6.content.x,e6.content.minX,e6.content.maxX),e6.content.y=clamp(e6.content.y,e6.content.minY,e6.content.maxY),tr.DRAGGING.mousemove(e);return}if(2===e6.touchButton&&e6.content.rightDraggable){th(tr.DRAGGING);let h=e6.content.attached&&!e6.content.dragOnly&&!e6.content.noDampen;e6.content=e6.content.rightDrag(e6.content,r,n),e6.content.dragLockX||(e6.content.x=(e6.pityStartX+td(l,h))/C+e6.offset.x),e6.content.dragLockY||(e6.content.y=(e6.pityStartY+td(i,h))/C+e6.offset.y),e6.content.x=clamp(e6.content.x,e6.content.minX,e6.content.maxX),e6.content.y=clamp(e6.content.y,e6.content.minY,e6.content.maxY),tr.DRAGGING.mousemove(e);return}let s=tu(r,n);if(void 0!==s){s.grabbable?void 0!==s.cursor?th(tr.HOVER,s.cursor(s,tr.HOVER)):s.dragOnly?th(tr.HOVER,"move"):th(tr.HOVER):th(tr.FREE);return}let c=e.clientX*DPR,u=e.clientY*DPR;if(c>=state.view.left&&c<=state.view.right&&u>=state.view.top&&u<=state.view.bottom){th(tr.BRUSH);return}th(tr.FREE)},mouseup(e){if(0!==e6.touchButton)return;e6.clickContent.click(e6.clickContent),e6.clickContent.dx=0,e6.clickContent.dy=0,e6.clickContent=void 0,e6.content.attached&&(e6.content.x=e6.pityStartX/C*DPR+e6.offset.x,e6.content.y=e6.pityStartY/C*DPR+e6.offset.y),e6.content.dx=0,e6.content.dy=0,e6.content=void 0;let t=e.clientX/C,l=e.clientY/C,i=tu(t,l);void 0!==i?void 0!==i.cursor?th(tr.HOVER,i.cursor(i,tr.HOVER)):i.dragOnly?th(tr.HOVER,"move"):th(tr.HOVER):th(tr.FREE)},rightmouseup(e){if(2!==e6.touchButton)return;e6.clickContent.rightClick(e6.clickContent),e6.clickContent.dx=0,e6.clickContent.dy=0,e6.clickContent=void 0,e6.content.attached&&(e6.content.x=e6.pityStartX/C*DPR+e6.offset.x,e6.content.y=e6.pityStartY/C*DPR+e6.offset.y),e6.content.dx=0,e6.content.dy=0,e6.content=void 0;let t=e.clientX/C,l=e.clientY/C,i=tu(t,l);void 0!==i?void 0!==i.cursor?th(tr.HOVER,i.cursor(i,tr.HOVER)):i.dragOnly?th(tr.HOVER,"move"):th(tr.HOVER):th(tr.FREE)}},tr.DRAGGING={cursor:"move",mousemove(e){e6.hasStartedDragging||(e6.hasStartedDragging=!0,e6.content=e6.content.drag(e6.content,e.clientX/C*DPR,e.clientY/C*DPR));let t=e6.content.x,l=e6.content.y;e6.content.dragLockX||(e6.content.x=e.clientX/C*DPR+e6.offset.x),e6.content.dragLockY||(e6.content.y=e.clientY/C*DPR+e6.offset.y),e6.content.x=clamp(e6.content.x,e6.content.minX,e6.content.maxX),e6.content.y=clamp(e6.content.y,e6.content.minY,e6.content.maxY);let i=e6.content.x-t,o=e6.content.y-l;e6.content.move(e6.content,i,o)},mouseup(e){if(0!==e6.touchButton)return;e6.hasStartedDragging=!0,e6.content.dragLockX||(e6.content.dx=e6.velocity.x*HAND_RELEASE),e6.content.dragLockY||(e6.content.dy=e6.velocity.y*HAND_RELEASE),e6.content.drop(e6.content),void 0!==e6.content.highlightedAtom&&(e6.content.place(e6.content,e6.content.highlightedAtom),void 0!==e6.content.highlight&&(tb(e6.content,e6.content.highlight),e6.content.highlight=void 0)),e6.content=void 0;let t=e.clientX/C,l=e.clientY/C,i=tu(t,l);void 0!==i&&i.grabbable?void 0!==i.cursor?th(tr.HOVER,i.cursor(i,tr.HOVER)):i.dragOnly?th(tr.HOVER,"move"):th(tr.HOVER):th(tr.FREE)},rightmouseup(e){if(2!==e6.touchButton)return;e6.hasStartedDragging=!0,e6.content.dragLockX||(e6.content.dx=e6.velocity.x*HAND_RELEASE),e6.content.dragLockY||(e6.content.dy=e6.velocity.y*HAND_RELEASE),e6.content.drop(e6.content),void 0!==e6.content.highlightedAtom&&(e6.content.place(e6.content,e6.content.highlightedAtom),void 0!==e6.content.highlight&&(tb(e6.content,e6.content.highlight),e6.content.highlight=void 0)),e6.content=void 0;let t=e.clientX/C,l=e.clientY/C,i=tu(t,l);void 0!==i&&i.grabbable?void 0!==i.cursor?th(tr.HOVER,i.cursor(i,tr.HOVER)):i.dragOnly?th(tr.HOVER,"move"):th(tr.HOVER):th(tr.FREE)}};let th=(e,t=e.cursor)=>{void 0!==e6.content&&void 0!==e6.content.cursor&&(t=e6.content.cursor(e6.content,e)),e9.style.cursor=t,e6.state=e};on.mousemove(e=>e6.state.mousemove?e6.state.mousemove(e):void 0),on.mousedown(e=>{0===e.button&&e6.state.mousedown&&e6.state.mousedown(e),1===e.button&&e6.state.middlemousedown&&e6.state.middlemousedown(e),2===e.button&&e6.state.rightmousedown&&e6.state.rightmousedown(e)}),on.mouseup(e=>{0===e.button&&e6.state.mouseup&&e6.state.mouseup(e),1===e.button&&e6.state.middlemouseup&&e6.state.middlemouseup(e),2===e.button&&e6.state.rightmouseup&&e6.state.rightmouseup(e)}),e6.state=tr.FREE;let ts={x:0,y:0,grab:(e,t,l,i=e)=>i,touch:(e,t=e)=>t},tc=({grabbable:e=!0,draggable:t=!0,click:l=()=>{},rightClick:i=()=>{},drag:o=e=>e,rightDrag:a=e=>e,move:r=()=>{},drop:n=()=>{},draw:d=()=>{},update:h=()=>{},offscreen:s=()=>!1,overlaps:c=()=>!1,grab:u=e=>e,touch:g=e=>e,highlighter:$=!1,hover:p=()=>{},place:f=()=>{},x:v=0,y:m=0,dx:_=0,dy:w=0,maxX:b=1/0,minX:S=-1/0,maxY:R=1/0,minY:C=-1/0,size:T=40,colour:k=Colour.splash(999),children:E=[],parent:z=ts,width:O=T,height:D=T,construct:P=()=>{},hasInner:A=!0,...I}={},...B)=>{let L={highlighter:$,place:f,hover:p,hasInner:A,move:r,drop:n,maxX:b,minX:S,maxY:R,minY:C,update:h,construct:P,draggable:t,width:O,height:D,touch:g,parent:z,children:E,draw:d,grabbable:e,click:l,drag:o,overlaps:c,offscreen:s,grab:u,x:v,y:m,dx:_,dy:w,size:T,colour:k,rightClick:i,rightDrag:a,...I};return L.construct(L,...B),L},tu=(e,t)=>{e*=DPR,t*=DPR;for(let l=state.colourTode.atoms.length-1;l>=0;l--){let i=state.colourTode.atoms[l];if(i.justVisual)continue;let o=tv(i,e,t);if(void 0!==o)return o}},tg=e=>{for(let t of e.children)t.behindParent&&tg(t);for(let l of(e.behindChildren&&e.draw(e),e.children))l.behindParent||tg(l);e.behindChildren||e.draw(e)},t$=e=>{let t=state.colourTode.atoms.indexOf(e);state.colourTode.atoms.splice(t,1)},tp=e=>{state.colourTode.atoms.push(e)},tf=e=>{for(let t of e.children)if(!tf(t))return!1;return e.offscreen(e)},tv=(e,t,l)=>{if(!e.behindChildren&&e.overlaps(e,t,l))return e;for(let i=e.children.length-1;i>=0;i--){let o=e.children[i];if(o.behindParent)continue;let a=tv(o,t,l);if(a)return a}if(e.behindChildren&&e.overlaps(e,t,l))return e;for(let r=e.children.length-1;r>=0;r--){let n=e.children[r];if(!n.behindParent)continue;let d=tv(n,t,l);if(d)return d}},tm=(e,t,l)=>{let i=e,o=e.touch(e);if(o!==i){let a=o.touch(o,t,l,i);i=o,o=a}e6.clickContent=o;let r=e,n=e.grab(e,t,l);if(void 0===n)return;if(n!==r){let d=n.grab(n,t,l,r);r=n,n=d}e6.content=n;let{x:h,y:s}=tx(n,{forceAbsolute:!0});return e6.offset.x=h-t*DPR,e6.offset.y=s-l*DPR,n.dx=0,n.dy=0,e.stayAtBack?t_(n):ty(n),n},ty=e=>{if(e.parent===ts)t$(e),tp(e);else{let t=e.parent.children.indexOf(e);e.parent.children.splice(t,1),e.parent.children.push(e),e.parent.stayAtBack?t_(e.parent):ty(e.parent)}},t_=e=>{if(e.parent===ts){let t=state.colourTode.atoms.indexOf(e);state.colourTode.atoms.splice(t,1),state.colourTode.atoms.unshift(e)}else{let l=e.parent.children.indexOf(e);e.parent.children.splice(l,1),e.parent.children.unshift(e),e.parent.stayAtBack?t_(e.parent):ty(e.parent)}},tx=(e,{forceAbsolute:t=!1}={})=>{let{x:l,y:i}=e;if(t||void 0===e.parent||e.hasAbsolutePosition)return{x:l,y:i};let{x:o,y:a}=tx(e.parent);return{x:l+o,y:i+a}},tw=(e,t,{bottom:l=!1}={})=>{let i=tc(t);return l?e.children.unshift(i):e.children.push(i),i.parent=e,i},tb=(e,t,{quiet:l=!1}={})=>{let i=e.children.indexOf(t);if(-1===i){if(l)return;throw Error("Can't delete child of atom because I can't find it!")}e.children.splice(i,1),t.parent=ts},t0=(e,t)=>{if(void 0===t)throw Error("Can't give child because child is undefined");if(void 0===e)throw Error("Can't give child because parent is undefined");t$(t),t.stayAtBack||t.behindOtherChildren?e.children.unshift(t):e.children.push(t),t.parent=e},tS=(e,t)=>{if(e6.content===t){let{x:l,y:i}=tx(e);e6.offset.x+=l,e6.offset.y+=i}tb(e,t),tp(t)},tR=3;borderColours=PREBUILT_BORDER_COLOURS;let tC=borderColours.clone;tC[999]=Colour.splash(999);let tT={draw(e){let{x:t,y:l}=tx(e),i=Math.round(t),o=Math.round(l),a=Math.round(e.width),r=Math.round(e.height),n=Math.round(e.width/2);if(e.hasBorder){if(e.hasInner){let d=tR;void 0===e.borderColour?(eJ.fillStyle=Colour.splash(e.colour.splash),e.isTool?(eJ.fillStyle=Colour.splash(e.colour.splash),d*=1.5):e.width===e.height&&(d*=1.5)):eJ.fillStyle=e.borderColour,eJ.beginPath(),eJ.rect(i,o,a,r),void 0!==e.stamp&&eJ.arc(i+n,o+n,Math.round((ly.size-tX/2)/2),0,2*Math.PI),e.isGradient?eJ.putImageData(e.gradient,i*C,o*C):(eJ.fill("evenodd"),eJ.beginPath(),eJ.fillStyle=e.colour,eJ.rect(i+d,o+d,a-2*d,r-2*d),void 0!==e.stamp&&eJ.arc(i+n,o+n,Math.round((ly.size-tX/2)/2)+d,0,2*Math.PI),eJ.fill("evenodd"))}else void 0===e.borderColour?eJ.strokeStyle=borderColours[e.colour.splash]:eJ.strokeStyle=e.borderColour,i=Math.round(t+.5)-.5,o=Math.round(l+.5)-.5,eJ.lineWidth=tR,eJ.strokeRect(i,o,a,r)}else eJ.fillStyle=e.colour,eJ.fillRect(i,o,a,r)},offscreen(e){let{x:t,y:i}=tx(e),o=t+e.width,a=i+e.height;return!!(o<0)||!!(a<0)||!!(t>l.width)||!!(i>l.height)},overlaps(e,t,l){let{x:i,y:o}=tx(e),a=tR;(e.isTool||e.isSquare||e.isTallRectangle)&&(a*=1.5);let r=i+e.width,n=o+e.height;return!(tr)&&!(l>n)}},tk={draw(e){let{x:t,y:l}=tx(e),i=t+e.width/2,o=l+e.height/2,a=e.width/2;if(e.hasBorder){e.isTool&&(e.borderColour=tC[e.colour.splash]),eJ.fillStyle=void 0!==e.borderColour?e.borderColour:Colour.Void,eJ.beginPath(),eJ.arc(i,o,a,0,2*Math.PI),eJ.fill();let r=void 0!==e.borderScale?e.borderScale:1;a=e.width/2-1.5*tR*r}eJ.fillStyle=e.colour,eJ.beginPath(),eJ.arc(i,o,a,0,2*Math.PI),eJ.fill()},offscreen:tT.offscreen,overlaps:tT.overlaps},tE=(e,[t,l],i=!1)=>{for(let o of e.cellAtoms){i&&(o=o.slot);let{x:a,y:r}=tx(o);if(a===t&&r===l)return!0}return!1},tz=(e,[t,l],i=!1)=>{for(let o of e.cellAtoms){i&&(o=o.slot);let{x:a,y:r}=tx(o);if(a===t&&r===l&&(o.isLeftSlot||o.isSlot))return!0}return!1},tO=e=>{if("number"==typeof e)state.brush.colour=e,squareTool.toolbarNeedsColourUpdate=!0,squareTool.value=makeArrayFromSplash(e);else{let t=makeDiagramCell({content:e});state.brush.colour=makeDiagram({left:[t]}),squareTool.value=t.content,squareTool.toolbarNeedsColourUpdate=!0}},tD={isSquare:!0,hasBorder:!0,draw(e){!e.value.isDiagram&&tT.draw(e)},overlaps:tT.overlaps,offscreen:tT.offscreen,touch:e=>(tO(e.value),e),click(e){e.joins.length>0?e.parent!==ts&&e.parent.isPaddle||(e.joinExpanded?e.joinUnepxand(e):e.joinExpand(e)):e.value.isDiagram||(e.expanded?e.unexpand(e):e.parent!==ts&&e.parent.isPaddle||e.expand(e)),tO(e.value)},expand(e){e.expanded=!0,e.createPicker(e),e.value.channels.some(e=>void 0===e)&&lU("triangle")},unexpand(e){e.expanded=!1,e.redExpanded=e.red&&e.red.expanded,e.greenExpanded=e.green&&e.green.expanded,e.blueExpanded=e.blue&&e.blue.expanded,e.deletePicker(e)},createPicker(e){let t=tw(e,lk);t.width+=tX,e.pickerHandle=t,e.pickerHandle.behindParent=!0;let l=tw(e,t8);if(e.pickerPad=l,void 0!==e.value.channels[2]){if(void 0===e.value.channels[2].variable){let i=tw(e,tW);i.channelSlot="blue",i.x+=tH+3*(tD.size+tH),i.value=e.value.channels[2],i.needsColoursUpdate=!0,e.blue=i,i.deletedOptions=e.deletedBlueOptions,e.blueExpanded&&e.blue.click(e.blue),e.blue.attached=!0}else{let o=e.variableAtoms[2];o.behindOtherChildren=!1,tp(o),t0(e,o),o.variable="blue",o.x=(tH+tD.size)*3+(tD.size+tH)/2-o.width/3,o.y=e.height/2-o.height/2,o.attached=!0,e.blue=o}}if(void 0!==e.value.channels[1]){if(void 0===e.value.channels[1].variable){let a=tw(e,tW);a.channelSlot="green",a.x+=tH+2*(tD.size+tH),a.value=e.value.channels[1],a.needsColoursUpdate=!0,e.green=a,a.deletedOptions=e.deletedGreenOptions,e.greenExpanded&&e.green.click(e.green),e.green.attached=!0}else{let r=e.variableAtoms[1];r.behindOtherChildren=!1,tp(r),t0(e,r),r.variable="green",r.x=(tH+tD.size)*2+(tD.size+tH)/2-r.width/3,r.y=e.height/2-r.height/2,r.attached=!0,e.green=r}}if(void 0!==e.value.channels[0]){if(void 0===e.value.channels[0].variable){let n=tw(e,tW);n.channelSlot="red",n.x+=tH+tD.size+tH,n.value=e.value.channels[0],n.needsColoursUpdate=!0,e.red=n,n.deletedOptions=e.deletedRedOptions,e.redExpanded&&e.red.click(e.red),e.red.attached=!0}else{let d=e.variableAtoms[0];d.behindOtherChildren=!1,tp(d),t0(e,d),d.x=tH+tD.size+(tD.size+tH)/2-d.width/3,d.y=e.height/2-d.height/2,d.attached=!0,e.red=d}}},deletePicker(e){tb(e,e.pickerPad),tb(e,e.pickerHandle),e.red&&(e.deletedRedOptions=e.red.options,tb(e,e.red)),e.green&&(e.deletedGreenOptions=e.green.options,tb(e,e.green)),e.blue&&(e.deletedBlueOptions=e.blue.options,tb(e,e.blue))},receiveNumber(e,t,l=t.channel,{expanded:i,numberAtom:o}={}){if(e.redExpanded=e.red&&e.red.expanded,e.greenExpanded=e.green&&e.green.expanded,e.blueExpanded=e.blue&&e.blue.expanded,void 0===e.variableAtoms&&(e.variableAtoms=[void 0,void 0,void 0]),void 0!==t&&void 0!==t.variable?e.variableAtoms[l]=o:e.variableAtoms[l]=void 0,void 0!==i){let a=tj[l];e[`${a}Expanded`]=i}if(e.value.channels[l]=t,e.deletePicker(e),e.createPicker(e),e.needsColoursUpdate=!0,e.colourTicker=1/0,e.parent!==ts){let r=e.parent;lg(r)}let n=makeDiagramCell({content:e.value});state.brush.colour=makeDiagram({left:[n]}),squareTool.toolbarNeedsColourUpdate=!0,l7.toolbarNeedsColourUpdate=!0,lY.toolbarNeedsColourUpdate=!0,l3.toolbarNeedsColourUpdate=!0},construct(e){e.needsColoursUpdate=!0,"number"==typeof state.brush.colour?e.value=makeArrayFromSplash(state.brush.colour):e.value=Q(state.brush.colour.left[0].content),e.colourId=0,e.dcolourId=1,e.colourTicker=1/0,e.joins=[],e.joinColourIds=[],e.variableAtoms=[],e.gradient=new ImageData(e.width*C,e.height*C),e.headGradient=new ImageData(e.width*C,e.height*C)},updateGradient(e){let t=Q(e.value);if(t.joins=[],e.colours=M(t),e.isGradient=!0,e.joins.length>0&&!e.joinExpanded){let l=[];for(let i of e.joins)i.updateGradient(i),l.push(i.gradient);e.headGradient=tL({colours:e.colours,width:e.width*C,height:e.height*C,stamp:e.value.stamp,gradient:e.headGradient});let o=[e.headGradient,...l];e.gradient=tB({gradients:o,width:e.width*C,height:e.height*C,stamp:e.value.stamp,mergedGradient:e.gradient})}else e.gradient=tL({colours:e.colours,width:e.width*C,height:e.height*C,gradient:e.gradient,stamp:e.value.stamp})},update(e){if(e.value.isDiagram){if(void 0===e.multiAtoms||0===e.multiAtoms.length){e.multiAtoms=[];let t=e.value,[l,i]=eo(t),o=e.width/l,a=e.height/i;for(let r of t.left){let n=tw(e,tD);n.x=r.x*o,n.y=r.y*a,n.width=r.width*o,n.height=r.height*a,n.value=r.content,e.multiAtoms.push(n)}}}else e.needsColoursUpdate&&(e.updateGradient(e),e.needsColoursUpdate=!1);let{x:d,y:h}=tx(e);if(e.highlightedAtom=void 0,e6.content===e&&e6.state===tr.DRAGGING){let s=d,c=h,u=d+e.width,g=h+e.height;if(void 0!==e.highlight&&(tb(e,e.highlight),e.highlight=void 0),void 0===e.highlightedAtom){let $=lp();for(let p of $){if(p===e||!p.isSquare)continue;p.joins.length>0&&p.joinExpanded&&(p=p.pickerPad);let{x:f,y:v}=tx(p),m=f,_=f+p.width,w=v,b=v+p.height;if(!(s>_)&&!(ub)){if(p.isPicker)e.highlightedAtom=p.parent;else{if(p.parent!==ts)continue;e.highlightedAtom=p}e.highlight=tw(e,lS,{bottom:!0}),e.highlight.hasBorder=!0,e.highlight.hasInner=!1,e.highlight.width=p.width,e.highlight.height=p.height,e.highlight.x=f,e.highlight.y=v;break}}}if(void 0===e.highlightedAtom)for(let S of paddles){if(!S.expanded)continue;let{x:R,y:C}=tx(S),T=R,k=R+S.width,E=C,z=C+S.height;if(!(s>k)&&!(uz)&&!(gT+S.rightTriangle.x?(e.highlight=tw(e,lS,{bottom:!0}),e.highlight.hasBorder=!0,e.highlight.colour=Colour.Grey,e.highlight.x=P,e.highlight.y=A,e.highlight.width=S.dummyRight.width,e.highlight.height=S.dummyRight.height,e.highlightedSide="right",e.highlightedAtom=S):(e.highlight=tw(e,lS,{bottom:!0}),e.highlight.hasBorder=!0,e.highlight.colour=Colour.Grey,e.highlight.x=O,e.highlight.y=D,e.highlight.width=S.dummyLeft.width,e.highlight.height=S.dummyLeft.height,e.highlightedSide="left",e.highlightedAtom=S);break}if(void 0!==S.rightTriangle&&s>T+S.rightTriangle.x){let I=1/0,B,L;for(let G of S.cellAtoms){let N=G.slot,{x:U,y:Y}=tx(N),X=U,H=U+N.width,V=Y,j=Y+N.height,W=[X,V],q=[X-N.width,V],Z=[X,V-N.height],F=[H,V],M=[X,j],K=Math.hypot(d-W[0],h-W[1]);void 0===G.slotted&&tz(S,W,!0)&&Ke,t.grab=e=>e.parent,t.dragOnly=!0;let l=tw(e,t8);e.pickerHandle=l,l.width=lk.height,l.x=e.width/2-l.width/2,l.height=lk.width,l.y=e.height,l.touch=e=>e,l.grab=e=>e.parent,l.dragOnly=!0;for(let i=0;ie.parent}if(e.needsColoursUpdate=!0,e.colourTicker=1/0,void 0!==e.multiAtoms)for(let a of e.multiAtoms)ty(a);e.attached=!1},joinUnepxand(e){e.joinExpanded=!1,tb(e,e.pickerPad),tb(e,e.pickerHandle);for(let t=0;t0&&e.joinExpanded)return e;if(e.isJoiner){let t=e.parent.joins.indexOf(e);e.parent.joins.splice(t,1),e.parent.value.joins.splice(t,1),e.parent.joinUnepxand(e.parent),e.parent.joins.length>0&&e.parent.joinExpand(e.parent),tS(e.parent,e),e.isJoiner=!1,e.touch=tD.touch}if(e.attached){let l=e.parent;if(e.slottee){if(e.attached=!1,e.slottee=!1,tS(l,e),e.cellAtom.slotted=void 0,e.cellAtom.isLeftSlot){tb(l,e.cellAtom);let i=l.cellAtoms.indexOf(e.cellAtom);l.cellAtoms.splice(i,1)}return e.cellAtom=void 0,ln(l),e}let{x:o,y:a}=e;e.attached=!1,tS(l,e);let r=l.cellAtoms.indexOf(e);if(l.cellAtoms.splice(r,1),e.slot=void 0,void 0!==l.rightTriangle&&void 0!==e.slotted){let n=tw(l,la,{bottom:!0});n.x=o,n.y=a,n.isLeftSlot=!0,l.cellAtoms.push(n),n.isSlot=!1,n.slotted=e.slotted,n.slotted.cellAtom=n,e.slotted=void 0}ln(l)}return e},size:40,expanded:!1},tP=(e,t)=>{let l=.5,i=.5;return[[1,0],[1,.5],[1,1],[.5,0],[.5,.5],[.5,1],[0,0],[0,.5],[0,1],]},tA=(e,t,l)=>{let i=[];for(let[o,a]of l){let r=[o-e,a-t],n=Math.hypot(...r);i.push(n)}return i},tI=e=>{let t=[];for(let l of e)t.push(l**2);return t},tB=({gradients:e,width:t,height:l,mergedGradient:i=new ImageData(t,l),stamp:o})=>{[t,l]=[t,l].map(e=>Math.round(e));let a=t*l*4;i.data.length!==a&&(i=new ImageData(t,l));let r=e.length,n=2*Math.PI/r,d=-n/2-Math.PI/2;2===r&&(d-=Math.PI/4);let h=e.map((e,t)=>t*n+n),s=0;for(let c=0;c2*Math.PI;)p-=2*Math.PI;let f=0,v=!1,m=0;for(;p>h[f];)if(++f>=e.length){f=0;break}let _=h[f]-p,w=f-1<0?h.length-1:f-1,b=h[w],S=b-p,R=f+1>=h.length?0:f+1,C;.05>Math.abs(S)?(v=!0,m=-S/.05/2+.5,C=w):.05>Math.abs(_)?(v=!0,m=_/.05/2+.5,C=R):p<.05&&(v=!0,m=p/.05/2+.5,C=w),v?(i.data[s]=e[f].data[s]*m+e[C].data[s]*(1-m),i.data[s+1]=e[f].data[s+1]*m+e[C].data[s+1]*(1-m),i.data[s+2]=e[f].data[s+2]*m+e[C].data[s+2]*(1-m),i.data[s+3]=e[f].data[s+3]*m+e[C].data[s+3]*(1-m)):(i.data[s]=e[f].data[s],i.data[s+1]=e[f].data[s+1],i.data[s+2]=e[f].data[s+2],i.data[s+3]=e[f].data[s+3]),s+=4}return i},tL=({colours:e,width:t,height:l,gradient:i=new ImageData(t,l),stamp:o})=>{[t,l]=[t,l].map(e=>Math.round(e));let a=t*l*4;i.data.length!==a&&(i=new ImageData(t,l));let r=1/0,n=-1/0,d=1/0,h=-1/0,s=1/0,c=-1/0;for(let u of e){let[g,$,p]=getRGB(u);gn&&(n=g),$h&&(h=$),pc&&(c=p)}let f=(e,t,l)=>Colour.splash((1===e?n:r)+(1===t?h:d)+(1===l?c:s)),v=[f(0,0,1),f(0,0,1),f(0,0,1),f(0,1,1),f(1,0,0),f(1,0,0),f(0,1,0),f(0,1,0),f(1,0,0)],m=tP(t,l),_=0;for(let w=0;we+t);for(let k=0;k<9;k++){let E=R[k],z=v[k];[0,1,2].forEach(e=>C[e]+=E*z[e])}let O=C.map(e=>e/T);if("circle"===o&&b>=t/4&&b<3*t/4&&w>=l/4&&w<3*l/4?i.data[_+3]=0:(i.data[_]=O[0],i.data[_+1]=O[1],i.data[_+2]=O[2],i.data[_+3]=255),(_+=4)>=i.data.length)break}return i},tG={size:tD.size,width:tD.size*Math.sqrt(3)/2,draw(e){let{x:t,y:l}=tx(e),i=e.size;e.isTool&&(i-=2.5*tR),e.isTool||(i-=2);let o=i,a=i*Math.sqrt(3)/2,r=t,n=l+1;e.isTool&&(n+=1.25*tR);let d=n+o,h=n+o/2;eJ.fillStyle=e.colour;let s=new Path2D;s.moveTo(r,n),s.lineTo(r+a,h),s.lineTo(r,d),s.closePath(),eJ.fillStyle=e.colour,eJ.fill(s),e.hasBorder&&(eJ.lineWidth=1.5*tR,eJ.strokeStyle=e.borderColour,e.isTool&&(eJ.strokeStyle=tC[e.colour.splash]),eJ.stroke(s))},overlaps(e,t,l){let{x:i,y:o}=tx(e),a=e.size,r=e.size*Math.sqrt(3)/2,n=i,d=o;return!(tn+r)&&!(l>d+a)},offscreen(e){let{x:t,y:i}=tx(e),o=e.size,a=e.size*Math.sqrt(3)/2,r=t,n=i;return!!(r+a<0)||!!(n+o<0)||!!(r>l.width)||!!(n>l.height)}},tN={size:tD.size,draw(e){let{x:t,y:l}=tx(e),i=e.size,o=e.size*Math.sqrt(3)/2,a=e.size-o,r=t,n=l+a/2,d=n+o;eJ.fillStyle=e.colour;let h=new Path2D;h.moveTo(r,d),h.lineTo(r+i/2,n),h.lineTo(r+i,d),h.closePath(),eJ.fillStyle=e.colour,eJ.fill(h),e.hasBorder&&(eJ.lineWidth=1.5*tR,eJ.strokeStyle=e.borderColour,eJ.stroke(h))},overlaps(e,t,l){let{x:i,y:o}=tx(e),a=e.size,r=e.size*Math.sqrt(3)/2,n=i,d=o;return!(tn+a)&&!(l>d+r)},offscreen(e){let{x:t,y:i}=tx(e),o=e.size,a=e.size*Math.sqrt(3)/2,r=t,n=i;return!!(r+o<0)||!!(n+a<0)||!!(r>l.width)||!!(n>l.height)}},tU={size:tD.size,draw(e){let{x:t,y:l}=tx(e),i=e.size,o=e.size*Math.sqrt(3)/2,a=e.size-o,r=t,n=l+a/2;eJ.fillStyle=e.colour;let d=new Path2D;d.moveTo(r,n),d.lineTo(r+i/2,n+o),d.lineTo(r+i,n),d.closePath(),eJ.fillStyle=e.colour,eJ.fill(d),e.hasBorder&&(eJ.lineWidth=1.5*tR,eJ.strokeStyle=e.borderColour,eJ.stroke(d))},overlaps(e,t,l){let{x:i,y:o}=tx(e),a=e.size,r=e.size*Math.sqrt(3)/2,n=i,d=o;return!(tn+a)&&!(l>d+r)},offscreen(e){let{x:t,y:i}=tx(e),o=e.size,a=e.size*Math.sqrt(3)/2,r=t,n=i;return!!(r+o<0)||!!(n+a<0)||!!(r>l.width)||!!(n>l.height)}},t7={size:tD.size,width:tD.size*Math.sqrt(3)/2,draw(e){let{x:t,y:l}=tx(e),i=e.size;e.isTool&&(i-=2.5*tR),e.isTool||(i-=2);let o=i,a=i*Math.sqrt(3)/2,r=t,n=r+a,d=l+1;e.isTool&&(d+=1.25*tR);let h=d+o,s=d+o/2;eJ.fillStyle=e.colour;let c=new Path2D;c.moveTo(n,d),c.lineTo(r,s),c.lineTo(n,h),c.closePath(),eJ.fillStyle=e.colour,eJ.fill(c),e.hasBorder&&(eJ.lineWidth=1.5*tR,eJ.strokeStyle=e.borderColour,e.isTool&&(eJ.strokeStyle=tC[e.colour.splash]),eJ.stroke(c))},overlaps(e,t,l){let{x:i,y:o}=tx(e),a=e.size,r=e.size*Math.sqrt(3)/2,n=i,d=o;return!(tn+r)&&!(l>d+a)},offscreen(e){let{x:t,y:i}=tx(e),o=e.size,a=e.size*Math.sqrt(3)/2,r=t,n=i;return!!(r+a<0)||!!(n+o<0)||!!(r>l.width)||!!(n>l.height)}},tY={behindOtherChildren:!0,expanded:!1,draw(e){"right"===e.direction?tG.draw(e):"down"===e.direction?tU.draw(e):"up"===e.direction?tN.draw(e):"left"===e.direction?t7.draw(e):tG.draw(e)},colour:Colour.splash(999),overlaps:tG.overlaps,offscreen:tG.offscreen,size:tD.size,width:tG.width,direction:"right",click(e){if(e.parent.isPaddle){e.parent.pinhole.locked=!e.parent.pinhole.locked,lg(e.parent);return}e.expanded?e.unexpand(e):e.expand(e)},expand(e){e.pad=tw(e,lR),e.handle=tw(e,lC),e.expanded=!0,e.upPick=tw(e,lz),e.downPick=tw(e,lO),"up"===e.direction&&(e.upPick.value=!0),"down"===e.direction&&(e.downPick.value=!0)},unexpand(e){tb(e,e.pad),tb(e,e.handle),tb(e,e.upPick),tb(e,e.downPick),e.expanded=!1},highlighter:!0,hover(e){if(e.highlightedSlot=void 0,"right"===e.direction){let{x:t,y:l}=tx(e),i=t,o=l,a=t+e.width,r=l+e.height;for(let n of paddles){if(!n.expanded||n.pinhole.locked||void 0!==n.rightTriangle)continue;let{x:d,y:h}=tx(n),s=d,c=d+n.width,u=h,g=h+n.height;if(!(i>c)&&!(ag)&&!(rT)&&!(mE)&&!(_G||mU)continue;let Y=["red","green","blue"].filter(e=>void 0===A[e]);if(0===Y.length)continue;let{x:X,y:H}=tx(A);for(let V of Y){let j=tV[V],W=X+A.size+2*tX+j*(tD.size+tH),q=H+tX,Z=Math.hypot($-W,p-q);Ze.parent,colour:Colour.Grey,width:tH+3*(tD.size+tH),height:tD.size,y:0,x:tD.size+tH,dragOnly:!0,isPicker:!0},tV={red:0,green:1,blue:2},tj=["red","green","blue",],tW={hasBorder:!0,draw:tT.draw,overlaps:tT.overlaps,offscreen:tT.offscreen,width:tD.size,y:(tD.size-t3)/2,height:t3,grab:e=>e,rightDraggable:!0,rightDrag(e){let t=tc(tW);tp(t);let{x:l,y:i}=tx(e);return e6.offset.x-=e.x-l,e6.offset.y-=e.y-i,t.value=H(e.value),e.expanded&&(t.createOptions(t),t.expanded=!0),t},drag(e){if(e.parent.isSquare){let t=e.parent;t[e.channelSlot]=void 0;let l=tV[e.channelSlot];t.receiveNumber(t,void 0,l),tS(t,e),e.channelSlot=tj[e.value.channel],e.updateColours(e),e.attached=!1,e.needsColoursUpdate=!0,e.colourTicker=1/0}else if(e.parent.isTallRectangle){let i=e.parent;tS(i,e),i.operationAtoms[e.highlightedSlot]=void 0;let o="padTop"===e.highlightedSlot?"add":"subtract";if(i.value[o]=void 0,e.attached=!1,i.expanded)i.unexpand(i),i.expand(i);else{let a="padTop"===e.highlightedSlot?"handleTop":"handleBottom";tb(i,i[a],{quiet:!0}),tb(i,i[e.highlightedSlot],{quiet:!0}),i.expand(i),i.unexpand(i)}}else if(e.parent.isPaddle){let r=e.parent;r.chance=void 0,tS(r,e),ln(r)}return e},construct(e){let t=Random.Uint8%3;e.value=X({values:[!1,!1,!1,!1,!1,!1,!1,!1,!1,!0],channel:t}),e.needsColoursUpdate=!0,e.colourId=0,e.dcolourId=1,e.colourTicker=1/0,e.selectionBack=tw(e,tK);let l=tw(e,t5);e.selectionTop=l,e.selectionTop.isTop=!0,l.dragOnly=!1;let i=tw(e,t5);e.selectionBottom=i,e.selectionBottom.isTop=!1,i.dragOnly=!1,e.positionSelection(e)},positionSelection(e,t,l,i,o){if(e.expanded){let a=t1;e.selectionTop.y=l-e.selectionTop.height,e.selectionBottom.y=t+a-e.selectionBottom.height,e.selectionTop.minY=i-e.selectionTop.height,e.selectionTop.maxY=e.selectionBottom.y-a,e.selectionBottom.minY=e.selectionTop.y+a,e.selectionBottom.maxY=o-e.selectionBottom.height+a}else e.selectionTop.y=-e.selectionTop.height,e.selectionBottom.y=e.height;e.positionSelectionBack(e);let r=e.children.indexOf(e.selectionTop);e.children.splice(r,1),e.children.push(e.selectionTop);let n=e.children.indexOf(e.selectionBottom);if(e.children.splice(n,1),e.children.push(e.selectionBottom),e.parent.isTallRectangle){let d="padTop"===e.highlightedSlot?"add":"subtract";e.parent.value[d]=e.value}},positionSelectionBack(e){e.selectionBack.x=-t5.height,e.selectionBack.y=e.selectionTop.y,e.selectionBack.height=e.selectionBottom.y-e.selectionTop.y+e.selectionTop.height,e.selectionBack.width=e.width+2*t5.height},update(e){if(e.expanded&&e.needsColoursUpdate&&(e.needsColoursUpdate=!1,e.isGradient=!0,e.gradient=tL({colours:e.colours,width:e.width*C,height:e.height*C,gradient:e.gradient}),e.updateColours(e)),!e.expanded&&e.needsColoursUpdate){if(e.needsColoursUpdate=!1,e.parent.isSquare){let t=[];for(let l=0;l<3;l++)if(l===e.value.channel)t[l]=e.value;else{let i=[!0,!1,!1,!1,!1,!1,!1,!1,!1,!1];t[l]=X({values:i,channel:l})}let o=Z({channels:t});e.colours=M(o)}else{let a;for(let r=0;r<10;r++){let n=e.value.values[r];if(!1===n)continue;let d=makeArrayFromSplash(`${r}${r}${r}`);void 0===a?a=d:a.joins.push(d)}e.colours=M(a)}e.isGradient=!0,e.gradient=tL({colours:e.colours,width:e.width*C,height:e.height*C,gradient:e.gradient})}if(e.highlightedAtom=void 0,e6.content===e&&e6.state===tr.DRAGGING){let{x:h,y:s}=tx(e),c=h,u=s,g=h+e.width,$=s+e.height;void 0!==e.highlight&&(tb(e,e.highlight),e.highlight=void 0);let p=1/0,f,v,m=lp();for(let _ of m){let w=_;if(w.isTallRectangle){if(!w.expanded)continue;let b=["padTop","padBottom"];for(let S of b){let R=w;for(;R.isTallRectangle&&void 0!==R.operationAtoms[S];)R=R.operationAtoms[S];if(!R.isTallRectangle||!R.expanded)continue;let T=R[S],{x:k,y:E}=tx(T),z=k,O=k+T.width,D=E,P=E+T.height;if(!(c>O)&&!(gP)){e.highlightedSlot=S,e.highlightedAtom=T;break}}}else{if(!_.isSquare||!_.expanded)continue;let{x:A,y:I}=tx(_.pickerPad),B=A,L=A+_.pickerPad.width,G=I,N=I+_.pickerPad.height;if(c>L||gN)continue;let U=["red","green","blue"].filter(e=>void 0===_[e]);if(0===U.length)continue;let{x:Y,y:H}=tx(_);for(let V of U){let j=tV[V],W=Y+_.size+2*tX+j*(tD.size+tH),q=H+tX,F=Math.hypot(h-W,s-q);F0)for(let s=0;s<10;s++){let c=e.options[s];if(e.parent.isSquare)h.values[9-s]=!0,s>0&&(h.values[9-s+1]=!1);else for(let u of d)u.values[9-s]=!0,s>0&&(u.values[9-s+1]=!1);let g=Z({channels:d}),$=M(g);c.colours=$,c.colourTicker=1/0,c!==e&&(c.needsColoursUpdateCountdown=s,c.needsColoursUpdate=!1)}},getCenterId(e){let t,l;for(let i=0;i[e,t+.13397460000000005/2*o]);let r=[];for(let n=0;n<6;n++){let d=wrap(n+1,0,5),h=a[n],s=a[d],c=[0,1].map(e=>(h[e]+s[e])/2);r.push(c)}let u=[t+i/2,l+o/2],g=a.map((e,t)=>{let l=wrap(t+1,0,5),i=a[wrap(t+1+1,0,5)],o=wrap(t+1+1,0,5),n=r[o],d=r[l];return[u,d,i,n]}),[$,...p]=a,f=new Path2D;for(let v of(f.moveTo(...$),p))f.lineTo(...v);if(f.closePath(),eJ.fillStyle=e.colour,eJ.fill(f),void 0!==e.ons)for(let m=0;m<6;m++){if(!e.ons[m])continue;let[_,...w]=g[m],b=new Path2D;for(let S of(b.moveTo(..._),w))b.lineTo(...S);b.closePath(),eJ.fillStyle=Colour.Silver,eJ.fill(b),eJ.lineWidth=1/C,eJ.strokeStyle=Colour.Silver,eJ.stroke(b)}e.hasBorder&&(eJ.lineWidth=1.5*tR,eJ.strokeStyle=e.borderColour,eJ.stroke(f),e.parent.isSquare&&lP.drawY(e,e.size-8,4))},getValue(e){let t=0;for(let l of e.ons)l&&t++;return t},click(e){e.expanded?e.unexpand(e):e.expand(e)},unexpand(e){for(let t of(e.expanded=!1,e.handles))tb(e,t);for(let l of e.buttons)tb(e,l);e.handles=[],e.buttons=[]},expand(e){e.expanded=!0,e.handles=[],e.buttons=[];let{width:t,height:l}=e,i=.22373758200000007*t,o=[[t,l/2-tM.height/2],[t-i,l-tM.height/2],[i,l-tM.height/2],[0,l/2-tM.height/2],[i,0-tM.height/2],[t-i,0-tM.height/2],],a=[[t,l/2],[t-i,l],[i,l],[0,l/2],[i,0],[t-i,0],];a=a.map(([t,l],i)=>{let[o,a]=[t-e.width/2,l-e.height/2],[r,n]=[];return i%3==0?[r,n]=[2.2*o,2.2*a]:[r,n]=[2*o,2*a],[r+e.width/2,n+e.height/2]});for(let r=0;r<6;r++){let n=tw(e,tM);n.rotation=r,n.x=o[r][0]-tM.width/2,n.y=o[r][1],e.handles.push(n);let d=tw(e,tF);d.x=a[r][0]-tF.size/2,d.y=a[r][1]-tF.size/2,e.buttons.push(d),d.id=r,e.ons[r]&&(d.inner.selected=!0,d.inner.colour=Colour.Silver)}},construct(e){e.ons=[!1,!1,!1,!1,!1,!1]},updateValue(e){let t=tV[e.variable],l=!e.ons[1]&&!e.ons[0]&&!e.ons[5],i=!e.ons[2]&&!e.ons[3]&&!e.ons[4],o=!l&&!i,a=[l||o,e.ons[1],e.ons[0],e.ons[5],!1,!1,!1,!1,!1,!1],r=[i||o,e.ons[2],e.ons[3],e.ons[4],!1,!1,!1,!1,!1,!1],n=X({values:a}),d=X({values:r}),h=X({channel:t,variable:e.variable,add:n,subtract:d});e.value=h},hover(e){let{x:t,y:l}=tx(e),i=t,o=l,a=t+e.width,r=l+e.height;for(let n of paddles){let{x:d,y:h}=tx(n),s=d+n.width,c=h,u=h+n.height;if(void 0===n.chance&&n.expanded&&i<=s&&a>=s&&(oc||r>c&&r{let[a,r]=[e-l,t-i],n=Math.sqrt(a**2+r**2),d=Math.atan2(r,a),[h,s]=[n*Math.cos(o+d),n*Math.sin(o+d),];return[l+h,i+s]},tF={size:tD.size,offscreen:tk.offscreen,overlaps:tk.overlaps,colour:Colour.Grey,grab:e=>e.parent,behindChildren:!0,draw(e){tk.draw(e)},construct(e){e.inner=tw(e,t4,{bottom:!1}),e.inner.x=e.width/2-e.inner.width/2,e.inner.y=e.height/2-e.inner.height/2},click(e){e.inner.selected?(e.inner.selected=!1,e.inner.colour=Colour.Grey):(e.inner.selected=!0,e.inner.colour=Colour.Silver);let t=e.parent;if(t.ons[e.id]=e.inner.selected,t.parent.isPaddle){let l=t.parent;ln(l)}else if(t.parent.isSquare){let i=t.parent;t.updateValue(t);let o=tV[t.variable];i.receiveNumber(i,t.value,o,{expanded:t.expanded,numberAtom:t})}ty(e.parent)}},t4={size:2*tD.size/3,offscreen:tk.offscreen,overlaps:tk.overlaps,grab:e=>e.parent,touch:e=>e.parent,draw:tk.draw,hasBorder:!0,borderColour:Colour.Black,colour:Colour.Grey},tM={offscreen:tT.offscreen,overlaps(e,t,l){e.y-=e.height/2,e.height*=2;let i=tT.overlaps(e,t,l);return e.height/=2,e.y+=e.height/2,i},colour:Colour.Grey,rotation:0,touch:e=>e.parent,grab:e=>e.parent,x:50,width:tD.size/2+tD.size/4,height:tD.size/3,draw(e){let{x:t,y:l}=tx(e),{width:i,height:o}=e,a=new Path2D,r=[[t,l],[t+i,l],[t+i,l+o],[t,l+o],];e.rotation>0&&(r=r.map(a=>tZ(a,[t+i/2,l+o/2],e.rotation*Math.PI/3)));let[n,...d]=r;for(let h of(a.moveTo(...n),d))a.lineTo(...h);eJ.fillStyle=e.colour,eJ.fill(a)}},t5={draw(e){let{x:t,y:l}=tx(e),i=Math.round(e.width),o=Math.round(e.height);eJ.fillStyle=Colour.Grey,eJ.fillRect(Math.round(t),Math.round(l),i,o)},overlaps:tT.overlaps,offscreen:tT.offscreen,height:t1-t3,width:tD.size+2*tX,x:-tX,dragOnly:!0,grab:e=>e.parent.expanded?e:e.parent,touch:e=>e.parent.expanded?e:e.parent,cursor:e=>e.parent.expanded?"ns-resize":"pointer",move(e){e.parent.positionSelectionBack(e.parent)},drop(e){let t=Math.round((e.y+t3/2)/t1),l=e.parent.value,[i,o]=e.parent.getStartAndEndId(e.parent),a=e.parent.getCenterId(e.parent);e.isTop&&(o=a-t),e.isTop||(i=a-(t-1));let r=[!1,!1,!1,!1,!1,!1,!1,!1,!1,!1];for(let n=i;n<=o;n++)r[n]=!0;let d=X({channel:l.channel,values:r});if(e.parent.value=d,e.parent.deleteOptions(e.parent),e.parent.createOptions(e.parent),e.dx=0,e.dy=0,e.parent.parent.isSquare){let h=e.parent.parent,s=tV[e.parent.channelSlot];h.receiveNumber(h,d,s)}if(e.parent.parent.isPaddle){let c=e.parent.parent;ln(c)}},dragLockX:!0},tK={overlaps:tT.overlaps,offscreen:tT.offscreen,width:(tD.size-t3)/2,height:tD.size,grab:e=>e.parent,touch:e=>e.parent,dragLockX:!0,draw:tT.draw,colour:Colour.Grey},t6={draw:tT.draw,overlaps:tT.overlaps,offscreen:tT.offscreen,height:t3,width:tD.size,grab:e=>e.parent,hasBorder:!0,colourTicker:1/0,colours:[999],colourId:0,dcolourId:1,update(e){e.needsColoursUpdateCountdown>=0&&(e.needsColoursUpdateCountdown--,e.needsColoursUpdateCountdown<0&&(e.needsColoursUpdate=!0)),e.needsColoursUpdate&&(e.updateColours(e),e.needsColoursUpdateCountdown=-1,e.needsColoursUpdate=!1)},getId(e){let t=e.parent,l=t.getCenterId(t),i=e.y/t1;return l-i},updateColours(e){e.isGradient=!0,e.gradient=tL({colours:e.colours,width:e.width*C,height:e.height*C,gradient:e.gradient})},touch(e){let t=e.getId(e);return e.parent.value.values[t]?e.parent:e},click(e){let t=[!1,!1,!1,!1,!1,!1,!1,!1,!1,!1];t[e.value]=!0;let l=X({values:t,channel:e.parent.value.channel}),i=e.parent;if(i.value=l,i.deleteOptions(i),i.createOptions(i),i.needsColoursUpdate=!0,i.parent.isSquare){let o=i.parent,a=tV[i.channelSlot];o.receiveNumber(o,l,a)}if(i.parent.isPaddle){let r=i.parent;ln(r)}},construct(e){if(e.pityTop){let t=tw(e,lt);t.y=-t.height}if(e.pityBottom){let l=tw(e,lt);l.y=e.height}}},t9=["red","green","blue",],tJ={behindChildren:!0,highlighter:!0,rightDraggable:!0,rightDrag(e){let t=tc(tJ);tp(t);let{x:l,y:i}=tx(e);return e6.offset.x-=e.x-l,e6.offset.y-=e.y-i,t.variable=e.variable,e.expanded&&t.expand(t),t.updateAppearance(t),t},drag(e){if(e.parent.isSquare){let t=e.parent;t[e.channelSlot]=void 0;let l=tV[e.channelSlot];t.receiveNumber(t,void 0,l),tS(t,e),e.updateAppearance(e),e.attached=!1}else if(e.parent.isTallRectangle){let i=e.parent;tS(i,e),i.operationAtoms[e.highlightedSlot]=void 0;let o="padTop"===e.highlightedSlot?"add":"subtract";if(i.value[o]=void 0,e.expanded&&(e.unexpand(e),e.expand(e)),e.attached=!1,i.expanded)i.unexpand(i),i.expand(i);else{let a="padTop"===e.highlightedSlot?"handleTop":"handleBottom";tb(i,i[a],{quiet:!0}),tb(i,i[e.highlightedSlot],{quiet:!0}),i.expand(i),i.unexpand(i)}}return e},hover(e){let{x:t,y:l}=tx(e),i=t,o=l,a=t+e.width,r=l+e.height,n=1/0,d,h,s=lp();for(let c of s){if(c===e)continue;if(c.isTallRectangle){if(!c.expanded)continue;let u=["padTop","padBottom"];for(let g of u){let $=c;for(;$.isTallRectangle&&void 0!==$.operationAtoms[g];)$=$.operationAtoms[g];if(!$.isTallRectangle||!$.expanded)continue;let p=$[g],{x:f,y:v}=tx(p),m=f,_=f+p.width,w=v,b=v+p.height;if(!(i>_)&&!(ab))return e.highlightedSlot=g,p}continue}if(!c.isSquare||!c.expanded)continue;let{x:S,y:R}=tx(c.pickerPad),C=S,T=S+c.pickerPad.width,k=R,E=R+c.pickerPad.height;if(i>T||aE)continue;let z=["red","green","blue"].filter(e=>void 0===c[e]);if(0===z.length)continue;let{x:O,y:D}=tx(c);for(let P of z){let A=tV[P],I=O+c.size+2*tX+A*(tD.size+tH),B=D+tX,L=Math.hypot(t-I,l-B);Le)),s.lineTo(...[r+a,d].map(e=>e)),s.lineTo(...[h,n+o].map(e=>e)),s.lineTo(...[r,d].map(e=>e)),s.closePath(),eJ.fillStyle=e.colour,eJ.fill(s),e.hasBorder&&(eJ.lineWidth=tR,eJ.strokeStyle=e.borderColour,e.isTool&&(eJ.lineWidth=1.5*tR,eJ.strokeStyle=tC[e.colour.splash]),eJ.stroke(s))},offscreen:tT.offscreen,overlaps:tT.overlaps,hasBorder:!0,isTallRectangle:!0,size:t3+tX/3*2,height:t3+tX/3*2,width:t3+tX/3*2,construct(e){e.variable=t9[Random.Uint8%3],e.value=X({variable:e.variable}),e.updateAppearance(e),e.isTool||(e.width+=tR/2,e.height+=tR/2,e.size+=tR/2),e.operationAtoms={padTop:void 0,padBottom:void 0}},makeOperationAtoms(e){if(void 0!==e.value.add&&void 0===e.operationAtoms.padtop){if(void 0===e.value.add.variable){let t=tw(e,tW);t.value=e.value.add,e.operationAtoms.padTop=t,t.x=e.padTop.x+tX,t.y=e.padTop.y+e.padTop.height/2-t.height/2,t.highlightedSlot="padTop"}else{let l=tw(e,tJ);l.value=e.value.add,l.variable=e.value.add.variable,l.makeOperationAtoms(l),l.highlightedSlot="padTop",l.x=0,l.y=e.padTop.y+e.padTop.height/2-l.height/2,l.updateAppearance(l),e.operationAtoms.padTop=l}}if(void 0!==e.value.subtract&&void 0===e.operationAtoms.padBottom&&void 0===e.value.subtract.variable){let i=tw(e,tW);i.value=e.value.subtract,e.operationAtoms.padBottom=i,i.x=e.padBottom.x+tX,i.y=e.padBottom.y+e.padBottom.height/2-i.height/2,i.highlightedSlot="padBottom"}},updateAppearance(e){"red"===e.variable?e.colour=Colour.Red:"green"===e.variable?e.colour=Colour.Green:"blue"===e.variable&&(e.colour=Colour.Blue),e.borderColour=borderColours[e.colour.splash]},expanded:!1,click(e){e.expanded?e.unexpand(e):e.expand(e)},expand(e){for(let t of(e.expanded=!0,void 0===e.value.add&&(e.y<0||!(e.parent.isTallRectangle&&e.parent.operationAtoms.padBottom===e))&&(e.handleTop=tw(e,lk),e.handleTop.width=e.handleTop.height,e.handleTop.height*=2,e.handleTop.y=e.height/2-e.handleTop.height,e.handleTop.x=e.width/2-e.handleTop.width/2,e.handleTop.behindParent=!0,e.padTop=tw(e,lT),e.padTop.height=t8.height,e.padTop.width=tD.size+2*tH,e.padTop.x=e.width/2-e.padTop.width/2,e.padTop.y=-e.padTop.height-tX),void 0===e.value.subtract&&(e.y>0||!(e.parent.isTallRectangle&&e.parent.operationAtoms.padTop===e))&&(e.handleBottom=tw(e,lk),e.handleBottom.width=e.handleBottom.height,e.handleBottom.height*=2,e.handleBottom.y=e.height/2,e.handleBottom.x=e.width/2-e.handleBottom.width/2,e.handleBottom.behindParent=!0,e.padBottom=tw(e,lT),e.padBottom.height=t8.height,e.padBottom.width=tD.size+2*tH,e.padBottom.x=e.width/2-e.padBottom.width/2,e.padBottom.y=e.height+tX),e.handleRight=tw(e,lk),e.handleRight.y=e.height/2-e.handleRight.height/2,e.handleRight.x=e.width/2,e.handleRight.width*=2.5,e.handleRight.behindParent=!0,e.padRight=tw(e,lT),e.padRight.height=t8.height,e.padRight.width=tX+(e.width+tX/1.5)*3,e.padRight.y=e.height/2-e.padRight.height/2,e.padRight.x=e.width/2+(tD.size+2*tH)/2+tX,e.red=tw(e,tQ),e.red.x=e.padRight.x+tX/Math.SQRT2,e.red.borderColour=Colour.Red,e.red.colour=Colour.Black,e.red.value="red",e.green=tw(e,tQ),e.green.x=e.padRight.x+tX/Math.SQRT2+(e.green.width+tX)*1,e.green.borderColour=Colour.Green,e.green.colour=Colour.Black,e.green.value="green",e.blue=tw(e,tQ),e.blue.x=e.padRight.x+tX/Math.SQRT2+(e.blue.width+tX)*2,e.blue.borderColour=Colour.Blue,e.blue.colour=Colour.Black,e.blue.value="blue",e.winnerPin=tw(e,le),e.winnerPin.x=e[e.variable].x+e.winnerPin.width/2,e.winnerPin.y=e.winnerPin.height/2,e.winnerPin.colour=e[e.variable].borderColour,e.winnerPin.borderColour=e.winnerPin.colour,["padTop","padBottom"])){let l=e.operationAtoms[t];void 0!==l&&(tp(l),t0(e,l))}for(let i of e.children)i.isTallRectangle&&i.expanded&&(i.unexpand(i),i.expand(i))},unexpand(e){e.expanded=!1,tb(e,e.red),tb(e,e.green),tb(e,e.blue),tb(e,e.padRight),tb(e,e.handleRight),tb(e,e.winnerPin),void 0===e.value.add&&(tb(e,e.padTop,{quiet:!0}),tb(e,e.handleTop,{quiet:!0})),void 0===e.value.subtract&&(tb(e,e.padBottom,{quiet:!0}),tb(e,e.handleBottom,{quiet:!0}))}},tQ={draw(e){tJ.draw(e)},offscreen:tT.offscreen,overlaps:tT.overlaps,hasBorder:!0,size:t3+tX/3*2,height:t3+tX/3*2,width:t3+tX/3*2,grab:e=>e.parent,click(e){if(e.value===e.parent.variable)return;e.parent.variable=e.value,e.parent.value.variable=e.value,e.parent.winnerPin.x=e.x+e.parent.winnerPin.width/2,e.parent.winnerPin.colour=e.borderColour,e.parent.winnerPin.borderColour=e.borderColour,e.parent.updateAppearance(e.parent);let t=e.parent,l=t,i=t.parent;for(;!i.isSquare;){if(i===ts)return;l=i,i=i.parent}let o=0;"green"===l.channelSlot&&(o=1),"blue"===l.channelSlot&&(o=2);let a=i.variableAtoms[o];i.receiveNumber(i,a.value,o,{expanded:a.expanded,numberAtom:a})}},le={draw(e){tJ.draw(e)},offscreen:tT.offscreen,overlaps:tT.overlaps,hasBorder:!0,size:(t3+tX/3*2)/2,height:(t3+tX/3*2)/2,width:(t3+tX/3*2)/2,grab:e=>e.parent,touch:e=>e.parent},lt={draw(){},overlaps:tT.overlaps,offscreen:tT.offscreen,grab:e=>e.parent.parent,touch:e=>e.parent,colour:Colour.Grey,width:tD.size,height:t1-t3,y:0,x:0};paddles=[];let ll=tD.size/2,li={stayAtBack:!0,attached:!0,noDampen:!0,isPaddle:!0,behindChildren:!0,draw:tT.draw,overlaps:tT.overlaps,offscreen:tT.offscreen,colour:Colour.Grey,size:tD.size+4*tX,width:tD.size+4*tX,height:tD.size+4*tX,dragOnly:!0,dragLockY:!0,scroll:0,rightTriangle:void 0,x:Math.round(ll),y:tD.size+tX+ll,construct(e){e.cellAtoms=[],e.slots=[];let t=tw(e,ly);e.handle=t,e.setLimits(e),e.x=e.minX,e.expanded=!1,e.pinhole=tw(t,l_),e.dummyLeft=tw(e,la),e.dummyLeft.visible=!1,e.dummyRight=tw(e,la),e.dummyRight.visible=!1,ln(e)},setLimits(e){e.maxX=e.handle.width,e.minX=e.handle.width-e.width},drop(e){let t=e.maxX-e.x,l=e.x-e.minX;te,rightDraggable:!0,getColour(e){let t=e.cellAtoms;if(0===t.length){let l=Z({channels:[void 0,void 0,void 0]});return l}if(1===t.length){let i=Q(t[0].value);return i}let o=lu(t),a=makeDiagram({left:o});return el(a),a},rightDrag(e){let t=e.cellAtoms;if(0===t.length){let l=tc(tD);e6.offset.x=-l.width/2,e6.offset.y=-l.height/2;let i=Z({channels:[void 0,void 0,void 0]});return tO(i),tp(l),l.value=i,l.update(l),l}if(1===t.length){let o=Q(t[0].value),a=t[0].clone(t[0]);return e6.offset.x=-a.width/2,e6.offset.y=-a.height/2,tO(o),tp(a),a.value=o,a.update(a),a}let r=tc(tD);e6.offset.x=-r.width/2,e6.offset.y=-r.height/2;let n=lu(t),d=makeDiagram({left:n});return el(d),r.value=d,tp(r),tO(d),r.update(r),r}},lo=(e,t)=>{let l=new Path2D,[i,...o]=t;for(let a of(l.moveTo(...i.map(e=>Math.round(e))),o))l.lineTo(...a.map(e=>Math.round(e)));l.closePath(),eJ.fillStyle=e,eJ.fill(l)},la={visible:!0,isSlot:!0,behindChildren:!0,draw(e){if(!e.visible)return;let[t,l]=tx(e);e.width,e.height,eJ.fillStyle=e.colour;let i=e.width/3,o=e.width/3,a=t+e.width/2-i/2,r=l+e.height/2-o/2;eJ.fillRect(...[a,r,i,o].map(e=>Math.round(e)))},offscreen:tT.offscreen,overlaps:tT.overlaps,colour:Colour.Black,size:tD.size,grab:e=>e.parent,dragOnly:!0},lr=tD.size,ln=e=>{let t=li.width,l=li.size;if(e.cellAtoms.length>0){let i=1/0,o=-1/0,a=-1/0,r=1/0;for(let n of e.cellAtoms){let d=n.x,h=n.y,s=d,c=d+lr,u=h,g=h+lr;sa&&(a=c),uo&&(o=g)}let $=0,p=0,f=li.height/2-tD.size/2,v=li.width/2-tD.size/2,m=f,_=v;for(let w of(i!==m&&(o+=$=m-i),r!==_&&(a+=p=_-r),e.cellAtoms))w.y+=$,w.x+=p;let b=a+v,S=o+f;t=b,l=S}for(let R of(void 0!==e.rightTriangle&&(e.rightTriangle.x=t,e.rightTriangle.y=l/2-e.rightTriangle.height/2,t=t+t+e.rightTriangle.width),(e.hasSymmetry||void 0!==e.chance)&&(t+=lb.size/3),e.width=t,e.height=l,e.setLimits(e),e.slots))tb(e,R);if(e.slots=[],void 0!==e.rightTriangle)for(let C of e.cellAtoms){let T=tw(e,la,{bottom:!0});C.slot=T,e.slots.push(T),T.x=C.x+e.rightTriangle.x+e.rightTriangle.width,T.y=C.y,T.cellAtom=C,void 0!==C.slotted&&(C.slotted.x=C.x+e.rightTriangle.x+e.rightTriangle.width,C.slotted.y=C.y,T.colour=Colour.Grey)}void 0!==e.rightTriangle&&(void 0!==e.cellAtoms[0]&&void 0!==e.cellAtoms[0].slot?e.offset=e.cellAtoms[0].slot.x-e.cellAtoms[0].x:e.offset=0),void 0!==e.symmetryCircle&&(e.symmetryCircle.x=e.width-e.symmetryCircle.width/2,e.symmetryCircle.y=e.height/2-e.symmetryCircle.height/2),void 0!==e.chance&&(e.chance.x=e.width-e.chance.width/2,e.chance.y=e.height/2-e.chance.height/2),void 0!==e.chance&&void 0!==e.symmetryCircle&&(e.symmetryCircle.y-=e.symmetryCircle.height/2,e.chance.y+=e.symmetryCircle.height/2,e.height>100&&(e.symmetryCircle.y-=tX/2,e.chance.y+=tX/2)),e.handle.y=e.height/2-e.handle.height/2,0===e.cellAtoms.length&&(e.dummyLeft.x=ll,e.dummyLeft.y=e.height/2-e.dummyLeft.height/2,e.dummyRight.x=e.width-ll-e.dummyLeft.width,e.dummyRight.y=e.height/2-e.dummyRight.height/2),lg(e),lf()},ld=e=>{let t=F(e);return 1===t.size},lh=(e,t)=>{for(let l=0;l<3;l++){let i=e.channels[l],o=t.channels[l];if(void 0===i&&void 0!==o||void 0!==i&&void 0===o||(void 0!==i||void 0!==o)&&i.variable!==o.variable)return!1}let a=M(e),r=M(t);for(let n of a){let d=r.indexOf(n);if(-1===d)return!1;r.splice(d,1)}return!(r.length>0)},ls=(e,t)=>{if(t.stamp)return;let l=ld(t);if(!l){let i;for(let o=0;o{let t=[...e].sort((e,t)=>e.xt.x?1:e.yt.y?1:0);return t},lu=e=>{let t=lc(e),l=t[0],i=[];for(let o of e){let a=(o.x-l.x)/o.width,r=(o.y-l.y)/o.height,n=Q(o.value),d=makeDiagramCell({x:a,y:r,content:n});i.push(d)}return i},lg=e=>{if(!e.expanded)return;void 0!==e.rightTriangle&&(e.pinhole.locked?e.rightTriangle.colour=Colour.splash(999):e.rightTriangle.colour=Colour.splash(0));let t=DRAGON_TRANSFORMATIONS.NONE;if(e.hasSymmetry){let[l,i,o]=lw(e.symmetryCircle.value),a=l>0,r=i>0,n=o>0,d=`${r?"X":""}${a?"Y":""}${n?"R":""}`;""===d?d="NONE":("XR"===d||"YR"===d)&&(d="XYR"),t=DRAGON_TRANSFORMATIONS[d]}let h=lc(e.cellAtoms),s=h[0],c=[],u=[],g=[];for(let $ of h){let p=($.x-s.x)/$.width,f=($.y-s.y)/$.height;if($.isLeftSlot){let v=X({values:[!0,!0,!0,!0,!0,!0,!0,!0,!0,!0],channel:0}),m=X({values:[!0,!0,!0,!0,!0,!0,!0,!0,!0,!0],channel:1}),_=X({values:[!0,!0,!0,!0,!0,!0,!0,!0,!0,!0],channel:2}),w=Z({channels:[v,m,_]});ls(g,w);let b=makeDiagramCell({x:p,y:f,content:w});c.push(b)}else if($.value.isDiagram){let S=lc($.value.left);for(let R of S){let C=Q(R.content);ls(g,C);let T=p+R.x,k=f+R.y,E=makeDiagramCell({x:T,y:k,width:R.width,height:R.height,content:C});c.push(E)}}else{let z=Q($.value);ls(g,z);let O=makeDiagramCell({x:p,y:f,content:z});c.push(O)}if(!$.isLeftSlot&&$.value.isDiagram){let D=ei($.value),[P,A]=eo(D),I=makeDiagramCell({x:p,y:f,instruction:DRAGON_INSTRUCTION.merge,splitX:P,splitY:A});u.push(I)}let B=void 0===$.slotted?void 0:$.slotted.value;if(void 0===B){let L=makeDiagramCell({x:p,y:f,instruction:DRAGON_INSTRUCTION.nothing});u.push(L)}else if(B.isDiagram){let G=ei(B),[N,U]=eo(G),Y=makeDiagramCell({x:p,y:f,instruction:DRAGON_INSTRUCTION.split,splitX:N,splitY:U});u.push(Y);let H=lc(B.left);for(let V of H){let j=Q(V.content);ls(g,j);let W=p+V.x,q=f+V.y,F=makeDiagramCell({x:W,y:q,width:V.width,height:V.height,content:j,instruction:DRAGON_INSTRUCTION.recolour});u.push(F)}}else{let M=Q(B);ls(g,M);let K=makeDiagramCell({x:p,y:f,content:M});u.push(K)}}let J=ei(makeDiagram({left:c,right:u})),ee=e.pinhole.locked,et=void 0===e.chance?void 0:e.chance.getValue(e.chance),el=makeRule({steps:[J],transformations:t,locked:ee,chance:et});e.rule=el,void 0!==e.registry&&ed(e.registry),ee&&void 0!==e.rightTriangle&&(e.registry=registerRule(el))},l$=(e=state.colourTode.atoms)=>{let t=[...e];for(let l of t)t.push(...l$(l.children));return t},lp=()=>{let e=[...state.colourTode.atoms];for(let t of paddles)for(let l of t.children)!l.isPinhole&&(l.isPaddleHandle||e.push(l));for(let i of e)i.isSquare&&i.expanded&&e.push(...i.children);return e},lf=()=>{if(paddles.length>1&&lU("triangle"),paddles.length>2){let e=0;for(let t of paddles)void 0!==t.rightTriangle&&e++;e>=2&&lU("hexagon")}let l;for(let i of paddles){if(void 0===l){i.y=li.y+li.scroll,l=i;continue}i.y=l.y+l.height+ll,l=i}},lv=(e,t=paddles.indexOf(e))=>{paddles.splice(t,1),void 0!==e.registry&&ed(e.registry),t$(e),lf()},lm=()=>{let e=tc(li);return paddles.push(e),lf(),tp(e),e},ly={isPaddleHandle:!0,attached:!0,behindChildren:!0,draw:tT.draw,overlaps:tT.overlaps,offscreen:tT.offscreen,colour:Colour.Grey,size:li.x,x:-li.x,y:li.size/2-li.x/2,touch:e=>e.parent.pinhole,grab:e=>e.parent.pinhole},l_={isPinhole:!0,attached:!0,locked:!1,borderScale:.5,borderColour:Colour.Black,draw(e){e.locked?(e.hasBorder=!0,e.colour=Colour.Grey):(e.hasBorder=!1,e.colour=Colour.Black),tk.draw(e)},overlaps:tk.overlaps,offscreen:tk.offscreen,colour:Colour.Black,size:ly.size-tX/2,y:tX/2/2,x:tX/2/2,click(e){let t=e.parent,l=t.parent;if(e.locked)e.locked=!1,l.grabbable=!0,t.draggable=!0,l.draggable=!0,e.draggable=!0,lg(l);else{for(let i of(e.locked=!0,t.draggable=!1,e.draggable=!1,l.cellAtoms)){if(i.expanded&&i.unexpand(i),void 0!==i.slotted){let o=i.slotted;o.expanded&&o.unexpand(o)}i.joins.length>0&&i.joinExpanded&&i.joinUnepxand(i)}0===l.cellAtoms.length&&(l.grabbable=!1,l.draggable=!1),lg(l)}},grab:e=>e.parent.parent},lx=new Map;lx.set(0,DRAGON_TRANSFORMATIONS.NONE),lx.set(100,DRAGON_TRANSFORMATIONS.X),lx.set(10,DRAGON_TRANSFORMATIONS.Y),lx.set(110,DRAGON_TRANSFORMATIONS.XY),lx.set(1,DRAGON_TRANSFORMATIONS.R),lx.set(111,DRAGON_TRANSFORMATIONS.XYR),lx.set(101,DRAGON_TRANSFORMATIONS.XYR),lx.set(11,DRAGON_TRANSFORMATIONS.XYR);let lw=getRGB,lb={hasBorder:!0,draw(e){if(tk.draw(e),void 0===e.value)return;let[t,l,i]=lw(e.value);t>0&&lD.drawX(e),l>0&&lP.drawY(e),i>0&&lA.drawR(e)},offscreen:tk.offscreen,overlaps:tk.overlaps,expanded:!1,borderColour:Colour.Grey,colour:Colour.Black,value:0,click(e){e.expanded?e.unexpand(e):e.expand(e)},expand(e){e.pad=tw(e,lT),e.handle=tw(e,lk),e.handle.width+=tX,e.expanded=!0;let[t,l,i]=lw(e.value);e.xToggle=tw(e,lD),e.yToggle=tw(e,lP),e.rToggle=tw(e,lA),t>0&&(e.xToggle.value=!0),l>0&&(e.yToggle.value=!0),i>0&&(e.rToggle.value=!0)},unexpand(e){tb(e,e.pad),tb(e,e.handle),tb(e,e.xToggle),tb(e,e.yToggle),tb(e,e.rToggle),e.expanded=!1},size:tD.size,update(e){let{x:t,y:l}=tx(e),i=state.colourTode.atoms.indexOf(e),o=t,a=l,r=t+e.width,n=l+e.height;if(e6.content===e)for(let d of paddles){let h=state.colourTode.atoms.indexOf(d),{x:s,y:c}=tx(d),u=s+d.width,g=c,$=c+d.height;if(!d.hasSymmetry&&d.expanded&&i>h&&o<=u&&r>=u&&(a<$&&a>g||n>g&&n<$)){void 0!==e.highlightPaddle&&tb(e,e.highlightPaddle),e.highlightPaddle=tw(e,lS,{bottom:!0}),e.highlightPaddle.width=l0,e.highlightPaddle.height=d.height,e.highlightPaddle.y=g,e.highlightPaddle.x=u-l0/2,e.highlightedPaddle=d;return}}void 0!==e.highlightPaddle&&(tb(e,e.highlightPaddle),e.highlightPaddle=void 0,e.highlightedPaddle=void 0)},drop(e){if(!e.attached&&void 0!==e.highlightedPaddle){let t=e.highlightedPaddle;e.attached=!0,t0(t,e),t.hasSymmetry=!0,t.symmetryCircle=e,ln(t),e.dx=0,e.dy=0}},drag(e){if(e.attached){let t=e.parent;e.attached=!1,tS(t,e),t.hasSymmetry=!1,t.symmetryCircle=void 0,ln(t)}return e},rightDraggable:!0,rightDrag(e){let t=tc(lb);t.value=e.value;let{x:l,y:i}=tx(e);return e6.offset.x-=e.x-l,e6.offset.y-=e.y-i,t.x=l,t.y=i,tp(t),t}},l0=tR,lS={behindParent:!0,draw:tT.draw,offscreen:tT.offscreen,overlaps:tT.overlaps,draggable:!1,grabbable:!1,justVisual:!0,colour:Colour.splash(999),borderColour:Colour.splash(999),hasAbsolutePosition:!0,hasInner:!1},lR={draw:tT.draw,offscreen:tT.offscreen,overlaps:tT.overlaps,dragOnly:!0,width:lb.size,x:lb.size*Math.sqrt(3)/2+tX,height:2*lb.size-tX,y:-lb.size/2+tX/2,colour:Colour.Grey,grab:e=>e.parent},lC={draw:tT.draw,offscreen:tT.offscreen,overlaps:tT.overlaps,dragOnly:!0,width:lb.size/2+tX,x:lb.size/2,height:lb.size/3,y:lb.size/2-lb.size/3/2,colour:Colour.Grey,grab:e=>e.parent},lT={draw:tT.draw,offscreen:tT.offscreen,overlaps:tT.overlaps,dragOnly:!0,width:lb.size,x:lb.size+tX,height:3*lb.size-tX,y:-(3*lb.size)/3+tX/2,colour:Colour.Grey,grab:e=>e.parent},lk={draw:tT.draw,offscreen:tT.offscreen,overlaps:tT.overlaps,dragOnly:!0,width:lb.size/2,x:lb.size/2+lb.size/4,height:lb.size/3,y:lb.size/2-lb.size/3/2,colour:Colour.Grey,grab:e=>e.parent},lE=(e,t)=>{switch(t=!t,e){case"right":return t?"down":"up";case"down":return t?"left":"right";case"left":return t?"up":"down";case"up":return t?"right":"left"}throw Error("Invalid rotation or clockwiseness")},lz={hasBorder:!0,colour:Colour.Black,borderColour:Colour.Black,draw(e){tN.draw(e)},touch:e=>(e.colour=Colour.Silver,e),click(e){let t=e.parent;e.colour=Colour.Black,t.direction=lE(t.direction,!0),e.value=!0,t.updateValue(t);let l=t.parent;l.isSquare&&l.receiveNumber(l,t.value,t.channelId,{expanded:t.expanded,numberAtom:t})},offscreen:tN.offscreen,overlaps:tN.overlaps,value:!1,size:tD.size-1.5*tX,grab:e=>e.parent,x:lR.x+lR.width/2-(tD.size-1.5*tX)/2,y:lR.y+1.5*tX/2},lO={hasBorder:!0,colour:Colour.Black,borderColour:Colour.Black,draw(e){tU.draw(e)},touch:e=>(e.colour=Colour.Silver,e),click(e){let t=e.parent;e.colour=Colour.Black,t.direction=lE(t.direction,!1),e.value=!0,t.updateValue(t);let l=t.parent;l.isSquare&&l.receiveNumber(l,t.value,t.channelId,{expanded:t.expanded,numberAtom:t})},offscreen:tU.offscreen,overlaps:tU.overlaps,value:!1,size:tD.size-1.5*tX,grab:e=>e.parent,x:lR.x+lR.width/2-(tD.size-1.5*tX)/2,y:lR.y+lR.height-(tD.size-1.5*tX)-tX/2},lD={hasBorder:!0,borderColour:Colour.Black,colour:Colour.Grey,draw(e){e.colour=e.value?Colour.Silver:Colour.Grey,tk.draw(e),e.drawX(e)},drawX(e){let{x:t,y:l}=tx(e),i=e.size,o=l+e.size/2-1*tR/2;eJ.fillStyle=e.borderColour,eJ.fillRect(t,o,i,1*tR)},offscreen:tk.offscreen,overlaps:tk.overlaps,expanded:!1,click(e){e.value=!e.value;let[t,l,i]=lw(e.parent.value);t=e.value?100:0,e.parent.value=t+l+i;let o=e.parent;if(o.parent!==ts){let a=o.parent;lg(a)}},value:!1,size:tD.size-tX,grab:e=>e.parent,x:lT.x+lT.width/2-(tD.size-tX)/2,y:lT.y+tX/2},lP={hasBorder:!0,borderColour:Colour.Black,colour:Colour.Grey,draw(e){e.colour=e.value?Colour.Silver:Colour.Grey,tk.draw(e),e.drawY(e)},drawY(e,t=e.size,l=0){let{x:i,y:o}=tx(e),a=i+e.size/2-1*tR/2;eJ.fillStyle=e.borderColour,eJ.fillRect(a,o+l,1*tR,t)},offscreen:tk.offscreen,overlaps:tk.overlaps,expanded:!1,click(e){e.value=!e.value;let[t,l,i]=lw(e.parent.value);l=e.value?10:0,e.parent.value=t+l+i;let o=e.parent;if(o.parent!==ts){let a=o.parent;lg(a)}},value:!1,size:tD.size-tX,grab:e=>e.parent,x:lT.x+lT.width/2-(tD.size-tX)/2,y:tX/2},lA={hasBorder:!0,borderColour:Colour.Black,colour:Colour.Grey,draw(e){e.colour=e.value?Colour.Silver:Colour.Grey,tk.draw(e),e.drawR(e)},drawR(e){let{x:t,y:l}=tx(e),i=t+e.size/2,o=l+e.size/2,a=e.size/2-3*tR;eJ.fillStyle=e.borderColour,eJ.beginPath(),eJ.arc(i,o,a,0,2*Math.PI),eJ.fill(),a-=tR,eJ.fillStyle=e.colour,eJ.beginPath(),eJ.arc(i,o,a,0,2*Math.PI),eJ.fill()},offscreen:tk.offscreen,overlaps:tk.overlaps,expanded:!1,click(e){e.value=!e.value;let[t,l,i]=lw(e.parent.value);i=e.value?1:0,e.parent.value=t+l+i;let o=e.parent;if(o.parent!==ts){let a=o.parent;lg(a)}},value:!1,size:tD.size-tX,grab:e=>e.parent,x:lT.x+lT.width/2-(tD.size-tX)/2,y:lT.y+lT.height-(tD.size-tX)-tX/2},lI=e=>{let t=tc({...tD});if(t.value=Q(e),void 0!==t.value){if(void 0!==t.value.joins)for(let l of t.value.joins){let i=lI(l);t.joins.push(i)}t.stamp=t.value.stamp}if(!t.value.isDiagram)for(let o=0;o<3;o++){let a=t.value.channels[o];if(void 0===a||void 0===a.variable)continue;let r=tc(tY);t.variableAtoms[o]=r,r.highlightedSlot=tj[o],r.channelId=o;let n=o-1<0?tj[2]:tj[o-1],d=o+1>2?tj[0]:tj[o+1];a.subtract?r.direction="down":a.add?r.direction="up":a.variable===n?r.direction="left":a.variable===d&&(r.direction="right"),r.updateValue(r)}return void 0!==t.value&&t.value.isDiagram&&t.update(t),t},lB=10,lL={element:tD,draw(e){(e.previousBrushColour!==state.brush.colour||e.toolbarNeedsColourUpdate)&&e.update(e),e.unlocked&&e.element.draw(e)},overlaps:(e,t,l)=>e.element.overlaps(e,t,l),grab:(e,t,l)=>e,drag(e){if(e===squareTool){let t=lI(e.value);return tp(t),t}let l=tc({...e.element,x:e.x,y:e.y});return tp(l),l.value,l},cursor:()=>"move"},lG=0,lN=(e,t)=>{let{width:l=tD.size,height:i=tD.size,size:o}=e,a=tH;i{let t=unlocks[e];t.unlocked||(t.unlocked=!0,t.grabbable=!0)};squareTool=lN(tD),lB+=tR;let l7=lN(tY,"triangle");lB-=tR;let lY=lN(lb,"circle"),lX=lN(t2,"hexagon"),l3={};lm(),squareTool.value=makeArrayFromSplash(state.brush.colour),lY.borderScale=1,squareTool.update=e=>{if(void 0===e.joinDrawId&&(e.joinDrawId=-1,e.joinDrawTimer=0),void 0!==e.value&&e===squareTool&&(e.previousBrushColour!==state.brush.colour||e.toolbarNeedsColourUpdate)){for(let t of(e.previousBrushColour=state.brush.colour,void 0===e.multiAtoms&&(e.multiAtoms=[]),e.multiAtoms))tb(e,t);if(e.multiAtoms=[],e.value.isDiagram){let l=e.value,[i,o]=eo(l),a=e.width/i,r=e.height/o;for(let n of l.left){let d=tw(e,tD);d.x=n.x*a,d.y=n.y*r,d.width=n.width*a,d.height=n.height*r,d.value=n.content,d.update(d),e.multiAtoms.push(d)}}}let h=Q(e.value);if(e.colours=M(h),e.colourId>=e.colours.length&&(e.colourId=0),e.toolbarNeedsColourUpdate&&e===squareTool){for(let s of(e.toolbarNeedsColourUpdate=!1,e.isGradient=!0,e.joins=[],e.value.joins)){let c=lI(s);e.joins.push(c)}tD.updateGradient(e)}else e.colour=Colour.splash(999),e.borderColour=Colour.splash(999)},l7.update=squareTool.update,lY.update=squareTool.update,l3.update=squareTool.update,lX.update=squareTool.update,on.keydown(e=>{(e.ctrlKey||e.metaKey)&&"s"===e.key?(e.preventDefault(),lW()):(e.ctrlKey||e.metaKey)&&"o"===e.key?(e.preventDefault(),lq()):(e.ctrlKey||e.metaKey)&&"c"===e.key&&(e.preventDefault(),l2())},{passive:!1}),on.paste(async e=>{let t=e.clipboardData.getData("text");if(""!==t){lj(t);return}let l=e.clipboardData.items[0],i=l.getAsFile(),o=await i.text();lj(o)}),on.dragover(e=>{e.stopPropagation(),e.preventDefault()},{passive:!1}),on.drop(async e=>{e.stopPropagation(),e.preventDefault();let t=e.dataTransfer.items[0],l=t.getAsFile(),i=await l.text();lj(i)},{passive:!1});let l1={},lH={};l1.cellAtoms=(e,t)=>{let l=[];for(let i of t)l.push({isLeftSlot:i.isLeftSlot,value:i.value,x:i.x,y:i.y,slotted:i.slotted?i.slotted.value:void 0});return l};let l8=!1;lH.cellAtoms=(e,t)=>{let l=[];for(let i of t){l8||(i.isLeftSlot||tO(i.value),l8=!0);let o=i.isLeftSlot?tc(la):lI(i.value);if(o.isLeftSlot=i.isLeftSlot,tp(o),t0(e,o),o.attached=!0,o.x=i.x,o.y=i.y,o.highlightedSide="left",l.push(o),void 0!==i.slotted){let a=lI(i.slotted);tp(a),t0(e,a),a.attached=!0,a.cellAtom=o,a.highlightedSide="slot",a.slottee=!0,o.slotted=a}}return l},l1.symmetryCircle=(e,t)=>{if(void 0!==t)return t.value},lH.symmetryCircle=(e,t)=>{let l=tw(e,lb);return l.value=t,l},l1.chance=(e,t)=>{if(void 0!==t)return t.ons},lH.chance=(e,t)=>{let l=tw(e,t2);return l.ons=t,l};l1.expanded=(e,t)=>t,l1.x=(e,t)=>t,l1.y=(e,t)=>t,l1.width=(e,t)=>t,l1.height=(e,t)=>t,l1.hasSymmetry=(e,t)=>t,lH.expanded=(e,t)=>t,lH.x=(e,t)=>t,lH.y=(e,t)=>t,lH.width=(e,t)=>t,lH.height=(e,t)=>t,lH.hasSymmetry=(e,t)=>t,l1.pinhole=(e,t)=>t.locked,lH.pinhole=(e,t)=>(e.pinhole.locked=t,e.pinhole),l1.rightTriangle=(e,t)=>void 0!==t,lH.rightTriangle=(e,t)=>{if(!t)return;let l=tw(e,tY);return l};let lV=()=>{let e=[];for(let t of paddles){let l={};for(let i in t){let o=l1[i];if(void 0===o)continue;let a=o(t,t[i]);void 0!==a&&(l[i]=a)}e.push(l)}return JSON.stringify(e)},lj=e=>{if(middleClicked){middleClicked=!1;return}l8=!1,lU("triangle"),lU("circle"),lU("hexagon");try{for(;paddles.length>0;)lv(paddles[paddles.length-1]);for(let t of JSON.parse(e)){let l=lm();for(let i in t){let o=lH[i];if(void 0===o)continue;let a=o(l,t[i]);void 0!==a&&(l[i]=a)}ln(l),lg(l)}lf()}catch(r){console.error(r),alert("Error loading rules... Sorry! Please contact @todepond :)")}},lW=async()=>{let e=lV(paddles);if(window.showSaveFilePicker)try{let t=await showSaveFilePicker({excludeAcceptAllOption:!0,suggestedName:"spell",startIn:"downloads",types:[{description:"JSON",accept:{"application/json":[".json"]}}]}),l=await t.createWritable();await l.write(e),await l.close()}catch(i){console.error("Failed to save file:",i)}else{let o=new Blob([e],{type:"application/json"}),a=URL.createObjectURL(o),r=document.createElement("a");r.href=a,r.download="spell.json",r.click(),URL.revokeObjectURL(a)}},lq=()=>{let e=document.createElement("input");e.type="file",e.onchange=async t=>{let l=e.files[0],i=await l.text();lj(i),Keyboard.Control=!1},e.click(),Keyboard.Control=!1},l2=()=>{let e=lV(paddles);print(e),navigator.clipboard.writeText(e)}});