179 lines
4.5 KiB
JavaScript
179 lines
4.5 KiB
JavaScript
import Cartographer from './cartographer.js';
|
|
|
|
import * as funky from './funky';
|
|
import {rangeInclusive, invSqrt2, sqrt2} from './utils.js';
|
|
|
|
import Square from './square.js';
|
|
import Point from './point.js';
|
|
|
|
const tilePointToSquare = ({tilePoint, pixelPoint}) => ({tilePoint: new Square(tilePoint), pixelPoint});
|
|
|
|
export default class CartographerPointyXY 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.maxWidth();
|
|
}
|
|
|
|
maxWidth() {
|
|
return this.scale * 2;
|
|
}
|
|
|
|
minWidth() {
|
|
return this.maxWidth() * invSqrt2;
|
|
}
|
|
|
|
horizontalOverhang() {
|
|
return this.maxWidth() * 0.5;
|
|
}
|
|
|
|
verticalOverhang() {
|
|
return this.maxWidth() * 0.5;
|
|
}
|
|
|
|
horizontalDistance() {
|
|
return this.maxWidth() / 2;
|
|
}
|
|
|
|
verticalDistance() {
|
|
return this.maxWidth() / 2;
|
|
}
|
|
|
|
calculateHorizontalScale(pixels, tiles) {
|
|
return pixels / (tiles / 2) / 2;
|
|
}
|
|
|
|
calculateVerticalScale(pixels, tiles) {
|
|
return pixels / (tiles / 2) / 2;
|
|
}
|
|
|
|
tileToPixel(square) {
|
|
square = square instanceof Square ? square : new Square(...arguments);
|
|
|
|
const x = square.getX();
|
|
const y = square.getY();
|
|
|
|
// (above/below axis) * (distance from axis) / (size)
|
|
let pixelX = (x < y ? -1 : 1) * (Math.abs(y - x) / sqrt2) * this.minWidth();
|
|
let pixelY = (-x < y ? 1 : -1) * (Math.abs(x + y) / sqrt2) * this.minWidth();
|
|
|
|
return new Point(pixelX + this.originX, this.originY - pixelY);
|
|
}
|
|
|
|
_pixelToTile (point) {
|
|
point = point instanceof Point ? point : new Point(...arguments);
|
|
|
|
const pixelX = point.getX() - this.originX;
|
|
const pixelY = this.originY - point.getY();
|
|
|
|
// (above/below axis) * (distance from axis) / (size)
|
|
const x = (-pixelX < pixelY ? 1 : -1) * (Math.abs(pixelX + pixelY) / sqrt2) / this.minWidth();
|
|
const y = (pixelX < pixelY ? 1 : -1) * (Math.abs(pixelY - pixelX) / sqrt2) / this.minWidth();
|
|
|
|
return new Square(x, y);
|
|
}
|
|
|
|
teleport ({x, y}) {
|
|
x = x % this.width;
|
|
y = y % this.height;
|
|
|
|
x = x < 0 ? this.width + x : x;
|
|
y = y < 0 ? this.height + y : y;
|
|
|
|
return new Point(x, y);
|
|
}
|
|
|
|
inBounds ({x, y}) {
|
|
if (this.negativeTiles) {
|
|
return (!this.width || Math.abs(x) <= Math.floor(this.width / 2))
|
|
&& (!this.height || Math.abs(y) <= Math.floor(this.height / 2));
|
|
}
|
|
else {
|
|
return (!this.width || (x >= 0 && x < this.width))
|
|
&& (!this.height || (y >= 0 && y < 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 lowerRightTile = this._pixelToTile(lowerRightPoint);
|
|
const upperRightTile = this._pixelToTile(upperRightPoint);
|
|
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;
|
|
|
|
const makeAPointPair = tilePoint => ({tilePoint, pixelPoint: this.tileToPixel(tilePoint)});
|
|
|
|
return funky.chain(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);
|
|
|
|
const makeAPoint = y => ({x, y});
|
|
|
|
return funky.chain(rows)
|
|
.map(makeAPoint)
|
|
.map(makeAPointPair)
|
|
.map(this.enforceBoundries)
|
|
.compact()
|
|
.map(tilePointToSquare)
|
|
.value();
|
|
})
|
|
.flatten()
|
|
.value();
|
|
}
|
|
}
|