@code-dot-org/maze
Advanced tools
Comparing version 2.2.0 to 2.3.0
{ | ||
"name": "@code-dot-org/maze", | ||
"version": "2.2.0", | ||
"version": "2.3.0", | ||
"description": "standalone project for the Maze app type", | ||
@@ -5,0 +5,0 @@ "main": "dist/main.js", |
@@ -220,3 +220,3 @@ const {SVG_NS, pegmanElements} = require('./constants'); | ||
if (options.row !== undefined) { | ||
rect.setAttribute('y', getPegmanYForRow(this.maze.skin, options.row)); | ||
rect.setAttribute('y', getPegmanYForRow(this.maze.skin, options.row, this.maze.SQUARE_SIZE)); | ||
} | ||
@@ -244,3 +244,3 @@ rect.setAttribute('width', this.maze.PEGMAN_WIDTH); | ||
if (options.row !== undefined) { | ||
img.setAttribute('y', getPegmanYForRow(this.maze.skin, options.row)); | ||
img.setAttribute('y', getPegmanYForRow(this.maze.skin, options.row, this.maze.SQUARE_SIZE)); | ||
} | ||
@@ -271,3 +271,3 @@ } | ||
rect.setAttribute('x', options.col * this.maze.SQUARE_SIZE + 1 + this.maze.PEGMAN_X_OFFSET); | ||
rect.setAttribute('y', getPegmanYForRow(this.maze.skin, options.row)); | ||
rect.setAttribute('y', getPegmanYForRow(this.maze.skin, options.row, this.maze.SQUARE_SIZE)); | ||
var img = document.getElementById(utils.getPegmanElementId(options.type, options.pegmanId)); | ||
@@ -277,3 +277,3 @@ var x = this.maze.SQUARE_SIZE * options.col - | ||
img.setAttribute('x', x); | ||
var y = getPegmanYForRow(this.maze.skin, options.row) - this.getPegmanFrameOffsetY_(options.animationRow); | ||
var y = getPegmanYForRow(this.maze.skin, options.row, this.maze.SQUARE_SIZE) - this.getPegmanFrameOffsetY_(options.animationRow); | ||
img.setAttribute('y', y); | ||
@@ -717,3 +717,3 @@ img.setAttribute('visibility', 'visible'); | ||
var clipRect = document.getElementById(utils.getPegmanElementId(pegmanElements.CLIP_RECT, pegmanId)); | ||
displayPegman(this.maze.skin, pegmanIcon, clipRect, x, y, frame); | ||
displayPegman(this.maze.skin, pegmanIcon, clipRect, x, y, frame, this.maze.SQUARE_SIZE); | ||
} | ||
@@ -726,4 +726,4 @@ | ||
addNewPegman(pegmanId, x, y, d) { | ||
addNewPegman(this.maze.skin, pegmanId, x, y, d, this.svg); | ||
addNewPegman(this.maze.skin, pegmanId, x, y, d, this.svg, this.maze.SQUARE_SIZE); | ||
} | ||
}; |
@@ -23,3 +23,3 @@ const {SVG_NS, pegmanElements} = require('./constants'); | ||
x * squareSize - frame * skin.pegmanWidth + 1 + xOffset); | ||
pegmanIcon.setAttribute('y', getPegmanYForRow(skin, y)); | ||
pegmanIcon.setAttribute('y', getPegmanYForRow(skin, y, squareSize)); | ||
@@ -30,3 +30,3 @@ clipRect.setAttribute('x', x * squareSize + 1 + xOffset); | ||
function addNewPegman(skin, pegmanId, x, y, direction, svg) { | ||
function addNewPegman(skin, pegmanId, x, y, direction, svg, squareSize = 50) { | ||
// Pegman's clipPath element, whose (x, y) is reset by Maze.displayPegman | ||
@@ -56,3 +56,3 @@ const pegmanClip = document.createElementNS(SVG_NS, 'clipPath'); | ||
displayPegman(skin, pegmanIcon, clipRect, x, y, | ||
tiles.directionToFrame(direction)); | ||
tiles.directionToFrame(direction), squareSize); | ||
@@ -110,3 +110,3 @@ | ||
if (subtype.start) { | ||
addNewPegman(skin, undefined, subtype.start.x, subtype.start.y, subtype.startDirection, svg); | ||
addNewPegman(skin, undefined, subtype.start.x, subtype.start.y, subtype.startDirection, svg, squareSize); | ||
} | ||
@@ -113,0 +113,0 @@ |
@@ -57,2 +57,4 @@ /** | ||
this.SQUARE_SIZE = null; | ||
this.SVG_WIDTH = null; | ||
this.SVG_HEIGHT = null; | ||
@@ -84,5 +86,14 @@ if (options.methods) { | ||
initWithSvg(svg) { | ||
// Adjust outer element size. | ||
svg.setAttribute('width', this.MAZE_WIDTH); | ||
svg.setAttribute('height', this.MAZE_HEIGHT); | ||
// Adjust outer element size to desired size of overall SVG. | ||
// This may be equal to the 'actual' maze size | ||
// (square size * num_columns x square size * num_rows) | ||
// if no svg size was provided by the skin. | ||
svg.setAttribute('width', this.SVG_WIDTH); | ||
svg.setAttribute('height', this.SVG_HEIGHT); | ||
// Adjust view box. View box width and height are the 'actual' maze dimensions. | ||
// This attribute combined with the width and height will scale the svg to our | ||
// desired size. We want to maintain the top corner location, so the min-x | ||
// and min-y values are set to 0. | ||
// See view box explanation here: https://css-tricks.com/scale-svg/ | ||
svg.setAttribute('viewBox', `0 0 ${this.MAZE_WIDTH} ${this.MAZE_HEIGHT}`); | ||
@@ -128,3 +139,3 @@ drawMap(svg, this.skin, this.subtype, this.map, this.SQUARE_SIZE); | ||
// Pixel height and width of each maze square (i.e. tile). | ||
this.SQUARE_SIZE = 50; | ||
this.SQUARE_SIZE = this.skin.squareSize || 50; | ||
this.PEGMAN_HEIGHT = this.skin.pegmanHeight; | ||
@@ -137,2 +148,5 @@ this.PEGMAN_WIDTH = this.skin.pegmanWidth; | ||
this.MAZE_HEIGHT = this.SQUARE_SIZE * this.map.ROWS; | ||
this.SVG_WIDTH = this.skin.svgWidth || this.MAZE_WIDTH; | ||
this.SVG_HEIGHT = this.skin.svgHeight || this.MAZE_HEIGHT; | ||
this.PATH_WIDTH = this.SQUARE_SIZE / 3; | ||
@@ -139,0 +153,0 @@ } |
@@ -11,4 +11,3 @@ import Subtype from './subtype'; | ||
// TODO: this should be defined by the level | ||
this.squareSize = 50; | ||
this.squareSize = this.skin_.squareSize; | ||
} | ||
@@ -38,3 +37,3 @@ | ||
/** | ||
* @override | ||
* @override | ||
* Draw the tiles making up the maze map. | ||
@@ -67,3 +66,2 @@ */ | ||
} | ||
tileId++; | ||
@@ -87,8 +85,8 @@ }); | ||
addPaint(pegmanId, color) { | ||
const col = this.maze_.getPegmanX(); | ||
const row = this.maze_.getPegmanY(); | ||
const col = this.maze_.getPegmanX(pegmanId); | ||
const row = this.maze_.getPegmanY(pegmanId); | ||
const cell = this.getCell(row, col); | ||
cell.setColor(color); | ||
// TODO: update color on map | ||
this.drawer.updateItemImage(row, col, true); | ||
} | ||
@@ -102,8 +100,9 @@ | ||
removePaint(pegmanId) { | ||
const col = this.maze_.getPegmanX(); | ||
const row = this.maze_.getPegmanY(); | ||
const col = this.maze_.getPegmanX(pegmanId); | ||
const row = this.maze_.getPegmanY(pegmanId); | ||
const cell = this.getCell(row, col); | ||
cell.setColor(null); | ||
// TODO: remove color from map | ||
this.drawer.resetTile(row, col); | ||
this.drawer.updateItemImage(row, col, true); | ||
} | ||
@@ -110,0 +109,0 @@ |
@@ -0,4 +1,82 @@ | ||
const { SQUARE_SIZE, 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` | ||
} | ||
// For creating the groups for each grid location | ||
function makeGrid(row, col, svg) { | ||
let id = "g" + row + "." + col; | ||
return svgElement("g", { | ||
transform: `translate(${col * SQUARE_SIZE + SQUARE_SIZE}, | ||
${row * SQUARE_SIZE + SQUARE_SIZE})` | ||
}, svg, id); | ||
} | ||
/** | ||
* 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 { | ||
@@ -12,2 +90,17 @@ | ||
resetTile(row, col) { | ||
let neighbors = [ | ||
"g" + row + "." + col, | ||
"g" + (row - 1) + "." + (col - 1), | ||
"g" + row + "." + (col - 1), | ||
"g" + (row - 1) + "." + col | ||
]; | ||
for (const neighbor of neighbors) { | ||
var node = document.getElementById(neighbor); | ||
if (node) { | ||
node.querySelectorAll('*').forEach(n => n.remove()); | ||
} | ||
} | ||
} | ||
/** | ||
@@ -25,2 +118,44 @@ * @override | ||
resetTiles() {} | ||
// 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(SQUARE_SIZE); | ||
let cutOut = cutout(SQUARE_SIZE); | ||
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}`); | ||
} | ||
} | ||
/** | ||
@@ -36,3 +171,3 @@ * @override | ||
this.drawTileHelper( | ||
super.drawTileHelper( | ||
svg, | ||
@@ -49,2 +184,48 @@ tileSheetLocation, | ||
} | ||
} | ||
/** | ||
* @override | ||
* This method is used to display the paint, so has to reprocess the entire grid | ||
* to get the paint glomming correct | ||
*/ | ||
updateItemImage(r, co, running) { | ||
// 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 = 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); | ||
} | ||
} | ||
} | ||
} | ||
}; |
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
547969
5439