import Cartographer from './cartographer.js'; import {rangeInclusive, sqrt2} from './utils.js'; import Square from './square.js'; import Point from './point.js'; export default class CartographerXY extends Cartographer { constructor(settings) { super(settings); [ 'maxWidth', 'minWidth', 'maxDistance', 'minDistance', 'getCellWidth', 'getCellHeight', 'getHorzDistance', 'getVertDistance', 'tileToPixel', 'pixelToTile', 'boundingBox', ].map(method => this[method] = this[method].bind(this)); } maxWidth() { return this.minWidth() * sqrt2; } minWidth() { return this.scale * 2; } maxDistance() { return this.maxWidth(); } minDistance() { return this.minWidth(); } getCellWidth() {return this.pointyTop ? this.maxWidth() : this.minWidth();} getCellHeight() {return this.pointyTop ? this.maxWidth() : this.minWidth();} getHorzDistance() {return this.pointyTop ? this.maxDistance() : this.minDistance();} getVertDistance() {return this.pointyTop ? this.maxDistance() : this.minDistance();} tileToPixel(square) { const scale = this.scale; // (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 flatDistance = a => this.minWidth() * a; 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;; return new Point(pixelX, pixelY); } pixelToTile(point) { let scale = this.scale; // (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 ? pointyDistanceX(pixelX, pixelY) : flatDistance(pixelX); let y = this.pointyTop ? pointyDistanceY(pixelX, pixelY) : flatDistance(pixelY); return new Square(x, y); } boundingBox(upperLeftPoint, upperRightPoint, lowerLeftPoint, lowerRightPoint) { const upperLeftTile = this.pixelToTile(upperLeftPoint); const lowerRightTile = this.pixelToTile(lowerRightPoint); const upperRightTile = this.pixelToTile(upperRightPoint); if (this.pointyTop) { const lowerLeftTile = this.pixelToTile(lowerLeftPoint); 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()); const rows = rangeInclusive(lowerRightTile.getY(), upperLeftTile.getY()); return columns.map(x => rows.map(y => new Square(x, y))); } } }