UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

221 lines (167 loc) 5.18 kB
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;