vasturiano / force-graph

Force-directed graph rendered on HTML5 canvas
https://vasturiano.github.io/force-graph/example/directional-links-particles/
MIT License
1.55k stars 246 forks source link

Fully Zoom Out option while loading #94

Open Arshad8464 opened 4 years ago

Arshad8464 commented 4 years ago

Hi I was trying to load the graph in a fully zoomed out position while first loading. But I couldn't get it through. I tried the zoom function but I dunno how to calculate a value for the fully zoomed out? Is there any option to set the left, right, bottom and top position of the graph? If so I can get the node position to set these positions.

Can you please advise how to achieve it? PS: Working with D3 Canvas Forced Graph

vasturiano commented 4 years ago

@Arshad8464 thanks for reaching out.

There is no direct option to do this, but you can get there by reading in the current positions of your nodes and determine their bounding box. Then adjust the zoom according to the value that fits those coordinates within your canvas dimensions. You can even translate the graph sideways using centerAt if your bounding box is asymmetrical, for an optimal fit.

Alexithemia commented 4 years ago

I used a calculation to set a zoom value that is based off of how many nodes exist in the graph. With forces being used, the distance the nodes end up apart should be somewhat predictable, and you can set a base zoom level and have it increase when there are a certain amount of nodes, in steps or with an equation.

tarun080698 commented 3 years ago

I used a calculation to set a zoom value that is based off of how many nodes exist in the graph. With forces being used, the distance the nodes end up apart should be somewhat predictable, and you can set a base zoom level and have it increase when there are a certain amount of nodes, in steps or with an equation.

how did you calculate zoom based on the number of nodes in the graph?

vasturiano commented 3 years ago

@tarun080698 please note that there is now a built-in zoomToFit method that includes this functionality, so you don't need to custom code it.

tarun080698 commented 3 years ago

Yes, I read the docs and thanks @vasturiano for highlighting the zoomToFit method again. But I used it as graph.zoomToFit(2000, 100)

the result is something like this... It is hiding and going out of the canvas view CPT2106111748-1303x801 I want that my nodes should fit in the canvas when the graph is generated.

can you help me with that?

vasturiano commented 3 years ago

@tarun080698 it's possibly a timing issue. If you're requesting a zoomToFit before the graph has completely cooled down, it will move out of its boundaries by the time the animation is finished. Have you looked into the example, for applying the fitting zoom after the graph motion has stopped? https://vasturiano.github.io/force-graph/example/fit-to-canvas/

tarun080698 commented 3 years ago

This works fine, it was the timing issue, you were right. CPT2106130251-885x815

Now the problem is when I click on any node to focus on that node, somehow, it goes back to the zoomToFit position. Perhaps, I can probably think of it as when I click any node, the engine starts and stops again which causes the trigger.

onNodeClick={(node, event) => {
    graph.centerAt(node.x, node.y, 1000)
    graph.zoom(6, 1000)
}}

It looks something like this CPT2106130257-882x801

How can I stop it? @vasturiano

vasturiano commented 3 years ago

Assuming you're applying the zoomToFit on onEngineStop, whenever you wish to cancel that functionality (which will happen everytime the engine re-heats and cools down again), you can simply re-assign the method. For example, whenever a node is clicked:

myGraph.onNodeClick(() => {
  myGraph.onEngineStop(() => {});
  ...
})

Or, if you'd like to have the zoomToFit apply only once the first time around:

myGraph.onEngineStop(() => {
  myGraph.zoomToFit(600);
  myGraph.onEngineStop(() => {});
});
tarun080698 commented 3 years ago

Hey @vasturiano, I am actually using in it a ReactJs App. Using a reference to graph to call on the method like zoom, zoomToFit and others. Now when I try to call the onEngineStop using the same reference. i gives me error.

<ForceGraph2D
    ref={fgRef}
    graphData={props.signalData}
    height={props.heightEle}
    width={props.widthEle}
    nodeId="id"
    autoPauseRedraw={false}
    onNodeClick={(node, event) => {
        fgRef.current.centerAt(node.x, node.y, 1000)
        fgRef.current.zoom(6, 1000)
        fgRef.current.onEngineStop(() => {// do nothing })
     }}
     onEngineStop={() => {
        fgRef.current.zoomToFit(1000, 100)
        fgRef.current.onEngineStop = () => {// do nothing}}}
     cooldownTicks={100}
          />

If I write it like in the above snippet, it does not work. and if i try to write that as

onEngineStop={() => {
    fgRef.current.zoomToFit(1000, 100)
    fgRef.current.onEngineStop(() => { // do nothing })
}}

it gives me the error "onEngineStop is not a function."

vasturiano commented 3 years ago

@tarun080698 that is expected, because onEngineStop is a component prop, not method.

You'd need to keep state of the first time it's run. Something like:

const [engineStoppedOnce, setEngineStoppedOnce] = useState(false);

and then

onEngineStop={() => {
    if (!engineStoppedOnce) {
      fgRef.current.zoomToFit(1000, 100);
      setEngineStoppedOnce(true);
    }
}}
tarun080698 commented 3 years ago

The error of onEngineStop seems to be resolved but my nodes are still spreading out of the canvas. CPT2106140942-1303x801

Here's snippet for zoomToFit

onEngineStop={() => {
              if (!stopEngine) {
                fgRef.current.zoomToFit(1000, 400)
                setStopEngine(true)
              }
            }}

Another thing I want to know is that how can I specify the distance between nodes. like in the above reference, If I want to specify that two diff graphs entities should be nearer to each other.

vasturiano commented 3 years ago

@tarun080698 to prevent disconnected section to stray too far from each other, you can try setting the maxDistance of the charge force. F.e.

myGraph.d3Force('charge').distanceMax(80); // adjust to taste

As for the engine stop part, could you make a simple reproducible example on codeSandbox, otherwise it's impossible to debug.

tarun080698 commented 3 years ago

@vasturiano, I have created an example with my data here. I found out that to limit zoom, I used onZoom which was causing this problem. Please verify my method in the provided sandbox. I want to control the zoom-in and zoom-out limit. So, in order to do that, I used onZoom and zoom() to achieve it. If my method is wrong, please help me with that. Thanks

vasturiano commented 3 years ago

@tarun080698 for that purpose, you have minZoom and maxZoom already available as props.

tarun080698 commented 3 years ago

@vasturiano, yes I checked the new update. After updating the library, if i set minZoom in props and zoomToFit() in onEnginerStop then zoomToFit does not work and if i comment the minZoom, zoomToFit works. I want to know if this is a limitation or something. I will write the code for the situation I explained. Please check it with and without comment here. I have commented the minZoom part.

vasturiano commented 3 years ago

If you restrict the minZoom to 1 it means the framework will not be able to zoom out further than unity. That conflicts with the zoomToFit functionality that tries to zoom out to fit the graph but is blocked by this restriction. I would advise to relax the minZoom restriction to a value that can at least accommodate for your whole chart to fit in the view.

tarun080698 commented 3 years ago

@vasturiano, that's exactly what I thought the reason for the zoomToFit not to work in some cases. I tried removing the min zoom and everything was working fine. Thanks for the assistance. Have a great time.

tarun080698 commented 3 years ago

@tarun080698 to prevent the disconnected section from stray too far from each other, you can try setting the maxDistance of the charge force. F.e.

myGraph.d3Force('charge').maxDistance(80); // adjust to taste

Hey, I tried this but it is setting my nodes closer not the different graph entities close. image As you can see in the image above, I want to reduce the distance marked by white lines.

tarun080698 commented 3 years ago

@vasturiano I managed to achieve the above requirement. I want to inform you about the function name you suggested has changed from maxDistance to distanceMax I guess. You can confirm it here here.

Updated code snippet: myGraph.d3Force('charge').distanceMax(80); // adjust to taste Thanks for the idea and docs reference.

vasturiano commented 3 years ago

Ah yes, my bad. I misspelled the method name, it's always been distanceMax. Thanks for the correction. 👍 I'll edit the comment above to not mislead other users.