UNPKG

@irysius/grid-math

Version:

Tools to assist with grid math and algorithms

196 lines (195 loc) 7.49 kB
(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; });