3

I am trying to add arrow from source to target in force directed report, sample of which is available here: http://plnkr.co/edit/Q69DkEH5Z9aOe92UwlOB?p=preview

I am trying to add arrow in the link from source to target in above graph at the middle of the link. Something like this:

Node1----->------Node2--------<-------Node3

But I am not sure how I can do this.

I saw one of the example here: https://stackoverflow.com/a/28050566/8052709 and tried to add this in my plunkr:

// build the arrow.
vis.append("svg:defs").selectAll("marker")
    .data(["end"])      // Different link/path types can be defined here
  .enter().append("svg:marker")    // This section adds in the arrows
    .attr("id", String)
    .attr("viewBox", "0 -5 10 10")
    .attr("refX", 15)
    .attr("refY", -1.5)
    .attr("markerWidth", 6)
    .attr("markerHeight", 6)
    .attr("orient", "auto")
  .append("svg:path")
    .attr("d", "M0,-5L10,0L0,5");

But it fails. I am not much proficient with drawing diagram using JS, so not sure what I am doing wrong.

Can anyone please help me here.

kaounKaoun
  • 601
  • 1
  • 9
  • 21
  • Did you add the marker to your links? From the linked example: `.attr("marker-end", "url(#end)");` – mzulch Apr 01 '18 at 07:44
  • yes.. but i get error: vis.append(...).append(...).attrs is not a function – kaounKaoun Apr 01 '18 at 07:50
  • the .attr call goes on your link selection, e.g. `link.enter().insert('line').attr('marker-end', 'url(#end)')` – mzulch Apr 01 '18 at 07:54
  • To clarify a bit more, there are two steps to adding a marker, in two different places in the SVG DOM - creating it as a child of the `defs` element, and referencing it by ID from the element(s) to which it should be applied – mzulch Apr 01 '18 at 07:59
  • forked plunker with visible arrows: http://plnkr.co/edit/nP9YBH7jCJIuxtPxByEq?p=preview – mzulch Apr 01 '18 at 08:00
  • thanks for this.. just a minor change if I can .. How can I add this arrow at middle of the link? you can post this as answer.. I can accept that. – kaounKaoun Apr 01 '18 at 08:07
  • I don't have time to write up a full answer at the moment, but it looks like you'll need to use marker-mid instead of marker-end. This would require changing the lines into paths/polylines, as normal lines don't have a midpoint, only a start and and end – mzulch Apr 01 '18 at 08:16
  • no problem very much appreciated for your help – kaounKaoun Apr 01 '18 at 08:33

1 Answers1

2

Here is a small demo:

<!DOCTYPE html>
<meta charset="utf-8">
<style>.links polyline { stroke: #999; }</style>
<svg width="400" height="200"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var graph = {
    "nodes": [
        {"id": "A", "group": 1},
        {"id": "B", "group": 2},
        {"id": "C", "group": 2},
        {"id": "D", "group": 3},
        {"id": "E", "group": 3}
    ],
    "links": [
        {"source": "A", "target": "B", "value": 1},
        {"source": "A", "target": "C", "value": 1},
        {"source": "B", "target": "C", "value": 1},
        {"source": "A", "target": "D", "value": 1},
        {"source": "D", "target": "E", "value": 1},
        {"source": "C", "target": "E", "value": 1}
    ]
};

var svg = d3.select("svg");

svg.append("svg:defs").selectAll("marker")
    .data(["arrow"])
    .enter().append("svg:marker")
    .attr("id", String)
    .attr("viewBox", "0 -5 10 10")
    .attr("refX", 2.5)
    .attr("refY", 0)
    .attr("markerWidth", 5)
    .attr("markerHeight", 5)
    .attr("orient", "auto")
    .append("svg:path")
    .attr("d", "M0,-5L10,0L0,5");

var color = d3.scaleOrdinal(d3.schemeCategory20);

var simulation = d3.forceSimulation()
    .force("link", d3.forceLink().id(function(d) { return d.id; }))
    .force("charge", d3.forceManyBody())
    .force("center", d3.forceCenter(svg.attr("width") / 2, svg.attr("height") / 2));

var link = svg.append("g")
    .attr("class", "links")
    .selectAll("line")
    .data(graph.links)
    .enter().append("polyline")
    .attr("stroke-width", function(d) { return Math.sqrt(d.value); })
    .attr("marker-mid", "url(#arrow)");

var node = svg.append("g")
    .attr("class", "nodes")
    .selectAll("circle")
    .data(graph.nodes)
    .enter().append("circle")
    .attr("r", 5)
    .attr("fill", function(d) { return color(d.group); })
    .call(d3.drag()
        .on("start", dragstarted)
        .on("drag", dragged)
        .on("end", dragended));

node.append("title")
    .text(function(d) { return d.id; });

simulation
    .nodes(graph.nodes)
    .on("tick", ticked);

simulation.force("link")
    .links(graph.links);

function ticked() {
    link.attr("points", function(d) {
        return d.source.x + "," + d.source.y + " " + 
            (d.source.x + d.target.x) / 2 + "," + (d.source.y + d.target.y) / 2 + " " +
            d.target.x + "," + d.target.y;
    });

    node.attr("cx", function(d) { return d.x; })
        .attr("cy", function(d) { return d.y; });
}

function dragstarted(d) {
    if (!d3.event.active) simulation.alphaTarget(0.5).restart();
    d.fx = d.x;
    d.fy = d.y;
}

function dragged(d) {
    d.fx = d3.event.x;
    d.fy = d3.event.y;
}

function dragended(d) {
    if (!d3.event.active) simulation.alphaTarget(0);
    d.fx = null;
    d.fy = null;
}
</script>

Credits: https://stackoverflow.com/a/15758791/18667225

Markus
  • 5,976
  • 5
  • 6
  • 21