import Cartographer from './cartographer.js'; import * as funky from './funky'; import {rangeInclusive, sqrt3} from './utils.js'; import Hex from './hex.js'; import Point from './point.js'; const tilePointToHex = ({tilePoint, pixelPoint}) => ({tilePoint: new Hex(tilePoint), pixelPoint}); export default class CartographerPointyXYZ extends Cartographer { constructor (settings) { super(settings); [ 'tileHeight', 'tileWidth', 'maxWidth', 'minWidth', 'horizontalOverhang', 'verticalOverhang', 'horizontalDistance', 'verticalDistance', 'calculateHorizontalScale', 'calculateVerticalScale', 'tileToPixel', '_pixelToTile', 'teleport', 'inBounds', 'enforceBoundries', 'boundingBox', ].map(method => this[method] = this[method].bind(this)); } tileHeight () { return this.maxWidth(); } tileWidth () { return this.minWidth(); } maxWidth () { return this.scale * 2; } minWidth () { return this.scale * sqrt3; } horizontalOverhang () { return 0; } verticalOverhang () { return this.maxWidth() * 0.25; } horizontalDistance () { return this.minWidth(); } verticalDistance () { return this.maxWidth() * (3/4); } calculateHorizontalScale (pixels, tiles) { return pixels / tiles / sqrt3; } calculateVerticalScale (pixels, tiles) { return pixels / (tiles * 0.75 + 0.25) / 2; } tileToPixel (hex) { hex = hex instanceof Hex ? hex : new Hex(...arguments); const pixelX = this.scale * sqrt3 * (hex.getQ() + (hex.getR() / 2)); const pixelY = this.scale * 3/2 * hex.getR(); return new Point(pixelX + this.originX, pixelY + this.originY); } _pixelToTile (point) { point = point instanceof Point ? point : new Point(...arguments); const pixelX = point.getX() - this.originX; const pixelY = point.getY() - this.originY; const q = ((pixelX * (sqrt3 / 3)) - (pixelY / 3)) / this.scale; const r = (pixelY * (2 / 3)) / this.scale; return new Hex(q, r); } teleport (hex) { hex = hex instanceof Hex ? hex : new Hex(hex); let {col, row} = Hex.cubeToEvenR(hex); if (this.radius) { } else { // ensure odd-width maps wrap properly if (this.height % 2) { const offset = Math.floor(row / this.height); let horizontalAdjust = offset / 2; horizontalAdjust = offset % 2 === 0 ? horizontalAdjust : row % 2 ? Math.ceil(horizontalAdjust) : Math.floor(horizontalAdjust); col -= horizontalAdjust; } const halfWidth = Math.floor(this.width / 2); const halfHeight = Math.floor(this.height / 2); if (this.negativeTiles) { col += halfWidth; row += halfHeight; } col = col % this.width; row = row % this.height; col = col < 0 ? col + this.width : col; row = row < 0 ? row + this.height : row; if (this.negativeTiles) { col -= halfWidth; row -= halfHeight; } return Hex.evenRToCube(col, row); } } inBounds (hex) { hex = hex instanceof Hex ? hex : new Hex(hex); if (this.radius) { if (this.negativeTiles) { return Math.max(Math.abs(hex.x), Math.abs(hex.y), Math.abs(hex.z)) <= Math.floor(this.radius); } else { return Math.max(Math.abs(hex.x - this.radius), Math.abs(hex.y + this.radius), Math.abs(hex.z)) <= this.radius; } } else if (this.width || this.height) { if (this.negativeTiles) { return (!this.width || (Math.abs(hex.x) < this.width / 2)) && (!this.height || (Math.abs(-hex.y - Math.floor(hex.x / 2)) < (this.height / 2))); } else { return (!this.width || (hex.x >= 0 && hex.x < this.width)) && (!this.height || (hex.y <= (Math.floor(hex.x / 2) * -1) && (-hex.y - Math.floor(hex.x / 2)) < this.height)); } } } enforceBoundries ({tilePoint, pixelPoint}) { return this.wrap ? ({tilePoint: this.teleport(tilePoint), pixelPoint}) : this.inBounds(tilePoint) ? ({tilePoint, pixelPoint}) : null; } boundingBox (upperLeftPoint, upperRightPoint, lowerLeftPoint, lowerRightPoint) { const upperLeftTile = this._pixelToTile(upperLeftPoint); const lowerLeftTile = this._pixelToTile(lowerLeftPoint); const lowerRightTile = this._pixelToTile(lowerRightPoint); const upperRightTile = this._pixelToTile(upperRightPoint); const rows = rangeInclusive(upperLeftTile.getR() -1 , lowerLeftTile.getR() + 1); const width = upperRightTile.getQ() - upperLeftTile.getQ(); const makeAPointPair = tilePoint => ({tilePoint, pixelPoint: this.tileToPixel(tilePoint)}); const processColumn = (r, index) => { const left = upperLeftTile.getQ() - Math.floor(index / 2); const right = left + width; const columns = rangeInclusive(left, right + 1); const makeAPoint = q => Hex.qrToCube(q, r); //({x: q, z: r, y: -q - r}); return funky.chain(columns) .map(makeAPoint) .map(makeAPointPair) .map(this.enforceBoundries) .compact() .map(tilePointToHex) .value(); }; return funky.chain(rows) .map(processColumn) .flatten() .value(); } }