zoomToFit() not returning perfectly fit viewport

I have a diagram with a GraphLinksModel, and in certain cases (namely smaller graphs, as far as I can tell?) I’m getting a viewport after a zoomToFit() that is not exactly matched to one of the bounds of the document.

I.e. I would expect (and maybe this is incorrect) if the document bound’s width and height are 20 and 20, I would expect either the viewport’s width or it’s height (but not necessarily both) to be 20.

This seems to be happening very consistently with my smaller diagrams, but not with larger ones, almost like the diagram is reluctant to make my nodes and links larger than a certain size, even though my overview diagram shows the model fitted like I would expect.

Is there some code or function to automatically give more space to a smaller set of nodes/links during a fit?

(I’m running Go.js version=“1.4.20”.)

Yes, that’s exactly right. By default Diagram.zoomToFit will not increase the scale to be larger than 1.0. (To be precise, the value will be no larger than CommandHandler.defaultScale.)

Note that the viewport’s width or height is determined by the size of the DIV element and the Diagram.scale. It can be much larger than the diagram contents, i.e. the Diagram.documentBounds.

So assuming there is no way to change that, can I do either of these two things:

A) Programmatically set the scale of the diagram without triggering a corresponding viewport change? I will size the viewport and then is there a way to peg that viewport to be treated as the diagram.scale being 1.0? Basically let me ‘reset’ the scale and tell the diagram what it’s correct starting point is? For technical reasons I’m unable to do this during initialization, as I have a two step load. I init the diagram with a plot([],[]), then later load data. I looked into using the delayInit stuff, but wasn’t able to make that do what I needed.


B) Make the diagram aware of div resize changes. I have a resize listener calling this.diagram.requestUpdate(), but that hasn’t been setting the scale like I need it too (maybe because I’m not on v1.6? Though the method itself shows up, not sure why since it’s marked as 1.6.) The only way I can seem to make the diagram properly register the size changes is a zoomToFit call, which has the obvious unwanted effect of blowing out my viewport.

You could try having CommandHandler.defaultScale initialized at a value larger than 1.0, and then reset it to a “normal” value in an “InitialLayoutCompleted” DiagramEvent listener.

I have no idea of whether that will work for you, but it’s the natural thing to try if you really want nodes to appear larger than normal if the document bounds is smaller than the viewport.

Now that I think about that, it seems like A is probably a cleaner solution than trying to make things always fit (since that does look pretty bad all the way zoomed in). Is there a way to do that?

I still don’t understand why you can’t call Diagram.zoomToFit in an “InitialLayoutCompleted” DiagramEvent listener.

How do you load the data “later”?

I can’t do that because it doesn’t solve my issue. I’m trying to keep a zoom% value displayed to the user. There are, as far as I can tell, two ways to do this:

If I just use the scale off the diagram, div resizes break the calculation, as the diagram scale isn’t changed by the div resize (even if I put requestUpdate() in a resize listener). Not sure why this is, though I would assume it’s because my version is old.

If I calculate it manually by doing algebra with the viewport and document bounds, then I can’t trust my math when the zoomToFit() doesn’t change the scale fully like I’d expect (the viewport’s bounds at 100% are not guaranteed to line up with a document bound) so my zoom to fit % ends up being something like 67%. This behavior isn’t necessarily something I’d want to change, since as you pointed out, it looks pretty bad if it gets overly zoomed.

I have a plot method that essentially builds an array of nodes and links then sets the model values to those arrays: this.diagram.model.nodeDataArray = nodes;
this.diagram.model.linkDataArray = links;

I’m loading data later with additional plot() calls. I keep around one diagram and init it at app launch, then when a user clicks into an object, I load it’s associated diagram. Then clear it with empty arrays and reuse it for future views (to keep around listeners and such). I’m assuming I can’t constantly call delayInitialization() if I’m doing this same init multiple times every time I need to show a new diagram? I haven’t been able to make it work yet.

Yes, if you are just replacing the Model.nodeDataArray, the old Nodes will be remove and new ones added, but there won’t be any new “initialization” of the diagram. That would happen if you replace the Diagram.model.

If you don’t have Diagram.autoScale set to anything (besides the default value), the Diagram.scale will not change automatically.

Whenever the Diagram.scale is changed, perhaps due to Diagram.zoomToFit, or due to (control) mouse-wheel events, or due to other code that might change the scale, there will be a “ViewportBoundsChanged” DiagramEvent. The same event happens after a call to Diagram.requestUpdate if the DIV has changed size. (The requestUpdate happens automatically if the browser window changes size.) You can use this DiagramEvent to keep a zoom % indicator up-to-date.