Home Reference Source Test Repository

src/js/data/node/circle-node.js

import Node from './node';
import Label from '../label';

/**
 * Node subclass for circle nodes.
 * @class CircleNode
 */
class CircleNode extends Node {

  radius = 30;

  /**
   * Constructs a CircleNode instance.
   * @param  {number} x - x-coordinate of the node.
   * @param  {number} y - y-coordinate of the node.
   * @constructs CircleNode
   */
  constructor(x, y) {
    super(x, y);
    this.label = new Label(this.x + this.radius + 4, this.y, this);
  }

  /**
   * Check if the node contains the given point.
   * @param  {number} x - x-coordinate of the point.
   * @param  {number} y - y-coordinate of the point.
   * @return {boolean} - Whether or not the node contains the point.
   * @override
   */
  containsPoint(x, y) {
    return this.distanceToPoint(x, y) <= this.radius;
  }

  /**
   * Find the distance from the node's coordinates to a given point.
   * @param  {number} x - x-coordinate of the point.
   * @param  {number} y - y-coordinate of the point.
   * @returns {number} - The distance to the point.
   * @override
   */
  distanceToPoint(x, y) {
    let dx = x - this.x;
    let dy = y - this.y;
    return Math.sqrt(dx * dx + dy * dy);
  }

  /**
   * Find the point on the edge of the node in the direction of the given point.
   * @param  {number} x - x-coordinate of the point.
   * @param  {number} y - y-coordinate of the point.
   * @returns {Object} - The edge point in the direction of the given point.
   * @property {number} x - x-coordinate of the edge point.
   * @property {number} y - y-coordinate of the edge point.
   * @override
   */
  edgePointInDirection(x, y) {
    if (x === this.x && y === this.y) {
      throw new Error('Point is at origin of Node');
    }
    let dx = x - this.x;
    let dy = y - this.y;
    let distance = Math.sqrt(dx * dx + dy * dy);
    return {
      x: this.x + dx * this.radius / distance,
      y: this.y + dy * this.radius / distance
    };
  }

  /**
   * Draw the node on the given canvas context.
   * @param  {CanvasRenderingContext2D} context - Canvas 2D context.
   * @override
   */
  draw(context) {
    context.fillStyle = this.fillColor;
    context.strokeStyle = this.isSelected ? this.selectedColor : this.color;
    context.lineWidth = this.lineWidth;

    // Create a new path
    context.beginPath();

    // Create an arc with center at (x, y)
    context.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
    // Draw to the canvas
    context.fill();
    context.stroke();

    if (this.nodeLabel !== '') {
      this.drawLabel(context);
    }

    if (this.isAcceptingState) {
      this.drawAcceptingState(context);
    }

    if (this.isStartingState) {
      this.drawStartingState(context);
    }
  }

  /**
   * Draw additional objects to display the node as an accepting state on the given canvas context.
   * @param {CanvasRenderingContext2D} context - Canvas 2D context.
   */
  drawAcceptingState(context) {
    context.fillStyle = this.fillColor;
    context.strokeStyle = this.isSelected ? this.selectedColor : this.color;
    context.moveTo(this.x + this.radius * 0.75, this.y);
    context.arc(this.x, this.y, this.radius * 0.75, 0, 2 * Math.PI);
    context.fill();
    context.stroke();
  }

  /**
   * Draw additional objects to display the node as a starting state on the given canvas context.
   * @param {CanvasRenderingContext2D} context - Canvas 2D context.
   */
  drawStartingState(context) {
    let endpoint = this.getAnglePoint(225);
    context.fillStyle = this.isSelected ? this.selectedColor : this.color;
    context.strokeStyle = this.isSelected ? this.selectedColor : this.color;
    context.moveTo(endpoint.x - 30, endpoint.y - 30);
    context.lineTo(endpoint.x, endpoint.y);
    context.stroke();
    context.beginPath();
    context.moveTo(endpoint.x, endpoint.y);
    context.lineTo(endpoint.x - 6 - 3, endpoint.y - 6 + 3);
    context.lineTo(endpoint.x - 6, endpoint.y - 6);
    context.lineTo(endpoint.x - 6 + 3, endpoint.y - 6 - 3);
    context.closePath();
    context.fill();
  }

  /**
   * Get the point on the edge of the node at the given angle.
   * @param  {number} angle - The angle (clockwise from +x axis).
   * @returns {Object} - The edge point in the direction of the given angle.
   * @property {number} x - x-coordinate of the edge point.
   * @property {number} y - y-coordinate of the edge point.
   * @override
   */
  getAnglePoint(angle) {
    return {
      x: this.x + this.radius * Math.cos(angle * Math.PI / 180),
      y: this.y + this.radius * Math.sin(angle * Math.PI / 180)
    };
  }

}

export { CircleNode };
export default CircleNode;