I have the following code that works, but I have two problems I need help with.
- The path line draws pass the arrow underneath the icon. How can I
force the path line from stopping at the arrow point? Some nodes can have two links and they overlap. How can I swap the ARC direction on the second link to prevent that from happening?
var json_data = data; var hash_lookup = []; json_data.nodes.forEach(function (d, i) { hash_lookup[d.id] = d; }); json_data.links.forEach(function (d, i) { d.source = hash_lookup[d.source]; d.target = hash_lookup[d.target]; }); var width = ($("#dependency_map").width()-10); var height = ($("#dependency_map").height()-10); d3.select("#dependency_map").selectAll("*").remove(); var force = d3.layout.force() .gravity(.05) .distance(100) .linkStrength(.4) .charge(-200) .size([width, height]); var zoom = d3.behavior.zoom() .scaleExtent([1, 10]) .on("zoom", zoomed); var svg = d3.select("#dependency_map") .append("svg:svg") .attr("width", width) .attr("height", height) .attr("pointer-events", "all") .append('svg:g') .call(zoom).on("dblclick.zoom", null) var rect = svg.append("rect") .attr("width", width) .attr("height", height) .style("fill", "none") .style("pointer-events", "all"); function dblclick(d) { d3.select(this).classed("fixed", d.fixed = false); } function dragstarted(d) { if(d3.event.sourceEvent.button == 0) { d3.event.sourceEvent.stopPropagation(); force.start(); } } function dragged(d) { if(d3.event.sourceEvent.button == 0) { d3.select(this).attr("cx", d.x = d3.event.x).attr("cy", d.y = d3.event.y); } } function dragended(d) { if(d3.event.sourceEvent.button == 0) { d3.select(this).classed("fixed", d.fixed = true); d3.select(this).classed("dragging", false); } } var drag = d3.behavior.drag() .on("dragstart", dragstarted) .on("drag", dragged) .on("dragend", dragended); var nodes = json_data.nodes, links = json_data.links; var container = svg.append("g"); // build the arrow. container.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"); // make an arch between nodes and a text label in the middle var path = container.selectAll("path.link").data(links); path.enter().append("path") .attr("class", "link") .attr("id",function(d,i) { return "linkId_" + i; }) .attr("marker-end", "url(#end)"); var linktext = container.append("svg:g").selectAll("g.linklabelholder").data(links); linktext.enter().append("g").attr("class", "linklabelholder") .append("text") .attr("class", "linklabel") .style("font-size", "0.5em") .attr("dy", "-2") .style("fill","#000") .append("textPath") .attr("startOffset","50%") .style("text-anchor","middle") .attr("xlink:href",function(d,i) { return "#linkId_" + i;}) .text(function(d) { return d.text; }); var node = container.selectAll("g.node") .data(nodes) .enter() .append("svg:g") .attr("class", "node") .on("dblclick", dblclick) .call(drag) .on("contextmenu", function(data, index) { console.log("Context Menu Click"); if (data.knownworkload == true) { d3.select('#cntxtMenu') .style('position', 'absolute') .style('left', data.px + 50 + "px") .style('top', data.py + "px") .style('display', 'block') .on('mouseleave', function() { d3.select('#cntxtMenu').style('display', 'none'); context = null; }); d3.selectAll('.workload_menu').on('click' , function(d) { var action = d3.select(this).attr("action"); var workload_id = data.guid; if (action == "information") { $.ajax({ async:true, url: "<%= url(format: :js) %>", data: { workload_id: workload_id } }); $('#cntxtMenu').hide(); } else { var servicestack_id = d3.select(this).attr("guid"); $.ajax({ async:false, url: "<%= url %>", data: { workload_id: workload_id, servicestack_id: servicestack_id, success: function (data, status, jqXHR) { }, } }); draw_map(); $('#cntxtMenu').hide(); } }); } d3.event.preventDefault(); }); function zoomed() { container.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")"); } nodes.forEach(function(d, i) { d.x = width/2 + i; d.y = height/2 + (1500 * d.depth); }); force.nodes(nodes) .links(links) .start(); node.append('svg:text') .attr('font-family', function (d) { return d.icon.fontfamily; }) .attr('font-size', '0.8em') .attr('fill', function (d) { return d.icon.color;}) .text(function (d) {return d.icon.icon;}) .attr("x", "-5px") .attr("y", "5px"); node.append("svg:text") .attr("y", "15px") .attr('font-size', '0.6em') .attr("text-anchor", "middle") .each(function (d) { var arr = d.name.split(" "); if (arr != undefined) { for (i = 0; i < arr.length; i++) { d3.select(this).append("tspan") .text(arr[i]) .attr("cy", (i == 0) ? 0 : 10) .attr("text-anchor", "left") .attr("class", "nodetext"); } } }); for (var i=0; i<path.length; i++) { if (i != 0 && path[i].source == path[i-1].source && path[i].target == path[i-1].target) { path[i].linknum = path[i-1].linknum + 1; } else {path[i].linknum = 1;}; }; node.append("svg:title") .text(function (d) { return d.tooltip; }); force.on("tick", function(e) { path.attr("d", function(d) { console.log(d); var dx = d.target.x - d.source.x, dy = d.target.y - d.source.y, dr = Math.sqrt(dx * dx + dy * dy); return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y; }); node.attr("transform", function (d) { return "translate(" + d.x + "," + d.y + ")"; }); });
}, });