Home Reference Source Test Repository

src/js/tool/edge-tool.js

import Tool from '../tool/tool';
import Node from '../data/node/node';
import SolidEdge from '../data/edge/solid-edge';
import DashedEdge from '../data/edge/dashed-edge';
import CircleNode from '../data/node/circle-node';

/**
 * Tool for adding edges to the graph.
 * @class EdgeTool
 */
class EdgeTool extends Tool {

  currentMode = 'solid';
  static modes = {
    solid: SolidEdge,
    dashed: DashedEdge
  };

  optionMap = {
    solid: {
      label: 'Solid'
    },
    dashed: {
      label: 'Dashed'
    }
  };

  optionContent = {
    html: '<canvas class="canvas-preview" width="50" height="50"></canvas>',
    init: (optionElement, mode) => {
      let canvas = optionElement.querySelector('canvas');
      let context = canvas.getContext('2d');
      context.clearRect(0, 0, canvas.width, canvas.height);
      let node1 = new CircleNode(12.5, 37.5);
      let node2 = new CircleNode(37.5, 12.5);
      let edge = new EdgeTool.modes[mode](node1, node2);
      for (let field of Object.keys(this.inputs)) {
        edge[field] = this.inputs[field];
      }
      edge.draw(context);
    }
  };

  inputs = {
    isDirected: false,
    color: '#000000',
    lineWidth: 1
  };

  inputTypes = [
    {
      type: 'boolean',
      name: 'isDirected',
      displayName: 'Directed'
    },
    {
      type: 'color',
      name: 'color',
      displayName: 'Color'
    },
    {
      type: 'number',
      name: 'lineWidth',
      displayName: 'Width'
    }
  ];

  start = null;
  dest = null;

  /**
   * Cancel any
   * @override
   */
  cancel() {
    this.deselect();
  }

  /**
   * Deselect the selected node (if any).
   */
  deselect() {
    if (this.start) {
      this.start.isSelected = false;
    }
    this.start = null;
    this.dest = null;
  }

  /**
   * Called when a node is selected. Determines whether an edge should be added or not.
   * @param  {Graph} graph - The current Graph object.
   * @param  {Node} node - The selected node.
   */
  selectNode(graph, node) {
    let EdgeClass = EdgeTool.modes[this.currentMode];
    if (this.start === null) {
      this.start = node;
      this.start.isSelected = true;
    } else {
      this.dest = node;
      if (this.start.id === this.dest.id) {
        // check for existing self-loop
        for (let edge of this.start.edges) {
          if (edge.startNode.id === edge.destNode.id) {
            this.dest = null;
            return;
          }
        }
      }
      let edge = new EdgeClass(this.start, this.dest);
      for (let field of Object.keys(this.inputs)) {
        if (field === 'isDirected' && this.start === this.dest) {
          continue;
        }
        edge[field] = this.inputs[field];
      }
      graph.addEdge(edge);
      this.start.isSelected = false;
      this.start = null;
      this.dest = null;
    }
  }

  /**
   * Handler for object selection. For this tool, only nodes can be selected. When a second node is selected, an edge is created and the tool state is reset.
   * @param  {Event} event - Event object from event listener.
   * @param  {Graph} graph - The current graph object.
   * @param  {(Node|Edge|Label)} obj - Selected graph component.
   * @param  {number} x - Mouse x-coordinate (in canvas coordinates).
   * @param  {number} y - Mouse y-coordinate (in canvas coordinates).
   * @override
   */
  selectObject(event, graph, obj, x, y) {
    if (obj instanceof Node) {
      this.selectNode(graph, obj);
    }
  }

  /**
   * Handler for clicking empty space. Deselect any selected nodes.
   * @param  {Event} event - Event object from event listener.
   * @param  {Graph} graph - The current graph object.
   * @param  {number} x - Mouse x-coordinate (in canvas coordinates).
   * @param  {number} y - Mouse y-coordinate (in canvas coordinates).
   * @override
   */
  selectNone(event, graph, x, y) {
    this.deselect();
  }

}

export { EdgeTool };
export default EdgeTool;