Files
Tessellate/src/cartographerPointyXYZ.js
2019-01-31 22:09:07 -05:00

261 lines
6.1 KiB
JavaScript

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, mapPoint, pixelPoint}) => ({
tile: tilePoint instanceof Hex ? tilePoint : new Hex(tilePoint),
mapTile: mapPoint instanceof Hex ? mapPoint :
mapPoint ? new Hex(mapPoint) :
mapPoint,
pixelPoint,
});
const zeroZeroZero = new Hex({x: 0, y: 0, z: 0});
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));
if (this.radius) {
this.mirrors = [
new Hex({ // East
x: 2 * this.radius + 1,
y: -this.radius - 1,
z: -this.radius,
}),
new Hex({ // North East
x: this.radius + 1,
y: this.radius,
z: -2 * this.radius - 1,
}),
new Hex({ // North West
x: -this.radius,
y: 2 * this.radius + 1,
z: -this.radius - 1,
}),
new Hex ({ // West
x: -2 * this.radius - 1,
y: this.radius + 1,
z: this.radius,
}),
new Hex ({ // South West
x: -this.radius - 1,
y: -this.radius,
z: 2 * this.radius + 1,
}),
new Hex ({ // South East
x: this.radius,
y: -2 * this.radius - 1,
z: this.radius + 1,
}),
];
}
}
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) {
if (!this.wrap) return hex;
hex = hex instanceof Hex ? hex : new Hex(hex);
if (this.radius) {
if (hex.distance(zeroZeroZero) <= this.radius) return hex;
const distances = this.mirrors.map(mirror => hex.distance(mirror));
const mirror = this.mirrors[distances.indexOf(Math.min(...distances))];
return this.teleport(hex.subtractHex(mirror));
}
else {
let {col, row} = Hex.cubeToEvenR(hex);
// 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));
}
}
else {
return true;
}
}
enforceBoundries ({tilePoint, pixelPoint}) {
return this.wrap ? {tilePoint, mapPoint: this.teleport(tilePoint), pixelPoint} :
this.inBounds(tilePoint) ? {tilePoint, mapPoint: tilePoint, pixelPoint} :
{tilePoint, mapPoint: null, pixelPoint};
}
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)
.map(tilePointToHex)
.value();
};
return funky.chain(rows)
.map(processColumn)
.flatten()
.value();
}
}