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';
|
||||
|
||||
const DEFAULTS = {
|
||||
adjustScaleMin: true,
|
||||
|
||||
// in pixels
|
||||
scale: 50,
|
||||
scaleMin: 10,
|
||||
@@ -21,6 +23,8 @@ export default class Cartographer {
|
||||
'setOriginX',
|
||||
'setOriginY',
|
||||
|
||||
'pixelToTile',
|
||||
|
||||
'zoom',
|
||||
|
||||
'remap',
|
||||
@@ -28,7 +32,13 @@ export default class Cartographer {
|
||||
|
||||
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);
|
||||
|
||||
@@ -46,7 +56,7 @@ export default class Cartographer {
|
||||
const widthMin = this.width ? this.calculateHorizontalScale(canvasWidth, this.width) : 0;
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -102,6 +112,7 @@ export default class Cartographer {
|
||||
const maxX = this.tileWidth() / 2;
|
||||
const minY = canvasHeight - (this.tileHeight() / 2);
|
||||
|
||||
if (!this.wrap) {
|
||||
this.originX = this.originX > maxX ? maxX : this.originX;
|
||||
this.originY = this.originY < minY ? minY : this.originY;
|
||||
|
||||
@@ -119,11 +130,13 @@ export default class Cartographer {
|
||||
this.originY = this.originY > maxY ? maxY : this.originY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_checkMoveNegativeTiles (event) {
|
||||
const colWidth = this.horizontalDistance();
|
||||
const rowHeight = this.verticalDistance();
|
||||
|
||||
if (!this.wrap) {
|
||||
if (this.width) {
|
||||
const canvasWidth = event.width;
|
||||
const halfBoardWidth = (this.width * colWidth + this.horizontalOverhang()) / 2;
|
||||
@@ -142,6 +155,12 @@ export default class Cartographer {
|
||||
this.originY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pixelToTile (point) {
|
||||
const tile = this._pixelToTile(point);
|
||||
return this.wrap ? this.teleport(tile) : tile;
|
||||
}
|
||||
|
||||
zoom (event) {
|
||||
const scaleOrig = this.scale;
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import Cartographer from './cartographer.js';
|
||||
|
||||
import * as funky from './funky';
|
||||
import {rangeInclusive, invSqrt2} from './utils.js';
|
||||
|
||||
import Square from './square.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 {
|
||||
constructor(settings) {
|
||||
@@ -26,7 +29,11 @@ export default class CartographerFlatXY extends Cartographer {
|
||||
'calculateVerticalScale',
|
||||
|
||||
'tileToPixel',
|
||||
'pixelToTile',
|
||||
'_pixelToTile',
|
||||
'teleport',
|
||||
|
||||
'inBounds',
|
||||
'enforceBoundries',
|
||||
'boundingBox',
|
||||
].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);
|
||||
}
|
||||
|
||||
pixelToTile(point) {
|
||||
_pixelToTile (point) {
|
||||
point = point instanceof Point ? point : new Point(...arguments);
|
||||
|
||||
const pixelX = point.getX() - this.originX;
|
||||
@@ -92,14 +99,51 @@ export default class CartographerFlatXY extends Cartographer {
|
||||
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 upperLeftTile = this._pixelToTile(upperLeftPoint);
|
||||
const lowerRightTile = this._pixelToTile(lowerRightPoint);
|
||||
const upperRightTile = this._pixelToTile(upperRightPoint);
|
||||
|
||||
const columns = rangeInclusive(upperLeftTile.getX(), upperRightTile.getX());
|
||||
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,10 +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});
|
||||
|
||||
const zeroZeroZero = new Hex({x: 0, y: 0, z: 0});
|
||||
|
||||
export default class CartographerFlatXYZ extends Cartographer {
|
||||
constructor (settings) {
|
||||
super(settings);
|
||||
@@ -26,9 +31,48 @@ export default class CartographerFlatXYZ extends Cartographer {
|
||||
'calculateVerticalScale',
|
||||
|
||||
'tileToPixel',
|
||||
'pixelToTile',
|
||||
'_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 () {
|
||||
@@ -80,7 +124,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;
|
||||
@@ -92,21 +136,113 @@ export default class CartographerFlatXYZ extends Cartographer {
|
||||
return new Hex(q, r);
|
||||
}
|
||||
|
||||
teleport (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.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 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 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 bottom = top + height;
|
||||
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 * 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);
|
||||
@@ -26,7 +29,11 @@ export default class CartographerPointyXY extends Cartographer {
|
||||
'calculateVerticalScale',
|
||||
|
||||
'tileToPixel',
|
||||
'pixelToTile',
|
||||
'_pixelToTile',
|
||||
'teleport',
|
||||
|
||||
'inBounds',
|
||||
'enforceBoundries',
|
||||
'boundingBox',
|
||||
].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);
|
||||
}
|
||||
|
||||
pixelToTile(point) {
|
||||
_pixelToTile (point) {
|
||||
point = point instanceof Point ? point : new Point(...arguments);
|
||||
|
||||
const pixelX = point.getX() - this.originX;
|
||||
@@ -97,11 +104,38 @@ export default class CartographerPointyXY extends Cartographer {
|
||||
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 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());
|
||||
|
||||
@@ -115,7 +149,9 @@ export default class CartographerPointyXY extends Cartographer {
|
||||
const midway = columns.length % 2 ? columns[aboutHalf] :
|
||||
(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 bottom = x < midway ? lowerRightIntercept - x : lowerLeftIntercept + x;
|
||||
|
||||
@@ -123,9 +159,22 @@ export default class CartographerPointyXY extends Cartographer {
|
||||
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 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,10 +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});
|
||||
|
||||
const zeroZeroZero = new Hex({x: 0, y: 0, z: 0});
|
||||
|
||||
export default class CartographerPointyXYZ extends Cartographer {
|
||||
constructor (settings) {
|
||||
super(settings);
|
||||
@@ -26,9 +31,48 @@ export default class CartographerPointyXYZ extends Cartographer {
|
||||
'calculateVerticalScale',
|
||||
|
||||
'tileToPixel',
|
||||
'pixelToTile',
|
||||
'_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 () {
|
||||
@@ -80,7 +124,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;
|
||||
@@ -92,21 +136,115 @@ export default class CartographerPointyXYZ extends Cartographer {
|
||||
return new Hex(q, r);
|
||||
}
|
||||
|
||||
teleport (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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 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 => 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) {
|
||||
let chainInstance = {
|
||||
log: function (predicate = () => true) {
|
||||
if (predicate(obj)) console.log(obj);
|
||||
return chainInstance;
|
||||
},
|
||||
|
||||
value: function () {
|
||||
return obj;
|
||||
}
|
||||
@@ -16,7 +21,7 @@ export function chain (obj) {
|
||||
}
|
||||
|
||||
export function compact (obj) {
|
||||
return filter(obj, val => val ? true : false);
|
||||
return filter(obj, val => val != null);
|
||||
}
|
||||
|
||||
export function contains (obj, value) {
|
||||
@@ -64,9 +69,12 @@ export function find (obj, predicate) {
|
||||
}
|
||||
}
|
||||
|
||||
export function flatten (list) {
|
||||
if (Array.isArray(list)) {
|
||||
return list.reduce((memo, element) => memo.concat(Array.isArray(element) ? flatten(element) : element), []);
|
||||
export function flatten (obj) {
|
||||
if (Array.isArray(obj)) {
|
||||
return obj.reduce((memo, element) => memo.concat(Array.isArray(element) ? flatten(element) : element), []);
|
||||
}
|
||||
else {
|
||||
return reduce((flat, prop) => flat.concat(prop), [])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
63
src/hex.js
63
src/hex.js
@@ -35,11 +35,56 @@ function roundOff(hex) {
|
||||
}
|
||||
|
||||
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() {
|
||||
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.z = arguments[1];
|
||||
this.y = computeY(this.x, this.z);
|
||||
@@ -73,6 +118,7 @@ export default class Hex extends Point {
|
||||
this.y = computeY(this.x, this.z);
|
||||
return this;
|
||||
}
|
||||
|
||||
setR (newR) {
|
||||
this.z = newR;
|
||||
this.y = computeY(this.x, this.z);
|
||||
@@ -84,6 +130,7 @@ export default class Hex extends Point {
|
||||
this.y = computeY(this.x, this.z);
|
||||
return this;
|
||||
}
|
||||
|
||||
moveR (byR) {
|
||||
this.z += byR;
|
||||
this.y = computeY(this.x, this.z);
|
||||
@@ -105,17 +152,31 @@ export default class Hex extends Point {
|
||||
return this;
|
||||
}
|
||||
|
||||
subtractHex (hex) {
|
||||
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.z = newAxial.r;
|
||||
this.y = computeY(this.x, this.y);
|
||||
return this;
|
||||
}
|
||||
|
||||
moveAxial (byAxial) {
|
||||
this.x += byAxial.q;
|
||||
this.z += byAxial.r;
|
||||
this.y = computeY(this.x, this.z);
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
25
src/main.js
25
src/main.js
@@ -130,7 +130,15 @@ class Demo {
|
||||
const key = `${ x },${ z != null ? z : y }`;
|
||||
|
||||
const pipMax = this.settings.tile === Tessellate.TILE_STYLES.HEX ? 7 : 9;
|
||||
|
||||
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) {
|
||||
@@ -218,7 +226,11 @@ class Demo {
|
||||
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;
|
||||
|
||||
const key = `${ x },${ z != null ? z : y }`;
|
||||
@@ -231,16 +243,15 @@ class Demo {
|
||||
});
|
||||
|
||||
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;
|
||||
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) {
|
||||
this.counts[2] += 1;
|
||||
Tessellate.Shapes.mine(context, scale, pixelPoint.getX(), pixelPoint.getY());
|
||||
Tessellate.Shapes.mine(context, scale, pixelX, pixelY);
|
||||
}
|
||||
else {
|
||||
if (!tile.pips && this.pipDefault !== null) {
|
||||
@@ -255,12 +266,12 @@ class Demo {
|
||||
|
||||
if (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 = [
|
||||
0, // tiles
|
||||
0, // pips
|
||||
@@ -273,7 +284,7 @@ class Demo {
|
||||
blue: 128,
|
||||
});
|
||||
|
||||
tilePoints.forEach(tilePoint => this.drawTile(tilePoint, context, scale));
|
||||
pointGroups.forEach(pointGroup => this.drawTile(pointGroup, context, scale));
|
||||
|
||||
this.ripples.forEach(({timestamp, cell}) => {
|
||||
const pressFactor = Math.min((now - timestamp) / PRESS_RIPPLE, 1);
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
|
||||
export default class Point {
|
||||
constructor(x, y) {
|
||||
if (typeof x === 'object') {
|
||||
y = x.y;
|
||||
x = x.x;
|
||||
}
|
||||
|
||||
// add zero to turn -0 into 0
|
||||
this.x = Math.round(x) + 0;
|
||||
this.y = Math.round(y) + 0;
|
||||
|
||||
@@ -113,12 +113,15 @@ export class Tessellate {
|
||||
|
||||
const cartographer = selectCartographer(this.settings.board, this.settings.orientation);
|
||||
this.cartographer = new cartographer(Object.assign(this.sketch.getSize(), funky.pick(this.settings, [
|
||||
'adjustScaleMin',
|
||||
'centerX',
|
||||
'centerY',
|
||||
'height',
|
||||
'width',
|
||||
'scale',
|
||||
'negativeTiles',
|
||||
'radius',
|
||||
'scale',
|
||||
'width',
|
||||
'wrap',
|
||||
])));
|
||||
}
|
||||
|
||||
@@ -128,6 +131,11 @@ export class Tessellate {
|
||||
this.settings.element = this.settings.element instanceof HTMLElement ? 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.height && (this.settings.height % 2 === 0)) {
|
||||
this.settings.height++;
|
||||
@@ -141,6 +149,7 @@ export class Tessellate {
|
||||
|
||||
tap (event) {
|
||||
let point = new Point(event.offsetX, event.offsetY);
|
||||
|
||||
let tile = this.cartographer.pixelToTile(point);
|
||||
|
||||
let tap = {
|
||||
@@ -214,7 +223,7 @@ export class Tessellate {
|
||||
const lowerLeft = new Point(0, 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) {
|
||||
@@ -222,6 +231,15 @@ export class Tessellate {
|
||||
const height = canvas.height;
|
||||
const width = canvas.width;
|
||||
|
||||
const corners = {
|
||||
upperLeftX: 0,
|
||||
upperLeftY: 0,
|
||||
lowerRightX: width,
|
||||
lowerRightY: height
|
||||
};
|
||||
|
||||
const pointGroups = this.getTilePoints(corners);
|
||||
|
||||
this.settings.draw({
|
||||
context,
|
||||
|
||||
@@ -231,12 +249,7 @@ export class Tessellate {
|
||||
lastNow: context.lastUTC,
|
||||
now: context.utc,
|
||||
|
||||
tilePoints: this.getTilePoints({
|
||||
upperLeftX: 0,
|
||||
upperLeftY: 0,
|
||||
lowerRightX: width,
|
||||
lowerRightY: height
|
||||
}),
|
||||
pointGroups,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user