Source: topo.js

/** @module */

import rbush from 'rbush'

import * as node from './node'
import * as edge from './edge'
import { getRingEdges, getFaceGeometry } from './face'

/**
 * Topology structure
 *
 * @typedef {Object} Topo
 * @property {module:node~Node[]} nodes
 * @property {Object} nodesTree
 * @property {module:edge~Edge[]} edges
 * @property {Object} edgesTree
 * @property {module:face~Face[]} faces
 * @property {Object} facesTree
 * @property {module:face~Face} universe
 * @property {function} getNodeByPoint Calls {@link module:node.getNodeByPoint} in the context of this object
 * @property {function} addIsoNode Calls {@link module:node.addIsoNode} in the context of this object
 * @property {function} removeIsoNode Calls {@link module:node.removeIsoNode} in the context of this object
 * @property {function} getEdgeByPoint Calls {@link module:edge.getEdgeByPoint} in the context of this object
 * @property {function} getEdgesByLine Calls {@link module:edge.getEdgesByLine} in the context of this object
 * @property {function} addIsoEdge Calls {@link module:edge.addIsoEdge} in the context of this object
 * @property {function} addEdgeNewFaces Calls {@link module:edge.addEdgeNewFaces} in the context of this object
 * @property {function} addEdgeModFace Calls {@link module:edge.addEdgeModFace} in the context of this object
 * @property {function} remEdgeNewFace Calls {@link module:edge.remEdgeNewFace} in the context of this object
 * @property {function} remEdgeModFace Calls {@link module:edge.remEdgeModFace} in the context of this object
 * @property {function} newEdgesSplit Calls {@link module:edge.newEdgesSplit} in the context of this object
 * @property {function} modEdgeSplit Calls {@link module:edge.modEdgeSplit} in the context of this object
 * @property {function} newEdgeHeal Calls {@link module:edge.newEdgeHeal} in the context of this object
 * @property {function} getRingEdges Calls {@link module:face.getRingEdges} in the context of this object
 * @property {function} getFaceGeometry Calls {@link module:face.getFaceGeometry} in the context of this object
 * @property {function} on Calls {@link module:topo.on} in the context of this object
 * @property {function} un Calls {@link module:topo.un} in the context of this object
 * @fires module:topo~addface
 * @fires module:topo~removeface
 * @fires module:topo~addedge
 * @fires module:topo~modedge
 * @fires module:topo~removeedge
 * @fires module:topo~addnode
 * @fires module:topo~removenode
 */

/**
 * Emitted when a face has been added to the topology.
 * @event module:topo~addface
 * @type {module:face~Face}
 */

/**
 * Emitted when a face has been removed from the topology.
 * @event module:topo~removeface
 * @type {module:face~Face}
 */

/**
 * Emitted when an edge has been added to the topology.
 * @event module:topo~addedge
 * @type {module:edge~Edge}
 */

/**
 * Emitted when an edge has been modified.
 * @event module:topo~modedge
 * @type {module:edge~Edge}
 */

/**
 * Emitted when an edge has been removed from the topology.
 * @event module:topo~removeedge
 * @type {module:edge~Edge}
 */

/**
 * Emitted when a node has been added to the topology.
 * @event module:topo~addnode
 * @type {module:node~Node}
 */

/**
 * Emitted when a node has been removed from the topology.
 * @event module:topo~removenode
 * @type {module:node~Node}
 */

/**
 * Create topology.
 *
 * @param {string} name
 * @param {number} srid
 * @param {number} tolerance
 * @return {module:topo~Topo}
 */
export function createTopology (name, srid, tolerance) {
  const nodes = []
  const nodesTree = rbush(16)
  const edges = []
  const edgesTree = rbush(16)
  const universe = { id: 0 }
  const faces = [universe]
  const facesTree = rbush(16)
  const topo = {
    name,
    srid,
    tolerance,
    nodes,
    nodesSeq: 1,
    nodesTree,
    edges,
    edgesSeq: 1,
    edgesTree,
    faces,
    facesSeq: 1,
    facesTree,
    universe,
    getNodeByPoint: (...args) => node.getNodeByPoint(topo, ...args),
    addIsoNode: (...args) => node.addIsoNode(topo, ...args),
    removeIsoNode: (...args) => node.removeIsoNode(topo, ...args),
    getEdgeByPoint: (...args) => edge.getEdgeByPoint(topo, ...args),
    getEdgesByLine: (...args) => edge.getEdgesByLine(topo, ...args),
    addIsoEdge: (...args) => edge.addIsoEdge(topo, ...args),
    addEdgeNewFaces: (...args) => edge.addEdgeNewFaces(topo, ...args),
    addEdgeModFace: (...args) => edge.addEdgeModFace(topo, ...args),
    remEdgeNewFace: (...args) => edge.remEdgeNewFace(topo, ...args),
    remEdgeModFace: (...args) => edge.remEdgeModFace(topo, ...args),
    newEdgesSplit: (...args) => edge.newEdgesSplit(topo, ...args),
    modEdgeSplit: (...args) => edge.modEdgeSplit(topo, ...args),
    newEdgeHeal: (...args) => edge.newEdgeHeal(topo, ...args),
    modEdgeHeal: (...args) => edge.modEdgeHeal(topo, ...args),
    getRingEdges: (...args) => getRingEdges(topo, ...args),
    getFaceGeometry: (...args) => getFaceGeometry(topo, ...args),
    observers: {
      'addface': [],
      'modface': [],
      'removeface': [],
      'addedge': [],
      'modedge': [],
      'removeedge': [],
      'addnode': [],
      'removenode': []
    },
    on: (...args) => on(topo, ...args),
    un: (...args) => un(topo, ...args)
  }
  return topo
}

/**
 * Registers a callback for a named event
 * @param {module:topo~Topo} topo Topology instance.
 * @param {string} name Event name.
 * @param {function} callback Callback function.
 */
export function on (topo, name, callback) {
  topo.observers[name].push(callback)
}

/**
 * Unregisters a callback for a named event
 * @param {module:topo~Topo} topo Topology instance.
 * @param {string} name Event name.
 * @param {function} callback Callback function.
 */
export function un (topo, name, callback) {
  const i = topo.observers[name].indexOf(callback)
  topo.observers[name].splice(i, 1)
}

export function trigger (topo, name, e) {
  topo.observers[name].forEach(o => o(e))
}

export function insertFace (topo, face) {
  const { faces } = topo
  face.id = topo.facesSeq++
  faces.push(face)
}

export function updateFaceTree (topo, face) {
  const { facesTree } = topo
  const coordinates = getFaceGeometry(topo, face)
  const xs = coordinates[0].map(c => c[0])
  const ys = coordinates[0].map(c => c[1])
  face.minX = Math.min(...xs)
  face.minY = Math.min(...ys)
  face.maxX = Math.max(...xs)
  face.maxY = Math.max(...ys)
  facesTree.insert(face)
}

export function deleteFace (topo, face) {
  const { faces, facesTree } = topo
  facesTree.remove(face)
  // delete faces[faces.indexOf(face)]
  faces.splice(faces.indexOf(face), 1)
}

export function insertEdge (topo, edge) {
  const { edges, edgesTree } = topo
  const xs = edge.coordinates.map(c => c[0])
  const ys = edge.coordinates.map(c => c[1])
  edge.id = topo.edgesSeq++
  edge.minX = Math.min(...xs)
  edge.minY = Math.min(...ys)
  edge.maxX = Math.max(...xs)
  edge.maxY = Math.max(...ys)
  edgesTree.insert(edge)
  edges.push(edge)
}

export function deleteEdge (topo, edge) {
  const { edges, edgesTree } = topo
  edgesTree.remove(edge)
  // delete edges[edges.indexOf(edge)]
  edges.splice(edges.indexOf(edge), 1)
}

export function insertNode (topo, node) {
  const { nodes, nodesTree } = topo

  const coordinate = node.coordinate

  node.id = topo.nodesSeq++
  node.minX = coordinate[0]
  node.minY = coordinate[1]
  node.maxX = coordinate[0]
  node.maxY = coordinate[1]

  nodesTree.insert(node)
  nodes.push(node)
}

export function deleteNode (topo, node) {
  const { nodes, nodesTree } = topo
  nodesTree.remove(node)
  // delete nodes[nodes.indexOf(node)]
  nodes.splice(nodes.indexOf(node), 1)
}