有一个示例,我尝试在d3.js的帮助下进行绘制。
我有3个大问题。这是代码(您可以在下面运行代码片段,它可能会起作用):
function getRandomInt(max, min = 0) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function fdSortShit(g, nodeDimensions) {
const gNodes = [];
const gLinks = [];
g.children().forEach(child => {
gNodes.push({
id: child,
w: nodeDimensions[child].w,
h: nodeDimensions[child].h,
radius:
Math.sqrt(
nodeDimensions[child].w * nodeDimensions[child].w + nodeDimensions[child].h * nodeDimensions[child].h
) / 2
});
});
g.edges().forEach(edge => {
gLinks.push({ source: edge.v, target: edge.w });
});
const data = {
nodes: gNodes,
links: gLinks
};
const nodes = data.nodes;
const links = data.links;
const linkNodeRad = 5;
const linkNodes = [];
links.forEach((link, idx) => {
if (link.source != link.target) {
linkNodes.push({
id: `link-node-${idx}`,
source: nodes.filter(e => {
return e.id == link.source;
})[0],
target: nodes.filter(e => {
return e.id == link.target;
})[0],
radius: linkNodeRad
});
}
});
const width = 800;
const height = 600;
var svg = d3
.select("body")
.append("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", "-400, -300, 800, 600");
function forceSimulation(nodes, links) {
return d3
.forceSimulation(nodes)
.force("link", d3.forceLink(links).id(d => d.id))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter())
.force(
"collision",
d3.forceCollide().radius(function(d) {
return d.radius;
})
);
}
var link = svg
.selectAll(".link")
.attr("stroke", "#fff")
.data(links)
.enter()
.append("line")
.attr("class", "link");
var node = svg
.append("g")
.selectAll("g")
.data(nodes)
.enter()
.append("g");
var circles = node
.append("circle")
.attr("class", "node")
.attr("r", node => {
return node.radius;
});
var text = node
.append("text")
.text(d => {
return d.id;
})
.attr("class", "node-caption")
.attr("x", 0)
.attr("y", 0);
var linkNode = svg
.selectAll(".link-node")
.data(linkNodes)
.enter()
.append("circle")
.attr("class", "link-node")
.attr("r", linkNodeRad);
function ticked() {
link
.attr("x1", d => d.source.x)
.attr("y1", d => d.source.y)
.attr("x2", d => d.target.x)
.attr("y2", d => d.target.y);
node.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
linkNode
.attr("cx", function(d) {
return (d.x = (d.source.x + d.target.x) * 0.5);
})
.attr("cy", function(d) {
return (d.y = (d.source.y + d.target.y) * 0.5);
});
}
forceSimulation(nodes.concat(linkNodes), links)
.on("tick", ticked)
.on("end", () => {
console.warn("END");
});
}
const coords = {};
const size = { min: 10, max: 30 };
const dotStr = "graph g { a--a;a--b;a--b;a--c;a--d;a--e;b--b1;c--c1;c--c2;d--d1;d--d2;d--d3;d--d4;e--e1;v--w;v--x;v--y;w--z;w--w1;x--x1;x--x2;y--y1;y--y2;y--y3;y--y4;z--z1;v--a; }";
const g = graphlibDot.read(dotStr);
g.children().forEach(child => {
const x = getRandomInt(1024 - 10, 10);
const y = getRandomInt(768 - 10, 10);
coords[child] = {
x: x,
y: y,
w: getRandomInt(size.max, size.min),
h: getRandomInt(size.max, size.min)
};
});
fdSortShit(g, coords);
svg {
background-color: lightgray;
}
circle.node {
fill: lightcoral;
}
circle.link-node {
fill: rgba(0, 0, 255, 0.2);
/* fill: transparent; */
}
line.link {
stroke: lightseagreen;
}
text.node-caption {
font: normal 10px courier new;
}
<script src="https://cdn.jsdelivr.net/npm/graphlib-dot@0.6.2/dist/graphlib-dot.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
图像如下:
第一个问题是:如何避免这种交叉?
我知道我不能躲避所有的边缘交叉点,但是我想将它们最小化。本示例是没有循环的树形图。我知道有一种无需边缘交叉即可构建的方法。但是我不知道如何使用这种算法。
但是路口仍然很烦人。
第二个问题是:如何不及时模拟力(我不需要动画),而只是绘制最终结果呢?当我使用forceSimulation.on("end", cb)
时,效果很好,但是开始和停止之间的延迟很大。..但这只是一个小例子。我等不及要再大一次了。
第三个问题是..如何应用强制变形设置?力能,刚度,斥力,阻尼等?在d3 @ 5上找不到它们
我的项目负责人想要的最终结果是:
我准备好进行对话了。
答案 0 :(得分:1)
我通过使用forceCollide和forceLink distance参数解决了这个问题:
var simulation = d3.forceSimulation()
.force('link', d3.forceLink().id(d => d.id).distance(100).strength(1))
.force('charge', d3.forceManyBody()) // ^ change this value
.force('collide', d3.forceCollide(110)) // change this value
.force('center', d3.forceCenter(width / 2, height / 2));
该想法是使不相关的节点互相排斥,同时保持链接距离较短。
在我的情况下效果很好,但是我的节点图比您的要简单得多。
答案 1 :(得分:0)
您可以在初始化部分中应用力设置。这是一个示例-
var simulation = d3.forceSimulation() //Modify link distance/strength here
.force("link", d3.forceLink().id(function (d) { return d.id; }).distance(80).strength(1))
.force("charge", d3.forceManyBody().strength(-15)) //Charge strength is here
.force("center", d3.forceCenter(width / 2, height / 2));
这可以用来解决您的问题之一...如果将“电荷”强度设置为一些较大的负数(例如-150),则节点将被强烈排斥,以使它们不会重叠,也不会执行任何操作他们的链接。如果您根本不希望拖动图形,那么这应该是避免重叠的全部条件。
高度负电荷的副作用是图形会很快建立,并且由于您不想在初次显示后实时模拟力,因此可以调用simulation.stop()
冻结或停止模拟。