GraphNode.js 3.24 KB
/**
 * User: CHT
 * Date: 2020/5/8
 * Time: 14:01
 */

import {
  minus,
  uuid,
  vector,
  mark
} from './utils'

import {
  direction,
  directionVector
} from './types'


export default class GraphNode {
  constructor (props, graph) {
    const {
      width = 180,
      height = 100,
      coordinate = [0, 0],
      meta = null
    } = props

    this.$options = props

    const id = props[mark.relationMark] || uuid('node')

    this.key = uuid('node')
    this.graph = graph

    this[mark.relationMark] = id
    this.coordinate = [...coordinate]
    this.meta = meta

    this.width = width
    this.height = height
  }

  get position () {
    return vector(this.coordinate)
      .add(this.graph.origin)
      .end
  }

  set position (position) {
    this.coordinate = vector(position)
      .minus(this.graph.origin)
      .end
  }

  get center () {
    return vector(this.coordinate)
      .add([this.width / 2, this.height / 2])
      .end
  }

  set center (position) {
    this.coordinate = vector(position)
      .minus([this.width / 2, this.height / 2])
      .end
  }

  get width () {
    return this._width
  }

  set width (w) {
    w = Math.floor(w)
    this._width = w > 50 ? w : 50
    this.angle()
  }

  get height () {
    return this._height
  }

  set height (h) {
    h = Math.floor(h)
    this._height = h > 20 ? h : 20
    this.angle()
  }

  angle () {
    const
      w = this.width / 2
      , h = this.height / 2
      , center = [0, 0]

    const topLeft = vector(center)
      .minus([w, h])
      .angle()
      .end

    const topRight = vector(center)
      .add([w, 0])
      .minus([0, h])
      .angle()
      .end

    const bottomRight = vector(center)
      .add([w, h])
      .angle()
      .end

    const bottomLeft = vector(center)
      .add([0, h])
      .minus([w, 0])
      .angle()
      .end

    this.angleList = [
      topLeft,
      topRight,
      bottomRight,
      bottomLeft
    ]
  }

  relative (offset) {
    const angle = vector(offset)
      .minus([this.width / 2, this.height / 2])
      .angle()
      .end
    const angleList = this.angleList
    const directionList = [
      direction.top,
      direction.right,
      direction.bottom,
      direction.left
    ]

    let dir = direction.left

    angleList.reduce((prev, current, idx) => {
      if (angle >= prev && angle < current) {
        dir = directionList[idx - 1]
      }
      return current
    })

    return {
      position: this.fixOffset(offset, dir),
      direction: directionVector[dir]
    }
  }

  fixOffset (offset, dir) {
    switch (dir) {
      case direction.top:
        offset[0] = this.width / 2
        offset[1] = 0
        break
      case direction.right:
        offset[0] = this.width
        offset[1] = this.height / 2
        break
      case direction.bottom:
        offset[0] = this.width / 2
        offset[1] = this.height
        break
      case direction.left:
      default:
        offset[0] = 0
        offset[1] = this.height / 2
        break
    }
    return offset
  }

  remove () {
    return this.graph.removeNode(this)
  }

  toJSON () {
    return {
      [mark.relationMark]: this[mark.relationMark],
      width: this.width,
      height: this.height,
      coordinate: [...this.coordinate],
      meta: this.meta
    }
  }
}