Merge branch 'wrap' of mcdoh/Tessellate into master

This commit is contained in:
mcdoh
2019-01-06 21:35:55 +00:00
committed by Gitea
10 changed files with 612 additions and 128 deletions

View File

@@ -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;

View File

@@ -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();
}
}

View File

@@ -1,12 +1,17 @@
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) {
constructor (settings) {
super(settings);
[
@@ -26,52 +31,91 @@ 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() {
tileHeight () {
return this.minWidth();
}
tileWidth() {
tileWidth () {
return this.maxWidth();
}
maxWidth() {
maxWidth () {
return this.scale * 2;
}
minWidth() {
minWidth () {
return this.scale * sqrt3;
}
horizontalOverhang() {
horizontalOverhang () {
return this.maxWidth() * 0.25;
}
verticalOverhang() {
verticalOverhang () {
return 0;
}
horizontalDistance() {
horizontalDistance () {
return this.maxWidth() * (3/4);
}
verticalDistance() {
verticalDistance () {
return this.minWidth();
}
calculateHorizontalScale(pixels, tiles) {
calculateHorizontalScale (pixels, tiles) {
return pixels / (tiles * 0.75 + 0.25) / 2;
}
calculateVerticalScale(pixels, tiles) {
calculateVerticalScale (pixels, tiles) {
return pixels / tiles / sqrt3;
}
tileToPixel(hex) {
tileToPixel (hex) {
hex = hex instanceof Hex ? hex : new Hex(...arguments);
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);
}
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);
}
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);
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 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();
}
}

View File

@@ -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();
}
}

View File

@@ -1,12 +1,17 @@
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) {
constructor (settings) {
super(settings);
[
@@ -26,52 +31,91 @@ 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() {
tileHeight () {
return this.maxWidth();
}
tileWidth() {
tileWidth () {
return this.minWidth();
}
maxWidth() {
maxWidth () {
return this.scale * 2;
}
minWidth() {
minWidth () {
return this.scale * sqrt3;
}
horizontalOverhang() {
horizontalOverhang () {
return 0;
}
verticalOverhang() {
verticalOverhang () {
return this.maxWidth() * 0.25;
}
horizontalDistance() {
horizontalDistance () {
return this.minWidth();
}
verticalDistance() {
verticalDistance () {
return this.maxWidth() * (3/4);
}
calculateHorizontalScale(pixels, tiles) {
calculateHorizontalScale (pixels, tiles) {
return pixels / tiles / sqrt3;
}
calculateVerticalScale(pixels, tiles) {
calculateVerticalScale (pixels, tiles) {
return pixels / (tiles * 0.75 + 0.25) / 2;
}
tileToPixel(hex) {
tileToPixel (hex) {
hex = hex instanceof Hex ? hex : new Hex(...arguments);
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);
}
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);
}
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);
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 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();
}
}

View File

@@ -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), [])
}
}

View File

@@ -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);
@@ -57,65 +102,81 @@ export default class Hex extends Point {
getY() {return this.y;}
getZ() {return this.z;}
setX(newX) {this.x = newX; return this;}
setY(newY) {this.y = newY; return this;}
setZ(newZ) {this.z = newZ; return this;}
setX (newX) {this.x = newX; return this;}
setY (newY) {this.y = newY; return this;}
setZ (newZ) {this.z = newZ; return this;}
moveX(byX) {this.x += byX; return this;}
moveY(byY) {this.y += byY; return this;}
moveZ(byZ) {this.z += byZ; return this;}
moveX (byX) {this.x += byX; return this;}
moveY (byY) {this.y += byY; return this;}
moveZ (byZ) {this.z += byZ; return this;}
getQ() {return this.x;}
getR() {return this.z;}
getQ () {return this.x;}
getR () {return this.z;}
setQ(newQ) {
setQ (newQ) {
this.x = newQ;
this.y = computeY(this.x, this.z);
return this;
}
setR(newR) {
setR (newR) {
this.z = newR;
this.y = computeY(this.x, this.z);
return this;
}
moveQ(byQ) {
moveQ (byQ) {
this.x += byQ;
this.y = computeY(this.x, this.z);
return this;
}
moveR(byR) {
moveR (byR) {
this.z += byR;
this.y = computeY(this.x, this.z);
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.y = newHex.y;
this.z = newHex.z;
return this;
}
moveHex(byHex) {
moveHex (byHex) {
this.x += byHex.x;
this.y += byHex.y;
this.z += byHex.z;
return this;
}
getAxial() {return {q: this.x, r: this.z};}
setAxial(newAxial) {
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) {
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));
}
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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,
});
}