From 9e0310b4f58780da20625674c2a07f73ce8d6cf6 Mon Sep 17 00:00:00 2001 From: Hannah Bergam Date: Thu, 17 Jun 2021 00:58:58 +0000 Subject: [PATCH 01/14] initial mostly raw copy of drafted algo --- src/neighborhoodDrawer.js | 349 ++++++++++++++------------------------ 1 file changed, 127 insertions(+), 222 deletions(-) diff --git a/src/neighborhoodDrawer.js b/src/neighborhoodDrawer.js index 5aea97d..a337dab 100644 --- a/src/neighborhoodDrawer.js +++ b/src/neighborhoodDrawer.js @@ -2,65 +2,6 @@ const { SVG_NS } = require("./drawer"); const Drawer = require("./drawer"); const tiles = require("./tiles"); -const ROTATE180 = "rotate(180)"; -const ROTATENEG90 = "rotate(-90)"; -const ROTATE90 = "rotate(90)"; -const ROTATE0 = "rotate(0)"; -const CUT = "cut"; -const PIE = "pie"; - -/** - * This is a helper for creating SVG Elements. - * Groups are created by grid tile, under which paths are nested. These groups - * begin with "g" in the id. By checking for this when determining its position - * within the hierarchy, we can nest these groups just before the pegman, - * ensuring the pegman will appear on top of the paint. - * - * @param tag representing the element type, 'g' for group, 'path' for paths - * @param props representing the details of the element - * @param parent the parent it should be nested under - * @param id the unique identifier, beginning with 'g' if a group element - * @returns the element itself - */ -function svgElement(tag, props, parent, id) { - var node = document.getElementById(id); - if (!node) { - node = document.createElementNS(SVG_NS, tag); - node.setAttribute("id", id); - } - Object.keys(props).map(function (key) { - node.setAttribute(key, props[key]); - }); - if (parent && id.startsWith("g")) { - let pegmanElement = parent.getElementsByClassName("pegman-location")[0]; - parent.insertBefore(node, pegmanElement); - } else if (parent) { - parent.appendChild(node); - } - return node; -} - -// Path drawing a quarter circle -// --+ -// / | -// +---+ -function quarterCircle(size) { - let halfSize = size / 2; - let quarterSize = size / 4; - return `m${halfSize} ${halfSize}h-${halfSize}c0-${quarterSize} ${quarterSize}-${halfSize} ${halfSize}-${halfSize}z`; -} - -// Path of the the slice of a square remaining once a quarter circle is -// removed from it -// +----+ -// | / -// + -function cutout(size) { - let halfSize = size / 2; - let quarterSize = size / 4; - return `m0 0v${halfSize}c0-${quarterSize} ${quarterSize}-${halfSize} ${halfSize}-${halfSize}z`; -} - /** * This drawer hosts all paint glomming logic. * A note on layering paint: If paint is applied on top of existing paint @@ -152,105 +93,6 @@ module.exports = class NeighborhoodDrawer extends Drawer { return this.map_.getCell(row, col).getColor() || null; } - // Helper method for determining color and path based on neighbors - pathCalculator( - subjectCell, - adjacent1, - adjacent2, - diagonal, - transform, - grid, - id - ) { - let pie = quarterCircle(this.squareSize); - let cutOut = cutout(this.squareSize); - let tag = "path"; - - // Add a quarter circle to the top left corner of the block if there is - // a color value there - if (subjectCell) { - svgElement( - tag, - { - d: pie, - stroke: subjectCell, - transform: transform, - fill: subjectCell, - }, - grid, - `${id}-${PIE}` - ); - } - // Add the cutout if the top left corner has a color and an adjacent cell - // shares that color, filling in the top left quadrant of the block entirely - if ( - subjectCell && - (subjectCell === adjacent1 || subjectCell === adjacent2) - ) { - svgElement( - tag, - { - d: cutOut, - stroke: subjectCell, - transform: transform, - fill: subjectCell, - }, - grid, - `${id}-${CUT}` - ); - } - // Otherwise, if the two adjacent corners have the same color, add the - // cutout shape with that color - else if ( - adjacent1 && - adjacent1 === adjacent2 && - (!diagonal || !subjectCell || subjectCell !== diagonal) - ) { - svgElement( - tag, - { d: cutOut, stroke: adjacent1, transform: transform, fill: adjacent1 }, - grid, - `${id}-${CUT}` - ); - } - // Fill in center corner only if an adjacent cell has the same color, or if - // the diagonal cell is same color and either adjacent is empty - // Note: this handles the "clover case", where we want each - // cell to "pop" out with its own color if diagonals are matching - else if ( - subjectCell && - (adjacent1 === subjectCell || - adjacent2 === subjectCell || - (diagonal === subjectCell && - (!adjacent1 || !adjacent2 || adjacent1 !== adjacent2))) - ) { - svgElement( - tag, - { - d: cutOut, - stroke: subjectCell, - transform: transform, - fill: subjectCell, - }, - grid, - `${id}-${CUT}` - ); - } - } - - makeGrid(row, col, svg) { - let id = "g" + row + "." + col; - return svgElement( - "g", - { - transform: `translate(${col * this.squareSize + this.squareSize}, - ${row * this.squareSize + this.squareSize})`, - }, - svg, - id - ); - } - /** * @override * Draw the given tile at row, col @@ -319,73 +161,136 @@ module.exports = class NeighborhoodDrawer extends Drawer { ); } - // Because this processes a grid of cells at a time, we start at -1 to allow for - // a 'padding' row and column with no color. - for (let row = -1; row < this.map_.ROWS; row++) { - for (let col = -1; col < this.map_.COLS; col++) { - /** - * In a grid of four cells: top left, top right, bottom left, bottom right - * So if we are painting cell 0, adjacent cells are 1 & 2, diagonal is 3 - * +-------+ - * | 0 | 1 | - * -------- - * | 2 | 3 | - * +-------+ - */ - let cells = [ - this.cellColor(row, col), - this.cellColor(row, col + 1), - this.cellColor(row + 1, col), - this.cellColor(row + 1, col + 1), - ]; + // If necessary, create padded rows with no color + /* + 0 1 2 + 3 4 5 + 6 7 8 + */ + let colors = [ + this.cellColor(row - 1, col - 1), // Top left + this.cellColor(row, col - 1), // Top + this.cellColor(row + 1, col - 1), // Top right + this.cellColor(row - 1, col), // Middle left + this.cellColor(row, col), // Middle + this.cellColor(row + 1, col), // Middle right + this.cellColor(row - 1, col + 1), // Bottom left + this.cellColor(row, col + 1), // Bottom + this.cellColor(row + 1, col + 1), // Bottom right + ]; + + const canvas = document.createElement("canvas"); + const context = canvas.getContext("2d"); + + if (colors[1] && colors[1] == colors[3]) { + context.beginPath(); + context.fillStyle = colors[1]; + context.moveTo(x * SIZE, y * SIZE); + if (colors[0] == colors[3]) { + context.lineTo((x + 0.4) * SIZE, y * SIZE); + context.lineTo(x * SIZE, (y + 0.4) * SIZE); + } else { + context.lineTo((x + 1) * SIZE, y * SIZE); + context.lineTo(x * SIZE, (y + 1) * SIZE); + } + context.fill(); + } - if (cells[0] || cells[1] || cells[2] || cells[3]) { - // Create grid block group - let grid = this.makeGrid(row, col, this.svg_); - let id0 = row + "." + col + "." + ROTATE180; - let id1 = row + "." + col + "." + ROTATENEG90; - let id2 = row + "." + col + "." + ROTATE90; - let id3 = row + "." + col + "." + ROTATE0; + if (colors[1] && colors[1] == colors[5]) { + context.beginPath(); + context.fillStyle = colors[1]; + context.moveTo((x + 1) * SIZE, y * SIZE); + if (colors[2] == colors[1]) { + context.lineTo((x + 0.6) * SIZE, y * SIZE); + context.lineTo((x + 1) * SIZE, (y + 0.4) * SIZE); + } else { + context.lineTo(x * SIZE, y * SIZE); + context.lineTo((x + 1) * SIZE, (y + 1) * SIZE); + } + context.fill(); + } - // Calculate all the svg paths based on neighboring cell colors - this.pathCalculator( - cells[0], - cells[1], - cells[2], - cells[3], - ROTATE180, - grid, - id0 - ); - this.pathCalculator( - cells[1], - cells[0], - cells[3], - cells[2], - ROTATENEG90, - grid, - id1 - ); - this.pathCalculator( - cells[2], - cells[0], - cells[3], - cells[1], - ROTATE90, - grid, - id2 - ); - this.pathCalculator( - cells[3], - cells[1], - cells[2], - cells[0], - ROTATE0, - grid, - id3 - ); - } + if (colors[7] && colors[7] == colors[3]) { + context.beginPath(); + context.fillStyle = colors[7]; + context.moveTo(x * SIZE, (y + 1) * SIZE); + if (colors[6] == colors[7]) { + context.lineTo((x + 0.4) * SIZE, (y + 1) * SIZE); + context.lineTo(x * SIZE, (y + 0.6) * SIZE); + } else { + context.lineTo(x * SIZE, y * SIZE); + context.lineTo((x + 1) * SIZE, (y + 1) * SIZE); } + context.fill(); + } + + if (colors[7] && colors[7] == colors[5]) { + context.beginPath(); + context.fillStyle = colors[7]; + context.moveTo((x + 1) * SIZE, (y + 1) * SIZE); + if (colors[8] == colors[7]) { + context.lineTo((x + 1) * SIZE, (y + 0.6) * SIZE); + context.lineTo((x + 0.6) * SIZE, (y + 1) * SIZE); + } else { + context.lineTo((x + 1) * SIZE, y * SIZE); + context.lineTo(x * SIZE, (y + 1) * SIZE); + } + context.fill(); + } + + if (colors[4]) { + context.beginPath(); + context.fillStyle = colors[4]; + if ( + colors[4] == colors[5] && + colors[4] == colors[7] && + colors[4] != colors[1] && + colors[4] != colors[3] + ) { + context.moveTo(x * SIZE, (y + 0.4) * SIZE); + context.lineTo((x + 0.4) * SIZE, y * SIZE); + } else { + context.moveTo(x * SIZE, y * SIZE); + } + + if ( + colors[4] == colors[3] && + colors[4] == colors[7] && + colors[4] != colors[1] && + colors[4] != colors[5] + ) { + context.lineTo((x + 0.6) * SIZE, y * SIZE); + context.lineTo((x + 1) * SIZE, (y + 0.4) * SIZE); + } else { + context.lineTo((x + 1) * SIZE, y * SIZE); + } + + if ( + colors[4] == colors[1] && + colors[4] == colors[3] && + colors[4] != colors[5] && + colors[4] != colors[7] + ) { + context.lineTo((x + 1) * SIZE, (y + 0.6) * SIZE); + context.lineTo((x + 0.6) * SIZE, (y + 1) * SIZE); + } else { + context.lineTo((x + 1) * SIZE, (y + 1) * SIZE); + } + + if ( + colors[4] == colors[1] && + colors[4] == colors[5] && + colors[4] != colors[3] && + colors[4] != colors[7] + ) { + context.lineTo((x + 0.4) * SIZE, (y + 1) * SIZE); + context.lineTo(x * SIZE, (y + 0.6) * SIZE); + } else { + context.lineTo(x * SIZE, (y + 1) * SIZE); + } + + context.fill(); } + return canvas; } }; From 70caa6470e5438274a0a8eb280c86a38656d5d20 Mon Sep 17 00:00:00 2001 From: Hannah Bergam Date: Mon, 21 Jun 2021 22:35:06 +0000 Subject: [PATCH 02/14] pulled in new svg paths --- src/neighborhoodDrawer.js | 379 ++++++++++++++++++++++++++------------ 1 file changed, 259 insertions(+), 120 deletions(-) diff --git a/src/neighborhoodDrawer.js b/src/neighborhoodDrawer.js index a337dab..ca0a244 100644 --- a/src/neighborhoodDrawer.js +++ b/src/neighborhoodDrawer.js @@ -2,6 +2,76 @@ const { SVG_NS } = require("./drawer"); const Drawer = require("./drawer"); const tiles = require("./tiles"); +const ROTATE180 = "rotate(180)"; +const ROTATENEG90 = "rotate(-90)"; +const ROTATE90 = "rotate(90)"; +const ROTATE0 = "rotate(0)"; +const CUT = "cut"; +const PIE = "pie"; + +/** + * This is a helper for creating SVG Elements. + * Groups are created by grid tile, under which paths are nested. These groups + * begin with "g" in the id. By checking for this when determining its position + * within the hierarchy, we can nest these groups just before the pegman, + * ensuring the pegman will appear on top of the paint. + * + * @param tag representing the element type, 'g' for group, 'path' for paths + * @param props representing the details of the element + * @param parent the parent it should be nested under + * @param id the unique identifier, beginning with 'g' if a group element + * @returns the element itself + */ +function svgElement(tag, props, parent, id) { + var node = document.getElementById(id); + if (!node) { + node = document.createElementNS(SVG_NS, tag); + node.setAttribute("id", id); + } + Object.keys(props).map(function (key) { + node.setAttribute(key, props[key]); + }); + if (parent && id.startsWith("g")) { + let pegmanElement = parent.getElementsByClassName("pegman-location")[0]; + parent.insertBefore(node, pegmanElement); + } else if (parent) { + parent.appendChild(node); + } + return node; +} + +/// Methods assume that the upper lefthand corner is 0,0. These paths can be rotated to make all four corner possibilities +function smallCornerPathString() { + return `M0,0 L${0.2 * this.squareSize},0 L0,${0.2 * this.squareSize} Z`; +} + +function trianglePathString() { + return `M0,0 L${this.squareSize},0 L0,${this.squareSize} Z`; +} + +function generateTruncatedSquareString( + topLeftIsTruncated, + topRightIsTruncated, + bottomRightIsTruncated, + bottomLeftIsTruncated +) { + const topLeftCorner = topLeftIsTruncated + ? `M0,${this.squareSize * 0.2} L${this.squareSize * 0.2},0` + : `M0,0`; + const topRightCorner = topRightIsTruncated + ? `L${this.squareSize * 0.3},0 L${this.squareSize},${this.squareSize * 0.2}` + : `L${this.squareSize},0`; + const bottomRightCorner = bottomRightIsTruncated + ? `L${this.squareSize},${this.squareSize * 0.3} L${this.squareSize * 0.3},${ + this.squareSize + }` + : `L${this.squareSize},${this.squareSize}`; + const bottomLeftCorner = bottomLeftIsTruncated + ? `L${this.squareSize * 0.2},${this.squareSize} L0,${this.squareSize * 0.3}` + : `L0,${this.squareSize}`; + return `${topLeftCorner} ${topRightCorner} ${bottomRightCorner} ${bottomLeftCorner} Z`; +} + /** * This drawer hosts all paint glomming logic. * A note on layering paint: If paint is applied on top of existing paint @@ -93,6 +163,129 @@ module.exports = class NeighborhoodDrawer extends Drawer { return this.map_.getCell(row, col).getColor() || null; } + centerFill(center, top, right, bottom, left, transform, grid, id) { + var path; + if (center == top && center == right && center != bottom && center != left) + path = generateTruncatedSquareString(false, false, false, true); + if (center == right && center == bottom && center != left && center != top) + path = generateTruncatedSquareString(true, false, false, false); + if (center == bottom && center == left && center != top && center != right) + path = generateTruncatedSquareString(false, true, false, false); + if (center == left && center == top && center != right && center != bottom) + path = generateTruncatedSquareString(false, false, true, false); + + svgElement( + "path", + { + d: path, + stroke: center, + transform: transform, + fill: center, + }, + grid, + `${id}-${PIE}` + ); + } + + // Helper method for determining color and path based on neighbors + pathCalculator( + subjectCell, + adjacent1, + adjacent2, + diagonal, + transform, + grid, + id + ) { + let pie = quarterCircle(this.squareSize); + let cutOut = cutout(this.squareSize); + let tag = "path"; + + // Add a quarter circle to the top left corner of the block if there is + // a color value there + if (subjectCell) { + svgElement( + tag, + { + d: pie, + stroke: subjectCell, + transform: transform, + fill: subjectCell, + }, + grid, + `${id}-${PIE}` + ); + } + // Add the cutout if the top left corner has a color and an adjacent cell + // shares that color, filling in the top left quadrant of the block entirely + if ( + subjectCell && + (subjectCell === adjacent1 || subjectCell === adjacent2) + ) { + svgElement( + tag, + { + d: cutOut, + stroke: subjectCell, + transform: transform, + fill: subjectCell, + }, + grid, + `${id}-${CUT}` + ); + } + // Otherwise, if the two adjacent corners have the same color, add the + // cutout shape with that color + else if ( + adjacent1 && + adjacent1 === adjacent2 && + (!diagonal || !subjectCell || subjectCell !== diagonal) + ) { + svgElement( + tag, + { d: cutOut, stroke: adjacent1, transform: transform, fill: adjacent1 }, + grid, + `${id}-${CUT}` + ); + } + // Fill in center corner only if an adjacent cell has the same color, or if + // the diagonal cell is same color and either adjacent is empty + // Note: this handles the "clover case", where we want each + // cell to "pop" out with its own color if diagonals are matching + else if ( + subjectCell && + (adjacent1 === subjectCell || + adjacent2 === subjectCell || + (diagonal === subjectCell && + (!adjacent1 || !adjacent2 || adjacent1 !== adjacent2))) + ) { + svgElement( + tag, + { + d: cutOut, + stroke: subjectCell, + transform: transform, + fill: subjectCell, + }, + grid, + `${id}-${CUT}` + ); + } + } + + makeGrid(row, col, svg) { + let id = "g" + row + "." + col; + return svgElement( + "g", + { + transform: `translate(${col * this.squareSize + this.squareSize}, + ${row * this.squareSize + this.squareSize})`, + }, + svg, + id + ); + } + /** * @override * Draw the given tile at row, col @@ -161,136 +354,82 @@ module.exports = class NeighborhoodDrawer extends Drawer { ); } - // If necessary, create padded rows with no color - /* - 0 1 2 - 3 4 5 - 6 7 8 - */ - let colors = [ + /* + We process each tile based on the 8 surrounding, and cellColor() will + simply return null if the neighboring cells are out of bounds + + 0 1 2 + 3 4 5 + 6 7 8 + + */ + + let cells = [ this.cellColor(row - 1, col - 1), // Top left this.cellColor(row, col - 1), // Top this.cellColor(row + 1, col - 1), // Top right this.cellColor(row - 1, col), // Middle left - this.cellColor(row, col), // Middle + this.cellColor(row, col), // Target cell this.cellColor(row + 1, col), // Middle right this.cellColor(row - 1, col + 1), // Bottom left this.cellColor(row, col + 1), // Bottom this.cellColor(row + 1, col + 1), // Bottom right ]; - const canvas = document.createElement("canvas"); - const context = canvas.getContext("2d"); - - if (colors[1] && colors[1] == colors[3]) { - context.beginPath(); - context.fillStyle = colors[1]; - context.moveTo(x * SIZE, y * SIZE); - if (colors[0] == colors[3]) { - context.lineTo((x + 0.4) * SIZE, y * SIZE); - context.lineTo(x * SIZE, (y + 0.4) * SIZE); - } else { - context.lineTo((x + 1) * SIZE, y * SIZE); - context.lineTo(x * SIZE, (y + 1) * SIZE); - } - context.fill(); - } - - if (colors[1] && colors[1] == colors[5]) { - context.beginPath(); - context.fillStyle = colors[1]; - context.moveTo((x + 1) * SIZE, y * SIZE); - if (colors[2] == colors[1]) { - context.lineTo((x + 0.6) * SIZE, y * SIZE); - context.lineTo((x + 1) * SIZE, (y + 0.4) * SIZE); - } else { - context.lineTo(x * SIZE, y * SIZE); - context.lineTo((x + 1) * SIZE, (y + 1) * SIZE); - } - context.fill(); - } - - if (colors[7] && colors[7] == colors[3]) { - context.beginPath(); - context.fillStyle = colors[7]; - context.moveTo(x * SIZE, (y + 1) * SIZE); - if (colors[6] == colors[7]) { - context.lineTo((x + 0.4) * SIZE, (y + 1) * SIZE); - context.lineTo(x * SIZE, (y + 0.6) * SIZE); - } else { - context.lineTo(x * SIZE, y * SIZE); - context.lineTo((x + 1) * SIZE, (y + 1) * SIZE); - } - context.fill(); - } - - if (colors[7] && colors[7] == colors[5]) { - context.beginPath(); - context.fillStyle = colors[7]; - context.moveTo((x + 1) * SIZE, (y + 1) * SIZE); - if (colors[8] == colors[7]) { - context.lineTo((x + 1) * SIZE, (y + 0.6) * SIZE); - context.lineTo((x + 0.6) * SIZE, (y + 1) * SIZE); - } else { - context.lineTo((x + 1) * SIZE, y * SIZE); - context.lineTo(x * SIZE, (y + 1) * SIZE); - } - context.fill(); - } - - if (colors[4]) { - context.beginPath(); - context.fillStyle = colors[4]; - if ( - colors[4] == colors[5] && - colors[4] == colors[7] && - colors[4] != colors[1] && - colors[4] != colors[3] - ) { - context.moveTo(x * SIZE, (y + 0.4) * SIZE); - context.lineTo((x + 0.4) * SIZE, y * SIZE); - } else { - context.moveTo(x * SIZE, y * SIZE); - } - - if ( - colors[4] == colors[3] && - colors[4] == colors[7] && - colors[4] != colors[1] && - colors[4] != colors[5] - ) { - context.lineTo((x + 0.6) * SIZE, y * SIZE); - context.lineTo((x + 1) * SIZE, (y + 0.4) * SIZE); - } else { - context.lineTo((x + 1) * SIZE, y * SIZE); - } - - if ( - colors[4] == colors[1] && - colors[4] == colors[3] && - colors[4] != colors[5] && - colors[4] != colors[7] - ) { - context.lineTo((x + 1) * SIZE, (y + 0.6) * SIZE); - context.lineTo((x + 0.6) * SIZE, (y + 1) * SIZE); - } else { - context.lineTo((x + 1) * SIZE, (y + 1) * SIZE); - } + // Create grid block group + let grid = this.makeGrid(row, col, this.svg_); + let id0 = row + "." + col + "." + ROTATE180; + let id1 = row + "." + col + "." + ROTATENEG90; + let id2 = row + "." + col + "." + ROTATE90; + let id3 = row + "." + col + "." + ROTATE0; + let id4 = row + "." + col + "." + "CENTER"; - if ( - colors[4] == colors[1] && - colors[4] == colors[5] && - colors[4] != colors[3] && - colors[4] != colors[7] - ) { - context.lineTo((x + 0.4) * SIZE, (y + 1) * SIZE); - context.lineTo(x * SIZE, (y + 0.6) * SIZE); - } else { - context.lineTo(x * SIZE, (y + 1) * SIZE); - } - - context.fill(); - } - return canvas; + // Calculate all the svg paths based on neighboring cell colors + this.pathCalculator( + cells[4], + cells[1], + cells[3], + cells[0], + ROTATE180, + grid, + id0 + ); + this.pathCalculator( + cells[4], + cells[1], + cells[5], + cells[2], + ROTATENEG90, + grid, + id1 + ); + this.pathCalculator( + cells[4], + cells[5], + cells[7], + cells[8], + ROTATE90, + grid, + id2 + ); + this.pathCalculator( + cells[4], + cells[7], + cells[3], + cells[6], + ROTATE0, + grid, + id3 + ); + this.centerFill( + cells[4], + cells[1], + cells[5], + cells[7], + cells[3], + ROTATE0, + grid, + id4 + ); } }; From 58c15bca0d78faa09a0f9bc43ce5f1905a35ff06 Mon Sep 17 00:00:00 2001 From: Hannah Bergam Date: Mon, 21 Jun 2021 23:05:18 +0000 Subject: [PATCH 03/14] removing old pie and cutout paths --- src/neighborhoodDrawer.js | 66 +++++++-------------------------------- 1 file changed, 11 insertions(+), 55 deletions(-) diff --git a/src/neighborhoodDrawer.js b/src/neighborhoodDrawer.js index ca0a244..62fe4a6 100644 --- a/src/neighborhoodDrawer.js +++ b/src/neighborhoodDrawer.js @@ -6,8 +6,8 @@ const ROTATE180 = "rotate(180)"; const ROTATENEG90 = "rotate(-90)"; const ROTATE90 = "rotate(90)"; const ROTATE0 = "rotate(0)"; -const CUT = "cut"; -const PIE = "pie"; +const TRIANGLE = "triangle"; +const CENTER = "center"; /** * This is a helper for creating SVG Elements. @@ -183,7 +183,7 @@ module.exports = class NeighborhoodDrawer extends Drawer { fill: center, }, grid, - `${id}-${PIE}` + `${id}-${CENTER}` ); } @@ -197,78 +197,34 @@ module.exports = class NeighborhoodDrawer extends Drawer { grid, id ) { - let pie = quarterCircle(this.squareSize); - let cutOut = cutout(this.squareSize); + let triangle = trianglePathString(); let tag = "path"; // Add a quarter circle to the top left corner of the block if there is // a color value there - if (subjectCell) { + if (subjectCell && adjacent1 === adjacent2 && adjacent1 === diagonal) { svgElement( tag, { - d: pie, + d: triangle, stroke: subjectCell, transform: transform, fill: subjectCell, }, grid, - `${id}-${PIE}` + `${id}-${TRIANGLE}` ); - } - // Add the cutout if the top left corner has a color and an adjacent cell - // shares that color, filling in the top left quadrant of the block entirely - if ( - subjectCell && - (subjectCell === adjacent1 || subjectCell === adjacent2) - ) { - svgElement( - tag, - { - d: cutOut, - stroke: subjectCell, - transform: transform, - fill: subjectCell, - }, - grid, - `${id}-${CUT}` - ); - } - // Otherwise, if the two adjacent corners have the same color, add the - // cutout shape with that color - else if ( - adjacent1 && - adjacent1 === adjacent2 && - (!diagonal || !subjectCell || subjectCell !== diagonal) - ) { - svgElement( - tag, - { d: cutOut, stroke: adjacent1, transform: transform, fill: adjacent1 }, - grid, - `${id}-${CUT}` - ); - } - // Fill in center corner only if an adjacent cell has the same color, or if - // the diagonal cell is same color and either adjacent is empty - // Note: this handles the "clover case", where we want each - // cell to "pop" out with its own color if diagonals are matching - else if ( - subjectCell && - (adjacent1 === subjectCell || - adjacent2 === subjectCell || - (diagonal === subjectCell && - (!adjacent1 || !adjacent2 || adjacent1 !== adjacent2))) - ) { + } else if (subjectCell && adjacent1 === adjacent2) { svgElement( tag, { - d: cutOut, + d: triangle, stroke: subjectCell, transform: transform, fill: subjectCell, }, grid, - `${id}-${CUT}` + `${id}-${TRIANGLE}` ); } } @@ -382,7 +338,7 @@ module.exports = class NeighborhoodDrawer extends Drawer { let id1 = row + "." + col + "." + ROTATENEG90; let id2 = row + "." + col + "." + ROTATE90; let id3 = row + "." + col + "." + ROTATE0; - let id4 = row + "." + col + "." + "CENTER"; + let id4 = row + "." + col + "." + CENTER; // Calculate all the svg paths based on neighboring cell colors this.pathCalculator( From 872f0a6a97df17e9d0d7cdcca2809ecadde08424 Mon Sep 17 00:00:00 2001 From: Hannah Bergam Date: Wed, 23 Jun 2021 00:52:57 +0000 Subject: [PATCH 04/14] glomming updates, rendering now --- src/neighborhoodDrawer.js | 164 +++++++++++++++++++++++++------------- 1 file changed, 109 insertions(+), 55 deletions(-) diff --git a/src/neighborhoodDrawer.js b/src/neighborhoodDrawer.js index 62fe4a6..e32889b 100644 --- a/src/neighborhoodDrawer.js +++ b/src/neighborhoodDrawer.js @@ -41,34 +41,33 @@ function svgElement(tag, props, parent, id) { } /// Methods assume that the upper lefthand corner is 0,0. These paths can be rotated to make all four corner possibilities -function smallCornerPathString() { - return `M0,0 L${0.2 * this.squareSize},0 L0,${0.2 * this.squareSize} Z`; +function smallCornerPathString(size) { + return `m0,0 L${0.4 * size},0 L0,${0.4 * size} Z`; } -function trianglePathString() { - return `M0,0 L${this.squareSize},0 L0,${this.squareSize} Z`; +function trianglePathString(size) { + return `m0,0 L${size},0 L0,${size} Z`; } function generateTruncatedSquareString( + size, topLeftIsTruncated, topRightIsTruncated, bottomRightIsTruncated, bottomLeftIsTruncated ) { const topLeftCorner = topLeftIsTruncated - ? `M0,${this.squareSize * 0.2} L${this.squareSize * 0.2},0` - : `M0,0`; + ? `m0,${size * 0.4} L${size * 0.4},0` + : `m0,0`; const topRightCorner = topRightIsTruncated - ? `L${this.squareSize * 0.3},0 L${this.squareSize},${this.squareSize * 0.2}` - : `L${this.squareSize},0`; + ? `L${size * 0.6},0 L${size},${size * 0.4}` + : `L${size},0`; const bottomRightCorner = bottomRightIsTruncated - ? `L${this.squareSize},${this.squareSize * 0.3} L${this.squareSize * 0.3},${ - this.squareSize - }` - : `L${this.squareSize},${this.squareSize}`; + ? `L${size},${size * 0.6} L${size * 0.6},${size}` + : `L${size},${size}`; const bottomLeftCorner = bottomLeftIsTruncated - ? `L${this.squareSize * 0.2},${this.squareSize} L0,${this.squareSize * 0.3}` - : `L0,${this.squareSize}`; + ? `L${size * 0.4},${size} L0,${size * 0.6}` + : `L0,${size}`; return `${topLeftCorner} ${topRightCorner} ${bottomRightCorner} ${bottomLeftCorner} Z`; } @@ -166,14 +165,61 @@ module.exports = class NeighborhoodDrawer extends Drawer { centerFill(center, top, right, bottom, left, transform, grid, id) { var path; if (center == top && center == right && center != bottom && center != left) - path = generateTruncatedSquareString(false, false, false, true); - if (center == right && center == bottom && center != left && center != top) - path = generateTruncatedSquareString(true, false, false, false); - if (center == bottom && center == left && center != top && center != right) - path = generateTruncatedSquareString(false, true, false, false); - if (center == left && center == top && center != right && center != bottom) - path = generateTruncatedSquareString(false, false, true, false); - + path = generateTruncatedSquareString( + this.squareSize, + false, + false, + false, + true + ); + else if ( + center == right && + center == bottom && + center != left && + center != top + ) + path = generateTruncatedSquareString( + this.squareSize, + false, + false, + true, + false + ); + else if ( + center == bottom && + center == left && + center != top && + center != right + ) + path = generateTruncatedSquareString( + this.squareSize, + false, + true, + false, + false + ); + else if ( + center == left && + center == top && + center != right && + center != bottom + ) + path = generateTruncatedSquareString( + this.squareSize, + true, + false, + false, + false + ); + else { + path = generateTruncatedSquareString( + this.squareSize, + false, + false, + false, + false + ); + } svgElement( "path", { @@ -197,12 +243,18 @@ module.exports = class NeighborhoodDrawer extends Drawer { grid, id ) { - let triangle = trianglePathString(); + let smallTriangle = smallCornerPathString(this.squareSize); + let triangle = trianglePathString(this.squareSize); let tag = "path"; // Add a quarter circle to the top left corner of the block if there is // a color value there - if (subjectCell && adjacent1 === adjacent2 && adjacent1 === diagonal) { + if ( + subjectCell && + subjectCell === adjacent1 && + adjacent1 === adjacent2 && + adjacent1 !== diagonal + ) { svgElement( tag, { @@ -214,7 +266,7 @@ module.exports = class NeighborhoodDrawer extends Drawer { grid, `${id}-${TRIANGLE}` ); - } else if (subjectCell && adjacent1 === adjacent2) { + } else if (subjectCell && subjectCell === diagonal) { svgElement( tag, { @@ -282,13 +334,13 @@ module.exports = class NeighborhoodDrawer extends Drawer { * @override * This method is used to display the paint and paint buckets. * It has to reprocess the entire grid to get the paint glomming correct, but - * it only updates the bucket at the specified itemRow and itemCol if necessary. - * @param {number} itemRow: row of update - * @param {number} itemCol: column of update + * it only updates the bucket at the specified row and col if necessary. + * @param {number} row: row of update + * @param {number} col: column of update * @param {boolean} running: if the maze is currently running (not used here, but part of signature of super) */ - updateItemImage(itemRow, itemCol, running) { - let cell = this.map_.getCell(itemRow, itemCol); + updateItemImage(tileRow, tileCol, running) { + let cell = this.map_.getCell(tileRow, tileCol); // if the cell value has ever been greater than 0, this has been or // is a paint can square. Ensure it is shown/hidden appropriately @@ -297,11 +349,11 @@ module.exports = class NeighborhoodDrawer extends Drawer { const newValue = cell.getCurrentValue() > 0 ? cell.getCurrentValue() : ""; // drawImage_ calls getAsset. If currentValue() is 0, getAsset will return // undefined and the paint can will be hidden. Otherwise we will get the paint can image. - super.drawImage_("", itemRow, itemCol, this.squareSize); + super.drawImage_("", tileRow, tileCol, this.squareSize); super.updateOrCreateText_( "counter", - itemRow, - itemCol, + tileRow, + tileCol, newValue, this.squareSize, 1, @@ -342,50 +394,52 @@ module.exports = class NeighborhoodDrawer extends Drawer { // Calculate all the svg paths based on neighboring cell colors this.pathCalculator( - cells[4], + cells[0], cells[1], cells[3], - cells[0], + cells[4], ROTATE180, grid, id0 ); this.pathCalculator( - cells[4], cells[1], - cells[5], - cells[2], + cells[0], + cells[4], + cells[3], ROTATENEG90, grid, id1 ); this.pathCalculator( cells[4], - cells[5], - cells[7], - cells[8], - ROTATE90, + cells[3], + cells[1], + cells[0], + ROTATE0, grid, id2 ); this.pathCalculator( - cells[4], - cells[7], cells[3], - cells[6], - ROTATE0, - grid, - id3 - ); - this.centerFill( cells[4], + cells[0], cells[1], - cells[5], - cells[7], - cells[3], - ROTATE0, + ROTATE90, grid, - id4 + id3 ); + if (cells[4]) { + this.centerFill( + cells[4], + cells[1], + cells[5], + cells[7], + cells[3], + ROTATE180, + grid, + id4 + ); + } } }; From a792d3e83b8188d9dc0739572aee9b7181af1742 Mon Sep 17 00:00:00 2001 From: Hannah Bergam Date: Wed, 23 Jun 2021 22:21:44 +0000 Subject: [PATCH 05/14] mostly working properly and optimized, still no comments or cleanup --- src/neighborhoodDrawer.js | 331 +++++++++++++++++++++++--------------- 1 file changed, 204 insertions(+), 127 deletions(-) diff --git a/src/neighborhoodDrawer.js b/src/neighborhoodDrawer.js index e32889b..e9a0a9d 100644 --- a/src/neighborhoodDrawer.js +++ b/src/neighborhoodDrawer.js @@ -2,11 +2,8 @@ const { SVG_NS } = require("./drawer"); const Drawer = require("./drawer"); const tiles = require("./tiles"); -const ROTATE180 = "rotate(180)"; -const ROTATENEG90 = "rotate(-90)"; -const ROTATE90 = "rotate(90)"; -const ROTATE0 = "rotate(0)"; const TRIANGLE = "triangle"; +const SMALLTRI = "smallCorner"; const CENTER = "center"; /** @@ -40,21 +37,44 @@ function svgElement(tag, props, parent, id) { return node; } -/// Methods assume that the upper lefthand corner is 0,0. These paths can be rotated to make all four corner possibilities -function smallCornerPathString(size) { +function smallCornerTopLeft(size) { return `m0,0 L${0.4 * size},0 L0,${0.4 * size} Z`; } -function trianglePathString(size) { +function smallCornerBottomLeft(size) { + return `m${size},0 L${0.6 * size},0 L${size},${0.4 * size} Z`; +} + +function smallCornerTopRight(size) { + return `m0,${size} L${0.4 * size},${size} L0,${0.6 * size} Z`; +} + +function smallCornerBottomRight(size) { + return `m${size},${size} L${0.6 * size},${size} L${size},${0.6 * size} Z`; +} + +function trianglePathTopLeft(size) { return `m0,0 L${size},0 L0,${size} Z`; } +function trianglePathBottomLeft(size) { + return `m${size},0 L${size},${size} L0,0 Z`; +} + +function trianglePathTopRight(size) { + return `m0,${size} L${size},${size} L0,0 Z`; +} + +function trianglePathBottomRight(size) { + return `m${size},${size} L${size},0 L0,${size} Z`; +} + function generateTruncatedSquareString( size, topLeftIsTruncated, - topRightIsTruncated, + bottomLeftIsTruncated, bottomRightIsTruncated, - bottomLeftIsTruncated + topRightIsTruncated ) { const topLeftCorner = topLeftIsTruncated ? `m0,${size * 0.4} L${size * 0.4},0` @@ -162,7 +182,7 @@ module.exports = class NeighborhoodDrawer extends Drawer { return this.map_.getCell(row, col).getColor() || null; } - centerFill(center, top, right, bottom, left, transform, grid, id) { + centerFill(center, top, right, bottom, left, grid, id) { var path; if (center == top && center == right && center != bottom && center != left) path = generateTruncatedSquareString( @@ -180,9 +200,9 @@ module.exports = class NeighborhoodDrawer extends Drawer { ) path = generateTruncatedSquareString( this.squareSize, + true, false, false, - true, false ); else if ( @@ -206,9 +226,9 @@ module.exports = class NeighborhoodDrawer extends Drawer { ) path = generateTruncatedSquareString( this.squareSize, - true, false, false, + true, false ); else { @@ -225,7 +245,6 @@ module.exports = class NeighborhoodDrawer extends Drawer { { d: path, stroke: center, - transform: transform, fill: center, }, grid, @@ -234,50 +253,164 @@ module.exports = class NeighborhoodDrawer extends Drawer { } // Helper method for determining color and path based on neighbors - pathCalculator( - subjectCell, - adjacent1, - adjacent2, - diagonal, - transform, - grid, - id - ) { - let smallTriangle = smallCornerPathString(this.squareSize); - let triangle = trianglePathString(this.squareSize); + pathCalculator(cellList, grid, id) { let tag = "path"; - // Add a quarter circle to the top left corner of the block if there is - // a color value there - if ( - subjectCell && - subjectCell === adjacent1 && - adjacent1 === adjacent2 && - adjacent1 !== diagonal - ) { + // 0 1 2 + // 3 4 5 + // 6 7 8 + + let center = cellList[4]; + let top = cellList[1]; + let right = cellList[5]; + let bottom = cellList[7]; + let left = cellList[3]; + + // if the center cell has paint, calculate its fill and corners + if (cellList[4]) { + this.centerFill(center, top, right, bottom, left, grid, id); + } else if (top && left && bottom && right) { + svgElement( + tag, + { + d: smallCornerTopRight(this.squareSize), + stroke: top, + fill: top, + }, + grid, + `${id}-${SMALLTRI}-tr` + ); + svgElement( + tag, + { + d: smallCornerTopLeft(this.squareSize), + stroke: top, + fill: top, + }, + grid, + `${id}-${SMALLTRI}-tl` + ); svgElement( tag, { - d: triangle, - stroke: subjectCell, - transform: transform, - fill: subjectCell, + d: smallCornerBottomLeft(this.squareSize), + stroke: top, + fill: top, }, grid, - `${id}-${TRIANGLE}` + `${id}-${SMALLTRI}-bl` ); - } else if (subjectCell && subjectCell === diagonal) { svgElement( tag, { - d: triangle, - stroke: subjectCell, - transform: transform, - fill: subjectCell, + d: smallCornerBottomRight(this.squareSize), + stroke: top, + fill: top, }, grid, - `${id}-${TRIANGLE}` + `${id}-${SMALLTRI}-br` ); + } else { + if (top && right) { + if (cellList[2]) { + svgElement( + tag, + { + d: smallCornerTopRight(this.squareSize), + stroke: top, + fill: top, + }, + grid, + `${id}-${SMALLTRI}-tr` + ); + } else { + svgElement( + tag, + { + d: trianglePathTopRight(this.squareSize), + stroke: top, + fill: top, + }, + grid, + `${id}-${TRIANGLE}-tr` + ); + } + } + if (right && bottom) { + if (cellList[8]) { + svgElement( + tag, + { + d: smallCornerBottomRight(this.squareSize), + stroke: right, + fill: right, + }, + grid, + `${id}-${SMALLTRI}-br` + ); + } else { + svgElement( + tag, + { + d: trianglePathBottomRight(this.squareSize), + stroke: right, + fill: right, + }, + grid, + `${id}-${TRIANGLE}-br` + ); + } + } + if (bottom && left) { + if (cellList[6]) { + svgElement( + tag, + { + d: smallCornerBottomLeft(this.squareSize), + stroke: bottom, + fill: bottom, + }, + grid, + `${id}-${SMALLTRI}-bl` + ); + } else { + svgElement( + tag, + { + d: trianglePathBottomLeft(this.squareSize), + stroke: bottom, + fill: bottom, + }, + grid, + `${id}-${TRIANGLE}-bl` + ); + } + } + if (left && top) { + if (cellList[0]) { + svgElement( + tag, + { + d: smallCornerTopLeft(this.squareSize), + stroke: left, + fill: left, + }, + grid, + `${id}-${SMALLTRI}-tl` + ); + } else { + svgElement( + tag, + { + d: trianglePathTopLeft(this.squareSize), + stroke: left, + fill: left, + }, + grid, + `${id}-${TRIANGLE}-tl` + ); + } + } } } @@ -286,8 +419,8 @@ module.exports = class NeighborhoodDrawer extends Drawer { return svgElement( "g", { - transform: `translate(${col * this.squareSize + this.squareSize}, - ${row * this.squareSize + this.squareSize})`, + transform: `translate(${col * this.squareSize}, + ${row * this.squareSize})`, }, svg, id @@ -339,8 +472,8 @@ module.exports = class NeighborhoodDrawer extends Drawer { * @param {number} col: column of update * @param {boolean} running: if the maze is currently running (not used here, but part of signature of super) */ - updateItemImage(tileRow, tileCol, running) { - let cell = this.map_.getCell(tileRow, tileCol); + updateItemImage(row, col, running) { + let cell = this.map_.getCell(row, col); // if the cell value has ever been greater than 0, this has been or // is a paint can square. Ensure it is shown/hidden appropriately @@ -349,11 +482,11 @@ module.exports = class NeighborhoodDrawer extends Drawer { const newValue = cell.getCurrentValue() > 0 ? cell.getCurrentValue() : ""; // drawImage_ calls getAsset. If currentValue() is 0, getAsset will return // undefined and the paint can will be hidden. Otherwise we will get the paint can image. - super.drawImage_("", tileRow, tileCol, this.squareSize); + super.drawImage_("", row, col, this.squareSize); super.updateOrCreateText_( "counter", - tileRow, - tileCol, + row, + col, newValue, this.squareSize, 1, @@ -362,84 +495,28 @@ module.exports = class NeighborhoodDrawer extends Drawer { ); } - /* - We process each tile based on the 8 surrounding, and cellColor() will - simply return null if the neighboring cells are out of bounds - - 0 1 2 - 3 4 5 - 6 7 8 - - */ - - let cells = [ - this.cellColor(row - 1, col - 1), // Top left - this.cellColor(row, col - 1), // Top - this.cellColor(row + 1, col - 1), // Top right - this.cellColor(row - 1, col), // Middle left - this.cellColor(row, col), // Target cell - this.cellColor(row + 1, col), // Middle right - this.cellColor(row - 1, col + 1), // Bottom left - this.cellColor(row, col + 1), // Bottom - this.cellColor(row + 1, col + 1), // Bottom right - ]; + for (let r = row - 1; r < row + 2; r++) { + for (let c = col - 1; c < col + 2; c++) { + let id = r + "." + c + "."; - // Create grid block group - let grid = this.makeGrid(row, col, this.svg_); - let id0 = row + "." + col + "." + ROTATE180; - let id1 = row + "." + col + "." + ROTATENEG90; - let id2 = row + "." + col + "." + ROTATE90; - let id3 = row + "." + col + "." + ROTATE0; - let id4 = row + "." + col + "." + CENTER; - - // Calculate all the svg paths based on neighboring cell colors - this.pathCalculator( - cells[0], - cells[1], - cells[3], - cells[4], - ROTATE180, - grid, - id0 - ); - this.pathCalculator( - cells[1], - cells[0], - cells[4], - cells[3], - ROTATENEG90, - grid, - id1 - ); - this.pathCalculator( - cells[4], - cells[3], - cells[1], - cells[0], - ROTATE0, - grid, - id2 - ); - this.pathCalculator( - cells[3], - cells[4], - cells[0], - cells[1], - ROTATE90, - grid, - id3 - ); - if (cells[4]) { - this.centerFill( - cells[4], - cells[1], - cells[5], - cells[7], - cells[3], - ROTATE180, - grid, - id4 - ); + let cells = [ + this.cellColor(r - 1, c - 1), // Top left + this.cellColor(r, c - 1), // Top + this.cellColor(r + 1, c - 1), // Top right + this.cellColor(r - 1, c), // Middle left + this.cellColor(r, c), // Target cell + this.cellColor(r + 1, c), // Middle right + this.cellColor(r - 1, c + 1), // Bottom left + this.cellColor(r, c + 1), // Bottom + this.cellColor(r + 1, c + 1), // Bottom right + ]; + + // Create grid block group for this center focus cell + let grid = this.makeGrid(r, c, this.svg_); + + // Calculate all the svg paths based on neighboring cell colors + this.pathCalculator(cells, grid, id); + } } } }; From 2465c26ec914e07dcac229fb9e0890f99db01f32 Mon Sep 17 00:00:00 2001 From: Hannah Bergam Date: Thu, 24 Jun 2021 01:09:29 +0000 Subject: [PATCH 06/14] optimized, cleaned duplicates, added comments --- src/neighborhoodDrawer.js | 285 ++++++++++++++++++++------------------ 1 file changed, 147 insertions(+), 138 deletions(-) diff --git a/src/neighborhoodDrawer.js b/src/neighborhoodDrawer.js index e9a0a9d..8d29a8c 100644 --- a/src/neighborhoodDrawer.js +++ b/src/neighborhoodDrawer.js @@ -37,44 +37,96 @@ function svgElement(tag, props, parent, id) { return node; } -function smallCornerTopLeft(size) { - return `m0,0 L${0.4 * size},0 L0,${0.4 * size} Z`; +/** + * The following functions create SVGs for the small corner cutouts + * + * @param tag the kind of SVG + * @param color the stroke and fill colors + * @param grid the parent element + * @param id the id label + * @param size the square size + */ +function smallCornerTopLeftSvg(tag, color, grid, id, size) { + svgElement( + tag, + { + d: `m0,0 L${0.4 * size},0 L0,${0.4 * size} Z`, + stroke: color, + fill: color, + }, + grid, + id + ); } -function smallCornerBottomLeft(size) { - return `m${size},0 L${0.6 * size},0 L${size},${0.4 * size} Z`; +function smallCornerBottomLeftSvg(tag, color, grid, id, size) { + svgElement( + tag, + { + d: `m0,${size} L0,${0.6 * size} L${0.4 * size},${size} Z`, + stroke: color, + fill: color, + }, + grid, + id + ); } -function smallCornerTopRight(size) { - return `m0,${size} L${0.4 * size},${size} L0,${0.6 * size} Z`; +function smallCornerTopRightSvg(tag, color, grid, id, size) { + svgElement( + tag, + { + d: `m${size},0 L${0.6 * size},0 L${size},${0.4 * size} Z`, + stroke: color, + fill: color, + }, + grid, + id + ); } -function smallCornerBottomRight(size) { - return `m${size},${size} L${0.6 * size},${size} L${size},${0.6 * size} Z`; +function smallCornerBottomRightSvg(tag, color, grid, id, size) { + svgElement( + tag, + { + d: `m${size},${size} L${0.6 * size},${size} L${size},${0.6 * size} Z`, + stroke: color, + fill: color, + }, + grid, + id + ); } +/** + * The following four functions return the path elements for each potential + * triangle position that takes up half the grid size. + * + * @param size for square size + * @returns the string representing the svg path + */ function trianglePathTopLeft(size) { return `m0,0 L${size},0 L0,${size} Z`; } function trianglePathBottomLeft(size) { - return `m${size},0 L${size},${size} L0,0 Z`; + return `m0,${size} L${size},${size} L0,0 Z`; } function trianglePathTopRight(size) { - return `m0,${size} L${size},${size} L0,0 Z`; + return `m${size},0 L${size},${size} L0,0 Z`; } function trianglePathBottomRight(size) { return `m${size},${size} L${size},0 L0,${size} Z`; } -function generateTruncatedSquareString( +function generateCenterPath( size, topLeftIsTruncated, - bottomLeftIsTruncated, + topRightIsTruncated, bottomRightIsTruncated, - topRightIsTruncated + bottomLeftIsTruncated ) { const topLeftCorner = topLeftIsTruncated ? `m0,${size * 0.4} L${size * 0.4},0` @@ -182,63 +234,37 @@ module.exports = class NeighborhoodDrawer extends Drawer { return this.map_.getCell(row, col).getColor() || null; } + /** + * Determines how much of this tile should be colored in based on the colors + * of the adjacent neighbors. + */ centerFill(center, top, right, bottom, left, grid, id) { var path; if (center == top && center == right && center != bottom && center != left) - path = generateTruncatedSquareString( - this.squareSize, - false, - false, - false, - true - ); + path = generateCenterPath(this.squareSize, false, false, false, true); else if ( center == right && center == bottom && center != left && center != top ) - path = generateTruncatedSquareString( - this.squareSize, - true, - false, - false, - false - ); + path = generateCenterPath(this.squareSize, true, false, false, false); else if ( center == bottom && center == left && center != top && center != right ) - path = generateTruncatedSquareString( - this.squareSize, - false, - true, - false, - false - ); + path = generateCenterPath(this.squareSize, false, true, false, false); else if ( center == left && center == top && center != right && center != bottom ) - path = generateTruncatedSquareString( - this.squareSize, - false, - false, - true, - false - ); + path = generateCenterPath(this.squareSize, false, false, true, false); else { - path = generateTruncatedSquareString( - this.squareSize, - false, - false, - false, - false - ); + path = generateCenterPath(this.squareSize, false, false, false, false); } svgElement( "path", @@ -269,59 +295,17 @@ module.exports = class NeighborhoodDrawer extends Drawer { // if the center cell has paint, calculate its fill and corners if (cellList[4]) { this.centerFill(center, top, right, bottom, left, grid, id); - } else if (top && left && bottom && right) { - svgElement( - tag, - { - d: smallCornerTopRight(this.squareSize), - stroke: top, - fill: top, - }, - grid, - `${id}-${SMALLTRI}-tr` - ); - svgElement( - tag, - { - d: smallCornerTopLeft(this.squareSize), - stroke: top, - fill: top, - }, - grid, - `${id}-${SMALLTRI}-tl` - ); - svgElement( - tag, - { - d: smallCornerBottomLeft(this.squareSize), - stroke: top, - fill: top, - }, - grid, - `${id}-${SMALLTRI}-bl` - ); - svgElement( - tag, - { - d: smallCornerBottomRight(this.squareSize), - stroke: top, - fill: top, - }, - grid, - `${id}-${SMALLTRI}-br` - ); } else { + // Check each set of adjacent neighbors and diagonal to determine if small + // corners or triangle half-grids should be drawn if (top && right) { if (cellList[2]) { - svgElement( + smallCornerTopRightSvg( tag, - { - d: smallCornerTopRight(this.squareSize), - stroke: top, - fill: top, - }, + top, grid, - `${id}-${SMALLTRI}-tr` + `${id}-${SMALLTRI}-tr`, + this.squareSize ); } else { svgElement( @@ -338,15 +322,12 @@ module.exports = class NeighborhoodDrawer extends Drawer { } if (right && bottom) { if (cellList[8]) { - svgElement( + smallCornerBottomRightSvg( tag, - { - d: smallCornerBottomRight(this.squareSize), - stroke: right, - fill: right, - }, + right, grid, - `${id}-${SMALLTRI}-br` + `${id}-${SMALLTRI}-br`, + this.squareSize ); } else { svgElement( @@ -363,15 +344,12 @@ module.exports = class NeighborhoodDrawer extends Drawer { } if (bottom && left) { if (cellList[6]) { - svgElement( + smallCornerBottomLeftSvg( tag, - { - d: smallCornerBottomLeft(this.squareSize), - stroke: bottom, - fill: bottom, - }, + bottom, grid, - `${id}-${SMALLTRI}-bl` + `${id}-${SMALLTRI}-bl`, + this.squareSize ); } else { svgElement( @@ -388,15 +366,12 @@ module.exports = class NeighborhoodDrawer extends Drawer { } if (left && top) { if (cellList[0]) { - svgElement( + smallCornerTopLeftSvg( tag, - { - d: smallCornerTopLeft(this.squareSize), - stroke: left, - fill: left, - }, + left, grid, - `${id}-${SMALLTRI}-tl` + `${id}-${SMALLTRI}-tl`, + this.squareSize ); } else { svgElement( @@ -411,9 +386,40 @@ module.exports = class NeighborhoodDrawer extends Drawer { ); } } + if (top && left && bottom && right) { + smallCornerTopRightSvg( + tag, + top, + grid, + `${id}-${SMALLTRI}-tr`, + this.squareSize + ); + smallCornerTopLeftSvg( + tag, + top, + grid, + `${id}-${SMALLTRI}-tl`, + this.squareSize + ); + smallCornerBottomLeftSvg( + tag, + bottom, + grid, + `${id}-${SMALLTRI}-bl`, + this.squareSize + ); + smallCornerBottomRightSvg( + tag, + bottom, + grid, + `${id}-${SMALLTRI}-br`, + this.squareSize + ); + } } } + // Creates the parent svg for this grid tile makeGrid(row, col, svg) { let id = "g" + row + "." + col; return svgElement( @@ -495,27 +501,30 @@ module.exports = class NeighborhoodDrawer extends Drawer { ); } - for (let r = row - 1; r < row + 2; r++) { - for (let c = col - 1; c < col + 2; c++) { - let id = r + "." + c + "."; - - let cells = [ - this.cellColor(r - 1, c - 1), // Top left - this.cellColor(r, c - 1), // Top - this.cellColor(r + 1, c - 1), // Top right - this.cellColor(r - 1, c), // Middle left - this.cellColor(r, c), // Target cell - this.cellColor(r + 1, c), // Middle right - this.cellColor(r - 1, c + 1), // Bottom left - this.cellColor(r, c + 1), // Bottom - this.cellColor(r + 1, c + 1), // Bottom right - ]; - - // Create grid block group for this center focus cell - let grid = this.makeGrid(r, c, this.svg_); - - // Calculate all the svg paths based on neighboring cell colors - this.pathCalculator(cells, grid, id); + // Only calculate colors for all neighbors if this cell has a color + if (this.cellColor(row, col)) { + for (let r = row - 1; r < row + 2; r++) { + for (let c = col - 1; c < col + 2; c++) { + let id = r + "." + c + "."; + + let cells = [ + this.cellColor(r - 1, c - 1), // Top left + this.cellColor(r - 1, c), // Top + this.cellColor(r - 1, c + 1), // Top right + this.cellColor(r, c - 1), // Middle left + this.cellColor(r, c), // Target cell + this.cellColor(r, c + 1), // Middle right + this.cellColor(r + 1, c - 1), // Bottom left + this.cellColor(r + 1, c), // Bottom + this.cellColor(r + 1, c + 1), // Bottom right + ]; + + // Create grid block group for this center focus cell + let grid = this.makeGrid(r, c, this.svg_); + + // Calculate all the svg paths based on neighboring cell colors + this.pathCalculator(cells, grid, id); + } } } } From dffd2e80b1900299badff6f5518e5a6a84493e37 Mon Sep 17 00:00:00 2001 From: Hannah Bergam Date: Thu, 24 Jun 2021 17:23:29 +0000 Subject: [PATCH 07/14] both algorithms working, set up with flag in createDrawer method --- src/neighborhood.js | 30 +- src/neighborhoodCircleDrawer.js | 391 ++++++++++++++++++ ...dDrawer.js => neighborhoodSquareDrawer.js} | 0 3 files changed, 412 insertions(+), 9 deletions(-) create mode 100644 src/neighborhoodCircleDrawer.js rename src/{neighborhoodDrawer.js => neighborhoodSquareDrawer.js} (100%) diff --git a/src/neighborhood.js b/src/neighborhood.js index 0ee1878..c68a2d0 100644 --- a/src/neighborhood.js +++ b/src/neighborhood.js @@ -1,6 +1,7 @@ import Subtype from "./subtype"; import NeighborhoodCell from "./neighborhoodCell"; -import NeighborhoodDrawer from "./neighborhoodDrawer"; +import NeighborhoodSquareDrawer from "./neighborhoodSquareDrawer"; +import NeighborhoodCircleDrawer from "./neighborhoodCircleDrawer"; import { Direction } from "./tiles"; module.exports = class Neighborhood extends Subtype { @@ -75,14 +76,25 @@ module.exports = class Neighborhood extends Subtype { /** * @override **/ - createDrawer(svg) { - this.drawer = new NeighborhoodDrawer( - this.maze_.map, - this.skin_, - svg, - this.squareSize, - this - ); + createDrawer(svg, drawer = "square") { + if (drawer === "square") { + console.log("this got called"); + this.drawer = new NeighborhoodSquareDrawer( + this.maze_.map, + this.skin_, + svg, + this.squareSize, + this + ); + } else { + this.drawer = new NeighborhoodCircleDrawer( + this.maze_.map, + this.skin_, + svg, + this.squareSize, + this + ); + } } /** diff --git a/src/neighborhoodCircleDrawer.js b/src/neighborhoodCircleDrawer.js new file mode 100644 index 0000000..5aea97d --- /dev/null +++ b/src/neighborhoodCircleDrawer.js @@ -0,0 +1,391 @@ +const { SVG_NS } = require("./drawer"); +const Drawer = require("./drawer"); +const tiles = require("./tiles"); + +const ROTATE180 = "rotate(180)"; +const ROTATENEG90 = "rotate(-90)"; +const ROTATE90 = "rotate(90)"; +const ROTATE0 = "rotate(0)"; +const CUT = "cut"; +const PIE = "pie"; + +/** + * This is a helper for creating SVG Elements. + * Groups are created by grid tile, under which paths are nested. These groups + * begin with "g" in the id. By checking for this when determining its position + * within the hierarchy, we can nest these groups just before the pegman, + * ensuring the pegman will appear on top of the paint. + * + * @param tag representing the element type, 'g' for group, 'path' for paths + * @param props representing the details of the element + * @param parent the parent it should be nested under + * @param id the unique identifier, beginning with 'g' if a group element + * @returns the element itself + */ +function svgElement(tag, props, parent, id) { + var node = document.getElementById(id); + if (!node) { + node = document.createElementNS(SVG_NS, tag); + node.setAttribute("id", id); + } + Object.keys(props).map(function (key) { + node.setAttribute(key, props[key]); + }); + if (parent && id.startsWith("g")) { + let pegmanElement = parent.getElementsByClassName("pegman-location")[0]; + parent.insertBefore(node, pegmanElement); + } else if (parent) { + parent.appendChild(node); + } + return node; +} + +// Path drawing a quarter circle +// --+ +// / | +// +---+ +function quarterCircle(size) { + let halfSize = size / 2; + let quarterSize = size / 4; + return `m${halfSize} ${halfSize}h-${halfSize}c0-${quarterSize} ${quarterSize}-${halfSize} ${halfSize}-${halfSize}z`; +} + +// Path of the the slice of a square remaining once a quarter circle is +// removed from it +// +----+ +// | / +// + +function cutout(size) { + let halfSize = size / 2; + let quarterSize = size / 4; + return `m0 0v${halfSize}c0-${quarterSize} ${quarterSize}-${halfSize} ${halfSize}-${halfSize}z`; +} + +/** + * This drawer hosts all paint glomming logic. + * A note on layering paint: If paint is applied on top of existing paint + * (that has not been removed/scraped), portions of the cell might still + * display the first layer of paint. Example: [blue][blue] in layer 1 will + * create a "pill" visual. If the second cell is then painted [yellow], the + * yellow circle will appear on top, with the blue cutouts still visible below. + */ +module.exports = class NeighborhoodDrawer extends Drawer { + constructor(map, skin, svg, squareSize, neighborhood) { + super(map, "", svg); + this.squareSize = squareSize; + this.neighborhood = neighborhood; + this.skin_ = skin; + } + + /** + * Set the color of this tile back to null, and remove any svg elements + * (colors) that currently exist on this tile and its neighbors. + * + * @param row + * @param col + */ + resetTile(row, col) { + let neighbors = [ + "g" + row + "." + col, + "g" + (row - 1) + "." + (col - 1), + "g" + row + "." + (col - 1), + "g" + (row - 1) + "." + col, + ]; + const cell = this.neighborhood.getCell(row, col); + cell.setColor(null); + for (const neighbor of neighbors) { + var node = document.getElementById(neighbor); + if (node) { + node.querySelectorAll("*").forEach((n) => n.remove()); + } + } + } + + /** + * @override + */ + getAsset(prefix, row, col) { + const cell = this.neighborhood.getCell(row, col); + // only cells with a value are handled by getAsset. + if (cell.getCurrentValue()) { + return this.skin_.paintCan; + } + } + + getBackgroundTileInfo(row, col) { + const cell = this.neighborhood.getCell(row, col); + // If the tile has an asset id and it is > 0 (0 is a blank tile and will always be added), + // return the sprite asset. + // Ignore the asset id if this is a start tile or the cell has an original value. + // Start tiles will handle placing the pegman separately, + // and tiles with a value are paint cans, which are handled as images instead of background tiles. + if ( + cell.getAssetId() != null && + cell.getAssetId() > 0 && + cell.getTile() !== tiles.SquareType.START && + !cell.getOriginalValue() + ) { + return this.getSpriteData(cell); + } + } + + getSpriteData(cell) { + return this.neighborhood.getSpriteMap()[cell.getAssetId()]; + } + + /** + * Calls resetTile for each tile in the grid, clearing all paint. + */ + resetTiles() { + for (let row = 0; row < this.map_.ROWS; row++) { + for (let col = 0; col < this.map_.COLS; col++) { + this.resetTile(row, col); + } + } + } + + // Quick helper to retrieve the color stored in this cell + // Ensures 'padding cells' (row/col < 0) have no color + cellColor(row, col) { + if (row >= this.map_.ROWS || row < 0) return null; + if (col >= this.map_.COLS || col < 0) return null; + return this.map_.getCell(row, col).getColor() || null; + } + + // Helper method for determining color and path based on neighbors + pathCalculator( + subjectCell, + adjacent1, + adjacent2, + diagonal, + transform, + grid, + id + ) { + let pie = quarterCircle(this.squareSize); + let cutOut = cutout(this.squareSize); + let tag = "path"; + + // Add a quarter circle to the top left corner of the block if there is + // a color value there + if (subjectCell) { + svgElement( + tag, + { + d: pie, + stroke: subjectCell, + transform: transform, + fill: subjectCell, + }, + grid, + `${id}-${PIE}` + ); + } + // Add the cutout if the top left corner has a color and an adjacent cell + // shares that color, filling in the top left quadrant of the block entirely + if ( + subjectCell && + (subjectCell === adjacent1 || subjectCell === adjacent2) + ) { + svgElement( + tag, + { + d: cutOut, + stroke: subjectCell, + transform: transform, + fill: subjectCell, + }, + grid, + `${id}-${CUT}` + ); + } + // Otherwise, if the two adjacent corners have the same color, add the + // cutout shape with that color + else if ( + adjacent1 && + adjacent1 === adjacent2 && + (!diagonal || !subjectCell || subjectCell !== diagonal) + ) { + svgElement( + tag, + { d: cutOut, stroke: adjacent1, transform: transform, fill: adjacent1 }, + grid, + `${id}-${CUT}` + ); + } + // Fill in center corner only if an adjacent cell has the same color, or if + // the diagonal cell is same color and either adjacent is empty + // Note: this handles the "clover case", where we want each + // cell to "pop" out with its own color if diagonals are matching + else if ( + subjectCell && + (adjacent1 === subjectCell || + adjacent2 === subjectCell || + (diagonal === subjectCell && + (!adjacent1 || !adjacent2 || adjacent1 !== adjacent2))) + ) { + svgElement( + tag, + { + d: cutOut, + stroke: subjectCell, + transform: transform, + fill: subjectCell, + }, + grid, + `${id}-${CUT}` + ); + } + } + + makeGrid(row, col, svg) { + let id = "g" + row + "." + col; + return svgElement( + "g", + { + transform: `translate(${col * this.squareSize + this.squareSize}, + ${row * this.squareSize + this.squareSize})`, + }, + svg, + id + ); + } + + /** + * @override + * Draw the given tile at row, col + */ + drawTile(svg, tileSheetLocation, row, col, tileId, tileSheetHref) { + // we have one background tile for neighborhood (we don't define paths like + // the other skins). Therefore our 'tile sheet' is just one square. + const tileSheetWidth = this.squareSize; + const tileSheetHeight = this.squareSize; + + super.drawTileHelper( + svg, + tileSheetLocation, + row, + col, + tileId, + tileSheetHref, + tileSheetWidth, + tileSheetHeight, + this.squareSize + ); + } + + // Iterates through all neighborhood assets and inserts them after the pegman + drawAssets() { + let assetList = this.neighborhood.getAssetList(); + var i; + for (i = 0; i < assetList.length; i++) { + let asset = assetList[i]; + let node = document.getElementById(asset); + let pegmanElement = + this.svg_.getElementsByClassName("pegman-location")[0]; + this.svg_.insertBefore(node, pegmanElement); + } + } + + /** + * @override + * This method is used to display the paint and paint buckets. + * It has to reprocess the entire grid to get the paint glomming correct, but + * it only updates the bucket at the specified itemRow and itemCol if necessary. + * @param {number} itemRow: row of update + * @param {number} itemCol: column of update + * @param {boolean} running: if the maze is currently running (not used here, but part of signature of super) + */ + updateItemImage(itemRow, itemCol, running) { + let cell = this.map_.getCell(itemRow, itemCol); + + // if the cell value has ever been greater than 0, this has been or + // is a paint can square. Ensure it is shown/hidden appropriately + // and with the correct value. + if (cell.getOriginalValue() > 0) { + const newValue = cell.getCurrentValue() > 0 ? cell.getCurrentValue() : ""; + // drawImage_ calls getAsset. If currentValue() is 0, getAsset will return + // undefined and the paint can will be hidden. Otherwise we will get the paint can image. + super.drawImage_("", itemRow, itemCol, this.squareSize); + super.updateOrCreateText_( + "counter", + itemRow, + itemCol, + newValue, + this.squareSize, + 1, + 1, + "karel-counter-text paint" + ); + } + + // Because this processes a grid of cells at a time, we start at -1 to allow for + // a 'padding' row and column with no color. + for (let row = -1; row < this.map_.ROWS; row++) { + for (let col = -1; col < this.map_.COLS; col++) { + /** + * In a grid of four cells: top left, top right, bottom left, bottom right + * So if we are painting cell 0, adjacent cells are 1 & 2, diagonal is 3 + * +-------+ + * | 0 | 1 | + * -------- + * | 2 | 3 | + * +-------+ + */ + let cells = [ + this.cellColor(row, col), + this.cellColor(row, col + 1), + this.cellColor(row + 1, col), + this.cellColor(row + 1, col + 1), + ]; + + if (cells[0] || cells[1] || cells[2] || cells[3]) { + // Create grid block group + let grid = this.makeGrid(row, col, this.svg_); + let id0 = row + "." + col + "." + ROTATE180; + let id1 = row + "." + col + "." + ROTATENEG90; + let id2 = row + "." + col + "." + ROTATE90; + let id3 = row + "." + col + "." + ROTATE0; + + // Calculate all the svg paths based on neighboring cell colors + this.pathCalculator( + cells[0], + cells[1], + cells[2], + cells[3], + ROTATE180, + grid, + id0 + ); + this.pathCalculator( + cells[1], + cells[0], + cells[3], + cells[2], + ROTATENEG90, + grid, + id1 + ); + this.pathCalculator( + cells[2], + cells[0], + cells[3], + cells[1], + ROTATE90, + grid, + id2 + ); + this.pathCalculator( + cells[3], + cells[1], + cells[2], + cells[0], + ROTATE0, + grid, + id3 + ); + } + } + } + } +}; diff --git a/src/neighborhoodDrawer.js b/src/neighborhoodSquareDrawer.js similarity index 100% rename from src/neighborhoodDrawer.js rename to src/neighborhoodSquareDrawer.js From a82d085f289f54c9d0cb5be34b3a18a03290e9ae Mon Sep 17 00:00:00 2001 From: Hannah Bergam Date: Thu, 24 Jun 2021 20:56:15 +0000 Subject: [PATCH 08/14] multiple color specifications, major refactor --- src/neighborhood.js | 1 - src/neighborhoodSquareDrawer.js | 153 ++++++++++---------------------- 2 files changed, 45 insertions(+), 109 deletions(-) diff --git a/src/neighborhood.js b/src/neighborhood.js index c68a2d0..c49276f 100644 --- a/src/neighborhood.js +++ b/src/neighborhood.js @@ -78,7 +78,6 @@ module.exports = class Neighborhood extends Subtype { **/ createDrawer(svg, drawer = "square") { if (drawer === "square") { - console.log("this got called"); this.drawer = new NeighborhoodSquareDrawer( this.maze_.map, this.skin_, diff --git a/src/neighborhoodSquareDrawer.js b/src/neighborhoodSquareDrawer.js index 8d29a8c..7f33277 100644 --- a/src/neighborhoodSquareDrawer.js +++ b/src/neighborhoodSquareDrawer.js @@ -5,6 +5,14 @@ const tiles = require("./tiles"); const TRIANGLE = "triangle"; const SMALLTRI = "smallCorner"; const CENTER = "center"; +const PATH = "path"; + +const SmallCorner = Object.freeze({ + topLeft: "topLeft", + topRight: "topRight", + bottomLeft: "bottomLeft", + bottomRight: "bottomRight", +}); /** * This is a helper for creating SVG Elements. @@ -40,61 +48,37 @@ function svgElement(tag, props, parent, id) { /** * The following functions create SVGs for the small corner cutouts * - * @param tag the kind of SVG * @param color the stroke and fill colors * @param grid the parent element * @param id the id label * @param size the square size + * @param corner the enum stating which corner to draw */ -function smallCornerTopLeftSvg(tag, color, grid, id, size) { - svgElement( - tag, - { - d: `m0,0 L${0.4 * size},0 L0,${0.4 * size} Z`, - stroke: color, - fill: color, - }, - grid, - id - ); -} - -function smallCornerBottomLeftSvg(tag, color, grid, id, size) { - svgElement( - tag, - { - d: `m0,${size} L0,${0.6 * size} L${0.4 * size},${size} Z`, - stroke: color, - fill: color, - }, - grid, - id - ); -} - -function smallCornerTopRightSvg(tag, color, grid, id, size) { - svgElement( - tag, - { - d: `m${size},0 L${0.6 * size},0 L${size},${0.4 * size} Z`, - stroke: color, - fill: color, - }, - grid, - id - ); -} - -function smallCornerBottomRightSvg(tag, color, grid, id, size) { +function smallCornerSvg(color, grid, id, size, corner) { + let finalId; + let shape; + if (corner === SmallCorner.topLeft) { + finalId = `${id}-${SMALLTRI}-tl`; + shape = `m0,0 L${0.4 * size},0 L0,${0.4 * size} Z`; + } else if (corner === SmallCorner.topRight) { + finalId = `${id}-${SMALLTRI}-tr`; + shape = `m${size},0 L${0.6 * size},0 L${size},${0.4 * size} Z`; + } else if (corner === SmallCorner.bottomLeft) { + finalId = `${id}-${SMALLTRI}-bl`; + shape = `m0,${size} L0,${0.6 * size} L${0.4 * size},${size} Z`; + } else if (corner === SmallCorner.bottomRight) { + finalId = `${id}-${SMALLTRI}-br`; + shape = `m${size},${size} L${0.6 * size},${size} L${size},${0.6 * size} Z`; + } svgElement( - tag, + PATH, { - d: `m${size},${size} L${0.6 * size},${size} L${size},${0.6 * size} Z`, + d: shape, stroke: color, fill: color, }, grid, - id + finalId ); } @@ -281,6 +265,7 @@ module.exports = class NeighborhoodDrawer extends Drawer { // Helper method for determining color and path based on neighbors pathCalculator(cellList, grid, id) { let tag = "path"; + let size = this.squareSize; // 0 1 2 // 3 4 5 @@ -298,15 +283,9 @@ module.exports = class NeighborhoodDrawer extends Drawer { } else { // Check each set of adjacent neighbors and diagonal to determine if small // corners or triangle half-grids should be drawn - if (top && right) { - if (cellList[2]) { - smallCornerTopRightSvg( - tag, - top, - grid, - `${id}-${SMALLTRI}-tr`, - this.squareSize - ); + if (top && right && top === right) { + if (cellList[2] && cellList[2] === top) { + smallCornerSvg(top, grid, id, size, SmallCorner.topRight); } else { svgElement( tag, @@ -320,15 +299,9 @@ module.exports = class NeighborhoodDrawer extends Drawer { ); } } - if (right && bottom) { - if (cellList[8]) { - smallCornerBottomRightSvg( - tag, - right, - grid, - `${id}-${SMALLTRI}-br`, - this.squareSize - ); + if (right && bottom && right === bottom) { + if (cellList[8] && cellList[8] === right) { + smallCornerSvg(right, grid, id, size, SmallCorner.bottomRight); } else { svgElement( tag, @@ -342,15 +315,9 @@ module.exports = class NeighborhoodDrawer extends Drawer { ); } } - if (bottom && left) { - if (cellList[6]) { - smallCornerBottomLeftSvg( - tag, - bottom, - grid, - `${id}-${SMALLTRI}-bl`, - this.squareSize - ); + if (bottom && left && bottom === left) { + if (cellList[6] && cellList[6] === bottom) { + smallCornerSvg(bottom, grid, id, size, SmallCorner.bottomLeft); } else { svgElement( tag, @@ -364,15 +331,9 @@ module.exports = class NeighborhoodDrawer extends Drawer { ); } } - if (left && top) { - if (cellList[0]) { - smallCornerTopLeftSvg( - tag, - left, - grid, - `${id}-${SMALLTRI}-tl`, - this.squareSize - ); + if (left && top && left === top) { + if (cellList[0] && cellList[0] === left) { + smallCornerSvg(left, grid, id, size, SmallCorner.topLeft); } else { svgElement( tag, @@ -387,34 +348,10 @@ module.exports = class NeighborhoodDrawer extends Drawer { } } if (top && left && bottom && right) { - smallCornerTopRightSvg( - tag, - top, - grid, - `${id}-${SMALLTRI}-tr`, - this.squareSize - ); - smallCornerTopLeftSvg( - tag, - top, - grid, - `${id}-${SMALLTRI}-tl`, - this.squareSize - ); - smallCornerBottomLeftSvg( - tag, - bottom, - grid, - `${id}-${SMALLTRI}-bl`, - this.squareSize - ); - smallCornerBottomRightSvg( - tag, - bottom, - grid, - `${id}-${SMALLTRI}-br`, - this.squareSize - ); + smallCornerSvg(top, grid, id, size, SmallCorner.topLeft); + smallCornerSvg(top, grid, id, size, SmallCorner.topRight); + smallCornerSvg(bottom, grid, id, size, SmallCorner.bottomLeft); + smallCornerSvg(bottom, grid, id, size, SmallCorner.bottomRight); } } } From 20fc919eacc2036439a41aa8d4030012e2430b7f Mon Sep 17 00:00:00 2001 From: Hannah Bergam Date: Thu, 24 Jun 2021 21:57:16 +0000 Subject: [PATCH 09/14] refactored triangles, added comments --- src/neighborhoodSquareDrawer.js | 175 +++++++++++++++----------------- 1 file changed, 84 insertions(+), 91 deletions(-) diff --git a/src/neighborhoodSquareDrawer.js b/src/neighborhoodSquareDrawer.js index 7f33277..097867a 100644 --- a/src/neighborhoodSquareDrawer.js +++ b/src/neighborhoodSquareDrawer.js @@ -7,7 +7,7 @@ const SMALLTRI = "smallCorner"; const CENTER = "center"; const PATH = "path"; -const SmallCorner = Object.freeze({ +const Corner = Object.freeze({ topLeft: "topLeft", topRight: "topRight", bottomLeft: "bottomLeft", @@ -57,18 +57,18 @@ function svgElement(tag, props, parent, id) { function smallCornerSvg(color, grid, id, size, corner) { let finalId; let shape; - if (corner === SmallCorner.topLeft) { + if (corner === Corner.topLeft) { finalId = `${id}-${SMALLTRI}-tl`; - shape = `m0,0 L${0.4 * size},0 L0,${0.4 * size} Z`; - } else if (corner === SmallCorner.topRight) { + shape = `m0,0 L${0.3 * size},0 L0,${0.3 * size} Z`; + } else if (corner === Corner.topRight) { finalId = `${id}-${SMALLTRI}-tr`; - shape = `m${size},0 L${0.6 * size},0 L${size},${0.4 * size} Z`; - } else if (corner === SmallCorner.bottomLeft) { + shape = `m${size},0 L${0.7 * size},0 L${size},${0.3 * size} Z`; + } else if (corner === Corner.bottomLeft) { finalId = `${id}-${SMALLTRI}-bl`; - shape = `m0,${size} L0,${0.6 * size} L${0.4 * size},${size} Z`; - } else if (corner === SmallCorner.bottomRight) { + shape = `m0,${size} L0,${0.7 * size} L${0.3 * size},${size} Z`; + } else if (corner === Corner.bottomRight) { finalId = `${id}-${SMALLTRI}-br`; - shape = `m${size},${size} L${0.6 * size},${size} L${size},${0.6 * size} Z`; + shape = `m${size},${size} L${0.7 * size},${size} L${size},${0.7 * size} Z`; } svgElement( PATH, @@ -83,26 +83,41 @@ function smallCornerSvg(color, grid, id, size, corner) { } /** - * The following four functions return the path elements for each potential - * triangle position that takes up half the grid size. + * Returns the svg element for the half-grid triangle depending on which + * corner is the source. * - * @param size for square size - * @returns the string representing the svg path + * @param color the stroke and fill colors + * @param grid the parent element + * @param id the id label + * @param size the square size + * @param corner the enum stating which corner to draw */ -function trianglePathTopLeft(size) { - return `m0,0 L${size},0 L0,${size} Z`; -} - -function trianglePathBottomLeft(size) { - return `m0,${size} L${size},${size} L0,0 Z`; -} - -function trianglePathTopRight(size) { - return `m${size},0 L${size},${size} L0,0 Z`; -} - -function trianglePathBottomRight(size) { - return `m${size},${size} L${size},0 L0,${size} Z`; +function triangleSvg(color, grid, id, size, corner) { + let finalId; + let shape; + if (corner === Corner.topLeft) { + finalId = `${id}-${TRIANGLE}-tl`; + shape = `m0,0 L${size},0 L0,${size} Z`; + } else if (corner === Corner.topRight) { + finalId = `${id}-${TRIANGLE}-tr`; + shape = `m${size},0 L${size},${size} L0,0 Z`; + } else if (corner === Corner.bottomLeft) { + finalId = `${id}-${TRIANGLE}-bl`; + shape = `m0,${size} L${size},${size} L0,0 Z`; + } else if (corner === Corner.bottomRight) { + finalId = `${id}-${TRIANGLE}-br`; + shape = `m${size},${size} L${size},0 L0,${size} Z`; + } + svgElement( + PATH, + { + d: shape, + stroke: color, + fill: color, + }, + grid, + finalId + ); } function generateCenterPath( @@ -113,16 +128,16 @@ function generateCenterPath( bottomLeftIsTruncated ) { const topLeftCorner = topLeftIsTruncated - ? `m0,${size * 0.4} L${size * 0.4},0` + ? `m0,${size * 0.3} L${size * 0.3},0` : `m0,0`; const topRightCorner = topRightIsTruncated - ? `L${size * 0.6},0 L${size},${size * 0.4}` + ? `L${size * 0.7},0 L${size},${size * 0.3}` : `L${size},0`; const bottomRightCorner = bottomRightIsTruncated - ? `L${size},${size * 0.6} L${size * 0.6},${size}` + ? `L${size},${size * 0.7} L${size * 0.7},${size}` : `L${size},${size}`; const bottomLeftCorner = bottomLeftIsTruncated - ? `L${size * 0.4},${size} L0,${size * 0.6}` + ? `L${size * 0.3},${size} L0,${size * 0.7}` : `L0,${size}`; return `${topLeftCorner} ${topRightCorner} ${bottomRightCorner} ${bottomLeftCorner} Z`; } @@ -262,15 +277,20 @@ module.exports = class NeighborhoodDrawer extends Drawer { ); } - // Helper method for determining color and path based on neighbors + /** + * Holds the bulk of the logic of coloring based on neighbor cells. The order + * of cells in the input list is as follows: + * + * 0 1 2 + * 3 4 5 + * 6 7 8 + * + * @param cellList representing the center (4) and its 8 surrounding + * @param grid the parent element we will add svg elements to + * @param id the row and column we're on in id form + */ pathCalculator(cellList, grid, id) { - let tag = "path"; let size = this.squareSize; - - // 0 1 2 - // 3 4 5 - // 6 7 8 - let center = cellList[4]; let top = cellList[1]; let right = cellList[5]; @@ -278,81 +298,54 @@ module.exports = class NeighborhoodDrawer extends Drawer { let left = cellList[3]; // if the center cell has paint, calculate its fill and corners - if (cellList[4]) { + if (center) { this.centerFill(center, top, right, bottom, left, grid, id); + } + // the circle case: ensure the center cell only has small corners + else if ( + top && + left && + bottom && + right && + top === left && + left === bottom && + bottom === right + ) { + smallCornerSvg(top, grid, id, size, Corner.topLeft); + smallCornerSvg(top, grid, id, size, Corner.topRight); + smallCornerSvg(bottom, grid, id, size, Corner.bottomLeft); + smallCornerSvg(bottom, grid, id, size, Corner.bottomRight); } else { - // Check each set of adjacent neighbors and diagonal to determine if small - // corners or triangle half-grids should be drawn + // Check each set of adjacent neighbors and corner cell to determine if + // small corners or triangle half-grids should be drawn if (top && right && top === right) { if (cellList[2] && cellList[2] === top) { - smallCornerSvg(top, grid, id, size, SmallCorner.topRight); + smallCornerSvg(top, grid, id, size, Corner.topRight); } else { - svgElement( - tag, - { - d: trianglePathTopRight(this.squareSize), - stroke: top, - fill: top, - }, - grid, - `${id}-${TRIANGLE}-tr` - ); + triangleSvg(top, grid, id, size, Corner.topRight); } } if (right && bottom && right === bottom) { if (cellList[8] && cellList[8] === right) { - smallCornerSvg(right, grid, id, size, SmallCorner.bottomRight); + smallCornerSvg(right, grid, id, size, Corner.bottomRight); } else { - svgElement( - tag, - { - d: trianglePathBottomRight(this.squareSize), - stroke: right, - fill: right, - }, - grid, - `${id}-${TRIANGLE}-br` - ); + triangleSvg(top, grid, id, size, Corner.bottomRight); } } if (bottom && left && bottom === left) { if (cellList[6] && cellList[6] === bottom) { - smallCornerSvg(bottom, grid, id, size, SmallCorner.bottomLeft); + smallCornerSvg(bottom, grid, id, size, Corner.bottomLeft); } else { - svgElement( - tag, - { - d: trianglePathBottomLeft(this.squareSize), - stroke: bottom, - fill: bottom, - }, - grid, - `${id}-${TRIANGLE}-bl` - ); + triangleSvg(top, grid, id, size, Corner.bottomLeft); } } if (left && top && left === top) { if (cellList[0] && cellList[0] === left) { - smallCornerSvg(left, grid, id, size, SmallCorner.topLeft); + smallCornerSvg(left, grid, id, size, Corner.topLeft); } else { - svgElement( - tag, - { - d: trianglePathTopLeft(this.squareSize), - stroke: left, - fill: left, - }, - grid, - `${id}-${TRIANGLE}-tl` - ); + triangleSvg(top, grid, id, size, Corner.topLeft); } } - if (top && left && bottom && right) { - smallCornerSvg(top, grid, id, size, SmallCorner.topLeft); - smallCornerSvg(top, grid, id, size, SmallCorner.topRight); - smallCornerSvg(bottom, grid, id, size, SmallCorner.bottomLeft); - smallCornerSvg(bottom, grid, id, size, SmallCorner.bottomRight); - } } } From f7529b2fd371571868a5d0c39175fd13701e3ccc Mon Sep 17 00:00:00 2001 From: Hannah Bergam Date: Thu, 24 Jun 2021 22:19:16 +0000 Subject: [PATCH 10/14] more cleaning, refactor constants, fix circle case --- src/neighborhoodSquareDrawer.js | 37 ++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/src/neighborhoodSquareDrawer.js b/src/neighborhoodSquareDrawer.js index 097867a..2a97004 100644 --- a/src/neighborhoodSquareDrawer.js +++ b/src/neighborhoodSquareDrawer.js @@ -7,6 +7,11 @@ const SMALLTRI = "smallCorner"; const CENTER = "center"; const PATH = "path"; +// These multipliers control how far across the grid the corners are cut +// To keep the corners "even", they should add up to 1 +const SMALLMULT = 0.3; +const LARGEMULT = 0.7; + const Corner = Object.freeze({ topLeft: "topLeft", topRight: "topRight", @@ -59,16 +64,18 @@ function smallCornerSvg(color, grid, id, size, corner) { let shape; if (corner === Corner.topLeft) { finalId = `${id}-${SMALLTRI}-tl`; - shape = `m0,0 L${0.3 * size},0 L0,${0.3 * size} Z`; + shape = `m0,0 L${SMALLMULT * size},0 L0,${SMALLMULT * size} Z`; } else if (corner === Corner.topRight) { finalId = `${id}-${SMALLTRI}-tr`; - shape = `m${size},0 L${0.7 * size},0 L${size},${0.3 * size} Z`; + shape = `m${size},0 L${LARGEMULT * size},0 L${size},${SMALLMULT * size} Z`; } else if (corner === Corner.bottomLeft) { finalId = `${id}-${SMALLTRI}-bl`; - shape = `m0,${size} L0,${0.7 * size} L${0.3 * size},${size} Z`; + shape = `m0,${size} L0,${LARGEMULT * size} L${SMALLMULT * size},${size} Z`; } else if (corner === Corner.bottomRight) { finalId = `${id}-${SMALLTRI}-br`; - shape = `m${size},${size} L${0.7 * size},${size} L${size},${0.7 * size} Z`; + shape = `m${size},${size} L${LARGEMULT * size},${size} L${size},${ + LARGEMULT * size + } Z`; } svgElement( PATH, @@ -128,16 +135,16 @@ function generateCenterPath( bottomLeftIsTruncated ) { const topLeftCorner = topLeftIsTruncated - ? `m0,${size * 0.3} L${size * 0.3},0` + ? `m0,${size * SMALLMULT} L${size * SMALLMULT},0` : `m0,0`; const topRightCorner = topRightIsTruncated - ? `L${size * 0.7},0 L${size},${size * 0.3}` + ? `L${size * LARGEMULT},0 L${size},${size * SMALLMULT}` : `L${size},0`; const bottomRightCorner = bottomRightIsTruncated - ? `L${size},${size * 0.7} L${size * 0.7},${size}` + ? `L${size},${size * LARGEMULT} L${size * LARGEMULT},${size}` : `L${size},${size}`; const bottomLeftCorner = bottomLeftIsTruncated - ? `L${size * 0.3},${size} L0,${size * 0.7}` + ? `L${size * SMALLMULT},${size} L0,${size * LARGEMULT}` : `L0,${size}`; return `${topLeftCorner} ${topRightCorner} ${bottomRightCorner} ${bottomLeftCorner} Z`; } @@ -311,6 +318,12 @@ module.exports = class NeighborhoodDrawer extends Drawer { left === bottom && bottom === right ) { + // We know half triangles have already been drawn: remove them + let gridId = "g" + id; + var node = document.getElementById(gridId); + if (node) { + node.querySelectorAll("*").forEach((n) => n.remove()); + } smallCornerSvg(top, grid, id, size, Corner.topLeft); smallCornerSvg(top, grid, id, size, Corner.topRight); smallCornerSvg(bottom, grid, id, size, Corner.bottomLeft); @@ -329,21 +342,21 @@ module.exports = class NeighborhoodDrawer extends Drawer { if (cellList[8] && cellList[8] === right) { smallCornerSvg(right, grid, id, size, Corner.bottomRight); } else { - triangleSvg(top, grid, id, size, Corner.bottomRight); + triangleSvg(right, grid, id, size, Corner.bottomRight); } } if (bottom && left && bottom === left) { if (cellList[6] && cellList[6] === bottom) { smallCornerSvg(bottom, grid, id, size, Corner.bottomLeft); } else { - triangleSvg(top, grid, id, size, Corner.bottomLeft); + triangleSvg(bottom, grid, id, size, Corner.bottomLeft); } } if (left && top && left === top) { if (cellList[0] && cellList[0] === left) { smallCornerSvg(left, grid, id, size, Corner.topLeft); } else { - triangleSvg(top, grid, id, size, Corner.topLeft); + triangleSvg(left, grid, id, size, Corner.topLeft); } } } @@ -435,7 +448,7 @@ module.exports = class NeighborhoodDrawer extends Drawer { if (this.cellColor(row, col)) { for (let r = row - 1; r < row + 2; r++) { for (let c = col - 1; c < col + 2; c++) { - let id = r + "." + c + "."; + let id = r + "." + c; let cells = [ this.cellColor(r - 1, c - 1), // Top left From 24f3585c8319ead70faca13ed64f3f529c55dd44 Mon Sep 17 00:00:00 2001 From: Hannah Bergam Date: Fri, 25 Jun 2021 21:13:56 +0000 Subject: [PATCH 11/14] fixed elbow drawing, simplified center collor fill --- src/neighborhoodSquareDrawer.js | 67 ++++++++++++++------------------- 1 file changed, 29 insertions(+), 38 deletions(-) diff --git a/src/neighborhoodSquareDrawer.js b/src/neighborhoodSquareDrawer.js index 2a97004..f382118 100644 --- a/src/neighborhoodSquareDrawer.js +++ b/src/neighborhoodSquareDrawer.js @@ -244,30 +244,20 @@ module.exports = class NeighborhoodDrawer extends Drawer { * Determines how much of this tile should be colored in based on the colors * of the adjacent neighbors. */ - centerFill(center, top, right, bottom, left, grid, id) { + centerFill(cellColorList, grid, id) { + let center = cellColorList[4]; + let top = cellColorList[1]; + let right = cellColorList[5]; + let bottom = cellColorList[7]; + let left = cellColorList[3]; var path; - if (center == top && center == right && center != bottom && center != left) + if (center == top && center == right && !bottom && !left) path = generateCenterPath(this.squareSize, false, false, false, true); - else if ( - center == right && - center == bottom && - center != left && - center != top - ) + else if (center == right && center == bottom && !left && !top) path = generateCenterPath(this.squareSize, true, false, false, false); - else if ( - center == bottom && - center == left && - center != top && - center != right - ) + else if (center == bottom && center == left && !top && !right) path = generateCenterPath(this.squareSize, false, true, false, false); - else if ( - center == left && - center == top && - center != right && - center != bottom - ) + else if (center == left && center == top && !right && !bottom) path = generateCenterPath(this.squareSize, false, false, true, false); else { path = generateCenterPath(this.squareSize, false, false, false, false); @@ -292,21 +282,28 @@ module.exports = class NeighborhoodDrawer extends Drawer { * 3 4 5 * 6 7 8 * - * @param cellList representing the center (4) and its 8 surrounding + * @param cellColorList representing the center (4) and its 8 surrounding * @param grid the parent element we will add svg elements to * @param id the row and column we're on in id form */ - pathCalculator(cellList, grid, id) { + pathCalculator(cellColorList, grid, id) { let size = this.squareSize; - let center = cellList[4]; - let top = cellList[1]; - let right = cellList[5]; - let bottom = cellList[7]; - let left = cellList[3]; + let center = cellColorList[4]; + let top = cellColorList[1]; + let right = cellColorList[5]; + let bottom = cellColorList[7]; + let left = cellColorList[3]; + + // If anything has been drawn in this cell already, remove it + let gridId = "g" + id; + var node = document.getElementById(gridId); + if (node) { + node.querySelectorAll("*").forEach((n) => n.remove()); + } // if the center cell has paint, calculate its fill and corners if (center) { - this.centerFill(center, top, right, bottom, left, grid, id); + this.centerFill(cellColorList, grid, id); } // the circle case: ensure the center cell only has small corners else if ( @@ -318,12 +315,6 @@ module.exports = class NeighborhoodDrawer extends Drawer { left === bottom && bottom === right ) { - // We know half triangles have already been drawn: remove them - let gridId = "g" + id; - var node = document.getElementById(gridId); - if (node) { - node.querySelectorAll("*").forEach((n) => n.remove()); - } smallCornerSvg(top, grid, id, size, Corner.topLeft); smallCornerSvg(top, grid, id, size, Corner.topRight); smallCornerSvg(bottom, grid, id, size, Corner.bottomLeft); @@ -332,28 +323,28 @@ module.exports = class NeighborhoodDrawer extends Drawer { // Check each set of adjacent neighbors and corner cell to determine if // small corners or triangle half-grids should be drawn if (top && right && top === right) { - if (cellList[2] && cellList[2] === top) { + if (cellColorList[2] && cellColorList[2] === top) { smallCornerSvg(top, grid, id, size, Corner.topRight); } else { triangleSvg(top, grid, id, size, Corner.topRight); } } if (right && bottom && right === bottom) { - if (cellList[8] && cellList[8] === right) { + if (cellColorList[8] && cellColorList[8] === right) { smallCornerSvg(right, grid, id, size, Corner.bottomRight); } else { triangleSvg(right, grid, id, size, Corner.bottomRight); } } if (bottom && left && bottom === left) { - if (cellList[6] && cellList[6] === bottom) { + if (cellColorList[6] && cellColorList[6] === bottom) { smallCornerSvg(bottom, grid, id, size, Corner.bottomLeft); } else { triangleSvg(bottom, grid, id, size, Corner.bottomLeft); } } if (left && top && left === top) { - if (cellList[0] && cellList[0] === left) { + if (cellColorList[0] && cellColorList[0] === left) { smallCornerSvg(left, grid, id, size, Corner.topLeft); } else { triangleSvg(left, grid, id, size, Corner.topLeft); From e84db872bc68674253cba83da4a365d72d0e3084 Mon Sep 17 00:00:00 2001 From: Hannah Bergam Date: Fri, 25 Jun 2021 22:52:00 +0000 Subject: [PATCH 12/14] added comments, more cleaning --- src/neighborhoodSquareDrawer.js | 62 +++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 23 deletions(-) diff --git a/src/neighborhoodSquareDrawer.js b/src/neighborhoodSquareDrawer.js index f382118..ad51b33 100644 --- a/src/neighborhoodSquareDrawer.js +++ b/src/neighborhoodSquareDrawer.js @@ -12,6 +12,7 @@ const PATH = "path"; const SMALLMULT = 0.3; const LARGEMULT = 0.7; +// This creates the js equivalent of an Enum for the corner names const Corner = Object.freeze({ topLeft: "topLeft", topRight: "topRight", @@ -91,7 +92,11 @@ function smallCornerSvg(color, grid, id, size, corner) { /** * Returns the svg element for the half-grid triangle depending on which - * corner is the source. + * corner is the source. For example, the following two are Corner.bottomLeft + * and Corner.bottomRight: + * . . + * | \ / | + * |___\ /___| * * @param color the stroke and fill colors * @param grid the parent element @@ -127,6 +132,14 @@ function triangleSvg(color, grid, id, size, corner) { ); } +/** + * Creates a path for a square with any number of corners 0-4 "cut out". + * For example, if only topRightIsTruncated = true: + * _____ + * | \ + * | | + * |______| + */ function generateCenterPath( size, topLeftIsTruncated, @@ -151,11 +164,6 @@ function generateCenterPath( /** * This drawer hosts all paint glomming logic. - * A note on layering paint: If paint is applied on top of existing paint - * (that has not been removed/scraped), portions of the cell might still - * display the first layer of paint. Example: [blue][blue] in layer 1 will - * create a "pill" visual. If the second cell is then painted [yellow], the - * yellow circle will appear on top, with the blue cutouts still visible below. */ module.exports = class NeighborhoodDrawer extends Drawer { constructor(map, skin, svg, squareSize, neighborhood) { @@ -276,23 +284,28 @@ module.exports = class NeighborhoodDrawer extends Drawer { /** * Holds the bulk of the logic of coloring based on neighbor cells. The order - * of cells in the input list is as follows: + * of cells in the input list is as follows, and are labeled accordingly: * * 0 1 2 * 3 4 5 * 6 7 8 * - * @param cellColorList representing the center (4) and its 8 surrounding + * @param cellColorList representing the colors of a grid of 9 cells * @param grid the parent element we will add svg elements to * @param id the row and column we're on in id form */ pathCalculator(cellColorList, grid, id) { let size = this.squareSize; - let center = cellColorList[4]; + + let topLeft = cellColorList[0]; let top = cellColorList[1]; + let topRight = cellColorList[2]; + let left = cellColorList[3]; + let center = cellColorList[4]; let right = cellColorList[5]; + let bottomLeft = cellColorList[6]; let bottom = cellColorList[7]; - let left = cellColorList[3]; + let bottomRight = cellColorList[8]; // If anything has been drawn in this cell already, remove it let gridId = "g" + id; @@ -305,7 +318,8 @@ module.exports = class NeighborhoodDrawer extends Drawer { if (center) { this.centerFill(cellColorList, grid, id); } - // the circle case: ensure the center cell only has small corners + // the circle case: ensure the center cell only has small corners if + // all surrounding cells are matching (this prevents a filled-in center) else if ( top && left && @@ -320,33 +334,35 @@ module.exports = class NeighborhoodDrawer extends Drawer { smallCornerSvg(bottom, grid, id, size, Corner.bottomLeft); smallCornerSvg(bottom, grid, id, size, Corner.bottomRight); } else { - // Check each set of adjacent neighbors and corner cell to determine if - // small corners or triangle half-grids should be drawn + // Check each set of adjacent neighbors and the corresponding corner cell + // to determine if small corners or triangle half-grids should be added. + // Only add the triangle half-grids if there is no color in the outside + // corner. if (top && right && top === right) { - if (cellColorList[2] && cellColorList[2] === top) { + if (topRight && topRight === top) { smallCornerSvg(top, grid, id, size, Corner.topRight); - } else { + } else if (!topRight) { triangleSvg(top, grid, id, size, Corner.topRight); } } if (right && bottom && right === bottom) { - if (cellColorList[8] && cellColorList[8] === right) { + if (bottomRight && bottomRight === right) { smallCornerSvg(right, grid, id, size, Corner.bottomRight); - } else { + } else if (!bottomRight) { triangleSvg(right, grid, id, size, Corner.bottomRight); } } if (bottom && left && bottom === left) { - if (cellColorList[6] && cellColorList[6] === bottom) { + if (bottomLeft && bottomLeft === bottom) { smallCornerSvg(bottom, grid, id, size, Corner.bottomLeft); - } else { + } else if (!bottomLeft) { triangleSvg(bottom, grid, id, size, Corner.bottomLeft); } } if (left && top && left === top) { - if (cellColorList[0] && cellColorList[0] === left) { + if (topLeft && topLeft === left) { smallCornerSvg(left, grid, id, size, Corner.topLeft); - } else { + } else if (!topLeft) { triangleSvg(left, grid, id, size, Corner.topLeft); } } @@ -406,8 +422,8 @@ module.exports = class NeighborhoodDrawer extends Drawer { /** * @override * This method is used to display the paint and paint buckets. - * It has to reprocess the entire grid to get the paint glomming correct, but - * it only updates the bucket at the specified row and col if necessary. + * It only updates the bucket at the specified row and col if necessary, and + * only updates the paint on the neighborhing cells. * @param {number} row: row of update * @param {number} col: column of update * @param {boolean} running: if the maze is currently running (not used here, but part of signature of super) From 8afc90b0ee4828031cf81ce36fdc5e96d4a58435 Mon Sep 17 00:00:00 2001 From: Hannah Bergam Date: Mon, 28 Jun 2021 19:10:43 +0000 Subject: [PATCH 13/14] refactored to simplify, added comments --- src/neighborhoodSquareDrawer.js | 39 ++++++++++++++------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/src/neighborhoodSquareDrawer.js b/src/neighborhoodSquareDrawer.js index ad51b33..01a8287 100644 --- a/src/neighborhoodSquareDrawer.js +++ b/src/neighborhoodSquareDrawer.js @@ -162,6 +162,19 @@ function generateCenterPath( return `${topLeftCorner} ${topRightCorner} ${bottomRightCorner} ${bottomLeftCorner} Z`; } +/** + * Determines whether we should create a small corner SVG or a grid half triangle SVG, + * if either. Add the corner cutout if the corner is the same color as the adjacent cells. + * Only add the triangle half-grids if there is no color in the outside corner. + */ +function cornerFill(grid, id, size, adjacentColor, cornerColor, corner) { + if (cornerColor && cornerColor === adjacentColor) { + smallCornerSvg(adjacentColor, grid, id, size, corner); + } else if (!cornerColor) { + triangleSvg(adjacentColor, grid, id, size, corner); + } +} + /** * This drawer hosts all paint glomming logic. */ @@ -336,35 +349,17 @@ module.exports = class NeighborhoodDrawer extends Drawer { } else { // Check each set of adjacent neighbors and the corresponding corner cell // to determine if small corners or triangle half-grids should be added. - // Only add the triangle half-grids if there is no color in the outside - // corner. if (top && right && top === right) { - if (topRight && topRight === top) { - smallCornerSvg(top, grid, id, size, Corner.topRight); - } else if (!topRight) { - triangleSvg(top, grid, id, size, Corner.topRight); - } + cornerFill(grid, id, size, top, topRight, Corner.topRight); } if (right && bottom && right === bottom) { - if (bottomRight && bottomRight === right) { - smallCornerSvg(right, grid, id, size, Corner.bottomRight); - } else if (!bottomRight) { - triangleSvg(right, grid, id, size, Corner.bottomRight); - } + cornerFill(grid, id, size, right, bottomRight, Corner.bottomRight); } if (bottom && left && bottom === left) { - if (bottomLeft && bottomLeft === bottom) { - smallCornerSvg(bottom, grid, id, size, Corner.bottomLeft); - } else if (!bottomLeft) { - triangleSvg(bottom, grid, id, size, Corner.bottomLeft); - } + cornerFill(grid, id, size, bottom, bottomLeft, Corner.bottomLeft); } if (left && top && left === top) { - if (topLeft && topLeft === left) { - smallCornerSvg(left, grid, id, size, Corner.topLeft); - } else if (!topLeft) { - triangleSvg(left, grid, id, size, Corner.topLeft); - } + cornerFill(grid, id, size, left, topLeft, Corner.topLeft); } } } From d273cddf5465883c0f5b41c80faab694a814a07d Mon Sep 17 00:00:00 2001 From: Hannah Bergam Date: Mon, 28 Jun 2021 21:54:07 +0000 Subject: [PATCH 14/14] updated name according to PR feedback --- src/neighborhoodSquareDrawer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/neighborhoodSquareDrawer.js b/src/neighborhoodSquareDrawer.js index 01a8287..97f4367 100644 --- a/src/neighborhoodSquareDrawer.js +++ b/src/neighborhoodSquareDrawer.js @@ -307,7 +307,7 @@ module.exports = class NeighborhoodDrawer extends Drawer { * @param grid the parent element we will add svg elements to * @param id the row and column we're on in id form */ - pathCalculator(cellColorList, grid, id) { + colorCells(cellColorList, grid, id) { let size = this.squareSize; let topLeft = cellColorList[0]; @@ -468,7 +468,7 @@ module.exports = class NeighborhoodDrawer extends Drawer { let grid = this.makeGrid(r, c, this.svg_); // Calculate all the svg paths based on neighboring cell colors - this.pathCalculator(cells, grid, id); + this.colorCells(cells, grid, id); } } }