Source: app/util/Map.js

/**
 * @module Map
 */
const kdTree = require('../lib/kdtreejs/kdTree');
const _ = require('lodash');
const Entity = require('../entity/Entity');
const Sampling = require('discrete-sampling');

let distanceFormula2 = function(e1, e2) {
    return Math.pow(e1.x - e2.x, 2) +
        Math.pow(e1.y - e2.y, 2);
};

let tree = null;
let list = {};

/**
 * @namespace Map
 */
let Map = {};


/**
 * Create a new map
 * 
 * @param {Array} entities 
 * @param {any} dimensions
 */
Map.create = function(entities, dimensions) {
    if (entities.length === 0 || !(entities[0] instanceof Entity)) {
        throw new TypeError('Invalid array');
    }
    let dim = dimensions;
    if (dim === undefined) dim = ['x', 'y'];
    let points = _.map(entities, function(entity, idx) {
        list[entity.id] = entity;
        return {
            x: entity.trueXY().x,
            y: entity.trueXY().y,
            id: entity.id,
        };
    });
    tree = new kdTree.kdTree(points, distanceFormula2, dim);
};

/**
 * 
 * 
 * @param {Object} entity  
 */
Map.insert = function(entity) {
    if (tree === null || list.length === 0) {
        this.create([entity]);
    } else if (list.hasOwnProperty(entity.id)) {
        // Do nothing
    } else {
        tree.insert({
            x: entity.trueXY().x,
            y: entity.trueXY().y,
            id: entity.id,
        });
        list[entity.id] = entity;
    }
};

/**
 * 
 * 
 * @param {Object} entity - Entity
 * @param {number} count - Number of neighbors
 * @param {any} maxDistance - Range to check within
 * @return {Array.Neighbors}
 */
Map.nearest = function(entity, count, maxDistance) {
    if (tree === null) {
        return [];
    }
    if (!(entity instanceof Entity)) {
        throw new TypeError('Invalid array');
    }
    point = {
        x: entity.trueXY().x,
        y: entity.trueXY().y,
        id: entity.id,
    };
    maxDistance = Math.pow(maxDistance, 2);
    if (count === undefined) {
        count = 1;
    }
    return tree.nearest(point, count + 1, maxDistance)
        .map((point) => {
            return [
                this.getByID(point[0].id),
                Math.sqrt(point[1]),
            ];
        })
        .filter((e) => (entity.id === point.id));
};

/**
 * Get n discrete samples from array
 * 
 * @param {number} [n=] - Number of samples to pick. Default = 1
 * @return {any[]}
 */
Map.discreteSamples = function(n = 1) {
    return Sampling.sample_from_array(Object.values(list), n, false);
};

Map.sampleRandom = function(max) {
    return Sampling.sample_from_array(Object.values(list),
                Math.floor(Math.random() * max), false);
};

/**
 * Get Entity with ID
 * 
 * @param {string} id - ID of `Entity`
 * @return {Entity}
 */
Map.getByID = function(id) {
    return list[id];
};

Map.printMap = function() {
    return tree.toJSON();
};

Map.toJSON = function() {
    return tree.toJSON();
};

module.exports = Map;