/** * User: CHT * Date: 2020/5/8 * Time: 14:01 */ import { uuid, vector, mark } from './utils' import { direction, directionVector } from './types' export default class GraphLink { static distance = 15 constructor (options, graph) { const { start, end = null, startAt = [0, 0], endAt = [0, 0], meta = null, } = options this.$options = options const id = options[mark.relationMark] || uuid('link') this.key = uuid('link') this[mark.relationMark] = id this.graph = graph this.start = start this.meta = meta this.end = end this.startDirection = directionVector[direction.top] this.endDirection = directionVector[direction.top] this.startAt = startAt this.endAt = endAt } get end () { return this._end } set end (node) { if (this.start === node) { return false } else { this._end = node } } get startAt () { return this._startAt } set startAt (offset) { const relative = this.start.relative(offset) this._startAt = relative.position this.startDirection = relative.direction } get endAt () { return this._endAt } set endAt (offset) { if (this.end) { const relative = this.end.relative(offset) this._endAt = relative.position this.endDirection = relative.direction } else { this._endAt = offset } } get movePosition () { return this._movePosition } set movePosition (offset) { this._movePosition = offset if (this.end) return const relative = this.start.relative( vector(offset) .minus(this.graph.origin) .minus(this.start.coordinate) .end ) this.endDirection = vector(relative.direction) .multiply(-1) .end } get pathPointList () { const pointList = this.coordinateList() , xList = [] , yList = [] pointList.forEach(item => { xList.push(item[0]) yList.push(item[1]) }) return { pointList, xList, yList, minX: Math.min(...xList), maxX: Math.max(...xList), minY: Math.min(...yList), maxY: Math.max(...yList) } } startCoordinate () { return vector(this.start.position) .add(this.startAt) .end } endCoordinate () { if (this.end) { return vector(this.end.position) .add(this.endAt) .end } else { return this.movePosition } } coordinateList (turnRatio = 0.5) { const entryPoint = this.startCoordinate() const exitPoint = this.endCoordinate() const entryDirection = this.startDirection let exitDirection = this.endDirection // 路径起点 const startPoint = vector(entryDirection) .multiply(GraphLink.distance) .add(entryPoint) .end // 路径终点 const endPoint = vector(exitDirection) .multiply(GraphLink.distance) .add(exitPoint) .end // 入口方向取反 exitDirection = vector(exitDirection) .multiply(-1) .end // 终点 - 起点 垂直 水平向量 const pathHorizontalVec = [endPoint[0] - startPoint[0], 0] const pathVerticalVec = [0, endPoint[1] - startPoint[1]] const startDirection = this.pathDirection( pathVerticalVec, pathHorizontalVec, entryDirection ) const endDirection = this.pathDirection( pathVerticalVec, pathHorizontalVec, exitDirection ) const splitNum = vector(startDirection) .dotProduct(endDirection) .end > 0 ? 2 : 1 const pathMiddle = endDirection === pathHorizontalVec ? pathVerticalVec : pathHorizontalVec let points = [] points.push(entryPoint, startPoint) if (splitNum === 1) { const point1 = vector(startPoint) .add(startDirection) .end const point2 = vector(point1) .add(endDirection) .end points.push(point1, point2) } else { const point1 = vector(startDirection) .multiply(turnRatio) .add(startPoint) .end const point2 = vector(point1) .add(pathMiddle) .end const point3 = vector(endDirection) .multiply(1 - turnRatio) .add(point2) .end points.push(point1, point2, point3) } points.push(exitPoint) return points } pathDirection (vertical, horizontal, direction) { if ( vector(horizontal) .parallel(direction) .end ) { if ( vector(horizontal) .dotProduct(direction) .end > 0 ) { return horizontal } else { return vertical } } else { if ( vector(vertical) .dotProduct(direction) .end > 0 ) { return vertical } else { return horizontal } } } isPointInLink (position, pathPointList) { const { pointList, minX, minY, maxX, maxY } = pathPointList || this.pathPointList const n = 5 if ( position[0] < minX - n || position[0] > maxX + n || position[1] < minY - n || position[1] > maxY + n ) { return false } for (let i = 0; i < pointList.length - 2; i++) { const prev = pointList[i] const current = pointList[i + 1] const top = Math.min(prev[1], current[1]) - n const right = Math.max(prev[0], current[0]) + n const bottom = Math.max(prev[1], current[1]) + n const left = Math.min(prev[0], current[0]) - n const [x, y] = position if (x > left && x < right && y > top && y < bottom) { return true } } return false } remove () { return this.graph.removeLink(this) } toJSON () { return { [mark.relationMark]: this[mark.relationMark], [mark.startMark]: this.start[mark.relationMark], [mark.endMark]: this.end[mark.relationMark], startAt: this.startAt, endAt: this.endAt, meta: this.meta } } }