diff --git a/src/cartographerFlatXY.js b/src/cartographerFlatXY.js index 27c4341..59cbdc7 100644 --- a/src/cartographerFlatXY.js +++ b/src/cartographerFlatXY.js @@ -50,6 +50,8 @@ export default class CartographerFlatXY extends Cartographer { } tileToPixel(square) { + square = typeof square === 'Square' ? square : new Square(...arguments); + const x = square.getX() * this.minWidth(); const y = square.getY() * this.minWidth(); @@ -57,6 +59,8 @@ export default class CartographerFlatXY extends Cartographer { } pixelToTile(point) { + point = typeof point === 'Point' ? point : new Point(x, y); + const pixelX = point.getX() - this.originX; const pixelY = this.originY - point.getY(); diff --git a/src/cartographerFlatXYZ.js b/src/cartographerFlatXYZ.js index 2769864..c776cc2 100644 --- a/src/cartographerFlatXYZ.js +++ b/src/cartographerFlatXYZ.js @@ -50,6 +50,8 @@ export default class CartographerFlatXYZ extends Cartographer { } tileToPixel(hex) { + hex = typeof hex === 'Hex' ? hex : new Hex(...arguments); + const pixelX = this.scale * 3/2 * hex.getQ(); const pixelY = this.scale * sqrt3 * (hex.getR() + (hex.getQ() / 2)); @@ -57,6 +59,8 @@ export default class CartographerFlatXYZ extends Cartographer { } pixelToTile(point) { + point = typeof point === 'Point' ? point : new Point(x, y); + const pixelX = point.getX() - this.originX; const pixelY = point.getY() - this.originY; diff --git a/src/cartographerPointyXY.js b/src/cartographerPointyXY.js index d1c29c9..bbffed0 100644 --- a/src/cartographerPointyXY.js +++ b/src/cartographerPointyXY.js @@ -50,6 +50,8 @@ export default class CartographerPointyXY extends Cartographer { } tileToPixel(square) { + square = typeof square === 'Square' ? square : new Square(...arguments); + const x = square.getX(); const y = square.getY(); @@ -61,6 +63,8 @@ export default class CartographerPointyXY extends Cartographer { } pixelToTile(point) { + point = typeof point === 'Point' ? point : new Point(x, y); + const pixelX = point.getX() - this.originX; const pixelY = this.originY - point.getY(); diff --git a/src/cartographerPointyXYZ.js b/src/cartographerPointyXYZ.js index 076f31b..437e024 100644 --- a/src/cartographerPointyXYZ.js +++ b/src/cartographerPointyXYZ.js @@ -50,6 +50,8 @@ export default class CartographerPointyXYZ extends Cartographer { } tileToPixel(hex) { + hex = typeof hex === 'Hex' ? hex : new Hex(...arguments); + const pixelX = this.scale * sqrt3 * (hex.getQ() + (hex.getR() / 2)); const pixelY = this.scale * 3/2 * hex.getR(); @@ -57,6 +59,8 @@ export default class CartographerPointyXYZ extends Cartographer { } pixelToTile(point) { + point = typeof point === 'Point' ? point : new Point(x, y); + const pixelX = point.getX() - this.originX; const pixelY = point.getY() - this.originY; diff --git a/src/cell.js b/src/cell.js index 1ee53ca..fcf4fde 100644 --- a/src/cell.js +++ b/src/cell.js @@ -1,23 +1,31 @@ import {random} from './utils.js'; +import { + HEX, CIRCLE, SQUARE, + FLAT, POINTY, + FILL, OUTLINE, +} from './consts.js'; + +const DEFAULTS = { + x: 0, + y: 0, + + scale: 10, + orientation: FLAT, + tileStyle: HEX, + drawStyle: FILL, + width: 1, + + red: 0, + green: 0, + blue: 0, + alpha: 0.5, +}; export default class Cell { constructor(settings) { this.getColor = this.getColor.bind(this); - this.x = 0; - this.y = 0; - this.scale = 10; - this.pointyTop = false; - this.tile = 'circle'; - this.style = 'filled'; - this.width = 1; - this.red = 0; - this.green = 0; - this.blue = 0; - this.alpha = 0.5; - this.created = Date.now(); - - Object.assign(this, settings); + Object.assign(this, DEFAULTS, settings); } getColor() { diff --git a/src/consts.js b/src/consts.js index 5658a30..a8b2c2e 100644 --- a/src/consts.js +++ b/src/consts.js @@ -17,3 +17,9 @@ export const TILE_ORIENTATIONS = { POINTY, }; +export const FILL = 'fill'; +export const OUTLINE = 'outline'; +export const DRAW_STYLES = { + FILL, + OUTLINE, +}; diff --git a/src/drawCircle.js b/src/drawCircle.js index 83a73b6..9a4402d 100644 --- a/src/drawCircle.js +++ b/src/drawCircle.js @@ -3,7 +3,7 @@ export default class DrawCircle { constructor(settings) { } - filled(context, scale, x, y, cell) { + fill(context, scale, x, y, cell) { context.beginPath(); context.arc(x, y, scale * cell.scale, 0, 2*Math.PI, false); context.fillStyle = cell.getColor(); diff --git a/src/drawHexagon.js b/src/drawHexagon.js index e4bcdb3..8f1cbed 100644 --- a/src/drawHexagon.js +++ b/src/drawHexagon.js @@ -51,7 +51,7 @@ export default class DrawHexagon { context.stroke(); } - filled(context, scale, x, y, cell) { + fill(context, scale, x, y, cell) { scale = scale * cell.scale; let hexCornerX = cell.orientation === POINTY ? this.pointyTopCornerX : this.flatTopCornerX; let hexCornerY = cell.orientation === POINTY ? this.pointyTopCornerY : this.flatTopCornerY; diff --git a/src/drawSquare.js b/src/drawSquare.js index f2d8c66..60dd4f2 100644 --- a/src/drawSquare.js +++ b/src/drawSquare.js @@ -9,7 +9,7 @@ export default class DrawSquare { this.diamondY = [0, -sqrt2, 0, sqrt2]; } - filled(context, scale, x, y, cell) { + fill(context, scale, x, y, cell) { scale = scale * cell.scale; let squareCornerX = cell.orientation === POINTY ? this.diamondX : this.squareX; let squareCornerY = cell.orientation === POINTY ? this.diamondY : this.squareY; diff --git a/src/funky.js b/src/funky.js index ce3dcc5..0f6c337 100644 --- a/src/funky.js +++ b/src/funky.js @@ -64,6 +64,12 @@ export function find (obj, predicate) { } } +export function flatten (list) { + if (Array.isArray(list)) { + return list.reduce((memo, element) => memo.concat(Array.isArray(element) ? flatten(element) : element), []); + } +} + export function forEach (obj, iteratee) { if (Array.isArray(obj)) { // native 'forEach' but the return below allows us to chain @@ -250,6 +256,7 @@ export let _ = { contains, filter, find, + flatten, forEach, groupBy, identity, diff --git a/src/main.js b/src/main.js index d7ff746..2f11d18 100644 --- a/src/main.js +++ b/src/main.js @@ -4,22 +4,34 @@ import {utils} from './tessellate.js'; import {DrawCircle, DrawHexagon, DrawSquare} from './tessellate.js'; import {Cell} from './tessellate.js'; +const DEFAULTS = { + board: Tessellate.BOARD_STYLES.HEX, + style: Tessellate.DRAW_STYLES.FILL, + orientation: Tessellate.TILE_ORIENTATIONS.FLAT, + tile: Tessellate.TILE_STYLES.HEX, +}; + class Demo { constructor() { - ['onTap', 'draw'].map(method => this[method] = this[method].bind(this)); + [ + 'onTap', + 'createTile', + 'drawTile', + 'draw', + ].map(method => this[method] = this[method].bind(this)); - this.map = []; + this.map = {}; + this.taps = []; const queryStringObj = utils.getQueryStringObj(); - let tessellate = new Tessellate(Object.assign({ + this.settings = Object.assign({}, DEFAULTS, queryStringObj); + + this.tessellate = new Tessellate(Object.assign({ element: '#container', - board: Tessellate.BOARD_STYLES.HEX, - tile: Tessellate.TILE_STYLES.HEX, tap: this.onTap, draw: this.draw, - orientation: Tessellate.TILE_ORIENTATIONS.FLAT, - }, queryStringObj)); + }, this.settings)); this.circle = new DrawCircle(); this.square = new DrawSquare(); @@ -30,28 +42,59 @@ class Demo { } onTap(tap) { - let scale = utils.random(10, 50); - console.log(tap.tile.getPoint()); - this.map.push(new Cell({ - tile: this.tiles[utils.random(this.tiles.length-1)], - style: this.styles[utils.random(this.styles.length-1)], - x: tap.point.x, - y: tap.point.y, - scale, - orientation: utils.random(1) ? Tessellate.TILE_ORIENTATIONS.FLAT : Tessellate.TILE_ORIENTATIONS.POINTY, + this.taps.push(this.createTile( + tap.tile.x, + tap.tile.y, + + utils.random(Tessellate.DRAW_STYLES), + utils.random(Tessellate.TILE_STYLES), + utils.random(Tessellate.TILE_ORIENTATIONS), + )); + console.log(this.map[this.map.length - 1]); + } + + createTile(x, y, drawStyle, tileStyle, orientation) { + return new Cell({ + x, y, + + scale: utils.random(7, 9) / 10, + + drawStyle, + tileStyle, + orientation, + red: utils.random(255), green: utils.random(255), blue: utils.random(255), alpha: utils.random(25,75)/100 - })); - console.log(this.map[this.map.length - 1]); + }); } - draw(context) { - let scale = 1; - this.map.forEach(cell => this[cell.tile][cell.style](context, scale, cell.x, cell.y, cell)); + drawTile({x, y, z}, context, scale) { + const key = `${ x },${ z != null ? z : y }`; + + this.map[key] = this.map[key] || this.createTile(x, y, this.settings.style, this.settings.tile, this.settings.orientation); + + const tile = this.map[key]; + const pixelPoint = this.tessellate.tileToPixel(x, y, z); + + Tessellate.TILES[tile.tileStyle][tile.drawStyle](context, scale, pixelPoint.getX(), pixelPoint.getY(), tile); + } + + draw({context, scale, tilePoints}) { + tilePoints.forEach(tilePoint => this.drawTile(tilePoint, context, scale)); + + this.taps.forEach(cell => { + const pixelPoint = this.tessellate.tileToPixel(cell.x, cell.y); + Tessellate.TILES[cell.tileStyle][cell.drawStyle](context, scale, pixelPoint.getX(), pixelPoint.getY(), cell); + }); + + if (!this.notFirstTime) { + this.notFirstTime = true; + console.log(tilePoints); + } } } diff --git a/src/tessellate.js b/src/tessellate.js index cc4444b..a1620c2 100644 --- a/src/tessellate.js +++ b/src/tessellate.js @@ -2,6 +2,9 @@ import * as utils from './utils'; export {utils}; +import * as funky from './funky'; +export {funky}; + import OnTap from './onTap.js'; import Point from './point.js'; import Sketch from './sketch.js'; @@ -25,6 +28,8 @@ import { BOARD_STYLES, FLAT, POINTY, TILE_ORIENTATIONS, + FILL, OUTLINE, + DRAW_STYLES, } from './consts.js'; const TILES = { @@ -61,9 +66,18 @@ export default class Tessellate { static get TILE_STYLES() {return TILE_STYLES} static get BOARD_STYLES() {return BOARD_STYLES} static get TILE_ORIENTATIONS() {return TILE_ORIENTATIONS} + static get DRAW_STYLES() {return DRAW_STYLES} constructor(settings) { - ['seedTiles', 'tap', 'draw', 'drawMap', 'move', 'zoom'].map(method => {this[method] = this[method].bind(this)}); + [ + 'tap', + 'move', + 'zoom', + 'pixelToTile', + 'tileToPixel', + 'getTilePoints', + 'draw'] + .map(method => {this[method] = this[method].bind(this)}); this.settings = Object.assign(DEFAULTS, settings); this.settings.element = this.settings.element instanceof HTMLElement ? this.settings.element : @@ -98,21 +112,6 @@ export default class Tessellate { }); } - seedTiles() { - this.map[0] = []; - - this.map[0][0] = new Cell({ - x: 0, - y: 0, - orientation: this.settings.orientation, - red: 0, - green: 0, - blue: 0, - alpha: 75/100, - scale: 9/10, - }); - } - tap(event) { let point = new Point(event.offsetX, event.offsetY); let tile = this.cartographer.pixelToTile(point); @@ -134,47 +133,41 @@ export default class Tessellate { this.cartographer.zoom(event); } - drawMap(context) { - const scale = this.cartographer.getScale(); + pixelToTile(x, y) { + return this.cartographer.pixelToTile(x, y); + } - const upperLeft = new Point(0, 0); - const upperRight = new Point(context.canvas.width, 0); - const lowerLeft = new Point(0, context.canvas.height); - const lowerRight = new Point(context.canvas.width, context.canvas.height); + tileToPixel(x, y, z) { + return this.cartographer.tileToPixel(x, y, z); + } - const tiles = this.cartographer.boundingBox(upperLeft, upperRight, lowerLeft, lowerRight); + getTilePoints({upperLeftX, upperLeftY, lowerRightX, lowerRightY}) { + const upperLeft = new Point(upperLeftX, upperLeftY); + const upperRight = new Point(lowerRightX, 0); + const lowerLeft = new Point(0, lowerRightY); + const lowerRight = new Point(lowerRightX, lowerRightY); - tiles.forEach(row => row.forEach(tile => { - const tilePoint = this.cartographer.tileToPixel(tile); - - if (!this.map[tile.getX()]) this.map[tile.getX()] = []; - - if (!this.map[tile.getX()][tile.getY()]) { - this.map[tile.getX()][tile.getY()] = new Cell({ - x: tile.getX(), - y: tile.getY(), - orientation: this.settings.orientation, - red: utils.random(64, 192), - green: utils.random(64, 192), - blue: utils.random(64, 192), - alpha: 0.75, - scale: utils.random(7, 9) / 10 - }); - } - - TILES[this.settings.tile].filled(context, scale, tilePoint.getX(), tilePoint.getY(), this.map[tile.getX()][tile.getY()]); - })); + return funky.flatten(this.cartographer.boundingBox(upperLeft, upperRight, lowerLeft, lowerRight)); } draw(context) { - let canvas = context.canvas; - let width = canvas.width; - let height = canvas.height; + const canvas = context.canvas; + const width = canvas.width; + const height = canvas.height; context.clearRect(0, 0, width, height); - this.drawMap(context); + this.settings.draw({ + context, - this.settings.draw(context); + scale: this.cartographer.getScale(), + + tilePoints: this.getTilePoints({ + upperLeftX: 0, + upperLeftY: 0, + lowerRightX: width, + lowerRightY: height + }), + }); } } diff --git a/src/utils.js b/src/utils.js index 3fec8cf..506a38a 100644 --- a/src/utils.js +++ b/src/utils.js @@ -44,6 +44,14 @@ export function hypotenuse(a, b) { } export function random(min, max) { + if (Array.isArray(min)) { + return min[random(min.length - 1)]; + } + + if (typeof min === 'object') { + return min[random(Object.keys(min))]; + } + if (max == null) { max = min; min = 0;