/**
## JSON Definition
{
"type": "NodeResident",
"nodeId": "city-hall",
// Optional. The id of the node that this entity should start on. Uses the entity's nodeId property if not set here.
"nodes": ['path','sidewalk','road'],
// Optional. This is a list of node types that this entity can reside on. If not set, entity can reside on any type of node.
"shares": ['friends','neighbors','city-council-members'],
// Optional. This is a list of entities that this entity can reside with on the same node. If not set, this entity cannot reside with any entities on the same node.
"speed": 5,
// Optional. Sets the speed with which the entity moves along an edge to an adjacent node. Default is 0 (instantaneous movement).
"updateOrientation": true
// Optional. Determines whether the entity's orientation is updated by movement across the NodeMap. Default is false.
}
*/
import {arrayCache, greenSplice} from '../utils/array.js';
import Vector from '../Vector.js';
import createComponentClass from '../factory.js';
export default createComponentClass(/** @lends platypus.components.Node.prototype */{
id: 'Node',
properties: {
/**
* If provided, treats these property names as neighbors, assigning them to the neighbors object. For example, ["east", "west"] creates `entity.east` and `entity.west` entity properties that are pointers to those neighbors.
*
* @property neighborProperties
* @type Array
* @default null
*/
neighborProperties: null
},
publicProperties: {
neighbors: null,
nodeId: '',
x: 0,
y: 0,
z: 0
},
/**
* This component causes an entity to be a position on a [[NodeMap]]. This component should not be confused with `NodeResident` which should be used on entities that move around on a NodeMap: `Node` simply represents a non-moving location on the NodeMap.
*
* @memberof platypus.components
* @uses platypus.Component
* @constructs
*/
initialize: function () {
const owner = this.owner;
this.nodeId = this.nodeId || owner.id || String(Math.random());
if ((typeof this.nodeId !== 'string') && (this.nodeId.length)) {
this.nodeId = this.nodeId.join('|');
}
owner.nodeId = this.nodeId;
owner.isNode = true;
this.map = owner.map = owner.map || owner.parent || null;
this.contains = owner.contains = arrayCache.setUp();
this.edgesContain = owner.edgesContain = arrayCache.setUp();
Vector.assign(owner, 'position', 'x', 'y', 'z');
if (!this.neighbors) {
this.neighbors = {};
}
if (this.neighborProperties) {
const properties = this.neighborProperties;
for (let i = 0; i < properties.length; i++) {
const
propertyName = properties[i],
value = owner[propertyName];
if (value) {
this.neighbors[propertyName] = value;
}
Object.defineProperty(owner, propertyName, {
get: () => this.neighbors[propertyName],
set: (value) => {
if (value !== this.neighbors[propertyName]) {
this.neighbors[propertyName] = value;
for (let i = 0; i < this.contains.length; i++) {
this.contains[i].triggerEvent('set-directions');
}
}
}
});
}
}
},
events: {
"add-neighbors": function (neighbors) {
const
directions = Object.keys(neighbors);
for (let i = 0; i < directions.length; i++) {
const
direction = directions[i];
this.neighbors[direction] = neighbors[direction];
}
for (let i = 0; i < this.contains.length; i++) {
this.contains[i].triggerEvent('set-directions');
}
},
"remove-neighbor": function (nodeOrNodeId) {
const
id = nodeOrNodeId.nodeId ?? nodeOrNodeId,
neighbors = Object.keys(this.neighbors);
for (let i = 0; i < neighbors.length; i++) {
const
neighbor = neighbors[i];
if ((neighbor === id) || (neighbor.nodeId === id)) {
delete this.neighbors[i];
}
}
}
},
methods: {
toJSON (entityProperties) {
const
neighborProperties = this.neighborProperties;
neighborProperties.forEach((neighbor) => {
const
neighborNode = this.neighbors[neighbor];
if (neighborNode) {
entityProperties[neighbor] = neighborNode.id;
}
});
entityProperties.nodeId = this.nodeId;
return {
type: 'Node',
neighborProperties
};
},
destroy () {
arrayCache.recycle(this.contains);
this.contains = this.owner.contains = null;
arrayCache.recycle(this.edgesContain);
this.edgesContain = this.owner.edgesContain = null;
}
},
publicMethods: {
joinNode (node, direction, mutual = false) {
this.neighbors[direction] = node;
for (let i = 0; i < this.contains.length; i++) {
this.contains[i].triggerEvent('set-directions');
}
if (mutual) {
node.joinNode(this.owner, (typeof mutual === 'boolean') ? direction : mutual);
}
},
/**
* Gets a neighboring node Entity.
*
* @memberof Node.prototype
* @param {String} desc Describes the direction to check.
* @returns {platypus.Entity}
*/
getNode: function (desc) {
const
neighbor = this.neighbors[desc];
//map check
if (!this.map && this.owner.map) {
this.map = this.owner.map;
}
if (neighbor) {
const
testNeighbor = neighbor.destroyed ? neighbor.id : neighbor;
if (testNeighbor.isNode) {
return testNeighbor;
} else if (typeof testNeighbor === 'string') {
const
actualNeighbor = this.map.getNode(testNeighbor);
if (actualNeighbor) {
this.neighbors[desc] = actualNeighbor;
return actualNeighbor;
}
} else if (testNeighbor.length) {
const
actualNeighbor = this.map.getNode(testNeighbor.join('|'));
if (actualNeighbor) {
this.neighbors[desc] = actualNeighbor;
return actualNeighbor;
}
}
}
return null;
},
/**
* Puts an entity on this node.
*
* @memberof Node.prototype
* @param {platypus.Entity} entity
* @returns {platypus.Entity}
*/
addToNode: function (entity) {
for (let i = 0; i < this.contains.length; i++) {
if (this.contains[i] === entity) {
return false;
}
}
this.contains.push(entity);
return entity;
},
/**
* Removes an entity from this node.
*
* @memberof Node.prototype
* @param {platypus.Entity} entity
* @returns {platypus.Entity}
*/
removeFromNode: function (entity) {
for (let i = 0; i < this.contains.length; i++) {
if (this.contains[i] === entity) {
return greenSplice(this.contains, i);
}
}
return false;
},
/**
* Adds an entity to this node's edges.
*
* @memberof Node.prototype
* @param {platypus.Entity} entity
* @returns {platypus.Entity}
*/
addToEdge: function (entity) {
for (let i = 0; i < this.edgesContain.length; i++) {
if (this.edgesContain[i] === entity) {
return false;
}
}
this.edgesContain.push(entity);
return entity;
},
/**
* Removes an entity from this node's edges.
*
* @memberof Node.prototype
* @param {platypus.Entity} entity
* @returns {platypus.Entity}
*/
removeFromEdge: function (entity) {
for (let i = 0; i < this.edgesContain.length; i++) {
if (this.edgesContain[i] === entity) {
return greenSplice(this.edgesContain, i);
}
}
return false;
}
}
});