132 lines
3.3 KiB
JavaScript
132 lines
3.3 KiB
JavaScript
import d3 from 'd3'
|
|
import _ from 'lodash'
|
|
|
|
import intersectNode from './intersect/intersect-node'
|
|
import util from './util'
|
|
|
|
function createEdgePaths (selection, g, arrows) {
|
|
const svgPaths = selection.selectAll('g.edgePath')
|
|
.data(g.edges(), function (e) { return util.edgeToId(e) })
|
|
.classed('update', true)
|
|
|
|
enter(svgPaths, g)
|
|
exit(svgPaths, g)
|
|
|
|
util.applyTransition(svgPaths, g)
|
|
.style('opacity', 1)
|
|
|
|
// Save DOM element in the path group, and set ID and class
|
|
svgPaths.each(function (e) {
|
|
const domEdge = d3.select(this)
|
|
const edge = g.edge(e)
|
|
edge.elem = this
|
|
|
|
if (edge.id) {
|
|
domEdge.attr('id', edge.id)
|
|
}
|
|
|
|
util.applyClass(domEdge, edge['class'],
|
|
(domEdge.classed('update') ? 'update ' : '') + 'edgePath')
|
|
})
|
|
|
|
svgPaths.selectAll('path.path')
|
|
.each(function (e) {
|
|
const edge = g.edge(e)
|
|
edge.arrowheadId = _.uniqueId('arrowhead')
|
|
|
|
const domEdge = d3.select(this)
|
|
.attr('marker-end', function () {
|
|
return 'url(#' + edge.arrowheadId + ')'
|
|
})
|
|
.style('fill', 'none')
|
|
|
|
util.applyTransition(domEdge, g)
|
|
.attr('d', function (e) { return calcPoints(g, e) })
|
|
|
|
util.applyStyle(domEdge, edge.style)
|
|
})
|
|
|
|
svgPaths.selectAll('defs *').remove()
|
|
svgPaths.selectAll('defs')
|
|
.each(function (e) {
|
|
const edge = g.edge(e)
|
|
const arrowhead = arrows[edge.arrowhead]
|
|
arrowhead(d3.select(this), edge.arrowheadId, edge, 'arrowhead')
|
|
})
|
|
|
|
return svgPaths
|
|
}
|
|
|
|
function calcPoints (g, e) {
|
|
const edge = g.edge(e)
|
|
const tail = g.node(e.v)
|
|
const head = g.node(e.w)
|
|
const points = edge.points.slice(1, edge.points.length - 1)
|
|
points.unshift(intersectNode(tail, points[0]))
|
|
points.push(intersectNode(head, points[points.length - 1]))
|
|
|
|
return createLine(edge, points)
|
|
}
|
|
|
|
function createLine (edge, points) {
|
|
const line = d3.svg.line()
|
|
.x(function (d) { return d.x })
|
|
.y(function (d) { return d.y })
|
|
|
|
if (_.has(edge, 'lineInterpolate')) {
|
|
line.interpolate(edge.lineInterpolate)
|
|
}
|
|
|
|
if (_.has(edge, 'lineTension')) {
|
|
line.tension(Number(edge.lineTension))
|
|
}
|
|
|
|
return line(points)
|
|
}
|
|
|
|
function getCoords (elem) {
|
|
const bbox = elem.getBBox()
|
|
const matrix = elem.ownerSVGElement.getScreenCTM()
|
|
.inverse()
|
|
.multiply(elem.getScreenCTM())
|
|
.translate(bbox.width / 2, bbox.height / 2)
|
|
return { x: matrix.e, y: matrix.f }
|
|
}
|
|
|
|
function enter (svgPaths, g) {
|
|
const svgPathsEnter = svgPaths.enter()
|
|
.append('g')
|
|
.attr('class', 'edgePath')
|
|
.style('opacity', 0)
|
|
svgPathsEnter.append('path')
|
|
.attr('class', 'path')
|
|
.attr('d', function (e) {
|
|
const edge = g.edge(e)
|
|
const sourceElem = g.node(e.v).elem
|
|
const points = _.range(edge.points.length).map(function () { return getCoords(sourceElem) })
|
|
return createLine(edge, points)
|
|
})
|
|
svgPathsEnter.append('defs')
|
|
}
|
|
|
|
function exit (svgPaths, g) {
|
|
const svgPathExit = svgPaths.exit()
|
|
util.applyTransition(svgPathExit, g)
|
|
.style('opacity', 0)
|
|
.remove()
|
|
|
|
util.applyTransition(svgPathExit.select('path.path'), g)
|
|
.attr('d', function (e) {
|
|
const source = g.node(e.v)
|
|
|
|
if (source) {
|
|
const points = _.range(this.getTotalLength()).map(function () { return source })
|
|
return createLine({}, points)
|
|
} else {
|
|
return d3.select(this).attr('d')
|
|
}
|
|
})
|
|
}
|
|
|
|
export default createEdgePaths
|