可以为力导向布局指定自定义力函数吗?
Can one specify a custom force function for a force-directed layout?
我想为力导向图布局尝试另一种族力函数。
对于每个节点 n_i
,我可以定义一个 "force function" f_i
这样
f_i ( n_i )
同样为零;和
f_i ( n_j )
,其中 n_i != n_j
是由于其他节点 n_j
. 对节点 n_i
施加的力
节点 n_i
上的合力应该是力 f_i ( n_j )
的矢量和,其中 n_j
范围超过所有其他节点 1.
有什么方法可以告诉d3.js在布局算法中使用这些自定义力函数吗?
[d3.js 的力导向布局的 documentation 描述了可以调整其内置力功能的各种方式,但我一直无法找到一种方法完全指定一个完全不同的力函数,即无法通过调整内置力函数的参数来实现的力函数。]
1IOW,没有 other/additional 力应该作用在节点 n_i
上,除了从其力函数 f_i
.
为此,您需要创建自己的自定义布局。据我所知,没有这方面的教程,但是现有力布局的源代码应该是一个很好的起点,因为从它的声音来看,您的自定义布局的结构将与它非常相似。
是的,你可以。归功于 Shan Carter and his bl.ocks example
let margin = {
top: 100,
right: 100,
bottom: 100,
left: 100
};
let width = 960,
height = 500,
padding = 1.5, // separation between same-color circles
clusterPadding = 6, // separation between different-color circles
maxRadius = 12;
let n = 200, // total number of nodes
m = 10, // number of distinct clusters
z = d3.scaleOrdinal(d3.schemeCategory20),
clusters = new Array(m);
let svg = d3.select('body')
.append('svg')
.attr('height', height)
.attr('width', width)
.append('g').attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');
let nodes = d3.range(200).map(() => {
let i = Math.floor(Math.random() * m),
radius = Math.sqrt((i + 1) / m * -Math.log(Math.random())) * maxRadius,
d = {
cluster: i,
r: radius
};
if (!clusters[i] || (radius > clusters[i].r)) clusters[i] = d;
return d;
});
let circles = svg.append('g')
.datum(nodes)
.selectAll('.circle')
.data(d => d)
.enter().append('circle')
.attr('r', (d) => d.r)
.attr('fill', (d) => z(d.cluster))
.attr('stroke', 'black')
.attr('stroke-width', 1);
let simulation = d3.forceSimulation(nodes)
.velocityDecay(0.2)
.force("x", d3.forceX().strength(.0005))
.force("y", d3.forceY().strength(.0005))
.force("collide", collide) // <<-------- CUSTOM FORCE
.force("cluster", clustering)//<<------- CUSTOM FORCE
.on("tick", ticked);
function ticked() {
circles
.attr('cx', (d) => d.x)
.attr('cy', (d) => d.y);
}
// Custom 'clustering' force implementation.
function clustering(alpha) {
nodes.forEach(function(d) {
var cluster = clusters[d.cluster];
if (cluster === d) return;
var x = d.x - cluster.x,
y = d.y - cluster.y,
l = Math.sqrt(x * x + y * y),
r = d.r + cluster.r;
if (l !== r) {
l = (l - r) / l * alpha;
d.x -= x *= l;
d.y -= y *= l;
cluster.x += x;
cluster.y += y;
}
});
}
// Custom 'collide' force implementation.
function collide(alpha) {
var quadtree = d3.quadtree()
.x((d) => d.x)
.y((d) => d.y)
.addAll(nodes);
nodes.forEach(function(d) {
var r = d.r + maxRadius + Math.max(padding, clusterPadding),
nx1 = d.x - r,
nx2 = d.x + r,
ny1 = d.y - r,
ny2 = d.y + r;
quadtree.visit(function(quad, x1, y1, x2, y2) {
if (quad.data && (quad.data !== d)) {
var x = d.x - quad.data.x,
y = d.y - quad.data.y,
l = Math.sqrt(x * x + y * y),
r = d.r + quad.data.r + (d.cluster === quad.data.cluster ? padding : clusterPadding);
if (l < r) {
l = (l - r) / l * alpha;
d.x -= x *= l;
d.y -= y *= l;
quad.data.x += x;
quad.data.y += y;
}
}
return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
});
});
}
<!doctype html>
<meta charset="utf-8">
<body>
<script src="//d3js.org/d3.v4.min.js"></script>
这里还有更多in-depth看一下这个主题。
我想为力导向图布局尝试另一种族力函数。
对于每个节点 n_i
,我可以定义一个 "force function" f_i
这样
f_i ( n_i )
同样为零;和f_i ( n_j )
,其中n_i != n_j
是由于其他节点n_j
. 对节点
n_i
施加的力
节点 n_i
上的合力应该是力 f_i ( n_j )
的矢量和,其中 n_j
范围超过所有其他节点 1.
有什么方法可以告诉d3.js在布局算法中使用这些自定义力函数吗?
[d3.js 的力导向布局的 documentation 描述了可以调整其内置力功能的各种方式,但我一直无法找到一种方法完全指定一个完全不同的力函数,即无法通过调整内置力函数的参数来实现的力函数。]
1IOW,没有 other/additional 力应该作用在节点 n_i
上,除了从其力函数 f_i
.
为此,您需要创建自己的自定义布局。据我所知,没有这方面的教程,但是现有力布局的源代码应该是一个很好的起点,因为从它的声音来看,您的自定义布局的结构将与它非常相似。
是的,你可以。归功于 Shan Carter and his bl.ocks example
let margin = {
top: 100,
right: 100,
bottom: 100,
left: 100
};
let width = 960,
height = 500,
padding = 1.5, // separation between same-color circles
clusterPadding = 6, // separation between different-color circles
maxRadius = 12;
let n = 200, // total number of nodes
m = 10, // number of distinct clusters
z = d3.scaleOrdinal(d3.schemeCategory20),
clusters = new Array(m);
let svg = d3.select('body')
.append('svg')
.attr('height', height)
.attr('width', width)
.append('g').attr('transform', 'translate(' + width / 2 + ',' + height / 2 + ')');
let nodes = d3.range(200).map(() => {
let i = Math.floor(Math.random() * m),
radius = Math.sqrt((i + 1) / m * -Math.log(Math.random())) * maxRadius,
d = {
cluster: i,
r: radius
};
if (!clusters[i] || (radius > clusters[i].r)) clusters[i] = d;
return d;
});
let circles = svg.append('g')
.datum(nodes)
.selectAll('.circle')
.data(d => d)
.enter().append('circle')
.attr('r', (d) => d.r)
.attr('fill', (d) => z(d.cluster))
.attr('stroke', 'black')
.attr('stroke-width', 1);
let simulation = d3.forceSimulation(nodes)
.velocityDecay(0.2)
.force("x", d3.forceX().strength(.0005))
.force("y", d3.forceY().strength(.0005))
.force("collide", collide) // <<-------- CUSTOM FORCE
.force("cluster", clustering)//<<------- CUSTOM FORCE
.on("tick", ticked);
function ticked() {
circles
.attr('cx', (d) => d.x)
.attr('cy', (d) => d.y);
}
// Custom 'clustering' force implementation.
function clustering(alpha) {
nodes.forEach(function(d) {
var cluster = clusters[d.cluster];
if (cluster === d) return;
var x = d.x - cluster.x,
y = d.y - cluster.y,
l = Math.sqrt(x * x + y * y),
r = d.r + cluster.r;
if (l !== r) {
l = (l - r) / l * alpha;
d.x -= x *= l;
d.y -= y *= l;
cluster.x += x;
cluster.y += y;
}
});
}
// Custom 'collide' force implementation.
function collide(alpha) {
var quadtree = d3.quadtree()
.x((d) => d.x)
.y((d) => d.y)
.addAll(nodes);
nodes.forEach(function(d) {
var r = d.r + maxRadius + Math.max(padding, clusterPadding),
nx1 = d.x - r,
nx2 = d.x + r,
ny1 = d.y - r,
ny2 = d.y + r;
quadtree.visit(function(quad, x1, y1, x2, y2) {
if (quad.data && (quad.data !== d)) {
var x = d.x - quad.data.x,
y = d.y - quad.data.y,
l = Math.sqrt(x * x + y * y),
r = d.r + quad.data.r + (d.cluster === quad.data.cluster ? padding : clusterPadding);
if (l < r) {
l = (l - r) / l * alpha;
d.x -= x *= l;
d.y -= y *= l;
quad.data.x += x;
quad.data.y += y;
}
}
return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
});
});
}
<!doctype html>
<meta charset="utf-8">
<body>
<script src="//d3js.org/d3.v4.min.js"></script>
这里还有更多in-depth看一下这个主题。