コンテンツへスキップ

力のシミュレーション

力のシミュレーションは、粒子(ノード)に作用する物理的な力をシミュレートするための 速度Verlet 数値積分器を実装しています。シミュレーションは、各ステップで一定の単位時間ステップΔt = 1、すべての粒子に対して一定の単位質量m = 1 を想定しています。その結果、粒子に作用する力Fは、時間間隔Δtにわたる一定の加速度aに相当し、粒子の速度に加えるだけでシミュレートできます。次に、この速度が粒子の位置に加えられます。

forceSimulation(nodes)

ソース · 指定された nodes 配列で、 がない新しいシミュレーションを作成します。nodes が指定されていない場合、デフォルトは空の配列になります。

警告

この関数は純粋ではありません。渡されたnodesを変更します。simulation.nodes を参照してください。

js
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数は、⌈logalphaMin)/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速度

位置⟨xy⟩と速度⟨vxvy⟩は、その後、およびシミュレーションによって変更される場合があります。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 は、シミュレーテッドアニーリングの温度にほぼ似ています。シミュレーションが「冷却」するにつれて、時間の経過とともに減少します。alphaalphaMin に達すると、シミュレーションが停止します。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 を返します。(デフォルトでは、新しいシミュレーションには力がありません。)たとえば、グラフをレイアウトするための新しいシミュレーションを作成するには、次のように記述します。

js
const simulation = d3.forceSimulation(nodes)
    .force("charge", d3.forceManyBody())
    .force("link", d3.forceLink(links))
    .force("center", d3.forceCenter());

指定された name の力を削除するには、force として null を渡します。たとえば、電荷力を削除するには、次のように記述します。

js
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 に一致する最初に割り当てられたリスナー(存在する場合)を返します。指定されたイベントがディスパッチされると、各 listenerthis コンテキストがシミュレーションとして呼び出されます。

typenames は、1 つ以上の typename を空白で区切った文字列です。各 typenametype であり、オプションでピリオド (.) と name が続きます。例: tick.foo および tick.bar。名前を使用すると、同じ type に複数のリスナーを登録できます。type は次のいずれかである必要があります。

  • tick - シミュレーションの内部タイマーの各ティック後。
  • end - alpha < alphaMin のときに、シミュレーションのタイマーが停止した後。

simulation.tick が手動で呼び出された場合、tick イベントはディスパッチされないことに注意してください。イベントは内部タイマーによってのみディスパッチされ、シミュレーションのインタラクティブなレンダリングを目的としています。シミュレーションに影響を与えるには、ティックイベントリスナー内でノードの位置や速度を変更するのではなく、を登録します。

詳細については、dispatch.onを参照してください。

カスタム力

とは、ノードの位置または速度を変更する関数です。それは、電荷や重力などの物理的な力をシミュレートしたり、ノードを境界ボックス内に保持したり、リンクされたノードを一定の距離に保つなどの幾何学的制約を解決したりできます。たとえば、ノードを原点に向かって移動させる力は次のとおりです。

js
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 を介してシミュレーションのノードが変更されたときに呼び出されます。力は、各力の適用中に繰り返し処理を行うことを避けるために、初期化中にノードごとのパラメータを評価するなど、必要な作業を実行する場合があります。