diff --git a/src/cartographer.js b/src/cartographer.js index 57b7e33..2342dcb 100644 --- a/src/cartographer.js +++ b/src/cartographer.js @@ -23,15 +23,15 @@ export default class Cartographer { Object.assign(this, SETTINGS_DEFAULTS, settings); + this._checkScale(this.canvasHeight, this.canvasWidth); + this.originX = has(this, 'originX') ? this.originX : - this.canvasWidth ? parseInt(this.canvasWidth / 2) : - 0; + this.negativeTiles ? parseInt(this.canvasWidth / 2) : + this.tileWidth() / 2; this.originY = has(this, 'originY') ? this.originY : - this.canvasHeight ? parseInt(this.canvasHeight / 2) : - 0; - - this._checkScale(this.canvasHeight, this.canvasWidth); + this.negativeTiles ? parseInt(this.canvasHeight / 2) : + this.canvasHeight - (this.tileHeight() / 2); } getOriginX() {return this.originX;} @@ -39,6 +39,13 @@ export default class Cartographer { getScale() {return this.scale;} + _checkScale(canvasHeight, canvasWidth) { + const heightMin = this.height ? this.calculateVerticalScale(canvasHeight, this.height) : 0; + const widthMin = this.width ? this.calculateHorizontalScale(canvasWidth, this.width) : 0; + this.scaleMin = Math.max(this.scaleMin, heightMin, widthMin); + this.scale = this.scaleMin > this.scale ? this.scaleMin : this.scale; + } + move(event) { if (event.deltaX) { @@ -54,12 +61,49 @@ export default class Cartographer { } _checkMove(event) { + if (this.negativeTiles) { + this._checkMoveNegativeTiles(event); + } + else { + this._checkMovePositiveTiles(event); + } + } + + _checkMovePositiveTiles(event) { + const canvasWidth = event.target.offsetWidth; + const canvasHeight = event.target.offsetHeight; + + const colWidth = this.horizontalDistance(); + const rowHeight = this.verticalDistance(); + + const maxX = this.tileWidth() / 2; + const minY = canvasHeight - (this.tileHeight() / 2); + + this.originX = this.originX > maxX ? maxX : this.originX; + this.originY = this.originY < minY ? minY : this.originY; + + if (this.width) { + const boardWidth = this.width * colWidth + this.horizontalOverhang(); + const minX = maxX - (boardWidth - canvasWidth); + + this.originX = this.originX < minX ? minX : this.originX; + } + + if (this.height) { + const boardHeight = this.height * rowHeight + this.verticalOverhang(); + const maxY = boardHeight - (this.tileHeight() / 2); + + this.originY = this.originY > maxY ? maxY : this.originY; + } + } + + _checkMoveNegativeTiles(event) { const colWidth = this.horizontalDistance(); const rowHeight = this.verticalDistance(); if (this.width) { const canvasWidth = event.target.offsetWidth; - const halfBoardWidth = (this.width * colWidth + colWidth) / 2; + const halfBoardWidth = (this.width * colWidth + this.horizontalOverhang()) / 2; this.originX = this.originX > halfBoardWidth ? halfBoardWidth : (canvasWidth - this.originX) > halfBoardWidth ? canvasWidth - halfBoardWidth : @@ -68,7 +112,7 @@ export default class Cartographer { if (this.height) { const canvasHeight = event.target.offsetHeight; - const halfBoardHeight = (this.height * rowHeight + rowHeight) / 2; + const halfBoardHeight = (this.height * rowHeight + this.verticalOverhang()) / 2; this.originY = this.originY > halfBoardHeight ? halfBoardHeight : (canvasHeight - this.originY) > halfBoardHeight ? canvasHeight - halfBoardHeight : @@ -76,13 +120,6 @@ export default class Cartographer { } } - _checkScale(canvasHeight, canvasWidth) { - const heightMin = this.height ? this.calculateVerticalScale(canvasHeight, this.height + 1) : 0; - const widthMin = this.width ? this.calculateHorizontalScale(canvasWidth, this.width + 1) : 0; - this.scaleMin = Math.max(this.scaleMin, heightMin, widthMin); - this.scale = this.scaleMin > this.scale ? this.scaleMin : this.scale; - } - zoom(event) { let scaleOrig = this.scale; diff --git a/src/cartographerFlatXY.js b/src/cartographerFlatXY.js index d5b9714..47112e6 100644 --- a/src/cartographerFlatXY.js +++ b/src/cartographerFlatXY.js @@ -10,9 +10,15 @@ export default class CartographerFlatXY extends Cartographer { super(settings); [ + 'tileHeight', + 'tileWidth', + 'maxWidth', 'minWidth', + 'horizontalOverhang', + 'verticalOverhang', + 'horizontalDistance', 'verticalDistance', @@ -25,6 +31,14 @@ export default class CartographerFlatXY extends Cartographer { ].map(method => this[method] = this[method].bind(this)); } + tileHeight() { + return this.minWidth(); + } + + tileWidth() { + return this.minWidth(); + } + maxWidth() { return this.minWidth() * sqrt2; } @@ -33,6 +47,14 @@ export default class CartographerFlatXY extends Cartographer { return this.scale * 2; } + horizontalOverhang() { + return 0; + } + + verticalOverhang() { + return 0; + } + horizontalDistance() { return this.minWidth(); } diff --git a/src/cartographerFlatXYZ.js b/src/cartographerFlatXYZ.js index 366a5c5..9ec6194 100644 --- a/src/cartographerFlatXYZ.js +++ b/src/cartographerFlatXYZ.js @@ -10,9 +10,15 @@ export default class CartographerFlatXYZ extends Cartographer { super(settings); [ + 'tileHeight', + 'tileWidth', + 'maxWidth', 'minWidth', + 'horizontalOverhang', + 'verticalOverhang', + 'horizontalDistance', 'verticalDistance', @@ -25,6 +31,14 @@ export default class CartographerFlatXYZ extends Cartographer { ].map(method => this[method] = this[method].bind(this)); } + tileHeight() { + return this.minWidth(); + } + + tileWidth() { + return this.maxWidth(); + } + maxWidth() { return this.scale * 2; } @@ -33,6 +47,14 @@ export default class CartographerFlatXYZ extends Cartographer { return this.scale * sqrt3; } + horizontalOverhang() { + return this.maxWidth() * 0.25; + } + + verticalOverhang() { + return 0; + } + horizontalDistance() { return this.maxWidth() * (3/4); } @@ -42,7 +64,7 @@ export default class CartographerFlatXYZ extends Cartographer { } calculateHorizontalScale(pixels, tiles) { - return pixels / (tiles * (3/4)) / 2; + return pixels / (tiles * 0.75 + 0.25) / 2; } calculateVerticalScale(pixels, tiles) { diff --git a/src/cartographerPointyXY.js b/src/cartographerPointyXY.js index 2c819eb..3146848 100644 --- a/src/cartographerPointyXY.js +++ b/src/cartographerPointyXY.js @@ -10,9 +10,15 @@ export default class CartographerPointyXY extends Cartographer { super(settings); [ + 'tileHeight', + 'tileWidth', + 'maxWidth', 'minWidth', + 'horizontalOverhang', + 'verticalOverhang', + 'horizontalDistance', 'verticalDistance', @@ -25,6 +31,14 @@ export default class CartographerPointyXY extends Cartographer { ].map(method => this[method] = this[method].bind(this)); } + tileHeight() { + return this.maxWidth(); + } + + tileWidth() { + return this.maxWidth(); + } + maxWidth() { return this.minWidth() * sqrt2; } @@ -33,6 +47,14 @@ export default class CartographerPointyXY extends Cartographer { return this.scale * 2; } + horizontalOverhang() { + return this.maxWidth() * 0.5; + } + + verticalOverhang() { + return this.maxWidth() * 0.5; + } + horizontalDistance() { return this.maxWidth() / 2; } @@ -42,11 +64,11 @@ export default class CartographerPointyXY extends Cartographer { } calculateHorizontalScale(pixels, tiles) { - return pixels / sqrt2 / tiles; + return pixels / sqrt2 / (tiles + 1); } calculateVerticalScale(pixels, tiles) { - return pixels / sqrt2 / tiles; + return pixels / sqrt2 / (tiles + 1); } tileToPixel(square) { diff --git a/src/cartographerPointyXYZ.js b/src/cartographerPointyXYZ.js index 68c7966..bd03d6b 100644 --- a/src/cartographerPointyXYZ.js +++ b/src/cartographerPointyXYZ.js @@ -10,9 +10,15 @@ export default class CartographerPointyXYZ extends Cartographer { super(settings); [ + 'tileHeight', + 'tileWidth', + 'maxWidth', 'minWidth', + 'horizontalOverhang', + 'verticalOverhang', + 'horizontalDistance', 'verticalDistance', @@ -25,6 +31,14 @@ export default class CartographerPointyXYZ extends Cartographer { ].map(method => this[method] = this[method].bind(this)); } + tileHeight() { + return this.maxWidth(); + } + + tileWidth() { + return this.minWidth(); + } + maxWidth() { return this.scale * 2; } @@ -33,6 +47,14 @@ export default class CartographerPointyXYZ extends Cartographer { return this.scale * sqrt3; } + horizontalOverhang() { + return 0; + } + + verticalOverhang() { + return this.maxWidth() * 0.25; + } + horizontalDistance() { return this.minWidth(); } @@ -46,7 +68,7 @@ export default class CartographerPointyXYZ extends Cartographer { } calculateVerticalScale(pixels, tiles) { - return pixels / (tiles * (3/4)) / 2; + return pixels / (tiles * 0.75 + 0.25) / 2; } tileToPixel(hex) { diff --git a/src/main.js b/src/main.js index 26ffc8c..0378102 100644 --- a/src/main.js +++ b/src/main.js @@ -14,19 +14,21 @@ const DEFAULTS = { class Demo { constructor() { [ + 'setOriginTile', 'onTap', 'createTile', 'drawTile', 'draw', ].map(method => this[method] = this[method].bind(this)); - this.map = {}; - this.taps = []; - const queryStringObj = utils.getQueryStringObj(); this.settings = Object.assign({}, DEFAULTS, queryStringObj); + this.map = {}; + this.taps = []; + this.setOriginTile(); + this.tessellate = new Tessellate(Object.assign({ element: '#container', tap: this.onTap, @@ -41,6 +43,38 @@ class Demo { this.styles = ['filled', 'outline']; } + setOriginTile() { + this.map['0,0'] = new Cell({ + x: 0, + y: 0, + scale: 1, + drawStyle: Tessellate.DRAW_STYLES.FILL, + tileStyle: this.settings.tile, + orientation: this.settings.orientation, + red: 255, + green: 255, + blue: 0, + alpha: 1, + }); + + utils.rangeInclusive(0, 5) + .map(interval => interval ? interval * 0.2 : 0.01) + .forEach(interval => { + this.taps.push(new Cell({ + x: 0, + y: 0, + scale: interval, + drawStyle: Tessellate.DRAW_STYLES.OUTLINE, + tileStyle: this.settings.tile, + orientation: this.settings.orientation, + red: 0, + green: 0, + blue: 0, + alpha: 1, + })); + }); + } + onTap(tap) { console.log(tap.tile.getPoint()); diff --git a/src/tessellate.js b/src/tessellate.js index a4b6c77..80d716a 100644 --- a/src/tessellate.js +++ b/src/tessellate.js @@ -1,4 +1,3 @@ - import * as utils from './utils'; export {utils}; @@ -44,6 +43,7 @@ const DEFAULTS = { tap: utils.noop, draw: utils.noop, orientation: FLAT, + negativeTiles: true, }; function selectCartographer(board, orientation) { @@ -70,18 +70,17 @@ export default class Tessellate { constructor(settings) { [ + 'checkSettings', 'tap', 'move', 'zoom', 'pixelToTile', 'tileToPixel', 'getTilePoints', - 'draw'] - .map(method => {this[method] = this[method].bind(this)}); + '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 : - document.querySelector(this.settings.element); + this.checkSettings(settings); this.sketch = new Sketch({ element: this.settings.element, @@ -96,17 +95,27 @@ export default class Tessellate { }); const cartographer = selectCartographer(this.settings.board, this.settings.orientation); - this.cartographer = new cartographer({ - height: this.settings.height, - width: this.settings.width, - scale: this.settings.scale, - + this.cartographer = new cartographer(Object.assign({ canvasWidth: this.sketch.getContext().canvas.width, canvasHeight: this.sketch.getContext().canvas.height, + }, funky.pick(this.settings, ['originX', 'originY', 'height', 'width', 'scale', 'negativeTiles']))); + } - originX: this.settings.originX || this.sketch.getContext().canvas.width / 2, - originY: this.settings.originY || this.sketch.getContext().canvas.height / 2, - }); + checkSettings(settings) { + + this.settings = Object.assign({}, DEFAULTS, settings); + this.settings.element = this.settings.element instanceof HTMLElement ? this.settings.element : + document.querySelector(this.settings.element); + + if (this.settings.negativeTiles) { + if (this.settings.height && (this.settings.height % 2 === 0)) { + this.settings.height++; + } + + if (this.settings.width && (this.settings.width % 2 === 0)) { + this.settings.width++; + } + } } tap(event) {