Place the simulation nodes in designated locations

Is there a proper way to position nodes at specific coordinates using simulation force in D3.js?

I am trying to place nodes at specific positions with forceX/Y simulation in D3.js. My understanding is that setting the strength to 0 should result in no force being applied to the X and Y coordinates, allowing the nodes to be placed at the defined positions. What would be the correct approach to achieve this?

drag_nodes()

function add_grids(g,width,height) {
    var m = 10
    var n = 10

    var xScale = d3.scaleLinear()
        .domain([0,m])
        .range([0, width])

    var yScale = d3.scaleLinear()
        .domain([0,n])
        .range([0,height])

    var xaxis = g.append('g')
        .attr('class','xaxis')
        .call(d3.axisTop(xScale)
              .tickFormat((d,i) => null) //i==0 ?  null:d)
              .tickSizeOuter(0)
              .tickSize(0)
              //.tickValues([])
             )
        .call(g => g.select(".domain").remove())

    var yaxis = g.append('g')
        .attr('class','yaxis')
        .call(d3.axisLeft(yScale)
              .tickFormat((d,i) => null)//i==0 ?  null:d)
              .tickSizeOuter(0)
              .tickSize(0)
              //.tickValues([])
             )
        .call(g => g.select(".domain").remove())

    g.selectAll("g.yaxis g.tick")
    .append("line")
    .attr("class", "gridline")
    .attr("x1", 0)
    .attr("y1", 0)
    .attr("x2", width)
    .attr("y2", 0)
    .attr("stroke", "#9ca5aecf") // line color
    //.attr("stroke-dasharray","4") // make it dashed;;

    g.selectAll("g.xaxis g.tick")
    .append("line")
    .attr("class", "gridline")
    .attr("x1", 0)
    .attr("y1", height)
    .attr("x2", 0)
    .attr("y2", 0)
    .attr("stroke", "#9ca5aecf") // line color
}

function drag_nodes() {
    var data = {
        nodes: [{
            name: "A",
            x: 100,
            y: 100
        }, {
            name: "B",
            x: 100,
            y: 400
        }, {
            name: "C",
            x: 400,
            y: 400
        }, {
            name: "D",
            x: 400,
            y: 100
        }],
        links: [{
            source: 'A',
            target: 'B'
        }, {
            source: 'B',
            target: 'C'
        }, {
            source: 'C',
            target: 'D'
        }, ]
    };

    var width = 500
    var height = 500

    var colors = d3.scaleOrdinal(d3.schemeCategory10);
    var svg = d3.select("body")
        .append("svg")
        .attr("width", width)
        .attr("height", height)
        .style('border','2px solid red')

    add_grids(svg,width,height)

    var drag = d3.drag()
        .on("start", dragstarted)
        .on("drag", dragged)
        .on("end", dragended);

    function dragstarted(event,d) {
        if (!event.active) simulation.alphaTarget(.1).restart();
        d.fx = d.x;
        d.fy = d.y;
    }
    function dragged(event,d) {
        d.fx = event.x;
        d.fy = event.y;
    }
    function dragended(event,d) {
        if (!event.active) simulation.alphaTarget(.1);
        d.fx = null;
        d.fy = null;
    }

    var link = svg.selectAll(".link")
        .data(data.links)
        .enter()
        .append("line")
        .attr("class", "link")
        .attr("fill", "none")
        .attr("stroke", "black");

    var node = svg.selectAll(".node")
        .data(data.nodes)
        .enter()
        .append("circle")
        .attr("class", "node")
        .attr("cx", function(d) {
            return d.x
        })
        .attr("cy", function(d) {
            return d.y
        })
        .attr("r", 15)
        .attr("fill", function(d, i) {
            return colors(i);
        })
        .call(drag);

    var simulation = d3.forceSimulation()
        .force("link", d3.forceLink().id(function(d) { return d.name; }))
        .force('x', d3.forceX(d => d.x).strength(0))
        .force('y', d3.forceY(d => d.y).strength(0))//.strength(1))
        .alpha(1).restart();

    simulation.nodes(data.nodes)
        .on("tick", ticked);

    simulation.force("link")
        .links(data.links);

    function ticked() {
        link.attr("x1", function(d) { return d.source.x; })
            .attr("y1", function(d) { return d.source.y; })
            .attr("x2", function(d) { return d.target.x; })
            .attr("y2", function(d) { return d.target.y; });

        node.attr("cx", function(d) { return d.x; })
            .attr("cy", function(d) { return d.y; })
        }
}
<script src="https://d3js.org/d3.v7.min.js"></script>

Answer №1

Your response was...

I initially thought that assigning a strength of 0 to X and Y would mean no force acting on them, allowing the nodes to be placed as defined. However, in reality, the link force actually pulls the nodes together, requiring the use of forceX and forceY with a high strength value.

Removing the links reveals this behavior:

drag_nodes()

function add_grids(g,width,height) {
    // Function code for adding grids here
}

function drag_nodes() {
    // Function code for dragging nodes here
}
<script src="https://d3js.org/d3.v7.min.js"></script>

In such cases, a simulation is unnecessary.

To counteract the pulling force of the links, a higher strength must be set for the positioning forces:

drag_nodes()

function add_grids(g, width, height) {
    // Updated function code for grid creation
}

function drag_nodes() {
    // Updated function code for node dragging with link force
}
<script src="https://d3js.org/d3.v7.min.js"></script>

Alternatively, in specific cases like this, the links' pushing force can be prevented by setting a precise link distance of 300:

drag_nodes()

function add_grids(g, width, height) {
    // Updated function code for grid creation
}

function drag_nodes() {
    // Updated function code for node dragging with link distance of 300
}
<script src="https://d3js.org/d3.v7.min.js"></script>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The "truth" parameter within the xmlHttpRequest .open() function

After referencing MDN, it mentioned that by default, the JavaScript function will keep running until the server response is received. This is essentially what AJAX is all about - Asynchronous JavaScript and XML. Even though I have some experience with A ...

The text does not change in the input field even with the .value method

I'm encountering an issue where I want to use .value to set the text of an input field. However, after setting the value of the input field, the text disappears when clicked on. I would like to find a solution where the text does not disappear upon cl ...

Ways to simulate a variable imported in the module being tested without it being a function parameter can be achieved by using describe.each and changing the mock value for each test

I have a requirement to test a function within my TypeScript module. module-to-test.ts import { config } from './app-config'; export const isSomethingWhatINeedSelector = createSelector( firstDependencySelector, secondDependencySelector ...

When making an AJAX request to an ASP.NET web method, strange characters are appended to the end of the response text. This issue seems

I need assistance with the following code: $.ajax({ type: 'POST', contentType: 'application/json; charset=utf-8', url: location, data: JSON.stringify(ajaxData), dataType: 'xml', success: ca ...

Upon refreshing the page, nested React routes may fail to display

When I navigate to the main routes and their nested routes, everything works fine. However, I encounter an issue specifically with the nested routes like /register. When I try to refresh the page, it comes up blank with no errors in the console. The main r ...

Activate the class using Vue with the v-for directive

I'm curious about the v-for functionality. Why is it necessary to use this.activeClass = {...this.activeClass} to update the component? I noticed that the component does not update after this line of code. if (this.activeClass[index]) { ...

Issue with Quantity Box not displaying values in Shopify store

I am currently seeking assistance with resolving an issue I have encountered on a Shopify theme that I recently customized. The problem lies within the quantity box on the live site where despite being able to adjust the quantity using the buttons, the act ...

What is the best method for streaming files from Java to the browser while preventing the user from downloading and saving the file?

I'm new to this and feeling a bit lost. Here's the situation: I have a web app (built with JavaScript/Reactjs) and a backend (Java using Liferay API) The server contains files (File A, B, C, etc.) of various types: pdf, excel, audio, txt, etc. ...

Exploring the capabilities of Typescript arrays by implementing a forEach loop in conjunction with the

I possess an array: set Array ( [0] => Array ( [name0] => J [name1] => L [name2] => C ) [1] => Array ( [data0] => 3,1,3 [data1] => 5,3 ...

Adding a new row to a Bootstrap table while maintaining the consistent style

Is there a way to dynamically add a new table row with different styling using jQuery? I'm facing this particular issue and need help in solving it. Below, I have included some screenshots of my code and the view for better understanding. Here is the ...

Preventing pageup/pagedown in Vuetify slider: Tips and tricks

I am currently using Vuetify 2.6 and have integrated a v-slider into my project. Whenever the user interacts with this slider, it gains focus. However, I have assigned PageUp and PageDown keys to other functions on the page and would like them to continue ...

Guide on creating a map function with hyphenated fields in mongoDB

While working on a project with Meteor and mongoDB, I encountered an issue. The problem arises when trying to retrieve the value of a field with a hyphenated name using map. How can I work around this obstacle? The specific field in my mongoDB collection ...

Several buttons sharing the same class name, however, only the first one is functional

As a newcomer to JavaScript, I am currently working on the front-end of a basic ecommerce page for a personal project. My query pertains to the uniformity of all items being displayed on the page, except for their names. When I click on the "buy" button f ...

Book Roulette: Your Next Random Read

I created a code for a random quote generator and now I want to create something similar, but with images this time. I am looking to add a feature on my page where it can recommend a book by displaying its cover image when a button is clicked. For the pre ...

Manipulate audio files by utilizing the web audio API and wavesurfer.js to cut and paste audio

I am currently working on developing a web-based editor that allows users to customize basic settings for their audio files. To achieve this, I have integrated wavesurfer.js as a plugin due to its efficient and cross-browser waveform solution. After prior ...

Iterating through elements within the ng-content directive in Angular using *ngFor

Is it possible to iterate through specific elements in ng-content and assign a different CSS class to each element? Currently, I am passing a parameter to enumerate child elements, but I would like to achieve this without using numbers. Here is an example ...

Efficiently and consistently refreshing the DOM with information while maintaining page integrity

I'm currently developing a Single Page Application (SPA) that utilizes AJAX to dynamically load content into a specific div. The layout consists of a side accordion menu, where clicking on an item loads relevant information in a neighboring div. Howev ...

Error: ws variable is not defined

I've integrated puppeteer into my React project using Webpack and Babel. I've included this simple code in my component, but unfortunately, I encountered an error. async goToPage() { const browser = await puppeteer.launch(); const page ...

The combination of loading and scrolling JavaScript code is not functioning properly on the website

I created an HTML webpage that includes some JavaScript code to enhance the user experience. However, I encountered an issue when trying to incorporate a load JavaScript function alongside the scroll JavaScript function on my page. The load script is posi ...

Do NodeJS and Express require sanitizing the request body for post requests to prevent cross-site scripting (XSS) attacks?

There is a website I have where users can input data into fields and submit it. Once submitted, the information is sent to my server through a POST request and saved in the database. However, this user-generated content is also returned to the front-end f ...