@irysius/grid-math
Version:
Tools to assist with grid math and algorithms
196 lines (195 loc) • 7.49 kB
JavaScript
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "./Vector2", "./Vector2", "./ScreenPosition", "./WorldPosition", "./WorldPosition", "./CellCoord", "./Rect", "./ScreenRect", "./ScreenRect", "./Size"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const Vector2 = require("./Vector2");
const Vector2_1 = require("./Vector2");
const ScreenPosition = require("./ScreenPosition");
const WorldPosition = require("./WorldPosition");
const WorldPosition_1 = require("./WorldPosition");
const CellCoord = require("./CellCoord");
const Rect_1 = require("./Rect");
const ScreenRect_1 = require("./ScreenRect");
const ScreenRect = require("./ScreenRect");
const Size_1 = require("./Size");
function isClientState(value) {
return (value &&
Rect_1.isRect(value.gridBounds, ScreenRect.TYPE) &&
Vector2_1.isVector2(value.position, WorldPosition.TYPE));
}
function isServerState(value) {
return (value &&
Size_1.isSize(value.cellSize) &&
Vector2_1.isVector2(value.cellOffset));
}
function fillClientState(state) {
return {
gridBounds: state.gridBounds,
position: state.position,
cellOffset: Vector2_1.isVector2(state.cellOffset) ? state.cellOffset : Vector2_1.create(0, 0),
cellSize: Size_1.isSize(state.cellSize) ? state.cellSize : Size_1.create(0, 0)
};
}
function fillServerState(state) {
return {
gridBounds: Rect_1.isRect(state.gridBounds) ? state.gridBounds : ScreenRect_1.create(0, 0, 0, 0),
position: Vector2_1.isVector2(state.position, WorldPosition.TYPE) ? state.position : WorldPosition_1.create(0, 0),
cellOffset: state.cellOffset,
cellSize: state.cellSize
};
}
function CoordManager(options) {
let state;
if (isClientState(options)) {
state = fillClientState(options);
}
else if (isServerState(options)) {
state = fillServerState(options);
}
else {
throw new Error('CoordManager initialized with invalid state.');
}
let cache = {};
function getGridBounds() {
if (!cache.gridBounds) {
let { gridBounds } = state;
cache.gridBounds = {
width: gridBounds.width,
height: gridBounds.height,
halfWidth: gridBounds.width / 2,
halfHeight: gridBounds.height / 2
};
}
return cache.gridBounds;
}
function getWorldTopLeft() {
if (!cache.worldTopLeft) {
let { position } = state;
let gridBounds = getGridBounds();
cache.worldTopLeft = {
x: position.x - gridBounds.halfWidth,
y: position.y - gridBounds.halfHeight,
type: 'world'
};
}
return cache.worldTopLeft;
}
function getState() {
return Object.assign({}, state);
}
function updateWithState(newState) {
state = Object.assign({}, state, newState);
// We anticipate cellSize and cellOffset to not change often
// followed by gridBounds
// followed by position, which should change the most frequently
let changedFields = Object.keys(newState);
if (changedFields.indexOf('gridBounds') !== -1) {
delete cache.gridBounds;
}
if (changedFields.indexOf('position') !== -1) {
delete cache.worldTopLeft;
}
}
function sp2gp(p) {
let { gridBounds } = state;
let { x: x$, y: y$ } = gridBounds;
let { x, y } = Vector2.subtract(p, { x: x$, y: y$ });
return { x, y, type: 'grid' };
}
function sp2wp(p) {
let gp = sp2gp(p);
let topLeft = getWorldTopLeft();
return {
x: topLeft.x + gp.x,
y: topLeft.y + gp.y,
type: 'world'
};
}
function sp2cell(p) {
let wp = sp2wp(p);
return wp2cell(wp);
}
function wp2gp(p) {
let { x, y } = Vector2.subtract(p, getWorldTopLeft());
return { x, y, type: 'grid' };
}
function wp2sp(p) {
let { gridBounds } = state;
let { x: x$, y: y$ } = gridBounds;
let { x, y } = Vector2.add({ x: x$, y: y$ }, wp2gp(p));
return { x, y, type: 'screen' };
}
function wp2cell(p) {
let { cellSize, cellOffset } = state;
return {
x: Math.floor((p.x + cellOffset.x) / cellSize.width),
y: Math.floor((p.y + cellOffset.y) / cellSize.height),
type: 'cell'
};
}
function cell2wp(p) {
let { cellSize, cellOffset } = state;
return {
x: p.x * cellSize.width - cellOffset.x,
y: p.y * cellSize.height - cellOffset.y,
type: 'world'
};
}
function cell2sp(p) {
let wp = cell2wp(p);
return wp2sp(wp);
}
function toWorldPosition(v) {
switch (v.type) {
case WorldPosition.TYPE:
return v;
case ScreenPosition.TYPE:
return sp2wp(v);
case CellCoord.TYPE:
return cell2wp(v);
default:
throw new Error(`Tried to convert an invalid Vector2 into a WorldPosition: ${v}`);
}
}
function toScreenPosition(v) {
switch (v.type) {
case WorldPosition.TYPE:
return wp2sp(v);
case ScreenPosition.TYPE:
return v;
case CellCoord.TYPE:
return cell2sp(v);
default:
throw new Error(`Tried to convert an invalid Vector2 into a ScreenPosition: ${v}`);
}
}
function toCellCoord(v) {
switch (v.type) {
case WorldPosition.TYPE:
return wp2cell(v);
case ScreenPosition.TYPE:
return sp2cell(v);
case CellCoord.TYPE:
return v;
default:
throw new Error(`Tried to convert an invalid Vector2 into a CellCoord: ${v}`);
}
}
return {
updateWithState,
getState,
toWorldPosition,
toScreenPosition,
toCellCoord,
getWorldTopLeft
};
}
exports.CoordManager = CoordManager;
});