UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

323 lines (249 loc) • 6.35 kB
/** * Supplementary structure to `Graph`, holds edges and neighbour nodes for fast access * Used internally by `Graph` * @template N */ export class NodeContainer { /** * Node being described * @type {N} */ node = null; /** * Attached edges * @type {Edge<N>[]} * @private */ __edges = []; /** * * @type {Map<N,number>} * Maps neighbour node to number of edges to that node from this one * @private */ __neighbors = new Map(); /** * NOTE: this method allocates memory internally * @returns {N[]} */ get neighbours() { return Array.from(this.__neighbors.keys()); } /** * * @return {number} */ getEdgeCount() { return this.__edges.length; } /** * Do not modify the returned value * @return {Edge<N>[]} */ getEdges() { return this.__edges; } /** * NOTE: this method allocates memory internally * @returns {N[]} */ get inNodes() { return this.inEdges.map(e => e.other(this.node)); } /** * NOTE: this method allocates memory internally * @returns {N[]} */ get outNodes() { return this.outEdges.map(e => e.other(this.node)); } /** * NOTE: this method allocates memory internally * @returns {Edge<N>[]} */ get outEdges() { /** * * @type {Edge<N>[]} */ const result = []; this.getOutgoingEdges(result); return result; } /** * NOTE: this method allocates memory internally * @returns {Edge<N>[]} */ get inEdges() { /** * * @type {Edge<N>[]} */ const result = []; this.getIncomingEdges(result); return result; } /** * * @param {Edge<N>[]} result * @returns {number} */ getIncomingEdges(result) { const edges = this.__edges; const edge_count = edges.length; let result_count = 0; for (let i = 0; i < edge_count; i++) { const edge = edges[i]; if (edge.isDirectedTowards(this.node)) { result.push(edge); result_count++; } } return result_count; } /** * * @param {Edge<N>[]} result * @returns {number} */ getOutgoingEdges(result) { const edges = this.__edges; const edge_count = edges.length; let result_count = 0; for (let i = 0; i < edge_count; i++) { const edge = edges[i]; if (edge.isDirectedAwayFrom(this.node)) { result.push(edge); result_count++; } } return result_count; } /** * * @return {number} */ getIncomingEdgeCount() { let r = 0; const edges = this.__edges; const edge_count = edges.length; const current_node = this.node; for (let i = 0; i < edge_count; i++) { const edge = edges[i]; if (edge.isDirectedTowards(current_node)) { r++; } } return r; } /** * * @param {N} other * @returns {boolean} */ edgeWithNodeExists(other) { return this.__neighbors.has(other); } /** * * @param {function(Edge<N>)} visitor * @param {*} [thisArg] * @returns {number} */ traverseEdges(visitor, thisArg) { let i = 0; const edges = this.__edges; const n = edges.length; for (; i < n; i++) { const edge = edges[i]; visitor.call(thisArg, edge); } return i; } /** * * @param {N} other * @returns {Edge<N>|undefined} */ getAnyEdgeWith(other) { if (!this.edgeWithNodeExists(other)) { return undefined; } /** * * @type {Edge[]} */ const edges = this.__edges; const n = edges.length; for (let i = 0; i < n; i++) { const edge = edges[i]; if (edge.contains(other)) { return edge; } } return undefined; } /** * Undirected edges don't count * @param {N} other * @returns {Edge<N>|undefined} */ getAnyDirectionEdgeTo(other) { /** * * @type {Edge[]} */ const edges = this.__edges; const n = edges.length; for (let i = 0; i < n; i++) { const edge = edges[i]; if ( (edge.second === other && edge.traversableForward()) || (edge.first === other && edge.traversableBackward()) ) { return edge; } } // not found return undefined; } /** * * @param {Edge} e * @returns {boolean} */ addEdge(e) { if (this.__edges.includes(e)) { return false; } const other_node = e.other(this.node); this.__edges.push(e); let existing_count = this.__neighbors.get(other_node); if (existing_count === undefined) { existing_count = 0; } this.__neighbors.set(other_node, existing_count + 1); return true; } /** * * @param {Edge} e * @returns {boolean} */ removeEdge(e) { const edges = this.__edges; const i = edges.indexOf(e); if (i === -1) { return false; } edges.splice(i, 1); const other_node = e.other(this.node); const existing_count = this.__neighbors.get(other_node); if (existing_count <= 1) { this.__neighbors.delete(other_node); } else { this.__neighbors.set(other_node, existing_count - 1); } return true; } }