gpt4 book ai didi

javascript - 如何在D3力模拟中实现圆盘形状?

转载 作者:塔克拉玛干 更新时间:2023-11-02 22:39:12 25 4
gpt4 key购买 nike

我正在尝试重新创建来自 Bussed out 的令人敬畏的“点流”可视化效果Nadieh Bremer 和 Shirely Wu 着。

original bubbles

我特别感兴趣的是“气泡”的圆形形状以及点到达气泡(黑色箭头)处的类似流体动力学的压缩。

我的做法是通过 .fx.fy(黑点)创建(三个)固定节点,并将所有其他节点链接到相应的固定节点。结果看起来很乱,气泡甚至没有在它们的中心节点周围形成,当我降低力使动画运行得慢一点时。

  const simulation = d3.forceSimulation(nodes)
.force("collide", d3.forceCollide((n, i) => i < 3 ? 0 : 7))
.force("links", d3.forceLink(links).strength(.06))

有什么关于力设置的想法可以产生更美观的结果吗?

我知道我必须随着时间的推移为小组分配设置动画以获得“涓流”效果(否则所有的点都会涌向他们的目的地),但我想从一个漂亮而圆润的开始模拟的稳定状态。

编辑

我确实检查了源代码,它只是重放预先记录的模拟数据,我猜是出于性能原因。

my result

最佳答案

以 Gerardo 的起点为基础,

我认为避免熵过大的关键点之一是指定速度衰减 - 这将有助于避免超出所需位置。太慢,你不会在流动停止的地方增加密度,太快,你的节点要么变得太困惑,要么超过它们的目的地,在太远和太短之间振荡。

许多体力在这里很有用 - 它可以保持节点间隔(而不是碰撞力),节点之间的排斥力被每个集群的定位力抵消。下面我使用了两个中心点和一个节点属性来确定使用哪一个。这些力必须相当弱 - 强大的力很容易导致过度修正。

我没有使用计时器,而是使用 simulation.find() 功能,每个滴答从一个集群中选择一个节点并切换它被吸引到哪个中心。 1000 个滴答后,下面的模拟将停止:

var canvas = d3.select("canvas");
var width = +canvas.attr("width");
var height = +canvas.attr("height");
var context = canvas.node().getContext('2d');

// Key variables:
var nodes = [];
var strength = -0.25; // default repulsion
var centeringStrength = 0.01; // power of centering force for two clusters
var velocityDecay = 0.15; // velocity decay: higher value, less overshooting
var outerRadius = 250; // new nodes within this radius
var innerRadius = 100; // new nodes outside this radius, initial nodes within.
var startCenter = [250,250]; // new nodes/initial nodes center point
var endCenter = [710,250]; // destination center
var n = 200; // number of initial nodes
var cycles = 1000; // number of ticks before stopping.



// Create a random node:
var random = function() {
var angle = Math.random() * Math.PI * 2;
var distance = Math.random() * (outerRadius - innerRadius) + innerRadius;
var x = Math.cos(angle) * distance + startCenter[0];
var y = Math.sin(angle) * distance + startCenter[1];

return {
x: x,
y: y,
strength: strength,
migrated: false
}
}

// Initial nodes:
for(var i = 0; i < n; i++) {
nodes.push(random());
}

var simulation = d3.forceSimulation()
.force("charge", d3.forceManyBody().strength(function(d) { return d.strength; } ))
.force("x1",d3.forceX().x(function(d) { return d.migrated ? endCenter[0] : startCenter[0] }).strength(centeringStrength))
.force("y1",d3.forceY().y(function(d) { return d.migrated ? endCenter[1] : startCenter[1] }).strength(centeringStrength))
.alphaDecay(0)
.velocityDecay(velocityDecay)
.nodes(nodes)
.on("tick", ticked);

var tick = 0;

function ticked() {
tick++;

if(tick > cycles) this.stop();

nodes.push(random()); // create a node
this.nodes(nodes); // update the nodes.

var migrating = this.find((Math.random() - 0.5) * 50 + startCenter[0], (Math.random() - 0.5) * 50 + startCenter[1], 10);
if(migrating) migrating.migrated = true;


context.clearRect(0,0,width,height);

nodes.forEach(function(d) {
context.beginPath();
context.fillStyle = d.migrated ? "steelblue" : "orange";
context.arc(d.x,d.y,3,0,Math.PI*2);
context.fill();
})
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<canvas width="960" height="500"></canvas>

这是一个 block view (片段会更好整页,参数是为它准备的)。初始节点与后来的节点在同一个环中形成(因此一开始会有一些争吵,但这是一个简单的解决方法)。在每个 tick 上,都会创建一个节点,并尝试将靠近中间的节点迁移到另一侧 - 这样就创建了一个流(与任何随机节点相反)。

对于流体,未链接的节点可能是最好的(我一直将其用于风模拟)- 链接的节点非常适合网或布等结构化 Material 。而且,和 Gerardo 一样,我也是 Nadieh 作品的粉丝,但将来也必须关注 Shirley 的作品。

关于javascript - 如何在D3力模拟中实现圆盘形状?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54344073/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com