Merge branch 'wrap' of mcdoh/Tessellate into master
This commit is contained in:
@@ -2,6 +2,8 @@ import {has} from './utils.js';
|
|||||||
import {pick} from './funky';
|
import {pick} from './funky';
|
||||||
|
|
||||||
const DEFAULTS = {
|
const DEFAULTS = {
|
||||||
|
adjustScaleMin: true,
|
||||||
|
|
||||||
// in pixels
|
// in pixels
|
||||||
scale: 50,
|
scale: 50,
|
||||||
scaleMin: 10,
|
scaleMin: 10,
|
||||||
@@ -21,6 +23,8 @@ export default class Cartographer {
|
|||||||
'setOriginX',
|
'setOriginX',
|
||||||
'setOriginY',
|
'setOriginY',
|
||||||
|
|
||||||
|
'pixelToTile',
|
||||||
|
|
||||||
'zoom',
|
'zoom',
|
||||||
|
|
||||||
'remap',
|
'remap',
|
||||||
@@ -28,7 +32,13 @@ export default class Cartographer {
|
|||||||
|
|
||||||
this.settings = Object.assign({}, DEFAULTS, settings);
|
this.settings = Object.assign({}, DEFAULTS, settings);
|
||||||
|
|
||||||
Object.assign(this, pick(this.settings, ['height', 'width', 'negativeTiles']));
|
Object.assign(this, pick(this.settings, [
|
||||||
|
'height',
|
||||||
|
'negativeTiles',
|
||||||
|
'radius',
|
||||||
|
'width',
|
||||||
|
'wrap',
|
||||||
|
]));
|
||||||
|
|
||||||
this.checkScale(this.settings.canvasHeight, this.settings.canvasWidth);
|
this.checkScale(this.settings.canvasHeight, this.settings.canvasWidth);
|
||||||
|
|
||||||
@@ -46,7 +56,7 @@ export default class Cartographer {
|
|||||||
const widthMin = this.width ? this.calculateHorizontalScale(canvasWidth, this.width) : 0;
|
const widthMin = this.width ? this.calculateHorizontalScale(canvasWidth, this.width) : 0;
|
||||||
|
|
||||||
this.scaleMax = this.settings.scaleMax;
|
this.scaleMax = this.settings.scaleMax;
|
||||||
this.scaleMin = Math.max(this.settings.scaleMin, heightMin, widthMin);
|
this.scaleMin = this.settings.adjustScaleMin ? Math.max(this.settings.scaleMin, heightMin, widthMin) : this.settings.scaleMin;
|
||||||
|
|
||||||
this.scale = this.scaleMin > this.settings.scale ? this.scaleMin : this.settings.scale;
|
this.scale = this.scaleMin > this.settings.scale ? this.scaleMin : this.settings.scale;
|
||||||
}
|
}
|
||||||
@@ -102,21 +112,23 @@ export default class Cartographer {
|
|||||||
const maxX = this.tileWidth() / 2;
|
const maxX = this.tileWidth() / 2;
|
||||||
const minY = canvasHeight - (this.tileHeight() / 2);
|
const minY = canvasHeight - (this.tileHeight() / 2);
|
||||||
|
|
||||||
this.originX = this.originX > maxX ? maxX : this.originX;
|
if (!this.wrap) {
|
||||||
this.originY = this.originY < minY ? minY : this.originY;
|
this.originX = this.originX > maxX ? maxX : this.originX;
|
||||||
|
this.originY = this.originY < minY ? minY : this.originY;
|
||||||
|
|
||||||
if (this.width) {
|
if (this.width) {
|
||||||
const boardWidth = this.width * colWidth + this.horizontalOverhang();
|
const boardWidth = this.width * colWidth + this.horizontalOverhang();
|
||||||
const minX = maxX - (boardWidth - canvasWidth);
|
const minX = maxX - (boardWidth - canvasWidth);
|
||||||
|
|
||||||
this.originX = this.originX < minX ? minX : this.originX;
|
this.originX = this.originX < minX ? minX : this.originX;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.height) {
|
if (this.height) {
|
||||||
const boardHeight = this.height * rowHeight + this.verticalOverhang();
|
const boardHeight = this.height * rowHeight + this.verticalOverhang();
|
||||||
const maxY = boardHeight - (this.tileHeight() / 2);
|
const maxY = boardHeight - (this.tileHeight() / 2);
|
||||||
|
|
||||||
this.originY = this.originY > maxY ? maxY : this.originY;
|
this.originY = this.originY > maxY ? maxY : this.originY;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,23 +136,30 @@ export default class Cartographer {
|
|||||||
const colWidth = this.horizontalDistance();
|
const colWidth = this.horizontalDistance();
|
||||||
const rowHeight = this.verticalDistance();
|
const rowHeight = this.verticalDistance();
|
||||||
|
|
||||||
if (this.width) {
|
if (!this.wrap) {
|
||||||
const canvasWidth = event.width;
|
if (this.width) {
|
||||||
const halfBoardWidth = (this.width * colWidth + this.horizontalOverhang()) / 2;
|
const canvasWidth = event.width;
|
||||||
|
const halfBoardWidth = (this.width * colWidth + this.horizontalOverhang()) / 2;
|
||||||
|
|
||||||
this.originX = this.originX > halfBoardWidth ? halfBoardWidth :
|
this.originX = this.originX > halfBoardWidth ? halfBoardWidth :
|
||||||
(canvasWidth - this.originX) > halfBoardWidth ? canvasWidth - halfBoardWidth :
|
(canvasWidth - this.originX) > halfBoardWidth ? canvasWidth - halfBoardWidth :
|
||||||
this.originX;
|
this.originX;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.height) {
|
||||||
|
const canvasHeight = event.height;
|
||||||
|
const halfBoardHeight = (this.height * rowHeight + this.verticalOverhang()) / 2;
|
||||||
|
|
||||||
|
this.originY = this.originY > halfBoardHeight ? halfBoardHeight :
|
||||||
|
(canvasHeight - this.originY) > halfBoardHeight ? canvasHeight - halfBoardHeight :
|
||||||
|
this.originY;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (this.height) {
|
pixelToTile (point) {
|
||||||
const canvasHeight = event.height;
|
const tile = this._pixelToTile(point);
|
||||||
const halfBoardHeight = (this.height * rowHeight + this.verticalOverhang()) / 2;
|
return this.wrap ? this.teleport(tile) : tile;
|
||||||
|
|
||||||
this.originY = this.originY > halfBoardHeight ? halfBoardHeight :
|
|
||||||
(canvasHeight - this.originY) > halfBoardHeight ? canvasHeight - halfBoardHeight :
|
|
||||||
this.originY;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
zoom (event) {
|
zoom (event) {
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
import Cartographer from './cartographer.js';
|
import Cartographer from './cartographer.js';
|
||||||
|
|
||||||
|
import * as funky from './funky';
|
||||||
import {rangeInclusive, invSqrt2} from './utils.js';
|
import {rangeInclusive, invSqrt2} from './utils.js';
|
||||||
|
|
||||||
import Square from './square.js';
|
|
||||||
import Point from './point.js';
|
import Point from './point.js';
|
||||||
|
import Square from './square.js';
|
||||||
|
|
||||||
|
const tilePointToSquare = ({tilePoint, pixelPoint}) => ({tilePoint: new Square(tilePoint), pixelPoint});
|
||||||
|
|
||||||
export default class CartographerFlatXY extends Cartographer {
|
export default class CartographerFlatXY extends Cartographer {
|
||||||
constructor(settings) {
|
constructor(settings) {
|
||||||
@@ -26,7 +29,11 @@ export default class CartographerFlatXY extends Cartographer {
|
|||||||
'calculateVerticalScale',
|
'calculateVerticalScale',
|
||||||
|
|
||||||
'tileToPixel',
|
'tileToPixel',
|
||||||
'pixelToTile',
|
'_pixelToTile',
|
||||||
|
'teleport',
|
||||||
|
|
||||||
|
'inBounds',
|
||||||
|
'enforceBoundries',
|
||||||
'boundingBox',
|
'boundingBox',
|
||||||
].map(method => this[method] = this[method].bind(this));
|
].map(method => this[method] = this[method].bind(this));
|
||||||
}
|
}
|
||||||
@@ -80,7 +87,7 @@ export default class CartographerFlatXY extends Cartographer {
|
|||||||
return new Point(x + this.originX, this.originY - y);
|
return new Point(x + this.originX, this.originY - y);
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
@@ -92,14 +99,51 @@ export default class CartographerFlatXY extends Cartographer {
|
|||||||
return new Square(x, y);
|
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) {
|
boundingBox(upperLeftPoint, upperRightPoint, lowerLeftPoint, lowerRightPoint) {
|
||||||
const upperLeftTile = this.pixelToTile(upperLeftPoint);
|
const upperLeftTile = this._pixelToTile(upperLeftPoint);
|
||||||
const lowerRightTile = this.pixelToTile(lowerRightPoint);
|
const lowerRightTile = this._pixelToTile(lowerRightPoint);
|
||||||
const upperRightTile = this.pixelToTile(upperRightPoint);
|
const upperRightTile = this._pixelToTile(upperRightPoint);
|
||||||
|
|
||||||
const columns = rangeInclusive(upperLeftTile.getX(), upperRightTile.getX());
|
const columns = rangeInclusive(upperLeftTile.getX(), upperRightTile.getX());
|
||||||
const rows = rangeInclusive(lowerRightTile.getY(), upperLeftTile.getY());
|
const rows = rangeInclusive(lowerRightTile.getY(), upperLeftTile.getY());
|
||||||
|
|
||||||
return columns.map(x => rows.map(y => new Square(x, y)));
|
const makeAPoint = x => rows.map(y => ({x, y}));
|
||||||
|
const makeAPointPair = tilePoint => ({tilePoint, pixelPoint: this.tileToPixel(tilePoint)});
|
||||||
|
|
||||||
|
return funky.chain(columns)
|
||||||
|
.map(makeAPoint)
|
||||||
|
.flatten()
|
||||||
|
.map(makeAPointPair)
|
||||||
|
.map(this.enforceBoundries)
|
||||||
|
.compact()
|
||||||
|
.map(tilePointToSquare)
|
||||||
|
.value();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,17 @@
|
|||||||
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});
|
||||||
|
|
||||||
|
const zeroZeroZero = new Hex({x: 0, y: 0, z: 0});
|
||||||
|
|
||||||
export default class CartographerFlatXYZ extends Cartographer {
|
export default class CartographerFlatXYZ extends Cartographer {
|
||||||
constructor(settings) {
|
constructor (settings) {
|
||||||
super(settings);
|
super(settings);
|
||||||
|
|
||||||
[
|
[
|
||||||
@@ -26,52 +31,91 @@ export default class CartographerFlatXYZ extends Cartographer {
|
|||||||
'calculateVerticalScale',
|
'calculateVerticalScale',
|
||||||
|
|
||||||
'tileToPixel',
|
'tileToPixel',
|
||||||
'pixelToTile',
|
'_pixelToTile',
|
||||||
|
'teleport',
|
||||||
|
|
||||||
|
'inBounds',
|
||||||
|
'enforceBoundries',
|
||||||
'boundingBox',
|
'boundingBox',
|
||||||
].map(method => this[method] = this[method].bind(this));
|
].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() {
|
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();
|
||||||
@@ -80,7 +124,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;
|
||||||
@@ -92,21 +136,113 @@ export default class CartographerFlatXYZ extends Cartographer {
|
|||||||
return new Hex(q, r);
|
return new Hex(q, r);
|
||||||
}
|
}
|
||||||
|
|
||||||
boundingBox(upperLeftPoint, upperRightPoint, lowerLeftPoint, lowerRightPoint) {
|
teleport (hex) {
|
||||||
const upperLeftTile = this.pixelToTile(upperLeftPoint);
|
hex = hex instanceof Hex ? hex : new Hex(hex);
|
||||||
const lowerLeftTile = this.pixelToTile(lowerLeftPoint);
|
|
||||||
const lowerRightTile = this.pixelToTile(lowerRightPoint);
|
if (this.radius) {
|
||||||
const upperRightTile = this.pixelToTile(upperRightPoint);
|
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.cubeToEvenQ(hex);
|
||||||
|
|
||||||
|
// ensure odd-width maps wrap properly
|
||||||
|
if (this.width % 2) {
|
||||||
|
const offset = Math.floor(col / this.width);
|
||||||
|
|
||||||
|
let verticalAdjust = offset / 2;
|
||||||
|
verticalAdjust = offset % 2 === 0 ? verticalAdjust :
|
||||||
|
col % 2 ? Math.ceil(verticalAdjust) :
|
||||||
|
Math.floor(verticalAdjust);
|
||||||
|
|
||||||
|
row -= verticalAdjust;
|
||||||
|
}
|
||||||
|
|
||||||
|
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.evenQToCube(col, row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return Math.max(Math.abs(x - this.radius), Math.abs(y + this.radius), Math.abs(z)) <= this.radius;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (this.width || this.height) {
|
||||||
|
if (this.negativeTiles) {
|
||||||
|
return (!this.width || (Math.abs(x) < this.width / 2))
|
||||||
|
&& (!this.height || (Math.abs(-y - Math.floor(x / 2)) < (this.height / 2)));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return (!this.width || (x >= 0 && x < this.width))
|
||||||
|
&& (!this.height || (y <= (Math.floor(x / 2) * -1) && (-y - Math.floor(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 columns = rangeInclusive(upperLeftTile.getQ() - 1, upperRightTile.getQ() + 1);
|
const columns = rangeInclusive(upperLeftTile.getQ() - 1, upperRightTile.getQ() + 1);
|
||||||
|
|
||||||
const height = lowerRightTile.getR() - upperRightTile.getR();
|
const height = lowerRightTile.getR() - upperRightTile.getR();
|
||||||
return columns.map((q, index) => {
|
|
||||||
|
const makeAPointPair = tilePoint => ({tilePoint, pixelPoint: this.tileToPixel(tilePoint)});
|
||||||
|
|
||||||
|
const processRow = (q, index) => {
|
||||||
const top = upperLeftTile.getR() - Math.floor(index / 2);
|
const top = upperLeftTile.getR() - Math.floor(index / 2);
|
||||||
const bottom = top + height;
|
const bottom = top + height;
|
||||||
const rows = rangeInclusive(top, bottom + 1);
|
const rows = rangeInclusive(top, bottom + 1);
|
||||||
|
|
||||||
return rows.map(r => new Hex(q, r));
|
const makeAPoint = r => Hex.qrToCube(q, r);
|
||||||
});
|
|
||||||
|
return funky.chain(rows)
|
||||||
|
.map(makeAPoint)
|
||||||
|
.map(makeAPointPair)
|
||||||
|
.map(this.enforceBoundries)
|
||||||
|
.compact()
|
||||||
|
.map(tilePointToHex)
|
||||||
|
.value();
|
||||||
|
};
|
||||||
|
|
||||||
|
return funky.chain(columns)
|
||||||
|
.map(processRow)
|
||||||
|
.flatten()
|
||||||
|
.value();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
import Cartographer from './cartographer.js';
|
import Cartographer from './cartographer.js';
|
||||||
|
|
||||||
|
import * as funky from './funky';
|
||||||
import {rangeInclusive, invSqrt2, sqrt2} from './utils.js';
|
import {rangeInclusive, invSqrt2, sqrt2} from './utils.js';
|
||||||
|
|
||||||
import Square from './square.js';
|
import Square from './square.js';
|
||||||
import Point from './point.js';
|
import Point from './point.js';
|
||||||
|
|
||||||
|
const tilePointToSquare = ({tilePoint, pixelPoint}) => ({tilePoint: new Square(tilePoint), pixelPoint});
|
||||||
|
|
||||||
export default class CartographerPointyXY extends Cartographer {
|
export default class CartographerPointyXY extends Cartographer {
|
||||||
constructor(settings) {
|
constructor(settings) {
|
||||||
super(settings);
|
super(settings);
|
||||||
@@ -26,7 +29,11 @@ export default class CartographerPointyXY extends Cartographer {
|
|||||||
'calculateVerticalScale',
|
'calculateVerticalScale',
|
||||||
|
|
||||||
'tileToPixel',
|
'tileToPixel',
|
||||||
'pixelToTile',
|
'_pixelToTile',
|
||||||
|
'teleport',
|
||||||
|
|
||||||
|
'inBounds',
|
||||||
|
'enforceBoundries',
|
||||||
'boundingBox',
|
'boundingBox',
|
||||||
].map(method => this[method] = this[method].bind(this));
|
].map(method => this[method] = this[method].bind(this));
|
||||||
}
|
}
|
||||||
@@ -84,7 +91,7 @@ export default class CartographerPointyXY extends Cartographer {
|
|||||||
return new Point(pixelX + this.originX, this.originY - pixelY);
|
return new Point(pixelX + this.originX, this.originY - pixelY);
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
@@ -97,11 +104,38 @@ export default class CartographerPointyXY extends Cartographer {
|
|||||||
return new Square(x, y);
|
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) {
|
boundingBox(upperLeftPoint, upperRightPoint, lowerLeftPoint, lowerRightPoint) {
|
||||||
const upperLeftTile = this.pixelToTile(upperLeftPoint);
|
const upperLeftTile = this._pixelToTile(upperLeftPoint);
|
||||||
const lowerRightTile = this.pixelToTile(lowerRightPoint);
|
const lowerRightTile = this._pixelToTile(lowerRightPoint);
|
||||||
const upperRightTile = this.pixelToTile(upperRightPoint);
|
const upperRightTile = this._pixelToTile(upperRightPoint);
|
||||||
const lowerLeftTile = this.pixelToTile(lowerLeftPoint);
|
const lowerLeftTile = this._pixelToTile(lowerLeftPoint);
|
||||||
|
|
||||||
const columns = rangeInclusive(lowerLeftTile.getX(), upperRightTile.getX());
|
const columns = rangeInclusive(lowerLeftTile.getX(), upperRightTile.getX());
|
||||||
|
|
||||||
@@ -115,7 +149,9 @@ export default class CartographerPointyXY extends Cartographer {
|
|||||||
const midway = columns.length % 2 ? columns[aboutHalf] :
|
const midway = columns.length % 2 ? columns[aboutHalf] :
|
||||||
(columns[aboutHalf - 1] + columns[aboutHalf]) / 2;
|
(columns[aboutHalf - 1] + columns[aboutHalf]) / 2;
|
||||||
|
|
||||||
return columns.map(x => {
|
const makeAPointPair = tilePoint => ({tilePoint, pixelPoint: this.tileToPixel(tilePoint)});
|
||||||
|
|
||||||
|
const processRow = x => {
|
||||||
let top = x < midway ? upperLeftIntercept + x : upperRightIntercept - x;
|
let top = x < midway ? upperLeftIntercept + x : upperRightIntercept - x;
|
||||||
let bottom = x < midway ? lowerRightIntercept - x : lowerLeftIntercept + x;
|
let bottom = x < midway ? lowerRightIntercept - x : lowerLeftIntercept + x;
|
||||||
|
|
||||||
@@ -123,9 +159,22 @@ export default class CartographerPointyXY extends Cartographer {
|
|||||||
top = Math.max(bottom, top);
|
top = Math.max(bottom, top);
|
||||||
|
|
||||||
// push out by 1 on either end to account for interlocking tiles
|
// push out by 1 on either end to account for interlocking tiles
|
||||||
const rows = rangeInclusive(bottom - 1, top + 1);
|
const row = rangeInclusive(bottom - 1, top + 1);
|
||||||
|
|
||||||
return rows.map(y => new Square(x, y));
|
const makeAPoint = y => ({x, y});
|
||||||
});
|
|
||||||
|
return funky.chain(row)
|
||||||
|
.map(makeAPoint)
|
||||||
|
.map(makeAPointPair)
|
||||||
|
.map(this.enforceBoundries)
|
||||||
|
.compact()
|
||||||
|
.map(tilePointToSquare)
|
||||||
|
.value();
|
||||||
|
};
|
||||||
|
|
||||||
|
return funky.chain(columns)
|
||||||
|
.map(processRow)
|
||||||
|
.flatten()
|
||||||
|
.value();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,17 @@
|
|||||||
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});
|
||||||
|
|
||||||
|
const zeroZeroZero = new Hex({x: 0, y: 0, z: 0});
|
||||||
|
|
||||||
export default class CartographerPointyXYZ extends Cartographer {
|
export default class CartographerPointyXYZ extends Cartographer {
|
||||||
constructor(settings) {
|
constructor (settings) {
|
||||||
super(settings);
|
super(settings);
|
||||||
|
|
||||||
[
|
[
|
||||||
@@ -26,52 +31,91 @@ export default class CartographerPointyXYZ extends Cartographer {
|
|||||||
'calculateVerticalScale',
|
'calculateVerticalScale',
|
||||||
|
|
||||||
'tileToPixel',
|
'tileToPixel',
|
||||||
'pixelToTile',
|
'_pixelToTile',
|
||||||
|
'teleport',
|
||||||
|
|
||||||
|
'inBounds',
|
||||||
|
'enforceBoundries',
|
||||||
'boundingBox',
|
'boundingBox',
|
||||||
].map(method => this[method] = this[method].bind(this));
|
].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() {
|
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));
|
||||||
@@ -80,7 +124,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;
|
||||||
@@ -92,21 +136,115 @@ export default class CartographerPointyXYZ extends Cartographer {
|
|||||||
return new Hex(q, r);
|
return new Hex(q, r);
|
||||||
}
|
}
|
||||||
|
|
||||||
boundingBox(upperLeftPoint, upperRightPoint, lowerLeftPoint, lowerRightPoint) {
|
teleport (hex) {
|
||||||
const upperLeftTile = this.pixelToTile(upperLeftPoint);
|
hex = hex instanceof Hex ? hex : new Hex(hex);
|
||||||
const lowerLeftTile = this.pixelToTile(lowerLeftPoint);
|
|
||||||
const lowerRightTile = this.pixelToTile(lowerRightPoint);
|
if (this.radius) {
|
||||||
const upperRightTile = this.pixelToTile(upperRightPoint);
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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 => 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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
16
src/funky.js
16
src/funky.js
@@ -1,5 +1,10 @@
|
|||||||
export function chain (obj) {
|
export function chain (obj) {
|
||||||
let chainInstance = {
|
let chainInstance = {
|
||||||
|
log: function (predicate = () => true) {
|
||||||
|
if (predicate(obj)) console.log(obj);
|
||||||
|
return chainInstance;
|
||||||
|
},
|
||||||
|
|
||||||
value: function () {
|
value: function () {
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
@@ -16,7 +21,7 @@ export function chain (obj) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function compact (obj) {
|
export function compact (obj) {
|
||||||
return filter(obj, val => val ? true : false);
|
return filter(obj, val => val != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function contains (obj, value) {
|
export function contains (obj, value) {
|
||||||
@@ -64,9 +69,12 @@ export function find (obj, predicate) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function flatten (list) {
|
export function flatten (obj) {
|
||||||
if (Array.isArray(list)) {
|
if (Array.isArray(obj)) {
|
||||||
return list.reduce((memo, element) => memo.concat(Array.isArray(element) ? flatten(element) : element), []);
|
return obj.reduce((memo, element) => memo.concat(Array.isArray(element) ? flatten(element) : element), []);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return reduce((flat, prop) => flat.concat(prop), [])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
99
src/hex.js
99
src/hex.js
@@ -35,11 +35,56 @@ function roundOff(hex) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default class Hex extends Point {
|
export default class Hex extends Point {
|
||||||
|
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;
|
||||||
|
|
||||||
|
return new Hex(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
if (arguments.length === 2) { // hex = Hex(q, r);
|
if (arguments.length === 1) {
|
||||||
|
const {q, r, s = -q - r} = arguments[0];
|
||||||
|
const {x, z, y = -x - z} = arguments[0];
|
||||||
|
|
||||||
|
this.x = !isNaN(q) ? q : x;
|
||||||
|
this.y = !isNaN(s) ? s : y;
|
||||||
|
this.z = !isNaN(r) ? r : z;
|
||||||
|
}
|
||||||
|
else if (arguments.length === 2) { // hex = Hex(q, r);
|
||||||
this.x = arguments[0];
|
this.x = arguments[0];
|
||||||
this.z = arguments[1];
|
this.z = arguments[1];
|
||||||
this.y = computeY(this.x, this.z);
|
this.y = computeY(this.x, this.z);
|
||||||
@@ -57,65 +102,81 @@ export default class Hex extends Point {
|
|||||||
getY() {return this.y;}
|
getY() {return this.y;}
|
||||||
getZ() {return this.z;}
|
getZ() {return this.z;}
|
||||||
|
|
||||||
setX(newX) {this.x = newX; return this;}
|
setX (newX) {this.x = newX; return this;}
|
||||||
setY(newY) {this.y = newY; return this;}
|
setY (newY) {this.y = newY; return this;}
|
||||||
setZ(newZ) {this.z = newZ; return this;}
|
setZ (newZ) {this.z = newZ; return this;}
|
||||||
|
|
||||||
moveX(byX) {this.x += byX; return this;}
|
moveX (byX) {this.x += byX; return this;}
|
||||||
moveY(byY) {this.y += byY; return this;}
|
moveY (byY) {this.y += byY; return this;}
|
||||||
moveZ(byZ) {this.z += byZ; return this;}
|
moveZ (byZ) {this.z += byZ; return this;}
|
||||||
|
|
||||||
getQ() {return this.x;}
|
getQ () {return this.x;}
|
||||||
getR() {return this.z;}
|
getR () {return this.z;}
|
||||||
|
|
||||||
setQ(newQ) {
|
setQ (newQ) {
|
||||||
this.x = newQ;
|
this.x = newQ;
|
||||||
this.y = computeY(this.x, this.z);
|
this.y = computeY(this.x, this.z);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
setR(newR) {
|
|
||||||
|
setR (newR) {
|
||||||
this.z = newR;
|
this.z = newR;
|
||||||
this.y = computeY(this.x, this.z);
|
this.y = computeY(this.x, this.z);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
moveQ(byQ) {
|
moveQ (byQ) {
|
||||||
this.x += byQ;
|
this.x += byQ;
|
||||||
this.y = computeY(this.x, this.z);
|
this.y = computeY(this.x, this.z);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
moveR(byR) {
|
|
||||||
|
moveR (byR) {
|
||||||
this.z += byR;
|
this.z += byR;
|
||||||
this.y = computeY(this.x, this.z);
|
this.y = computeY(this.x, this.z);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
getPoint() { return {x: this.x, y: this.y, z: this.z}; }
|
getPoint () { return {x: this.x, y: this.y, z: this.z}; }
|
||||||
|
|
||||||
setHex(newHex) {
|
setHex (newHex) {
|
||||||
this.x = newHex.x;
|
this.x = newHex.x;
|
||||||
this.y = newHex.y;
|
this.y = newHex.y;
|
||||||
this.z = newHex.z;
|
this.z = newHex.z;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
moveHex(byHex) {
|
moveHex (byHex) {
|
||||||
this.x += byHex.x;
|
this.x += byHex.x;
|
||||||
this.y += byHex.y;
|
this.y += byHex.y;
|
||||||
this.z += byHex.z;
|
this.z += byHex.z;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
getAxial() {return {q: this.x, r: this.z};}
|
subtractHex (hex) {
|
||||||
setAxial(newAxial) {
|
this.x -= hex.x;
|
||||||
|
this.y -= hex.y;
|
||||||
|
this.z -= hex.z;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
getAxial () {return {q: this.x, r: this.z};}
|
||||||
|
|
||||||
|
setAxial (newAxial) {
|
||||||
this.x = newAxial.q;
|
this.x = newAxial.q;
|
||||||
this.z = newAxial.r;
|
this.z = newAxial.r;
|
||||||
this.y = computeY(this.x, this.y);
|
this.y = computeY(this.x, this.y);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
moveAxial(byAxial) {
|
|
||||||
|
moveAxial (byAxial) {
|
||||||
this.x += byAxial.q;
|
this.x += byAxial.q;
|
||||||
this.z += byAxial.r;
|
this.z += byAxial.r;
|
||||||
this.y = computeY(this.x, this.z);
|
this.y = computeY(this.x, this.z);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
distance (hex) {
|
||||||
|
return Math.max(Math.abs(this.x - hex.x), Math.abs(this.y - hex.y), Math.abs(this.z - hex.z));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
27
src/main.js
27
src/main.js
@@ -130,7 +130,15 @@ class Demo {
|
|||||||
const key = `${ x },${ z != null ? z : y }`;
|
const key = `${ x },${ z != null ? z : y }`;
|
||||||
|
|
||||||
const pipMax = this.settings.tile === Tessellate.TILE_STYLES.HEX ? 7 : 9;
|
const pipMax = this.settings.tile === Tessellate.TILE_STYLES.HEX ? 7 : 9;
|
||||||
this.map[key].pips = Tessellate.utils.random(1, pipMax);
|
|
||||||
|
if (this.map[key]) {
|
||||||
|
this.map[key].pips = Tessellate.utils.random(1, pipMax);
|
||||||
|
|
||||||
|
console.log(`{${ x }, ${ y }${ z != null ? `, ${ z }` : ''}}`);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log('ERROR - no tile', key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pressStart(tap) {
|
pressStart(tap) {
|
||||||
@@ -218,7 +226,11 @@ class Demo {
|
|||||||
return tile;
|
return tile;
|
||||||
}
|
}
|
||||||
|
|
||||||
drawTile({x, y, z}, context, scale) {
|
drawTile(pointGroup, context, scale) {
|
||||||
|
const {x, y, z} = pointGroup.tilePoint;
|
||||||
|
const pixelX = pointGroup.pixelPoint.getX();
|
||||||
|
const pixelY = pointGroup.pixelPoint.getY();
|
||||||
|
|
||||||
this.counts[0] += 1;
|
this.counts[0] += 1;
|
||||||
|
|
||||||
const key = `${ x },${ z != null ? z : y }`;
|
const key = `${ x },${ z != null ? z : y }`;
|
||||||
@@ -231,16 +243,15 @@ class Demo {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let tile = this.map[key];
|
let tile = this.map[key];
|
||||||
const pixelPoint = this.tessellate.tileToPixel(x, y, z);
|
|
||||||
|
|
||||||
const fadeFactor = this.gray ? Math.min((Date.now() - this.gray) / 5000, 1) : null;
|
const fadeFactor = this.gray ? Math.min((Date.now() - this.gray) / 5000, 1) : null;
|
||||||
tile = this.gray ? this.fadeToGray(tile, fadeFactor) : tile;
|
tile = this.gray ? this.fadeToGray(tile, fadeFactor) : tile;
|
||||||
|
|
||||||
Tessellate.TILES[tile.tileStyle][tile.drawStyle](context, scale, pixelPoint.getX(), pixelPoint.getY(), tile);
|
Tessellate.TILES[tile.tileStyle][tile.drawStyle](context, scale, pixelX, pixelY, tile);
|
||||||
|
|
||||||
if (this.mined) {
|
if (this.mined) {
|
||||||
this.counts[2] += 1;
|
this.counts[2] += 1;
|
||||||
Tessellate.Shapes.mine(context, scale, pixelPoint.getX(), pixelPoint.getY());
|
Tessellate.Shapes.mine(context, scale, pixelX, pixelY);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (!tile.pips && this.pipDefault !== null) {
|
if (!tile.pips && this.pipDefault !== null) {
|
||||||
@@ -255,12 +266,12 @@ class Demo {
|
|||||||
|
|
||||||
if (tile.pips) {
|
if (tile.pips) {
|
||||||
this.counts[1] += tile.pips;
|
this.counts[1] += tile.pips;
|
||||||
Tessellate.Shapes.pips(context, scale, pixelPoint.getX(), pixelPoint.getY(), tile);
|
Tessellate.Shapes.pips(context, scale, pixelX, pixelY, tile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
draw({context, height, width, scale, tilePoints, now, lastNow}) {
|
draw({context, height, width, scale, pointGroups, now, lastNow}) {
|
||||||
this.counts = [
|
this.counts = [
|
||||||
0, // tiles
|
0, // tiles
|
||||||
0, // pips
|
0, // pips
|
||||||
@@ -273,7 +284,7 @@ class Demo {
|
|||||||
blue: 128,
|
blue: 128,
|
||||||
});
|
});
|
||||||
|
|
||||||
tilePoints.forEach(tilePoint => this.drawTile(tilePoint, context, scale));
|
pointGroups.forEach(pointGroup => this.drawTile(pointGroup, context, scale));
|
||||||
|
|
||||||
this.ripples.forEach(({timestamp, cell}) => {
|
this.ripples.forEach(({timestamp, cell}) => {
|
||||||
const pressFactor = Math.min((now - timestamp) / PRESS_RIPPLE, 1);
|
const pressFactor = Math.min((now - timestamp) / PRESS_RIPPLE, 1);
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
|
|
||||||
export default class Point {
|
export default class Point {
|
||||||
constructor(x, y) {
|
constructor(x, y) {
|
||||||
|
if (typeof x === 'object') {
|
||||||
|
y = x.y;
|
||||||
|
x = x.x;
|
||||||
|
}
|
||||||
|
|
||||||
// add zero to turn -0 into 0
|
// add zero to turn -0 into 0
|
||||||
this.x = Math.round(x) + 0;
|
this.x = Math.round(x) + 0;
|
||||||
this.y = Math.round(y) + 0;
|
this.y = Math.round(y) + 0;
|
||||||
|
|||||||
@@ -113,12 +113,15 @@ export class Tessellate {
|
|||||||
|
|
||||||
const cartographer = selectCartographer(this.settings.board, this.settings.orientation);
|
const cartographer = selectCartographer(this.settings.board, this.settings.orientation);
|
||||||
this.cartographer = new cartographer(Object.assign(this.sketch.getSize(), funky.pick(this.settings, [
|
this.cartographer = new cartographer(Object.assign(this.sketch.getSize(), funky.pick(this.settings, [
|
||||||
|
'adjustScaleMin',
|
||||||
'centerX',
|
'centerX',
|
||||||
'centerY',
|
'centerY',
|
||||||
'height',
|
'height',
|
||||||
'width',
|
|
||||||
'scale',
|
|
||||||
'negativeTiles',
|
'negativeTiles',
|
||||||
|
'radius',
|
||||||
|
'scale',
|
||||||
|
'width',
|
||||||
|
'wrap',
|
||||||
])));
|
])));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,6 +131,11 @@ export class Tessellate {
|
|||||||
this.settings.element = this.settings.element instanceof HTMLElement ? this.settings.element :
|
this.settings.element = this.settings.element instanceof HTMLElement ? this.settings.element :
|
||||||
document.querySelector(this.settings.element);
|
document.querySelector(this.settings.element);
|
||||||
|
|
||||||
|
if (this.settings.radius) {
|
||||||
|
this.settings.height = this.settings.radius * 2 + 1;
|
||||||
|
this.settings.width = this.settings.radius * 2 + 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.settings.negativeTiles) {
|
if (this.settings.negativeTiles) {
|
||||||
if (this.settings.height && (this.settings.height % 2 === 0)) {
|
if (this.settings.height && (this.settings.height % 2 === 0)) {
|
||||||
this.settings.height++;
|
this.settings.height++;
|
||||||
@@ -141,6 +149,7 @@ export class Tessellate {
|
|||||||
|
|
||||||
tap (event) {
|
tap (event) {
|
||||||
let point = new Point(event.offsetX, event.offsetY);
|
let point = new Point(event.offsetX, event.offsetY);
|
||||||
|
|
||||||
let tile = this.cartographer.pixelToTile(point);
|
let tile = this.cartographer.pixelToTile(point);
|
||||||
|
|
||||||
let tap = {
|
let tap = {
|
||||||
@@ -214,7 +223,7 @@ export class Tessellate {
|
|||||||
const lowerLeft = new Point(0, lowerRightY);
|
const lowerLeft = new Point(0, lowerRightY);
|
||||||
const lowerRight = new Point(lowerRightX, lowerRightY);
|
const lowerRight = new Point(lowerRightX, lowerRightY);
|
||||||
|
|
||||||
return funky.flatten(this.cartographer.boundingBox(upperLeft, upperRight, lowerLeft, lowerRight));
|
return this.cartographer.boundingBox(upperLeft, upperRight, lowerLeft, lowerRight);
|
||||||
}
|
}
|
||||||
|
|
||||||
draw (context) {
|
draw (context) {
|
||||||
@@ -222,6 +231,15 @@ export class Tessellate {
|
|||||||
const height = canvas.height;
|
const height = canvas.height;
|
||||||
const width = canvas.width;
|
const width = canvas.width;
|
||||||
|
|
||||||
|
const corners = {
|
||||||
|
upperLeftX: 0,
|
||||||
|
upperLeftY: 0,
|
||||||
|
lowerRightX: width,
|
||||||
|
lowerRightY: height
|
||||||
|
};
|
||||||
|
|
||||||
|
const pointGroups = this.getTilePoints(corners);
|
||||||
|
|
||||||
this.settings.draw({
|
this.settings.draw({
|
||||||
context,
|
context,
|
||||||
|
|
||||||
@@ -231,12 +249,7 @@ export class Tessellate {
|
|||||||
lastNow: context.lastUTC,
|
lastNow: context.lastUTC,
|
||||||
now: context.utc,
|
now: context.utc,
|
||||||
|
|
||||||
tilePoints: this.getTilePoints({
|
pointGroups,
|
||||||
upperLeftX: 0,
|
|
||||||
upperLeftY: 0,
|
|
||||||
lowerRightX: width,
|
|
||||||
lowerRightY: height
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user