Create a vertical graph with multiple nodes and links in d3 forcelayout

301 views Asked by At

I want to show a graph that can have multiple nodes and links as a vertical graph, top to bottom.

I'm getting my data from the back end as 2 separate arrays of nodes and links and each link has a sourceId and targetId so I assumed forcelayout with the built-in functinoality for nodes and links would be good.

I have started with forcelayout but I'm struggling with finding a way to position the nodes and the relevant links top to bottom where I can have also a node that points to multiple nodes.

So for example if I have a node that points to 3 other nodes, the 3 other nodes would appear under their parent node and would be speeded out in the same graph level.

Here's a picture I found for illustration: enter image description here

Here's a simple example with only 2 nodes and 1 link:

(The link is not vertical but the main issue )

I chose forcelayout because it has nodes and links functionality.

const data = {
    "nodes": [
        {
            "id": 1,
            "name": "node1",
        },
        {
            "id": 2,
            "name": "node2",
        }
    ],
    "links": [
        {
            "source": 1,
            "target": 2,
            "weight": 3,
        }
    ]
};

const charge = -100;
const width = 600;
const height = 600;

let _canvas = d3.select("#Target");

const chargeForce = d3.forceManyBody()
    .strength(charge);

const forceLink = d3.forceLink(data.links)
    .id((d) => d.id)
    .distance(50);

const simulation = d3.forceSimulation(data.nodes)
    .force("charge", chargeForce)
    .force("link", forceLink);

_createStartArrowDefinition();
_createEndArrowDefinition();
_createNodeFilterDefinition();
_createResourceIconFilterDefinition();

const node = _canvas
    .selectAll("rect")
    .data(data.nodes)
    .enter()
    .append("rect")
    .attr("class", "node")
    .attr("fill", "red")
    .attr("filter", "url(#nodeShadow)")
    .attr("rx", "17")
    .attr("width", "335")
    .attr("height", 30);

const link = _canvas
    .selectAll("path.link")
    .data(data.links)
    .enter()
    .append("path")
    .attr("stroke", "#3278E0")
    .attr("fill", "none")
    .attr("marker-end", "url(#arrowEnd)")
    .attr("marker-start", "url(#arrowStart)");

const lineGenerator = d3.line();

simulation.on("tick", () => {

    node
        .attr("x", (d) => d.x)
        .attr("y", (d) => d.y);

    link.attr("d", (d) => {

        return lineGenerator([
            [d.source.x + 150, d.source.y + 30],
            [d.target.x + 150, d.target.y + 20]
        ]);

    });

});

function _createStartArrowDefinition() {
    _canvas.select("defs")
        .append("svg:marker")
        .attr("id", "arrowStart")
        .attr("refX", 6)
        .attr("refY", 9)
        .attr("markerWidth", 22)
        .attr("markerHeight", 22)
        .attr("orient", "auto")
        .append("circle")
        .attr("cx", "4")
        .attr("cy", "9")
        .attr("r", "3")
        .attr("orient", "auto")
        .style("stroke", "#3278E0")
        .style("fill", "none");
}

function _createEndArrowDefinition() {
    _canvas.append("defs")
        .append("svg:marker")
        .attr("id", "arrowEnd")
        .attr("refX", 6)
        .attr("refY", 6)
        .attr("markerWidth", 12)
        .attr("markerHeight", 12)
        .attr("orient", "auto")
        .append("path")
        .attr("d", "M-5,-3 L7,6 L-8,20")
        .style("stroke", "#3278E0")
        .style("fill", "none");
}

function _createNodeFilterDefinition() {
    _canvas.select("defs")
        .append("filter")
        .attr("width", "115%")
        .attr("height", "115%")
        .attr("id", "nodeShadow")
        .append("feDropShadow")
        .attr("dx", 0)
        .attr("dy", 1)
        .attr("stdDeviation", 1)
        .attr("flood-opacity", 1)
        .attr("flood-color", "lightgray");;
}

function _createResourceIconFilterDefinition() {
    _canvas.select("defs")
        .append("filter")
        .attr("id", "circleShadow")
        .attr("width", "120%")
        .attr("height", "120%")
        .append("feDropShadow")
        .attr("dx", -1)
        .attr("dy", 1)
        .attr("stdDeviation", 3)
        .attr("flood-opacity", 1)
        .attr("flood-color", "lightgray");
}
.links line {
    stroke-opacity: 0.6;
    stroke-width: 1px;
    fill: none;
}

.node {
    fill: #FFFFFF;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div>
    <svg id="Target">
    </svg>
</div>

0

There are 0 answers