Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Control on Link Length, Collision detection and Node spacing #2

Closed
Masrico opened this issue Apr 5, 2018 · 8 comments
Closed

Control on Link Length, Collision detection and Node spacing #2

Masrico opened this issue Apr 5, 2018 · 8 comments
Assignees

Comments

@Masrico
Copy link

Masrico commented Apr 5, 2018

@vasturiano Thanks for all the good work.

I am working on this graph with higher number of nodes. But when i have built it the nodes size is reduced a lot, i have custom sized it in nodeCanvasObject(). There are multiple groups and distance between this groups is increased a lot, which the groups come closer, can you please suggest how to get groups closer?

And the links length is also more, wish to control this, any suggestions ?
And i have fixed div, width and height and want my force graph not to cross those limits. I have checked collision detection example but that is not serving my purpose. Can you please suggest?

Note: have given all group names as "Sample Group" but all of them have different ids
image

@vasturiano vasturiano self-assigned this Apr 5, 2018
@Masrico
Copy link
Author

Masrico commented Apr 6, 2018

Sorry @vasturiano for pushing this. Did you get a chance to check this ?

@vasturiano
Copy link
Owner

vasturiano commented Apr 6, 2018

Hi @Masrico, if the intention is to better adjust the proportions of the link lengths to the node sizes, I would suggest using .nodeRelSize(<num>) (or in your nodeCanvasObject function if you're using that) to get the right balance, and then set the 'zoom' level to what is the most appropriate. You can set this by calling (after initializing the graph).zoom(<num>).

This should yield a good visual balance.

If, on the other hand, you actually need to modify the internal behavior of the forces that control the graph, you can do that using .d3Force(). For example, to change the target distance of links:

graph.d3Force('link').distance(link => <num>)

Or even add an additional force that restricts the nodes to a particular bounding box, like:

graph.d3Force('contain', function() { <your code> })

@Masrico
Copy link
Author

Masrico commented Apr 8, 2018

Thanks for the suggestions @vasturiano and very quick responses.

I have tried using nodeRelSize by increasing and decreasing the number but it is not effecting much on the nodes plot. Dont know how to access links in nodeCanvasObject, yes am using this for text representation, an example would be great.
graph.d3Force('link').distance(link => ) have changed the number (increased and decreased), when i do this am loosing all my changed done in nodeCanvasObject and it plots the default graph.

I tried using graph.d3Force('contain', function(box) { }); where the box gives me 0.7 and not really sure how to manipulate this.

My graph config is as below.

Graph.graphData(dataWithNodesAndEdges)
.nodeId('name')
.nodeVal('value')
//.linkDirectionalParticles(1)
//.nodeRelSize(2)
.d3Force('contain', function(box) {

            })
        .nodeLabel(node => unescape(node.name))
        .nodeAutoColorBy('group')
        .linkAutoColorBy('group')
        .d3AlphaDecay(0.3)
        .d3VelocityDecay(0.01)
        .linkSource('source')
        .linkTarget('target')
     // .zoom(Zoom)
        .onNodeClick(GetGroupedNodes)
        .cooldownTime(100000)
        .linkWidth(0.2)
        .nodeCanvasObject((node, ctx, globalScale) => {

            if (unescape(node.name) == unescape(node.group)) {
                const label = unescape(node.name);
                const fontSize = 12 / globalScale;
                ctx.font = `${fontSize}px Sans-Serif`;
                const textWidth = ctx.measureText(label).width;
                const bckgDimensions = [textWidth, fontSize].map(n => n + fontSize * 0.2); // some padding
                ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
                ctx.fillRect(node.x - bckgDimensions[0] / 2, node.y - bckgDimensions[1] / 2, ...bckgDimensions);
                ctx.textAlign = 'center';
                ctx.textBaseline = 'middle';
                // ctx.fillStyle = node.color;
                ctx.fillStyle = "#000";
                ctx.fillText(label, node.x, node.y);
            }
            else
            {
                ctx.beginPath();
                ctx.fillStyle = node.color;
                ctx.arc(node.x, node.y, circleScale(node.value), 0, 2 * Math.PI);
                ctx.fill();
            }
        });

Also, wish the graph cools by itself without a cooldowntime but when i remove it, its becoming static quickly without proper node arrangements.

Can you please give any suggestions on this config, if any examples to browse through ?

@vasturiano
Copy link
Owner

Hi @Masrico, here's some answers to your questions:

  • nodeRelSize does not work for your case because you're using nodeCanvasObject. You should adjust the custom nodes sizes in that method, possibly by adjusting the text fontSize.
  • nodeCanvasObject only applies to nodes, not links. Links will always be represented as lines that go from one node position to another. You can style them using the link styling methods listed in the docs, but not actually change their geometry.
  • When calling .d3Force('...'), you should do this after the graph has been initialized and not chain any graph methods after it. Because the returning object of that method is a d3-force specific reference and not the graph itself.
  • Regarding the 'contain' force, there is an example here / source that declares a bounding box force. You can try following the same syntax and adjust it to your use case.
  • If you wish for the graph to jump immediately to its final state without animating the build-in, you can set the cooldownTicks to 0 (or cooldownTime as you mentioned) and warmupTicks to a value large enough that allows the graph to build to a proper state. Generally a value of 200 or so is sufficient. The higher the value, the longer the initial rendering of the graph will take while its running all the engine iterations in the background.

@Masrico
Copy link
Author

Masrico commented Apr 9, 2018

@vasturiano,

thanks for all your suggestions and inputs. Will try various options given and check where the graph stands.
For nodes to come close, is there any alternative or suggestion for links, as they are having larger length's or change the geometry of links.

@Masrico
Copy link
Author

Masrico commented Apr 9, 2018

@vasturiano
Also, is their a way where text node is over the circle nodes.
.nodeCanvasObject((node, ctx, globalScale) => {

        if (unescape(node.name) == unescape(node.group)) {
            const label = unescape(node.name);
            const fontSize = 12 / globalScale;
            ctx.font = `${fontSize}px Sans-Serif`;
            const textWidth = ctx.measureText(label).width;
            const bckgDimensions = [textWidth, fontSize].map(n => n + fontSize * 0.2); // some padding
            ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
            ctx.fillRect(node.x - bckgDimensions[0] / 2, node.y - bckgDimensions[1] / 2, ...bckgDimensions);
            ctx.textAlign = 'center';
            ctx.textBaseline = 'middle';
            // ctx.fillStyle = node.color;
            ctx.fillStyle = "#000";
            ctx.fillText(label, node.x, node.y);
        }
        else
        {
            ctx.beginPath();
            ctx.fillStyle = node.color;
            ctx.arc(node.x, node.y, circleScale(node.value), 0, 2 * Math.PI);
            ctx.fill();
        }
    }

In the screen shot above you can see that the text node is overlapped by the circle nodes and limits the visibility of text. May be i create the circle arcs first and then created text nodes, but not sure how can that be achieved with the force geometry ?

@vasturiano
Copy link
Owner

@Masrico the nodes are drawn in the order present in the supplied nodes array.
That means that if you wish to have some nodes in "front" of other nodes, just sort your nodes array to follow that logic. For example, have your text-based nodes at the end of the array so they are drawn last.

@Masrico
Copy link
Author

Masrico commented Apr 11, 2018

Thanks @vasturiano ,
Yes i figured that out when i accidentally reversed the list :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants