create-edge-paths.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. import * as d3 from 'd3'
  2. import _ from 'lodash'
  3. import intersectNode from './intersect/intersect-node'
  4. import util from './util'
  5. function createEdgePaths (selection, g, arrows) {
  6. let svgPaths = selection.selectAll('g.edgePath')
  7. .data(g.edges(), function (e) { return util.edgeToId(e) })
  8. .classed('update', true)
  9. enter(svgPaths, g)
  10. exit(svgPaths, g)
  11. svgPaths = selection.selectAll('g.edgePath')
  12. util.applyTransition(svgPaths, g)
  13. .style('opacity', 1)
  14. // Save DOM element in the path group, and set ID and class
  15. svgPaths.each(function (e) {
  16. const domEdge = d3.select(this)
  17. const edge = g.edge(e)
  18. edge.elem = this
  19. if (edge.id) {
  20. domEdge.attr('id', edge.id)
  21. }
  22. util.applyClass(domEdge, edge['class'],
  23. (domEdge.classed('update') ? 'update ' : '') + 'edgePath')
  24. })
  25. svgPaths.selectAll('path.path')
  26. .each(function (e) {
  27. const edge = g.edge(e)
  28. edge.arrowheadId = _.uniqueId('arrowhead')
  29. const domEdge = d3.select(this)
  30. .attr('marker-end', function () {
  31. return 'url(#' + edge.arrowheadId + ')'
  32. })
  33. .style('fill', 'none')
  34. util.applyTransition(domEdge, g)
  35. .attr('d', function (e) { return calcPoints(g, e) })
  36. util.applyStyle(domEdge, edge.style)
  37. })
  38. svgPaths.selectAll('defs *').remove()
  39. svgPaths.selectAll('defs')
  40. .each(function (e) {
  41. const edge = g.edge(e)
  42. const arrowhead = arrows[edge.arrowhead]
  43. arrowhead(d3.select(this), edge.arrowheadId, edge, 'arrowhead')
  44. })
  45. return svgPaths
  46. }
  47. function calcPoints (g, e) {
  48. const edge = g.edge(e)
  49. const tail = g.node(e.v)
  50. const head = g.node(e.w)
  51. const points = edge.points.slice(1, edge.points.length - 1)
  52. points.unshift(intersectNode(tail, points[0]))
  53. points.push(intersectNode(head, points[points.length - 1]))
  54. return createLine(edge, points)
  55. }
  56. function createLine (edge, points) {
  57. const line = d3.line()
  58. .x(function (d) { return d.x })
  59. .y(function (d) { return d.y })
  60. line.curve(edge.curve)
  61. return line(points)
  62. }
  63. function getCoords (elem) {
  64. const bbox = elem.getBBox()
  65. const matrix = elem.ownerSVGElement.getScreenCTM()
  66. .inverse()
  67. .multiply(elem.getScreenCTM())
  68. .translate(bbox.width / 2, bbox.height / 2)
  69. return { x: matrix.e, y: matrix.f }
  70. }
  71. function enter (svgPaths, g) {
  72. const svgPathsEnter = svgPaths.enter()
  73. .append('g')
  74. .attr('class', 'edgePath')
  75. .style('opacity', 0)
  76. svgPathsEnter.append('path')
  77. .attr('class', 'path')
  78. .attr('d', function (e) {
  79. const edge = g.edge(e)
  80. const sourceElem = g.node(e.v).elem
  81. const points = _.range(edge.points.length).map(function () { return getCoords(sourceElem) })
  82. return createLine(edge, points)
  83. })
  84. svgPathsEnter.append('defs')
  85. }
  86. function exit (svgPaths, g) {
  87. const svgPathExit = svgPaths.exit()
  88. util.applyTransition(svgPathExit, g)
  89. .style('opacity', 0)
  90. .remove()
  91. util.applyTransition(svgPathExit.select('path.path'), g)
  92. .attr('d', function (e) {
  93. const source = g.node(e.v)
  94. if (source) {
  95. const points = _.range(this.getTotalLength()).map(function () { return source })
  96. return createLine({}, points)
  97. } else {
  98. return d3.select(this).attr('d')
  99. }
  100. })
  101. }
  102. export default createEdgePaths