@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
221 lines (167 loc) • 5.18 kB
JavaScript
import { assert } from "../../core/assert.js";
import { qt_collect_by_circle } from "../../core/geom/2d/quad-tree/qt_collect_by_circle.js";
import { QuadTreeNode } from "../../core/geom/2d/quad-tree/QuadTreeNode.js";
import { max2 } from "../../core/math/max2.js";
import { OffsetScaleTransform2D } from "../../engine/ecs/terrain/ecs/OffsetScaleTransform2D.js";
/**
*
* @type {QuadTreeDatum<MarkerNode>[]}
*/
const marker_collection = [];
export class GridData {
width = 0;
height = 0;
/**
* @readonly
* @type {OffsetScaleTransform2D}
*/
transform = new OffsetScaleTransform2D();
/**
* @readonly
* @type {QuadTreeNode<MarkerNode>}
*/
markers = new QuadTreeNode();
/**
* Discrete data layers
* @readonly
* @type {GridDataLayer[]}
*/
layers = [];
/**
*
* @param {GridDataLayer} layer
*/
addLayer(layer) {
assert.equal(layer.isGridDataLayer, true, 'layer.GridDataLayer !== true');
const existing = this.getLayerById(layer.id);
if (existing === layer) {
//layer is already attached
return;
}
if (existing !== undefined) {
throw new Error(`Layer with ID '${layer.id}' already exists. ID must be unique`);
}
layer.resize(this.width, this.height);
this.layers.push(layer);
}
/**
*
* @param {string} id
* @returns {GridDataLayer|undefined}
*/
getLayerById(id) {
assert.isString(id, 'id');
const layers = this.layers;
const n = layers.length;
for (let i = 0; i < n; i++) {
const layer = layers[i];
if (layer.id === id) {
return layer;
}
}
return undefined;
}
/**
*
* @param {number} x
* @param {number} y
* @param {number} radius
* @param {MarkerNodeMatcher} matcher
* @returns {boolean}
*/
containsMarkerInCircle(x, y, radius, matcher) {
return this.countMarkerInCircle(x, y, radius, matcher) > 0;
}
/**
*
* @param {number} x
* @param {number} y
* @param {number} radius
* @param {MarkerNodeMatcher} matcher
* @returns {number}
*/
countMarkerInCircle(x, y, radius, matcher) {
const match_count = qt_collect_by_circle(marker_collection, 0, this.markers, x, y, radius);
let result = 0;
for (let i = 0; i < match_count; i++) {
const marker_datum = marker_collection[i];
/**
*
* @type {MarkerNode|null}
*/
const marker = marker_datum.data;
const distance_limit = radius + marker.size;
const distance_limit_sqr = distance_limit * distance_limit;
// check refined containment
const position = marker.position;
const dx = x - position.x;
const dy = y - position.y;
const distance_sqr = dx * dx + dy * dy;
if (distance_sqr > distance_limit_sqr) {
continue;
}
if (matcher.match(marker)) {
result++;
}
}
return result;
}
/**
*
* @param tileSize
*/
computeScale(tileSize) {
this.transform.scale_x = tileSize;
this.transform.scale_y = tileSize;
this.transform.offset_x = tileSize / 2;
this.transform.offset_y = tileSize / 2;
}
/**
*
* @param {MarkerNode} node
*/
addMarker(node) {
const p = node.position;
const x = p.x;
const y = p.y;
const r = node.size;
this.markers.add(node, x - r, y - r, x + r, y + r);
}
/**
*
* @param {number} width
* @param {number} height
*/
resize(width, height) {
assert.isNumber(width, 'width');
assert.isNumber(height, 'height');
assert.isNonNegativeInteger(width, 'width');
assert.isNonNegativeInteger(height, 'height');
assert.isFiniteNumber(width, 'width');
assert.isFiniteNumber(height, 'width');
if (width === this.width && height === this.height) {
//no need, already the right size
return;
}
this.width = width;
this.height = height;
const layers = this.layers;
const n = layers.length;
for (let i = 0; i < n; i++) {
const layer = layers[i];
layer.resize(width, height);
}
// resize the quad tree pre-emptively, to avoid expensive re-balancing operations later on
this.markers.resize(
-max2(1, width * 0.5),
-max2(1, height * 0.5),
max2(width + 1, width * 1.5),
max2(height + 1, height * 1.5)
);
}
}
/**
* @readonly
* @type {boolean}
*/
GridData.prototype.isGridData = true;