dash14 / v-network-graph

An interactive network graph visualization component for Vue 3
https://dash14.github.io/v-network-graph/
MIT License
504 stars 44 forks source link

Force layout initial positions #153

Closed piliadis closed 3 months ago

piliadis commented 3 months ago

Hi there @dash14 ! I have a similar question with #88.

I'm using a force layout and have saved a graph layout in the backend. When the user returns to the graph, I want to display it exactly as they left it, so they can continue their work from where they left off. However, when I try to do this, the simulation creates a new layout.

I assume this happens because I'm adding each node and layout successively.

So, my basic question is: how can I initialize a graph layout with a stored "steady-state" layout, without running the simulation again?

Thank you in advance!

dash14 commented 3 months ago

Hi @piliadis, My apologies for missed your comment on #88. It can be initialized as in the following example to stay in the position specified by the layouts variable.

App.vue

<script setup lang="ts">
import { reactive } from "vue";
import * as vNG from "v-network-graph";
import {
  ForceLayout,
  ForceNodeDatum,
  ForceEdgeDatum,
} from "v-network-graph/lib/force-layout";
import data from "./data.ts";

const configs = reactive(
  vNG.defineConfigs({
    view: {
      layoutHandler: new ForceLayout({
        createSimulation: (d3, nodes, edges) => {
          // d3-force parameters
          const forceLink = d3
            .forceLink<ForceNodeDatum, ForceEdgeDatum>(edges)
            .id((d: ForceNodeDatum) => d.id);
          return d3
            .forceSimulation(nodes)
            .force("edge", forceLink.distance(40).strength(0.5))
            .force("charge", d3.forceManyBody().strength(-800))
            .force("center", d3.forceCenter().strength(0.05))
            .stop();  // Do not start force simulation
        },
      }),
    },
    node: {
      label: {
        visible: false,
      },
    },
  })
);
</script>

<template>
  <v-network-graph
    :nodes="data.nodes"
    :edges="data.edges"
    :layouts="data.layouts"
    :configs="configs"
  />
</template>

data.ts

import { Nodes, Edges, Layouts } from "v-network-graph";

const nodes: Nodes = {
  node0: {},
  node1: {},
  node2: {},
  node3: {},
  node4: {},
  node5: {},
  node6: {},
  node7: {},
  node8: {},
  node9: {},
  node10: {},
  node11: {},
  node12: {},
  node13: {},
  node14: {},
  node15: {},
  node16: {},
  node17: {},
  node18: {},
  node19: {},
};

const edges: Edges = {
  "edge0-5": { source: "node0", target: "node5" },
  "edge1-0": { source: "node1", target: "node0" },
  "edge2-0": { source: "node2", target: "node0" },
  "edge3-0": { source: "node3", target: "node0" },
  "edge4-0": { source: "node4", target: "node0" },
  "edge5-10": { source: "node5", target: "node10" },
  "edge6-5": { source: "node6", target: "node5" },
  "edge7-5": { source: "node7", target: "node5" },
  "edge8-5": { source: "node8", target: "node5" },
  "edge9-5": { source: "node9", target: "node5" },
  "edge10-15": { source: "node10", target: "node15" },
  "edge11-10": { source: "node11", target: "node10" },
  "edge12-10": { source: "node12", target: "node10" },
  "edge13-10": { source: "node13", target: "node10" },
  "edge14-10": { source: "node14", target: "node10" },
  "edge15-0": { source: "node15", target: "node0" },
  "edge16-15": { source: "node16", target: "node15" },
  "edge17-15": { source: "node17", target: "node15" },
  "edge18-15": { source: "node18", target: "node15" },
  "edge19-15": { source: "node19", target: "node15" },
};

const layouts: Layouts = {
  nodes: {
    node0: { x: -60.87750719624168, y: 139.36447883238867 },
    node1: { x: -78.9426727174486, y: 239.2033412827509 },
    node2: { x: -153.12996649281942, y: 78.60053356787151 },
    node3: { x: 22.641554764360148, y: 197.09573065300916 },
    node4: { x: -158.31588906058238, y: 178.4163125743503 },
    node5: { x: 94.58029430216386, y: 16.808732307873587 },
    node6: { x: 196.47949561051934, y: 60.21127404714264 },
    node7: { x: 25.53512963091519, y: 89.1054779895258 },
    node8: { x: 183.5443251879376, y: -38.8453427169957 },
    node9: { x: 119.29390296975366, y: 123.65730584906274 },
    node10: { x: 61.64968268984751, y: -117.82198930125729 },
    node11: { x: 166.9741444044181, y: -137.3731923956643 },
    node12: { x: 7.972911503114123, y: -214.97506298854498 },
    node13: { x: 107.84469552510342, y: -217.90335394353573 },
    node14: { x: -38.07585403810045, y: -126.32276874638217 },
    node15: { x: -103.69961755906638, y: -50.94464844279595 },
    node16: { x: 4.4374278034083865, y: -35.86054352339347 },
    node17: { x: -201.7287046661169, y: -73.16896435063305 },
    node18: { x: -135.63760418552198, y: -148.21039965871745 },
    node19: { x: -61.19402541527051, y: 39.47149029400862 },
  },
};

export default {
  nodes,
  edges,
  layouts,
};

CodeSandbox: https://codesandbox.io/p/devbox/nostalgic-dijkstra-4mlzhf?file=%2Fsrc%2FApp.vue%3A19%2C30

I hope this will be helpful.

piliadis commented 3 months ago

Thank you for the help @dash14 ! The .stop() function was what i was looking for! P.S. Amazing framework—usable even by hobbyists!