dash14 / v-network-graph

An interactive network graph visualization component for Vue 3

Home Page:https://dash14.github.io/v-network-graph/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Question regards to arrow on edges (Bidirectional)

runemy opened this issue · comments

Hi,

https://dash14.github.io/v-network-graph/examples/appearance.html#arrow-on-edges

With this data:

const nodes: Nodes = {
node4: { name: "N4" },
node5: { name: "N5" }
}

const edges: Edges = {
edge4: { source: "node4", target: "node5" },
edge5: { source: "node5", target: "node4" }
}

I'm trying to figure out how I can create one (one not two) line/link between these two nodes with arrow in both edges.
I cannot figure out how to achive this. Have I missed something or is this not possible?

Some links are direct and some links are bidirect. Showing bidirect links with two lines (each with an arrow on the edge) gives not a good visual view.

Hi @runemy,
Do I understand correctly that there are two links (edges) between two nodes, but you want them to look like a single bidirectional link?
Unfortunately, in this library, the data structure needs to match the appearance; if you want it to look like a single link, you need to pass the data as a single edge as well.
As shown in the example below, I suggest merging multiple links into a single bi-directional link before passing it to v-network-graph.

// App.vue
<script setup lang="ts">
import { computed } from "vue";
import * as vNG from "v-network-graph";
import data from "./data";

interface MyEdge extends vNG.Edge {
  arrow: { source: boolean; target: boolean };
}

const edges = computed(() => mergeEdges(data.edges));

function mergeEdges(edges: vNG.Edges): Record<string, MyEdge> {
  const newEdges: Record<string, MyEdge> = {};
  Object.values(edges).forEach((edge) => {
    const pair = [edge.source, edge.target].sort();
    const newEdgeId = pair.join(":");
    if (!(newEdgeId in newEdges)) {
      newEdges[newEdgeId] = {
        ...edge,
        arrow: { source: false, target: false },
      };
    }
    const targetSide = edge.target === pair[1] ? "target" : "source";
    newEdges[newEdgeId].arrow[targetSide] = true;
  });
  return newEdges;
}

const configs = vNG.defineConfigs({
  edge: {
    selectable: true,
    normal: {
      width: 2,
    },
    type: "straight",
    marker: {
      source: {
        type: ([edge]) => (edge.arrow.source ? "arrow" : "none"),
      },
      target: {
        type: ([edge]) => (edge.arrow.target ? "arrow" : "none"),
      },
    },
  },
});
</script>

<template>
  <v-network-graph
    :nodes="data.nodes"
    :edges="edges"
    :layouts="data.layouts"
    :configs="configs"
  />
</template>
// data.ts
import { Nodes, Edges, Layouts } from "v-network-graph"

const nodes: Nodes = {
  node4: { name: "N4" },
  node5: { name: "N5" }
}

const edges: Edges = {
  edge4: { source: "node4", target: "node5" },
  edge5: { source: "node5", target: "node4" }
}

const layouts: Layouts = {
  nodes: {
    node4: { x: 240, y: 80 },
    node5: { x: 320, y: 0 },
  },
}

export default {
  nodes,
  edges,
  layouts,
}

Sorry if this is not the answer you are expecting....

Thank you :) This is almost what I was expecting. I think I will be able to solve my challenge with this code.

This is adjusted code:

interface MyEdge extends vNG.Edge {
  arrow: { bidirect: boolean; };
}

const edges = computed(() => mergeEdges(data.edges));

function mergeEdges(edges: vNG.Edges): Record<string, MyEdge> {
  const tmpEdges: Set<string> = new Set();
  const newEdges: Record<string, MyEdge> = {};
  
  for (const edge of Object.values(edges)) {
    const srctrg = `${edge.source}:${edge.target}`;
    
    if (!tmpEdges.has(srctrg)) {
      tmpEdges.add(srctrg);
      const pair = [edge.source, edge.target].sort();
      const newEdgeId = pair.join(":");
      
      if (!newEdges.hasOwnProperty(newEdgeId)) newEdges[newEdgeId] = { ...edge, arrow: { bidirect: false } };
      else newEdges[newEdgeId].arrow.bidirect = true;
    }
  }
  
  return newEdges;
}

const configs = vNG.defineConfigs({
  edge: {
    selectable: true,
    normal: {
      width: 2,
    },
    type: "straight",
    marker: {
      source: {
        type: ([edge]) => (edge.arrow.bidirect ? "arrow" : "none"),
      },
      target: {
        type: "arrow",
      },
    },
  },
});