diff --git a/src/cartographerXY.js b/src/cartographerXY.js index 5ce18e7..38539bb 100644 --- a/src/cartographerXY.js +++ b/src/cartographerXY.js @@ -51,14 +51,19 @@ export default class CartographerXY extends Cartographer { getVertDistance() {return this.pointyTop ? this.maxDistance() : this.minDistance();} tileToPixel(square) { - let scale = this.scale; + const scale = this.scale; - const minWidth = (a) => this.minWidth() * a; + // (above/below axis) * (distance from axis) / (size) + const pointyDistanceX = (x, y) => (x < y ? -1 : 1) * (Math.abs(y - x) / sqrt2) * this.minWidth(); + const pointyDistanceY = (x, y) => (-x < y ? 1 : -1) * (Math.abs(x + y) / sqrt2) * this.minWidth(); - const maxWidth = (a, b) => (this.maxWidth() * a) + ((b % 2) * (this.maxWidth() / 2)); + const flatDistance = a => this.minWidth() * a; - let pixelX = this.pointyTop ? maxWidth(square.getX(), square.getY()) : minWidth(square.getX()); - let pixelY = this.pointyTop ? (this.maxWidth() / 2) * square.getY() : minWidth(square.getY()); + const x = square.getX(); + const y = square.getY(); + + let pixelX = this.pointyTop ? pointyDistanceX(x, y) : flatDistance(x); + let pixelY = this.pointyTop ? pointyDistanceY(x, y) : flatDistance(y); pixelX += this.originX; pixelY = this.originY - pixelY;; @@ -69,38 +74,53 @@ export default class CartographerXY extends Cartographer { pixelToTile(point) { let scale = this.scale; - const radiusLong = a => (a / (this.maxWidth() / 2)); - const radiusShort = a => a / this.minWidth(); + // (above/below axis) * (distance from axis) / (size) + const pointyDistanceX = (x, y) => (-x < y ? 1 : -1) * (Math.abs(x + y) / sqrt2) / this.minWidth(); + const pointyDistanceY = (x, y) => (x < y ? 1 : -1) * (Math.abs(y - x) / sqrt2) / this.minWidth(); + + const flatDistance = a => a / this.minWidth(); let pixelX = point.getX() - this.originX; let pixelY = this.originY - point.getY(); - let x = this.pointyTop ? radiusLong(pixelX, pixelY) : radiusShort(pixelX); - let y = this.pointyTop ? radiusLong(pixelY, pixelX) : radiusShort(pixelY); + let x = this.pointyTop ? pointyDistanceX(pixelX, pixelY) : flatDistance(pixelX); + let y = this.pointyTop ? pointyDistanceY(pixelX, pixelY) : flatDistance(pixelY); return new Square(x, y); } - boundingBox(upperLeftPoint, lowerRightPoint) { - let upperRightPoint = new Point(lowerRightPoint.getX(), upperLeftPoint.getY()); - - let upperLeftTile = this.pixelToTile(upperLeftPoint); - let lowerRightTile = this.pixelToTile(lowerRightPoint); - let upperRightTile = this.pixelToTile(upperRightPoint); + boundingBox(upperLeftPoint, upperRightPoint, lowerLeftPoint, lowerRightPoint) { + const upperLeftTile = this.pixelToTile(upperLeftPoint); + const lowerRightTile = this.pixelToTile(lowerRightPoint); + const upperRightTile = this.pixelToTile(upperRightPoint); if (this.pointyTop) { - for (let col = 0; col <= width; col++) { - tiles[col] = []; - let x = upperLeftTile.getX() + col; - const bottomRow = upperLeftTile.getY() - height; + const lowerLeftTile = this.pixelToTile(lowerLeftPoint); - for (let y = upperLeftTile.getY(); y >= bottomRow; y--) { - // TODO: this results in missing tiles (e.g. (1,0)) - // fix by tilting coordinates 45 degrees? - x = this.pointyTop ? x - (y % 2) : x; - tiles[col].push(new Square(x, y)); - } - } + const columns = rangeInclusive(lowerLeftTile.getX(), upperRightTile.getX()); + + const upperLeftIntercept = upperLeftTile.getY() - upperLeftTile.getX(); + const upperRightIntercept = upperLeftTile.getY() + upperLeftTile.getX(); + + const lowerLeftIntercept = lowerRightTile.getY() - lowerRightTile.getX(); + const lowerRightIntercept = lowerRightTile.getY() + lowerRightTile.getX(); + + const aboutHalf = Math.floor(columns.length / 2); + const midway = columns.length % 2 ? columns[aboutHalf] : + (columns[aboutHalf - 1] + columns[aboutHalf]) / 2; + + return columns.map(x => { + let top = x < midway ? upperLeftIntercept + x : upperRightIntercept - x; + let bottom = x < midway ? lowerRightIntercept - x : lowerLeftIntercept + x; + + bottom = Math.min(bottom, top); + top = Math.max(bottom, top); + + // push out by 1 on either end to account for interlocking tiles + const rows = rangeInclusive(bottom - 1, top + 1); + + return rows.map(y => new Square(x, y)); + }); } else { const columns = rangeInclusive(upperLeftTile.getX(), upperRightTile.getX()); diff --git a/src/main.js b/src/main.js index 2855030..de71f96 100644 --- a/src/main.js +++ b/src/main.js @@ -17,7 +17,7 @@ class Demo { tile: Tessellate.TILE_STYLES.SQUARE, tap: this.onTap, draw: this.draw, - pointyTop: false, //utils.random(1) ? true : false, + pointyTop: true, //utils.random(1) ? true : false, }); this.circle = new DrawCircle(); diff --git a/src/tessellate.js b/src/tessellate.js index f7abf7c..9c9e4ad 100644 --- a/src/tessellate.js +++ b/src/tessellate.js @@ -43,7 +43,7 @@ export default class Tessellate { static get BOARD_STYLES () {return {HEX, SQUARE}} constructor(settings) { - ['tap', 'draw', 'drawMap', 'move', 'zoom'].map(method => {this[method] = this[method].bind(this)}); + ['seedTiles', 'tap', 'draw', 'drawMap', 'move', 'zoom'].map(method => {this[method] = this[method].bind(this)}); this.settings = utils.extend(DEFAULTS, settings); this.settings.element = this.settings.element instanceof HTMLElement ? this.settings.element : @@ -62,6 +62,7 @@ export default class Tessellate { }); this.map = []; + this.seedTiles(); const boardSettings = { pointyTop: this.settings.pointyTop, @@ -72,6 +73,54 @@ export default class Tessellate { this.cartographer = this.settings.board === HEX ? new CartographerXYZ(boardSettings) : new CartographerXY(boardSettings); } + seedTiles() { + this.map[0] = []; +// this.map[1] = []; +// this.map[-1] = []; +// this.map[-5] = []; + + this.map[0][0] = new Cell({ + x: 0, + y: 0, + pointyTop: this.settings.pointyTop, + red: 0, + green: 0, + blue: 0, + alpha: 75/100, + scale: 9/10, + }); +// this.map[1][1] = new Cell({ +// x: 1, +// y: 1, +// pointyTop: this.settings.pointyTop, +// red: 0, +// green: 0, +// blue: 255, +// alpha: 90/100, +// scale: 9/10, +// }); +// this.map[-1][-1] = new Cell({ +// x: -1, +// y: -1, +// pointyTop: this.settings.pointyTop, +// red: 255, +// green: 0, +// blue: 0, +// alpha: 90/100, +// scale: 9/10, +// }); +// this.map[-5][5] = new Cell({ +// x: -5, +// y: 5, +// pointyTop: this.settings.pointyTop, +// red: 0, +// green: 255, +// blue: 0, +// alpha: 90/100, +// scale: 9/10, +// }); + } + tap(event) { let point = new Point(event.offsetX, event.offsetY); let tile = this.cartographer.pixelToTile(point); @@ -94,12 +143,16 @@ export default class Tessellate { } drawMap(context) { - let scale = this.cartographer.getScale(); - let upperLeft = new Point(0, 0); - let lowerRight = new Point(context.canvas.width, context.canvas.height); - let tiles = this.cartographer.boundingBox(upperLeft, lowerRight); + const scale = this.cartographer.getScale(); - let height = tiles.length; + 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); + + const tiles = this.cartographer.boundingBox(upperLeft, upperRight, lowerLeft, lowerRight); + + const height = tiles.length; for (let r=0; r index + start); + if (start > end) { + const swap = start; + + start = end; + end = swap; + } + + return Array.from(Array(Math.abs(end - start)), (_value, index) => index + start); } export function rangeInclusive(start, end) { @@ -72,6 +79,6 @@ export function rangeInclusive(start, end) { start = 0; } - return range(start, end+1); + return range(Math.min(start, end), Math.max(start, end)+1); }