visjs-network
Version:
A dynamic, browser-based network visualization library.
207 lines (190 loc) • 5.32 kB
JavaScript
import BezierEdgeBase from './util/BezierEdgeBase'
/**
* A Dynamic Bezier Edge. Bezier curves are used to model smooth gradual
* curves in paths between nodes. The Dynamic piece refers to how the curve
* reacts to physics changes.
*
* @extends BezierEdgeBase
*/
class BezierEdgeDynamic extends BezierEdgeBase {
/**
* @param {Object} options
* @param {Object} body
* @param {Label} labelModule
*/
constructor(options, body, labelModule) {
//this.via = undefined; // Here for completeness but not allowed to defined before super() is invoked.
super(options, body, labelModule) // --> this calls the setOptions below
this._boundFunction = () => {
this.positionBezierNode()
}
this.body.emitter.on('_repositionBezierNodes', this._boundFunction)
}
/**
*
* @param {Object} options
*/
setOptions(options) {
// check if the physics has changed.
let physicsChange = false
if (this.options.physics !== options.physics) {
physicsChange = true
}
// set the options and the to and from nodes
this.options = options
this.id = this.options.id
this.from = this.body.nodes[this.options.from]
this.to = this.body.nodes[this.options.to]
// setup the support node and connect
this.setupSupportNode()
this.connect()
// when we change the physics state of the edge, we reposition the support node.
if (physicsChange === true) {
this.via.setOptions({ physics: this.options.physics })
this.positionBezierNode()
}
}
/**
* Connects an edge to node(s)
*/
connect() {
this.from = this.body.nodes[this.options.from]
this.to = this.body.nodes[this.options.to]
if (
this.from === undefined ||
this.to === undefined ||
this.options.physics === false
) {
this.via.setOptions({ physics: false })
} else {
// fix weird behaviour where a self referencing node has physics enabled
if (this.from.id === this.to.id) {
this.via.setOptions({ physics: false })
} else {
this.via.setOptions({ physics: true })
}
}
}
/**
* remove the support nodes
* @returns {boolean}
*/
cleanup() {
this.body.emitter.off('_repositionBezierNodes', this._boundFunction)
if (this.via !== undefined) {
delete this.body.nodes[this.via.id]
this.via = undefined
return true
}
return false
}
/**
* Bezier curves require an anchor point to calculate the smooth flow. These points are nodes. These nodes are invisible but
* are used for the force calculation.
*
* The changed data is not called, if needed, it is returned by the main edge constructor.
* @private
*/
setupSupportNode() {
if (this.via === undefined) {
var nodeId = 'edgeId:' + this.id
var node = this.body.functions.createNode({
id: nodeId,
shape: 'circle',
physics: true,
hidden: true
})
this.body.nodes[nodeId] = node
this.via = node
this.via.parentEdgeId = this.id
this.positionBezierNode()
}
}
/**
* Positions bezier node
*/
positionBezierNode() {
if (
this.via !== undefined &&
this.from !== undefined &&
this.to !== undefined
) {
this.via.x = 0.5 * (this.from.x + this.to.x)
this.via.y = 0.5 * (this.from.y + this.to.y)
} else if (this.via !== undefined) {
this.via.x = 0
this.via.y = 0
}
}
/**
* Draw a line between two nodes
* @param {CanvasRenderingContext2D} ctx
* @param {ArrowOptions} values
* @param {Node} viaNode
* @private
*/
_line(ctx, values, viaNode) {
this._bezierCurve(ctx, values, viaNode)
}
/**
*
* @returns {Node|undefined|*|{index, line, column}}
*/
getViaNode() {
return this.via
}
/**
* Combined function of pointOnLine and pointOnBezier. This gives the coordinates of a point on the line at a certain percentage of the way
*
* @param {number} percentage
* @param {Node} viaNode
* @returns {{x: number, y: number}}
* @private
*/
getPoint(percentage, viaNode = this.via) {
let t = percentage
let x, y
if (this.from === this.to) {
let [cx, cy, cr] = this._getCircleData(this.from)
let a = 2 * Math.PI * (1 - t)
x = cx + cr * Math.sin(a)
y = cy + cr - cr * (1 - Math.cos(a))
} else {
x =
Math.pow(1 - t, 2) * this.fromPoint.x +
2 * t * (1 - t) * viaNode.x +
Math.pow(t, 2) * this.toPoint.x
y =
Math.pow(1 - t, 2) * this.fromPoint.y +
2 * t * (1 - t) * viaNode.y +
Math.pow(t, 2) * this.toPoint.y
}
return { x: x, y: y }
}
/**
*
* @param {Node} nearNode
* @param {CanvasRenderingContext2D} ctx
* @returns {*}
* @private
*/
_findBorderPosition(nearNode, ctx) {
return this._findBorderPositionBezier(nearNode, ctx, this.via)
}
/**
*
* @param {number} x1
* @param {number} y1
* @param {number} x2
* @param {number} y2
* @param {number} x3
* @param {number} y3
* @returns {number}
* @private
*/
_getDistanceToEdge(x1, y1, x2, y2, x3, y3) {
// x3,y3 is the point
return this._getDistanceToBezierEdge(x1, y1, x2, y2, x3, y3, this.via)
}
}
export default BezierEdgeDynamic