首先我要声明,我对 D3 和 Javascript 完全陌生。经过一些实验,我试图开发一个类似树的结构,但问题是,节点可以有多个父节点。
输入:
带有节点和链接信息的Json,例如,
var graph = {
"nodes": [ { "id" : 0},
{ "id" : 1},
{ "id" : 2},
{ "id" : 3},
{ "id" : 4},
{ "id" : 5}
],
"links": [ { "target": 5, "source": 1 },
{ "target": 2, "source": 0 },
{ "target": 3, "source": 4 },
{ "target": 1, "source": 2},
{ "target": 1, "source": 0 },
]
};
注意:这仅供引用,因为我如何获取数据,可能有数千个节点。
输出:
我想要像树结构一样的输出,有点像 this ,也就是说,节点必须以类似深度/级别的结构绘制,根位于顶部,相同级别的节点以最小化链接重叠的方式排列。
我面临的问题:
- 由于我只给出节点及其源和目标,而不是它们的坐标,因此我必须解析整个列表计算级别,然后根据该级别划分高度,然后绘制节点。处理此类问题最有效的方法是什么?我应该深入研究算法解决方案吗?
或者
- D3 中是否有一些图形绘制函数可以将此 JSON 作为输入并完成其余工作?
请注意,节点可以有多个父节点,我的主要重点是以正确/分层顺序绘制这些节点,因此我面临的主要问题是计算仅给定的所有节点的 X/Y 坐标如上所述输入。
请帮助我更好地理解这个主题,谢谢。
最佳答案
一整天都在断断续续地思考这个问题。通过内置的 d3 构造,我认为 this大约是您所能达到的最接近的程度。我已获取您的数据并将其转换为版本 4 示例:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.links line {
stroke: #aaa;
stroke-width: 5px;
}
.nodes circle {
pointer-events: all;
stroke: none;
}
</style>
<svg width="600" height="300"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) {
return d.id;
}))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2))
.force("y", d3.forceY())
var graph = {
"nodes": [{
"id": 0
}, {
"id": 1
}, {
"id": 2
}, {
"id": 3
}, {
"id": 4
}, {
"id": 5
}],
"links": [{
"target": 5,
"source": 1
}, {
"target": 2,
"source": 0
}, {
"target": 3,
"source": 4
}, {
"target": 1,
"source": 2
}, {
"target": 1,
"source": 0
},
]
};
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter().append("line");
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.enter().append("circle")
.attr("r", 10);
node.append("title")
.text(function(d) {
return d.id;
});
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.links);
function ticked() {
var k = 6 * simulation.alpha();
// Push sources up and targets down to form a weak tree.
link
.each(function(d) {
d.source.y -= k, d.target.y += k;
})
.attr("x1", function(d) {
return d.source.x;
})
.attr("y1", function(d) {
return d.source.y;
})
.attr("x2", function(d) {
return d.target.x;
})
.attr("y2", function(d) {
return d.target.y;
});
node
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
});
}
</script>
如果你不想要移动和沉降的效果,可以预先计算布局使其static .
我还尝试将数据转换为分层格式以与 d3.tree 一起使用但没有太多运气复制你的图像:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.node circle {
fill: #999;
}
.node text {
font: 10px sans-serif;
}
.node--internal circle {
fill: #555;
}
.node--internal text {
text-shadow: 0 1px 0 #fff, 0 -1px 0 #fff, 1px 0 0 #fff, -1px 0 0 #fff;
}
.link {
fill: none;
stroke: #555;
stroke-opacity: 0.4;
stroke-width: 1.5px;
}
</style>
<svg width="300" height="300"></svg>
<script src="//d3js.org/d3.v4.min.js"></script>
<script>
var graph = {
"nodes": [{
"id": 0
}, {
"id": 1
}, {
"id": 2
}, {
"id": 3
}, {
"id": 4
}, {
"id": 5
}],
"links": [{
"target": 5,
"source": 1
},{
"target": 2,
"source": 0
}, {
"target": 3,
"source": 4
}, {
"target": 1,
"source": 2
}, {
"target": 1,
"source": 0
}]
};
var root = {
id: 0,
data: graph.nodes[0],
children: []
}
function recurChild(obj) {
graph.links.forEach(function(d) {
if (d.source === obj.id) {
var c = {
id: d.target,
data: graph.nodes[d.target],
children: []
};
obj.children.push(c);
recurChild(c);
}
});
}
recurChild(root);
root = d3.hierarchy(root);
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
g = svg.append("g").attr("transform", "translate(40,0)");
var tree = d3.tree()
.size([height, width - 160]);
tree(root);
console.log(root.descendants())
var link = g.selectAll(".link")
.data(root.descendants().slice(1))
.enter().append("path")
.attr("class", "link")
.attr("d", function(d) {
return "M" + d.y + "," + d.x + "C" + (d.y + d.parent.y) / 2 + "," + d.x + " " + (d.y + d.parent.y) / 2 + "," + d.parent.x + " " + d.parent.y + "," + d.parent.x;
});
var node = g.selectAll(".node")
.data(root.descendants())
.enter().append("g")
.attr("class", function(d) {
return "node" + (d.children ? " node--internal" : " node--leaf");
})
.attr("transform", function(d) {
return "translate(" + d.y + "," + d.x + ")";
})
node.append("circle")
.attr("r", 2.5);
node.append("text")
.attr("dy", 3)
.attr("x", function(d) {
return d.children ? -8 : 8;
})
.style("text-anchor", function(d) {
return d.children ? "end" : "start";
})
.text(function(d) {
return d.data.id;
});
</script>
d3.hierarchy 实际上意味着,没有多个父结构的直接层次结构。
关于javascript - D3 图形布局 - 具有多个父级的树结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39818279/