From 52017e8e306f87f38b38011716f17e3e837a214b Mon Sep 17 00:00:00 2001 From: gavin Date: Fri, 5 Oct 2018 20:22:11 -0400 Subject: [PATCH] Higher FPS! (#19) --- src/cartographerFlatXY.js | 6 +-- src/cartographerPointyXY.js | 6 +-- src/drawHexagon.js | 30 +++++++++++- src/drawShapes.js | 95 ++++++++++++++++++++++++++----------- src/drawSquare.js | 10 ++-- src/main.js | 49 +++++++++++++++---- src/utils.js | 13 +++++ 7 files changed, 160 insertions(+), 49 deletions(-) diff --git a/src/cartographerFlatXY.js b/src/cartographerFlatXY.js index 47112e6..ad522c0 100644 --- a/src/cartographerFlatXY.js +++ b/src/cartographerFlatXY.js @@ -1,6 +1,6 @@ import Cartographer from './cartographer.js'; -import {rangeInclusive, sqrt2} from './utils.js'; +import {rangeInclusive, invSqrt2} from './utils.js'; import Square from './square.js'; import Point from './point.js'; @@ -40,11 +40,11 @@ export default class CartographerFlatXY extends Cartographer { } maxWidth() { - return this.minWidth() * sqrt2; + return this.scale * 2; } minWidth() { - return this.scale * 2; + return this.maxWidth() * invSqrt2; } horizontalOverhang() { diff --git a/src/cartographerPointyXY.js b/src/cartographerPointyXY.js index 3146848..6ccf2ad 100644 --- a/src/cartographerPointyXY.js +++ b/src/cartographerPointyXY.js @@ -1,6 +1,6 @@ import Cartographer from './cartographer.js'; -import {rangeInclusive, sqrt2} from './utils.js'; +import {rangeInclusive, invSqrt2, sqrt2} from './utils.js'; import Square from './square.js'; import Point from './point.js'; @@ -40,11 +40,11 @@ export default class CartographerPointyXY extends Cartographer { } maxWidth() { - return this.minWidth() * sqrt2; + return this.scale * 2; } minWidth() { - return this.scale * 2; + return this.maxWidth() * invSqrt2; } horizontalOverhang() { diff --git a/src/drawHexagon.js b/src/drawHexagon.js index 56c99f5..c48e437 100644 --- a/src/drawHexagon.js +++ b/src/drawHexagon.js @@ -1,5 +1,5 @@ import {FLAT, POINTY} from './consts.js'; -import {getColor, range, toFixed} from './utils.js'; +import {getColor, range, toFixed, quickCanvas} from './utils.js'; export default class DrawHexagon { constructor(settings) { @@ -50,6 +50,34 @@ export default class DrawHexagon { context.stroke(); } +// fill(context, scale, x, y, cell) { +// if (cell.cacheScale !== scale) { +// cell.cacheScale = scale; +// scale = scale * cell.scale; +// +// cell.cacheHalfWidth = scale; +// cell.cacheHalfHeight = scale; +// +// cell.cacheHex = quickCanvas((context, height, width) => { +// const hexCornerX = cell.orientation === POINTY ? this.pointyTopCornerX : this.flatTopCornerX; +// const hexCornerY = cell.orientation === POINTY ? this.pointyTopCornerY : this.flatTopCornerY; +// +// context.beginPath(); +// context.moveTo(scale + scale * hexCornerX[0], scale + scale * hexCornerY[0]); +// context.lineTo(scale + scale * hexCornerX[1], scale + scale * hexCornerY[1]); +// context.lineTo(scale + scale * hexCornerX[2], scale + scale * hexCornerY[2]); +// context.lineTo(scale + scale * hexCornerX[3], scale + scale * hexCornerY[3]); +// context.lineTo(scale + scale * hexCornerX[4], scale + scale * hexCornerY[4]); +// context.lineTo(scale + scale * hexCornerX[5], scale + scale * hexCornerY[5]); +// +// context.fillStyle = getColor(cell.color); +// context.fill(); +// }, scale * 2); +// } +// +// context.drawImage(cell.cacheHex, x - cell.cacheHalfWidth, y - cell.cacheHalfHeight); +// } + fill(context, scale, x, y, cell) { scale = scale * cell.scale; let hexCornerX = cell.orientation === POINTY ? this.pointyTopCornerX : this.flatTopCornerX; diff --git a/src/drawShapes.js b/src/drawShapes.js index dcc3ae6..0cecd99 100644 --- a/src/drawShapes.js +++ b/src/drawShapes.js @@ -1,4 +1,4 @@ -import {getColor, range, sqrt2, toFixed} from './utils.js'; +import {getColor, quickCanvas, range, invSqrt2, toFixed} from './utils.js'; import {HEX, SQUARE, FLAT, POINTY} from './consts.js'; const DEFAULTS = { @@ -52,7 +52,7 @@ const DEFAULTS = { green: 0, }, }, - distance: 0.4, + distance: 0.6, }, sides: 6, @@ -61,8 +61,8 @@ const DEFAULTS = { function generateFlatSquarePips () { const pips = []; - const pipX = [0, 1, 1, -1, -1, 1, -1, 0, 0]; - const pipY = [0, 1, -1, -1, 1, 0, 0, -1, 1]; + const pipX = [0, invSqrt2, invSqrt2, -invSqrt2, -invSqrt2, invSqrt2, -invSqrt2, 0, 0]; + const pipY = [0, invSqrt2, -invSqrt2, -invSqrt2, invSqrt2, 0, 0, -invSqrt2, invSqrt2]; const getVertex = n => [pipX[n], pipY[n]]; @@ -81,8 +81,8 @@ function generateFlatSquarePips () { function generatePointySquarePips () { const pips = []; - const pipX = [0, sqrt2, 0, -sqrt2, 0, sqrt2 / 2, -sqrt2 / 2, sqrt2 / 2, -sqrt2 / 2]; - const pipY = [0, 0, -sqrt2, 0, sqrt2, -sqrt2 / 2, sqrt2 / 2, sqrt2 / 2, -sqrt2 / 2]; + const pipX = [0, 1, 0, -1, 0, 0.5, -0.5, 0.5, -0.5]; + const pipY = [0, 0, -1, 0, 1, -0.5, 0.5, 0.5, -0.5]; const getVertex = n => [pipX[n], pipY[n]]; @@ -144,6 +144,9 @@ export default class DrawShapes { this.slicesX = range(slices).map(slice => toFixed(Math.cos(((slice / slices) * sides) * (2 * Math.PI) / sides))); this.slicesY = range(slices).map(slice => toFixed(Math.sin(((slice / slices) * sides) * (2 * Math.PI) / sides))); + this.pipCache = {}; + this.mineCache = {}; + this.pipVertices = { [HEX]: { [FLAT]: generateFlatHexPips(this.slicesX, this.slicesY), @@ -174,41 +177,75 @@ export default class DrawShapes { context.arc(x, y, pipRadius, 0, Math.PI*2, true); } - pips (context, scale, x, y, cell, pip = this.settings.pip) { + setPipCache (pips, scale) { + const pipDistance = scale * this.settings.pip.distance; //* cell.scale; + const pipBodyRadius = scale * this.settings.pip.body.scale; + const pipBorderRadius = scale * this.settings.pip.border.scale; + + this.pipCache.scale = scale; + this.pipCache.height = (Math.ceil(pipBorderRadius) + 1) * 2; + this.pipCache.width = this.pipCache.height; + + const pipCenter = this.pipCache.height / 2; + + this.pipCache.pip = quickCanvas((context, height, width) => { + context.beginPath(); + context.arc(pipCenter, pipCenter, pipBorderRadius, 0, Math.PI*2, true); + context.closePath(); + context.fillStyle = getColor(this.settings.pip.border.color); + context.fill(); + + context.beginPath(); + context.arc(pipCenter, pipCenter, pipBodyRadius, 0, Math.PI*2, true); + context.closePath(); + context.fillStyle = getColor(this.settings.pip.body.color); + context.fill(); + }, this.pipCache.height, this.pipCache.width); + + const pipsCenter = scale - pipCenter; + + this.pipCache.pips = pips.map(vertices => { + return quickCanvas(context => { + vertices.forEach(([pipX, pipY]) => context.drawImage(this.pipCache.pip, pipsCenter + (pipDistance * pipX), pipsCenter + (pipDistance * pipY))); + }, scale * 2); + }); + } + + pips (context, scale, x, y, cell) { const {tileStyle, orientation, pips} = cell; if (this.pipVertices[tileStyle] && this.pipVertices[tileStyle][orientation] && this.pipVertices[tileStyle][orientation][pips]) { - const pipBodyRadius = scale * pip.body.scale; - const pipBorderRadius = scale * pip.border.scale; - const pipDistance = scale * pip.distance; - context.beginPath(); + if (scale !== this.pipCache.scale) { + this.setPipCache(this.pipVertices[tileStyle][orientation], scale); + } - this.pipVertices[tileStyle][orientation][pips] - .forEach(([pipX, pipY]) => this.pip(context, scale, x,y, pipX, pipY, pipBorderRadius, pipDistance)); + const scaleWidth = (scale * 2) * cell.scale; + const scaleHeight = (scale * 2) * cell.scale; - context.closePath(); - context.fillStyle = getColor(pip.border.color); - context.fill(); - - context.beginPath(); - - this.pipVertices[tileStyle][orientation][pips] - .forEach(([pipX, pipY]) => this.pip(context, scale, x,y, pipX, pipY, pipBodyRadius, pipDistance)); - - context.closePath(); - context.fillStyle = getColor(pip.body.color); - context.fill(); + context.drawImage(this.pipCache.pips[pips], x - (scaleWidth / 2), y - (scaleHeight / 2), scaleWidth, scaleHeight); } } mine (context, scale, x, y, mine = this.settings.mine) { - this._mine(context, scale, x, y, mine.border); - this._horns(context, scale, x, y, mine.border.horns, mine.border.color); - this._mine(context, scale, x, y, mine.body); - this._horns(context, scale, x, y, mine.body.horns, mine.body.color); + if (scale !== this.mineCache.scale) { + this.setMineCache(scale); + } + + context.drawImage(this.mineCache.mine, x - scale, y - scale, scale * 2, scale * 2); + } + + setMineCache (scale) { + this.mineCache.scale = scale; + + this.mineCache.mine = quickCanvas(context => { + this._mine(context, scale, scale, scale, this.settings.mine.border); + this._horns(context, scale, scale, scale, this.settings.mine.border.horns, this.settings.mine.border.color); + this._mine(context, scale, scale, scale, this.settings.mine.body); + this._horns(context, scale, scale, scale, this.settings.mine.body.horns, this.settings.mine.body.color); + }, scale * 2); } _mine (context, scale, x, y, mine) { diff --git a/src/drawSquare.js b/src/drawSquare.js index cf3928b..baf171f 100644 --- a/src/drawSquare.js +++ b/src/drawSquare.js @@ -1,14 +1,14 @@ -import {getColor, sqrt2} from './utils.js'; +import {getColor, invSqrt2, sqrt2} from './utils.js'; import {FLAT, POINTY} from './consts.js'; export default class DrawSquare { constructor(settings) { this.settings = Object.assign({}, settings); - this.squareX = [1, 1, -1, -1]; - this.squareY = [1, -1, -1, 1]; - this.diamondX = [sqrt2, 0, -sqrt2, 0, sqrt2 / 2, -sqrt2 / 2]; - this.diamondY = [0, -sqrt2, 0, sqrt2, -sqrt2 / 2, sqrt2 / 2]; + this.squareX = [invSqrt2, invSqrt2, -invSqrt2, -invSqrt2]; + this.squareY = [invSqrt2, -invSqrt2, -invSqrt2, invSqrt2]; + this.diamondX = [1, 0, -1, 0, 0.5, -0.5]; + this.diamondY = [0, -1, 0, 1, -0.5, 0.5]; } fill(context, scale, x, y, cell) { diff --git a/src/main.js b/src/main.js index a5e26d7..4778a87 100644 --- a/src/main.js +++ b/src/main.js @@ -27,7 +27,7 @@ class Demo { constructor() { [ 'setOriginTile', - 'setFadeToGray', + 'setupKeydown', 'tap', 'pressStart', 'press', @@ -50,7 +50,7 @@ class Demo { }); this.setOriginTile(); - this.setFadeToGray(); + this.setupKeydown(); this.tessellate = new Tessellate(Object.assign({ element: '#container', @@ -99,10 +99,27 @@ class Demo { }); } - setFadeToGray () { + setupKeydown () { + this.pipDefault = null; + window.addEventListener('keydown', event => { - if (!event.repeat && event.key === 'Enter') { - this.gray = this.gray ? null : Date.now(); + if (!event.repeat) { + if (event.key === 'Enter') { + this.gray = this.gray ? null : Date.now(); + } + + if (event.key === `~`) { + this.mined = true; + } + + if (event.key === '`') { + this.pipDefault = '*'; + } + + const num = parseInt(event.key); + if (num >= 0 && num <= 9) { + this.pipDefault = num; + } } }); } @@ -221,9 +238,25 @@ class Demo { Tessellate.TILES[tile.tileStyle][tile.drawStyle](context, scale, pixelPoint.getX(), pixelPoint.getY(), tile); - if (tile.pips) { - this.counts[1] += tile.pips; - Tessellate.Shapes.pips(context, scale, pixelPoint.getX(), pixelPoint.getY(), tile); + if (this.mined) { + this.counts[2] += 1; + Tessellate.Shapes.mine(context, scale, pixelPoint.getX(), pixelPoint.getY()); + } + else { + if (!tile.pips && this.pipDefault !== null) { + if (this.pipDefault === '*') { + const pipMax = this.settings.tile === Tessellate.TILE_STYLES.HEX ? 7 : 9; + tile.pips = Tessellate.utils.random(1, pipMax); + } + else { + tile = Tessellate.utils.extend(tile, {pips: this.pipDefault}); + } + } + + if (tile.pips) { + this.counts[1] += tile.pips; + Tessellate.Shapes.pips(context, scale, pixelPoint.getX(), pixelPoint.getY(), tile); + } } } diff --git a/src/utils.js b/src/utils.js index 59bee80..84d9dda 100644 --- a/src/utils.js +++ b/src/utils.js @@ -9,6 +9,9 @@ export const sqrt2 = Math.sqrt(2); // (2*S gives long width) export const sqrt3 = Math.sqrt(3); +// leg factor of isoscelese right triangle with unit hypotenuse +export const invSqrt2 = 1 / sqrt2 + export function clone(obj) { return JSON.parse(JSON.stringify(obj)); } @@ -141,3 +144,13 @@ export function extend (obj, ...sources) { return extended; } + +export function quickCanvas (draw, height, width = height) { + const canvas = document.createElement('canvas'); + canvas.height = height; + canvas.width = width; + + draw(canvas.getContext('2d'), canvas.height, canvas.width); + + return canvas; +}