diff --git a/src/cartographerFlatXYZ.js b/src/cartographerFlatXYZ.js index 9deade0..805a6eb 100644 --- a/src/cartographerFlatXYZ.js +++ b/src/cartographerFlatXYZ.js @@ -161,7 +161,7 @@ const positiveFarEast3x3 = [{ }]; export default class CartographerFlatXYZ extends Cartographer { - constructor(settings) { + constructor (settings) { super(settings); [ @@ -190,47 +190,47 @@ export default class CartographerFlatXYZ extends Cartographer { ].map(method => this[method] = this[method].bind(this)); } - tileHeight() { + tileHeight () { return this.minWidth(); } - tileWidth() { + tileWidth () { return this.maxWidth(); } - maxWidth() { + maxWidth () { return this.scale * 2; } - minWidth() { + minWidth () { return this.scale * sqrt3; } - horizontalOverhang() { + horizontalOverhang () { return this.maxWidth() * 0.25; } - verticalOverhang() { + verticalOverhang () { return 0; } - horizontalDistance() { + horizontalDistance () { return this.maxWidth() * (3/4); } - verticalDistance() { + verticalDistance () { return this.minWidth(); } - calculateHorizontalScale(pixels, tiles) { + calculateHorizontalScale (pixels, tiles) { return pixels / (tiles * 0.75 + 0.25) / 2; } - calculateVerticalScale(pixels, tiles) { + calculateVerticalScale (pixels, tiles) { return pixels / tiles / sqrt3; } - tileToPixel(hex) { + tileToPixel (hex) { hex = hex instanceof Hex ? hex : new Hex(...arguments); const pixelX = this.scale * 3/2 * hex.getQ(); @@ -239,7 +239,7 @@ export default class CartographerFlatXYZ extends Cartographer { return new Point(pixelX + this.originX, pixelY + this.originY); } - _pixelToTile(point) { + _pixelToTile (point) { point = point instanceof Point ? point : new Point(...arguments); const pixelX = point.getX() - this.originX; @@ -253,7 +253,7 @@ export default class CartographerFlatXYZ extends Cartographer { teleport (hex) { hex = hex instanceof Hex ? hex : new Hex(hex); - let {col, row} = hex.getOffsetHex(); + let {col, row} = Hex.cubeToEvenQ(hex); if (this.radius) { } @@ -289,11 +289,11 @@ export default class CartographerFlatXYZ extends Cartographer { row -= halfHeight; } - return Hex.offsetToCube(col, row); + return Hex.evenQToCube(col, row); } } - inBounds ({x, y, z = -x - y}) { + inBounds ({x, z, y = -x - z}) { if (this.radius) { if (this.negativeTiles) { return Math.max(Math.abs(x), Math.abs(y), Math.abs(z)) <= Math.floor(this.radius); @@ -320,7 +320,7 @@ export default class CartographerFlatXYZ extends Cartographer { null; } - boundingBox(upperLeftPoint, upperRightPoint, lowerLeftPoint, lowerRightPoint) { + boundingBox (upperLeftPoint, upperRightPoint, lowerLeftPoint, lowerRightPoint) { const upperLeftTile = this._pixelToTile(upperLeftPoint); const lowerLeftTile = this._pixelToTile(lowerLeftPoint); const lowerRightTile = this._pixelToTile(lowerRightPoint); @@ -337,7 +337,7 @@ export default class CartographerFlatXYZ extends Cartographer { const bottom = top + height; const rows = rangeInclusive(top, bottom + 1); - const makeAPoint = r => ({x: q, z: r, y: -q - r}); + const makeAPoint = r => Hex.qrToCube(q, r); return funky.chain(rows) .map(makeAPoint) @@ -348,21 +348,6 @@ export default class CartographerFlatXYZ extends Cartographer { .value(); }; -// if (debugged < 1) { -// -// [positive3x3, positiveEast3x3, positiveMiddleEast3x3, positiveFarEast3x3].forEach(set => { -// set.forEach((input, index) => { -// const result = this.teleport(input, true); -// -// if (result.x === positive3x3[index].x && result.y === positive3x3[index].y && result.z === positive3x3[index].z) console.log(input, '->', positive3x3[index]); -// else console.log(input, '><', positive3x3[index], result); -// console.log('---------------------------------------------------'); -// }); -// -// console.log('-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-'); -// }); -// } - return funky.chain(columns) .map(processRow) .flatten() diff --git a/src/cartographerPointyXYZ.js b/src/cartographerPointyXYZ.js index 7bd11de..aab082f 100644 --- a/src/cartographerPointyXYZ.js +++ b/src/cartographerPointyXYZ.js @@ -1,12 +1,15 @@ 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) { + constructor (settings) { super(settings); [ @@ -26,54 +29,56 @@ export default class CartographerPointyXYZ extends Cartographer { 'calculateVerticalScale', 'tileToPixel', - 'pixelToTile', + '_pixelToTile', + 'teleport', 'inBounds', + 'enforceBoundries', 'boundingBox', ].map(method => this[method] = this[method].bind(this)); } - tileHeight() { + tileHeight () { return this.maxWidth(); } - tileWidth() { + tileWidth () { return this.minWidth(); } - maxWidth() { + maxWidth () { return this.scale * 2; } - minWidth() { + minWidth () { return this.scale * sqrt3; } - horizontalOverhang() { + horizontalOverhang () { return 0; } - verticalOverhang() { + verticalOverhang () { return this.maxWidth() * 0.25; } - horizontalDistance() { + horizontalDistance () { return this.minWidth(); } - verticalDistance() { + verticalDistance () { return this.maxWidth() * (3/4); } - calculateHorizontalScale(pixels, tiles) { + calculateHorizontalScale (pixels, tiles) { return pixels / tiles / sqrt3; } - calculateVerticalScale(pixels, tiles) { + calculateVerticalScale (pixels, tiles) { return pixels / (tiles * 0.75 + 0.25) / 2; } - tileToPixel(hex) { + tileToPixel (hex) { hex = hex instanceof Hex ? hex : new Hex(...arguments); const pixelX = this.scale * sqrt3 * (hex.getQ() + (hex.getR() / 2)); @@ -82,7 +87,7 @@ export default class CartographerPointyXYZ extends Cartographer { return new Point(pixelX + this.originX, pixelY + this.originY); } - pixelToTile(point) { + _pixelToTile (point) { point = point instanceof Point ? point : new Point(...arguments); const pixelX = point.getX() - this.originX; @@ -94,45 +99,108 @@ export default class CartographerPointyXYZ extends Cartographer { return new Hex(q, r); } - inBounds (q, r, s = -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(q), Math.abs(r), Math.abs(s)) <= Math.floor(this.radius); + 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(q - this.radius), Math.abs(r + this.radius), Math.abs(s)) <= this.radius; + 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.height || (Math.abs(r) < this.height / 2)) - && (!this.width || (Math.abs(-q - Math.floor(r / 2)) < (this.width / 2))); + 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.height || (r >= 0 && r < this.height)) - && (!this.width || (q <= (Math.floor(r / 2)) && (q - Math.floor(r / 2)) < this.width)); + 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)); } } } - 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); + 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(); - return rows.map((r, index) => { + + 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); - return columns.map(q => ({q, r})) - .reduce((flat, list) => flat.concat(list), []) - .filter(({q, r}) => this.inBounds(q, r)) - .map(({q, r}) => new Hex(q, r)); - }); + 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(); } } diff --git a/src/hex.js b/src/hex.js index c46c4b9..b46febf 100644 --- a/src/hex.js +++ b/src/hex.js @@ -35,7 +35,37 @@ function roundOff(hex) { } export default class Hex extends Point { - static offsetToCube (col, row) { + static qrToCube (q, r) { + return { + x: q, + y: computeY(q, r), + z: r, + }; + } + + static cubeToEvenR ({x, y, z}) { + const col = x + (z + (z & 1)) / 2;; + const row = z; + + return {col, row}; + } + + static evenRToCube (col, row) { + const x = col - (row + (row & 1)) / 2;; + const z = row; + const y = -x - z; + + return new Hex(x, y, z); + } + + static cubeToEvenQ ({x, y, z}) { + const col = x; + const row = z + (x + (x & 1)) / 2; + + return {col, row}; + } + + static evenQToCube (col, row) { const x = col; const z = row - (col + (col & 1)) / 2; const y = -x - z; @@ -137,11 +167,4 @@ export default class Hex extends Point { this.y = computeY(this.x, this.z); return this; } - - getOffsetHex () { - const col = this.x; - const row = this.z + (this.x + (this.x & 1)) / 2; - - return {col, row}; - } }