wrapping for pointy hexagons

This commit is contained in:
Gavin McDonald
2019-01-05 16:51:52 -05:00
parent 87c2fd9885
commit 28f9647c3a
3 changed files with 149 additions and 73 deletions

View File

@@ -161,7 +161,7 @@ const positiveFarEast3x3 = [{
}]; }];
export default class CartographerFlatXYZ extends Cartographer { export default class CartographerFlatXYZ extends Cartographer {
constructor(settings) { constructor (settings) {
super(settings); super(settings);
[ [
@@ -190,47 +190,47 @@ export default class CartographerFlatXYZ extends Cartographer {
].map(method => this[method] = this[method].bind(this)); ].map(method => this[method] = this[method].bind(this));
} }
tileHeight() { tileHeight () {
return this.minWidth(); return this.minWidth();
} }
tileWidth() { tileWidth () {
return this.maxWidth(); return this.maxWidth();
} }
maxWidth() { maxWidth () {
return this.scale * 2; return this.scale * 2;
} }
minWidth() { minWidth () {
return this.scale * sqrt3; return this.scale * sqrt3;
} }
horizontalOverhang() { horizontalOverhang () {
return this.maxWidth() * 0.25; return this.maxWidth() * 0.25;
} }
verticalOverhang() { verticalOverhang () {
return 0; return 0;
} }
horizontalDistance() { horizontalDistance () {
return this.maxWidth() * (3/4); return this.maxWidth() * (3/4);
} }
verticalDistance() { verticalDistance () {
return this.minWidth(); return this.minWidth();
} }
calculateHorizontalScale(pixels, tiles) { calculateHorizontalScale (pixels, tiles) {
return pixels / (tiles * 0.75 + 0.25) / 2; return pixels / (tiles * 0.75 + 0.25) / 2;
} }
calculateVerticalScale(pixels, tiles) { calculateVerticalScale (pixels, tiles) {
return pixels / tiles / sqrt3; return pixels / tiles / sqrt3;
} }
tileToPixel(hex) { tileToPixel (hex) {
hex = hex instanceof Hex ? hex : new Hex(...arguments); hex = hex instanceof Hex ? hex : new Hex(...arguments);
const pixelX = this.scale * 3/2 * hex.getQ(); 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); return new Point(pixelX + this.originX, pixelY + this.originY);
} }
_pixelToTile(point) { _pixelToTile (point) {
point = point instanceof Point ? point : new Point(...arguments); point = point instanceof Point ? point : new Point(...arguments);
const pixelX = point.getX() - this.originX; const pixelX = point.getX() - this.originX;
@@ -253,7 +253,7 @@ export default class CartographerFlatXYZ extends Cartographer {
teleport (hex) { teleport (hex) {
hex = hex instanceof Hex ? hex : new Hex(hex); hex = hex instanceof Hex ? hex : new Hex(hex);
let {col, row} = hex.getOffsetHex(); let {col, row} = Hex.cubeToEvenQ(hex);
if (this.radius) { if (this.radius) {
} }
@@ -289,11 +289,11 @@ export default class CartographerFlatXYZ extends Cartographer {
row -= halfHeight; 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.radius) {
if (this.negativeTiles) { if (this.negativeTiles) {
return Math.max(Math.abs(x), Math.abs(y), Math.abs(z)) <= Math.floor(this.radius); 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; null;
} }
boundingBox(upperLeftPoint, upperRightPoint, lowerLeftPoint, lowerRightPoint) { boundingBox (upperLeftPoint, upperRightPoint, lowerLeftPoint, lowerRightPoint) {
const upperLeftTile = this._pixelToTile(upperLeftPoint); const upperLeftTile = this._pixelToTile(upperLeftPoint);
const lowerLeftTile = this._pixelToTile(lowerLeftPoint); const lowerLeftTile = this._pixelToTile(lowerLeftPoint);
const lowerRightTile = this._pixelToTile(lowerRightPoint); const lowerRightTile = this._pixelToTile(lowerRightPoint);
@@ -337,7 +337,7 @@ export default class CartographerFlatXYZ extends Cartographer {
const bottom = top + height; const bottom = top + height;
const rows = rangeInclusive(top, bottom + 1); 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) return funky.chain(rows)
.map(makeAPoint) .map(makeAPoint)
@@ -348,21 +348,6 @@ export default class CartographerFlatXYZ extends Cartographer {
.value(); .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) return funky.chain(columns)
.map(processRow) .map(processRow)
.flatten() .flatten()

View File

@@ -1,12 +1,15 @@
import Cartographer from './cartographer.js'; import Cartographer from './cartographer.js';
import * as funky from './funky';
import {rangeInclusive, sqrt3} from './utils.js'; import {rangeInclusive, sqrt3} from './utils.js';
import Hex from './hex.js'; import Hex from './hex.js';
import Point from './point.js'; import Point from './point.js';
const tilePointToHex = ({tilePoint, pixelPoint}) => ({tilePoint: new Hex(tilePoint), pixelPoint});
export default class CartographerPointyXYZ extends Cartographer { export default class CartographerPointyXYZ extends Cartographer {
constructor(settings) { constructor (settings) {
super(settings); super(settings);
[ [
@@ -26,54 +29,56 @@ export default class CartographerPointyXYZ extends Cartographer {
'calculateVerticalScale', 'calculateVerticalScale',
'tileToPixel', 'tileToPixel',
'pixelToTile', '_pixelToTile',
'teleport',
'inBounds', 'inBounds',
'enforceBoundries',
'boundingBox', 'boundingBox',
].map(method => this[method] = this[method].bind(this)); ].map(method => this[method] = this[method].bind(this));
} }
tileHeight() { tileHeight () {
return this.maxWidth(); return this.maxWidth();
} }
tileWidth() { tileWidth () {
return this.minWidth(); return this.minWidth();
} }
maxWidth() { maxWidth () {
return this.scale * 2; return this.scale * 2;
} }
minWidth() { minWidth () {
return this.scale * sqrt3; return this.scale * sqrt3;
} }
horizontalOverhang() { horizontalOverhang () {
return 0; return 0;
} }
verticalOverhang() { verticalOverhang () {
return this.maxWidth() * 0.25; return this.maxWidth() * 0.25;
} }
horizontalDistance() { horizontalDistance () {
return this.minWidth(); return this.minWidth();
} }
verticalDistance() { verticalDistance () {
return this.maxWidth() * (3/4); return this.maxWidth() * (3/4);
} }
calculateHorizontalScale(pixels, tiles) { calculateHorizontalScale (pixels, tiles) {
return pixels / tiles / sqrt3; return pixels / tiles / sqrt3;
} }
calculateVerticalScale(pixels, tiles) { calculateVerticalScale (pixels, tiles) {
return pixels / (tiles * 0.75 + 0.25) / 2; return pixels / (tiles * 0.75 + 0.25) / 2;
} }
tileToPixel(hex) { tileToPixel (hex) {
hex = hex instanceof Hex ? hex : new Hex(...arguments); hex = hex instanceof Hex ? hex : new Hex(...arguments);
const pixelX = this.scale * sqrt3 * (hex.getQ() + (hex.getR() / 2)); 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); return new Point(pixelX + this.originX, pixelY + this.originY);
} }
pixelToTile(point) { _pixelToTile (point) {
point = point instanceof Point ? point : new Point(...arguments); point = point instanceof Point ? point : new Point(...arguments);
const pixelX = point.getX() - this.originX; const pixelX = point.getX() - this.originX;
@@ -94,45 +99,108 @@ export default class CartographerPointyXYZ extends Cartographer {
return new Hex(q, r); 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) { if (this.radius) {
if (this.negativeTiles) {
return Math.max(Math.abs(q), Math.abs(r), Math.abs(s)) <= Math.floor(this.radius);
} }
else { else {
return Math.max(Math.abs(q - this.radius), Math.abs(r + this.radius), Math.abs(s)) <= this.radius; // 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) { else if (this.width || this.height) {
if (this.negativeTiles) { if (this.negativeTiles) {
return (!this.height || (Math.abs(r) < this.height / 2)) return (!this.width || (Math.abs(hex.x) < this.width / 2))
&& (!this.width || (Math.abs(-q - Math.floor(r / 2)) < (this.width / 2))); && (!this.height || (Math.abs(-hex.y - Math.floor(hex.x / 2)) < (this.height / 2)));
} }
else { else {
return (!this.height || (r >= 0 && r < this.height)) return (!this.width || (hex.x >= 0 && hex.x < this.width))
&& (!this.width || (q <= (Math.floor(r / 2)) && (q - Math.floor(r / 2)) < 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) { enforceBoundries ({tilePoint, pixelPoint}) {
const upperLeftTile = this.pixelToTile(upperLeftPoint); return this.wrap ? ({tilePoint: this.teleport(tilePoint), pixelPoint}) :
const lowerLeftTile = this.pixelToTile(lowerLeftPoint); this.inBounds(tilePoint) ? ({tilePoint, pixelPoint}) :
const lowerRightTile = this.pixelToTile(lowerRightPoint); null;
const upperRightTile = this.pixelToTile(upperRightPoint); }
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 rows = rangeInclusive(upperLeftTile.getR() -1 , lowerLeftTile.getR() + 1);
const width = upperRightTile.getQ() - upperLeftTile.getQ(); 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 left = upperLeftTile.getQ() - Math.floor(index / 2);
const right = left + width; const right = left + width;
const columns = rangeInclusive(left, right + 1); const columns = rangeInclusive(left, right + 1);
return columns.map(q => ({q, r})) const makeAPoint = q => Hex.qrToCube(q, r); //({x: q, z: r, y: -q - r});
.reduce((flat, list) => flat.concat(list), [])
.filter(({q, r}) => this.inBounds(q, r)) return funky.chain(columns)
.map(({q, r}) => new Hex(q, r)); .map(makeAPoint)
}); .map(makeAPointPair)
.map(this.enforceBoundries)
.compact()
.map(tilePointToHex)
.value();
};
return funky.chain(rows)
.map(processColumn)
.flatten()
.value();
} }
} }

View File

@@ -35,7 +35,37 @@ function roundOff(hex) {
} }
export default class Hex extends Point { 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 x = col;
const z = row - (col + (col & 1)) / 2; const z = row - (col + (col & 1)) / 2;
const y = -x - z; const y = -x - z;
@@ -137,11 +167,4 @@ export default class Hex extends Point {
this.y = computeY(this.x, this.z); this.y = computeY(this.x, this.z);
return this; return this;
} }
getOffsetHex () {
const col = this.x;
const row = this.z + (this.x + (this.x & 1)) / 2;
return {col, row};
}
} }