力のシミュレーション
力のシミュレーションは、粒子(ノード)に作用する物理的な力をシミュレートするための 速度Verlet 数値積分器を実装しています。シミュレーションは、各ステップで一定の単位時間ステップΔt = 1、すべての粒子に対して一定の単位質量m = 1 を想定しています。その結果、粒子に作用する力Fは、時間間隔Δtにわたる一定の加速度aに相当し、粒子の速度に加えるだけでシミュレートできます。次に、この速度が粒子の位置に加えられます。
forceSimulation(nodes)
ソース · 指定された nodes 配列で、力 がない新しいシミュレーションを作成します。nodes が指定されていない場合、デフォルトは空の配列になります。
警告
この関数は純粋ではありません。渡されたnodesを変更します。simulation.nodes を参照してください。
const simulation = d3.forceSimulation(nodes);
シミュレーターは自動的に開始します。シミュレーションの実行中にtickイベントをリッスンするには、simulation.on を使用します。代わりにシミュレーションを手動で実行したい場合は、simulation.stop を呼び出し、必要に応じて simulation.tick を呼び出します。
simulation.restart()
ソース · シミュレーションの内部タイマーを再起動し、シミュレーションを返します。simulation.alphaTarget または simulation.alpha と組み合わせて、ノードのドラッグ時など、インタラクション中にシミュレーションを「再加熱」したり、simulation.stop で一時停止した後にシミュレーションを再開したりするために、このメソッドを使用できます。
simulation.stop()
ソース · シミュレーションの内部タイマーが実行中の場合、そのタイマーを停止し、シミュレーションを返します。タイマーが既に停止している場合、このメソッドは何もしません。このメソッドは、シミュレーションを手動で実行する場合に役立ちます。simulation.tick を参照してください。
simulation.tick(iterations)
ソース · 指定されたiterationsの数だけシミュレーションを手動でステップ実行し、シミュレーションを返します。iterationsが指定されていない場合、デフォルトは1(シングルステップ)です。
各イテレーションで、現在のalphaを(alphaTarget - alpha)× alphaDecay だけ増加させます。次に、登録された各力を呼び出し、新しいalphaを渡します。次に、各ノードの速度をvelocity × velocityDecay だけ減速させます。最後に、各ノードの位置をvelocityだけ増やします。
このメソッドは、イベントをディスパッチしません。イベントは、作成時、または simulation.restart の呼び出しによってシミュレーションが自動的に開始されたときに、内部タイマーによってのみディスパッチされます。シミュレーションが開始されたときの自然なtick数は、⌈log(alphaMin)/log(1 - alphaDecay)⌉です。デフォルトでは、これは300です。
このメソッドは、simulation.stop と組み合わせて、静的な力指向レイアウトを計算するために使用できます。大きなグラフの場合、静的なレイアウトは、ユーザーインターフェイスがフリーズするのを避けるために、Webワーカーで計算する必要があります。
simulation.nodes(nodes)
ソース · nodes が指定された場合、シミュレーションのノードを指定されたオブジェクト配列に設定し、必要に応じて位置と速度を初期化し、次にバインドされた力を 再初期化 します。シミュレーションを返します。nodes が指定されていない場合、コンストラクターに指定されたシミュレーションのノード配列を返します。
警告
この関数は純粋ではありません。渡されたnodesを変更して、インデックス node.index、位置 node.x と node.y、および速度 node.vx と node.vy を割り当てます。位置と速度は、simulation.tick が実行されるにつれてさらに更新されます。
各nodeはオブジェクトである必要があります。次のプロパティは、シミュレーションによって割り当てられます。
index
- nodesへのノードのゼロベースのインデックスx
- ノードの現在のx位置y
- ノードの現在のy位置vx
- ノードの現在のx速度vy
- ノードの現在のy速度
位置⟨x、y⟩と速度⟨vx、vy⟩は、その後、力およびシミュレーションによって変更される場合があります。vxまたはvyのいずれかがNaNの場合、速度は⟨0,0⟩に初期化されます。xまたはyのいずれかがNaNの場合、位置はフィロタクシー配置で初期化されます。これは、決定論的で均一な分布を保証するように選択されています。
特定の場所にノードを固定するには、次の2つの追加プロパティを指定できます。
fx
- ノードの固定されたx位置fy
- ノードの固定されたy位置
各tickの終了時に、任意の力を適用した後、定義されたnode.fxを持つノードは、node.xがこの値にリセットされ、node.vxがゼロに設定されます。同様に、定義されたnode.fyを持つノードは、node.yがこの値にリセットされ、node.vyがゼロに設定されます。以前に固定されていたノードの固定を解除するには、node.fxとnode.fyをnullに設定するか、これらのプロパティを削除します。
ノードの追加またはシミュレーションからの削除など、指定されたnodesの配列が変更された場合、変更をシミュレーションおよびバインドされた力に通知するために、このメソッドを新しい(または変更された)配列で再度呼び出す必要があります。シミュレーションは、指定された配列の防御的なコピーを作成しません。
simulation.alpha(alpha)
ソース · alpha は、シミュレーテッドアニーリングの温度にほぼ似ています。シミュレーションが「冷却」するにつれて、時間の経過とともに減少します。alpha が alphaMin に達すると、シミュレーションが停止します。simulation.restart を参照してください。
alpha が指定された場合、現在のalphaを指定された範囲[0,1]の数値に設定し、このシミュレーションを返します。alpha が指定されていない場合、現在のalpha値を返します。デフォルトは1です。
simulation.alphaMin(min)
ソース · min が指定された場合、最小alphaを指定された範囲[0,1]の数値に設定し、このシミュレーションを返します。min が指定されていない場合、現在の最小alpha値を返します。デフォルトは0.001です。現在の alpha が最小alphaよりも小さい場合、シミュレーションの内部タイマーは停止します。デフォルトのアルファ減衰率の約0.0228は、300回のイテレーションに対応します。
simulation.alphaDecay(decay)
ソース · decay が指定された場合、alpha の減衰率を指定された [0,1] の範囲の数値に設定し、このシミュレーションを返します。decay が指定されていない場合は、現在の alpha の減衰率(デフォルトは 0.0228… = 1 - pow(0.001, 1 / 300) で、0.001 はデフォルトの 最小 alpha)を返します。
alpha 減衰率は、現在の alpha が目的の ターゲット alpha にどれだけ早く補間されるかを決定します。デフォルトのターゲット alpha はゼロなので、デフォルトではこれがシミュレーションがどれだけ早く冷却するかを制御します。減衰率が高いほど、シミュレーションはより速く安定しますが、局所的な最小値に陥るリスクがあります。低い値ほど、シミュレーションの実行に時間がかかりますが、通常はより良いレイアウトに収束します。シミュレーションを現在の alpha で永遠に実行するには、decay レートをゼロに設定します。または、最小 alpha より大きい ターゲット alpha を設定します。
simulation.alphaTarget(target)
ソース · target が指定された場合、現在のターゲット alpha を指定された [0,1] の範囲の数値に設定し、このシミュレーションを返します。target が指定されていない場合は、現在のターゲット alpha 値(デフォルトは 0)を返します。
simulation.velocityDecay(decay)
ソース · decay が指定された場合、速度減衰係数を指定された [0,1] の範囲の数値に設定し、このシミュレーションを返します。decay が指定されていない場合は、現在の速度減衰係数(デフォルトは 0.4)を返します。減衰係数は大気摩擦に似ています。tick 中に力が加わった後、各ノードの速度は 1 - decay が掛けられます。alpha 減衰率を下げるのと同様に、速度減衰を少なくすると、より良い解に収束する可能性がありますが、数値的な不安定性や振動のリスクがあります。
simulation.force(name, force)
ソース · force が指定された場合、指定された name の 力 を割り当て、このシミュレーションを返します。force が指定されていない場合は、指定された名前の力を返すか、そのような力がない場合は undefined を返します。(デフォルトでは、新しいシミュレーションには力がありません。)たとえば、グラフをレイアウトするための新しいシミュレーションを作成するには、次のように記述します。
const simulation = d3.forceSimulation(nodes)
.force("charge", d3.forceManyBody())
.force("link", d3.forceLink(links))
.force("center", d3.forceCenter());
指定された name の力を削除するには、force として null を渡します。たとえば、電荷力を削除するには、次のように記述します。
simulation.force("charge", null);
simulation.find(x, y, radius)
ソース · 指定された検索 radius で、位置 ⟨x,y⟩ に最も近いノードを返します。radius が指定されていない場合は、無限大がデフォルト値になります。検索領域内にノードがない場合は、undefined を返します。
simulation.randomSource(source)
ソース)
source が指定された場合、乱数生成に使用される関数を設定します。これは 0 (含む) から 1 (含まない) の間の数値を返す関数である必要があります。source が指定されていない場合は、このシミュレーションの現在の乱数ソース(デフォルトは固定シードの 線形合同法乱数発生器)を返します。 random.source も参照してください。
simulation.on(typenames, listener)
ソース · listener が指定された場合、指定された typenames のイベント listener を設定し、このシミュレーションを返します。同じ型と名前のイベントリスナーが既に登録されている場合、新しいリスナーが追加される前に既存のリスナーが削除されます。listener が null の場合、指定された typenames の現在のイベントリスナー(存在する場合)を削除します。listener が指定されていない場合は、指定された typenames に一致する最初に割り当てられたリスナー(存在する場合)を返します。指定されたイベントがディスパッチされると、各 listener は this
コンテキストがシミュレーションとして呼び出されます。
typenames は、1 つ以上の typename を空白で区切った文字列です。各 typename は type であり、オプションでピリオド (.
) と name が続きます。例: tick.foo
および tick.bar
。名前を使用すると、同じ type に複数のリスナーを登録できます。type は次のいずれかである必要があります。
tick
- シミュレーションの内部タイマーの各ティック後。end
- alpha < alphaMin のときに、シミュレーションのタイマーが停止した後。
simulation.tick が手動で呼び出された場合、tick イベントはディスパッチされないことに注意してください。イベントは内部タイマーによってのみディスパッチされ、シミュレーションのインタラクティブなレンダリングを目的としています。シミュレーションに影響を与えるには、ティックイベントリスナー内でノードの位置や速度を変更するのではなく、力を登録します。
詳細については、dispatch.onを参照してください。
カスタム力
力とは、ノードの位置または速度を変更する関数です。それは、電荷や重力などの物理的な力をシミュレートしたり、ノードを境界ボックス内に保持したり、リンクされたノードを一定の距離に保つなどの幾何学的制約を解決したりできます。たとえば、ノードを原点に向かって移動させる力は次のとおりです。
function force(alpha) {
for (let i = 0, n = nodes.length, node, k = alpha * 0.1; i < n; ++i) {
node = nodes[i];
node.vx -= node.x * k;
node.vy -= node.y * k;
}
}
通常、力はノードの現在の位置 ⟨x,y⟩ を読み取り、ノードの速度 ⟨vx,vy⟩ を変化させます。力は、ノードの予測される次の位置である ⟨x + vx,y + vy⟩ を「先読み」することもできます。これは、反復緩和を通じて幾何学的制約を解決するために必要です。力は位置を直接変更することもできます。これは、シミュレーションにエネルギーを加えるのを避けるために役立つ場合があります。たとえば、ビューポートでシミュレーションを再センタリングする場合などです。
force(alpha)
この力を適用します。オプションで指定された alpha を観測します。通常、力は以前に force.initialize に渡されたノードの配列に適用されますが、一部の力はノードのサブセットに適用されたり、異なる動作をする場合があります。たとえば、forceLink は各リンクのソースとターゲットに適用されます。
force.initialize(nodes)
この力に nodes の配列と random ソースを提供します。このメソッドは、simulation.force を介して力がシミュレーションにバインドされたとき、および simulation.nodes を介してシミュレーションのノードが変更されたときに呼び出されます。力は、各力の適用中に繰り返し処理を行うことを避けるために、初期化中にノードごとのパラメータを評価するなど、必要な作業を実行する場合があります。