nodos de arbol d3.js con los mismos padres

Soy nuevo en D3. Así que estoy tratando de representar un gráfico en el que dos o más hijos podrían tener el mismo padre. Me pregunto cómo podría hacer que los enlaces vuelvan a dirigirse al mismo nodo. Estoy teniendo enlaces rotos …

Cualquier ayuda sería genial.

Aquí está mi código …

var margin = { top: 100,right: 50,bottom: 200,left: 1360 }, customNodes = new Array(), layer_wider_label = new Array(), label_w = 70, branch_w = 70, m = [100, 500, 100, 500], realWidth = window.innerWidth, realHeight = window.innerHeight, h = realHeight,// -m[0] -m[2], w = realWidth,// -m[0] -m[0], width = 3700 - margin.right - margin.left, height = 2050 - margin.top - margin.bottom; root = (function () { var json = null; $.ajax({ 'async': false, 'global': false, 'url': "/PedigreeImport/json/tree6.json", 'dataType': "json", 'success': function (data) { json = data; } }); return json; })(); var i = 0, duration = 550, rectW = 80, rectH = 17, ms; var tree = d3.layout.tree().nodeSize([120, 50]); var diagonal = d3.svg.diagonal() .projection(function (d) { return [dx + rectW / 2, (height-dy) + rectH / 2]; }); var svg = d3.select("#graphDiv").append("svg") .attr("width", width + margin.right + margin.left) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("class","drawarea") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); var ms = document.getElementById('maxStep').value; var tmpNodes = d3.layout.tree().size([450, 300]).nodes(root); root.x0 = function(d) { return dx; };//0; root.y0 = function(d) { return height - dy; };//height / 2; root.depth = parseInt(root.layer); customNodes.push(root); prepareNodes(root.children); updateNodesXOffset() //root.children.forEach(collapse); update(root); d3.select("#graphDiv").style("height", "660px"); function collapse(d) { if (d.children) { d._children = d.children; d._children.forEach(collapse); d.children = null; } } /*d3.select("svg") .call(d3.behavior.zoom() .scaleExtent([0.5,5]) .on("zoom", zoom));*/ function updateNodesXOffset(){ var x_offsets = new Array(); x_offsets[0] = 0; customNodes.forEach(function(node) { node.x = 0; if (node.layer > 0) { node.x = x_offsets[node.layer - 1] + layer_wider_label[node.layer - 1] + branch_w; x_offsets[node.layer] = node.x; } }); } function prepareNodes(nodes) { nodes.forEach(function(node) { //alert('try'); prepareNode(node); if (node.children) { prepareNodes(node.children); } }); } function prepareNode(node) { node.y = getNodeY(node.id); //.on("click", click); //fake element to calculate labels area width. var fakeTxtBox = svg.append("svg:text") .attr("id", "fakeTXT") .attr("text-anchor", "right") .text(node.name + " : " + node.gid) //.on("click", click(node)); var this_label_w = fakeTxtBox.node().getComputedTextLength(); svg.select("#fakeTXT").remove(); if (layer_wider_label[node.layer] == null) { layer_wider_label[node.layer] = this_label_w; } else { if (this_label_w > layer_wider_label[node.layer]) { layer_wider_label[node.layer] = this_label_w; } } // node.x = nodex; //x will be set node.depth = parseInt(node.layer); customNodes.push(node); //node.on("click", click(node)); } function getNodeY(id) { var ret = 0; tmpNodes.some(function(node) { if (node.id === id) { //return x:d3.tree has a vertical layout by default. //ret = node.x ret = node.x; return; } }) return ret; } function update(source) { // Compute the new tree layout. var nodes = tree.nodes(root).reverse(), links = tree.links(nodes,function (d) { return d.id || (d.id = ++i) }); // Normalize for fixed-depth. nodes.forEach(function (d) { dy = d.depth * 100; }); // Update the nodes… var node = svg.selectAll("g.node") .data(nodes, function (d) { return d.id || (d.id = ++i); }); //if(node.depth <= ms){ // Enter any new nodes at the parent's previous position. var nodeEnter = node.enter() .append("g") .attr("class", "node") .attr("transform", function (d) { return "translate(" + source.x0 + "," + source.y0 + ")"; }) .on("click", click); //var txtBox = nodeEnter.append("text") //var txtW = txtBox.node().getComputedTextLength(); nodeEnter.append("rect","text") .attr("width", rectW) .attr("height", rectH) .attr("stroke", "black") .attr("stroke-width", 0.5) .attr("rx", 4) .attr("ry", 4) .style("fill", function (d) { return d._children ? "#0099FF" : "#fff"; }); nodeEnter.append("text") .attr("x", rectW / 2) .attr("y", rectH / 2) .attr("stroke", node.current ? "#ffffff" : node.children ? "#ffffff" : "#000000") .attr("stroke-width", 0.5) //.attr("stroke", "white") .attr("dy", ".15em") .attr("text-anchor", "middle") .text(function (d) { return d.name; }); // Transition nodes to their new position. var nodeUpdate = node.transition() .duration(duration) .attr("transform", function (d) { return "translate(" + dx + "," + (height-dy) + ")"; }); nodeUpdate.select("rect","text") .attr("width", rectW) .attr("height", rectH) .attr("stroke", "black") .attr("stroke-width", 0.5) .attr("rx", 4) .attr("ry", 4) .style("fill", function (d) { return d._children ? "#0099FF" : "#fff"; }); nodeUpdate.select("text") .style("fill-opacity", 1); // Transition exiting nodes to the parent's new position. var nodeExit = node.exit() .transition() .duration(duration) .attr("transform", function (d) { return "translate(" + source.x + "," + (height-source.y) + ")"; }) .remove(); nodeExit.select("rect") .attr("width", rectW) .attr("height", rectH) .attr("stroke", "black") .attr("stroke-width", 1); nodeExit.select("text"); // Update the links… var link = svg.selectAll("path.link") .data(links, function (d) { return d.target.id; }) .attr("class", function(d) { return d.warning === "true" ? "link warning" : "link"}); // Enter any new links at the parent's previous position. link.enter().insert("path", "g") .attr("class", function(d) { return d.warning === "true" ? "link warning" : "link" }) .attr("x", rectW / 2) .attr("y", rectH / 2) .attr("d", function (d) { var o = { x: source.x0.id, y: (height-source.y0) }; return diagonal({ source: o, target: o }); }); // Transition links to their new position. link.transition() .duration(duration) .attr("class", function(d) { return d.warning === "true" ? "link warning" : "link" }) .attr("d", diagonal); // Transition exiting nodes to the parent's new position. link.exit().transition() .duration(duration) .attr("d", function (d) { var o = { x: source.x.id, y: (height-source.y) }; return diagonal({ source: o, target: o }); }) .attr("class", function(d) { return d.warning === "true" ? "link warning" : "link" }) .remove(); // Stash the old positions for transition. nodes.forEach(function (d) { d.x0 = dy; d.y0 = dx; }); //} } // Toggle children on click. function click(d) { if (d.children) { d._children = d.children; d.children = null; } else { d.children = d._children; d._children = null; } update(d); document.getElementById('gid').innerHTML = d.gid; document.getElementById('gname').innerHTML = d.name; document.getElementById('gmethod').innerHTML = d.method; document.getElementById('gmtype').innerHTML = d.methodtype; document.getElementById('gdate').innerHTML = d.date; document.getElementById('gcountry').innerHTML = d.country; document.getElementById('gloc').innerHTML = d.location; document.getElementById('gcname').innerHTML = d.cname; document.getElementById('gref').innerHTML = d.ref; document.getElementById('gpid1').innerHTML = d.gpid1; document.getElementById('gpid2').innerHTML = d.gpid2; } function zoom() { var scale = d3.event.scale, translation = d3.event.translate, tbound = -h * scale, bbound = h * scale, lbound = (-w + m[1]) * scale, rbound = (w - m[3]) * scale; // limit translation to thresholds translation = [ Math.max(Math.min(translation[0], rbound), lbound), Math.max(Math.min(translation[1], bbound), tbound) ]; d3.select(".drawarea") .attr("transform", "translate(" + translation + ")" + " scale(" + scale + ")"); } 

Creo que lo mejor sería echar un vistazo a una biblioteca diferente, utilizando una combinación de Dagre ( https://github.com/dagrejs/dagre ) y cytoscape

Hay una biblioteca llamada Dagre-D3 que puede usar como procesador para esta biblioteca, por lo que se parece a la solución D3 que desea.

Consulte este violín para ver la implementación básica con Dagre y Cytoscape: https://jsfiddle.net/KateJean/xweudjvm/

 var cy = cytoscape({ container: document.getElementById('cy'), elements: { nodes: [ { data: { id: '1' } }, { data: { id: '2' } }, { data: { id: '3' } }, { data: { id: '4' } }, { data: { id: '5' } }, { data: { id: '6' } }, { data: { id: '7' } }, { data: { id: '8' } }, { data: { id: '9' } }, { data: { id: '10' } }, { data: { id: '11' } }, { data: { id: '12' } }, { data: { id: '13' } }, { data: { id: '14' } }, { data: { id: '15' } }, { data: { id: '16' } }, { data: { id: '17' } }, { data: { id: '18' } } ], edges: [ { data: { source: '1', target: '2' } }, { data: { source: '1', target: '3' } }, { data: { source: '2', target: '4' } }, { data: { source: '4', target: '5' } }, { data: { source: '4', target: '6' } }, { data: { source: '5', target: '6' } }, { data: { source: '5', target: '7' } }, { data: { source: '7', target: '8' } }, { data: { source: '3', target: '9' } }, { data: { source: '3', target: '10' } }, { data: { source: '10', target: '11' } }, { data: { source: '11', target: '12' } }, { data: { source: '12', target: '13' } }, { data: { source: '12', target: '14' } }, { data: { source: '14', target: '15' } }, { data: { source: '15', target: '16' } }, { data: { source: '16', target: '17' } }, { data: { source: '16', target: '18' } } ] }, layout: { name: "dagre", rankDir: 'TB' //love this. you can quickly change the orientation here from LR(left to right) TB (top to bottom), RL, BT. Great dropdown option for users here. }, style: [{ selector: 'node', style: { 'label': 'data(id)', 'width': '30%', 'font-size': '20px', 'text-valign': 'center', 'shape': 'circle', 'background-color': 'rgba(113,158,252,1)', 'border': '2px grey #ccc' } }, { selector: 'edge', style: { 'width': 2, 'line-color': '#ccc', 'target-arrow-color': '#ccc', 'target-arrow-shape': 'triangle' } }] });