Network: use separate refresh indicator in NodeBase, instead of width… #2885
Conversation
After staring long and hard at the code of the image shapes, I figured out how to best use So now the refresh of shapes |
@@ -53,6 +54,7 @@ | |||
network.on("click", function (params) { | |||
params.event = "[original event]"; | |||
document.getElementById('eventSpan').innerHTML = '<h2>Click event:</h2>' + JSON.stringify(params, null, 4); | |||
console.log('click event, getNodeAt returns: ' + this.getNodeAt(params.pointer.DOM)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for updating the example. Perfect!
@@ -380,8 +380,7 @@ class Node { | |||
* @private | |||
*/ | |||
_reset() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we get rid of the _reset
function and replace it with needRefresh
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup, can do.
* | ||
* @protected | ||
*/ | ||
needRefresh(selected, hover) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is a "private" function. it should start with a underscore: _needRefresh
.
Also i would rename it to _needsRefresh
but that is just personal taste.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That was my initial thought too. However, it's not strictly private
but protected
, in the sense that it is called from derived classes. So I wasn't 100% sure to make it look like a private method.
Are there any guidelines for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are no guidelines. I don't know the code good enough to know if there is a schema for this. Have a look at the surrounding code - you decide.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still think you should add the "s": needsRefresh
:-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are no guidelines
Actually, I think there should be. In due time, I will make a proposal; there are a couple of style issues that bother me.
needsRefresh
Yes, that's better. Silly me.
@@ -380,8 +380,7 @@ class Node { | |||
* @private | |||
*/ | |||
_reset() { | |||
this.shape.width = undefined; | |||
this.shape.height = undefined; | |||
this.shape.doRefresh = true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For me doRefresh
sounds like a function. What about this.shape._refreshNeeded
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, but without the '_' prefix, because it is not private.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, I guess
* @protected | ||
*/ | ||
needRefresh(selected, hover) { | ||
if (this.doRefresh) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is a little dangerous to do a if (this.doRefresh)
because this is valid e.g. also if doRefresh is a valid String or any object. That's why I would recommend to invert the logic:
if (!this.doRefresh) {
return (this.width === undefined) || (this.labelModule.differentState(selected, hover));
}
this.doRefresh = false;
return true;
* | ||
* @protected | ||
*/ | ||
needRefresh(selected, hover) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still think you should add the "s": needsRefresh
:-)
@@ -84,6 +89,7 @@ class CircleImageBase extends NodeBase { | |||
this.width = width; | |||
this.height = height; | |||
this.radius = 0.5 * this.width; | |||
this.refreshNeeded = false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@wimrijnders Are you sure we don't need the refresh here because of the hover/selected state? I'm not sure I understand it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The purpose of this line is to turn off the indicator variable after a triggered resize calculation.
As far as I can tell, all the required resizing has been done in this block before this line. The value is set to false to prevent the resize calculation from triggering continuously.
In summary, when disregarding the rest of the logic, what happens here is:
if (refreshNeeded === true) {
// Do the resize calculation
refreshNeeded = false;
}
I hope this explains the intention. I believe this code is correct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, you mean: why is not hover
and selected
checked for triggering the resize calculation, just like with the rest of the shapes.
The direct answer is: I don't know, this is how the code worked before I added the changes and I did not want to change the logic too much.
I'll look at the code to see if I can understand this myself.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I understand what is happening; needsRefresh()
is/should be called in the child classes. So the changes I made to CircleImageBase
are misplaced.
needsRefresh()
is already called in Circle.resize()
. For completeness, the call should also be used in CircularImage.resize()
.
I will make an update to the PR which I hope will clarify the point. Well spotted, @mojoaxel.
OK, figured it out. I moved the test for refresh from I also did some cleanup and refactoring in the process, to better understand the code. But this was becoming a never-ending story: the more I look, the more I see things that need looking at. So, I left some TODO's in the code and quickly closed off before I found more. So, please glance at the TODO's to see if they're OK. |
Regarding getNodeAt() not being required within event listeners - I don't believe this to be the case for "oncontext" events? The on context event does not explicitly select the node that has been right clicked and therefore the node ID does not necessarily get passed in params. I am still having the problem with getNodeAt using the latest version of VIS. |
This PR is a resolution of #1847 and #2436. Please examine these for the problem description.
Discussion
It should be noted that it is not necessary to call
getNodeAt()
within an event listener to determine the handled node; the node id is passed to the event listener in the supplied parameter:However, there are situations imaginable where
getNodeAt()
is called outside of an event listener,so it still makes sense to ensure that this method is working correctly.
Analysis
The basic problem is that within
Node
, the dimensionthis.shape.width
has a double functionas an indicator that a shape should be recalculated. This happens in method
refresh()
, whichis called by
select()
(among others):The undefined value of the width gets picked up during redraw in the shape instances, where it is
used in a lazy fashion to recalculate the shape. Almost all shapes do it in the following way:
The exceptions are shapes
CircularImage
andImage
, which defer the calculation until the contained image is loaded.In practice, when a Node is clicked on, the
click
event will fire beforeNetwork.redraw()
has been called. So, during theclick
event, the shape's width member isundefined
.The width is also used within
getNodeAt()
to determine the selected node, ultimately within methodNode.isOverlappingWith()
. Because the width is undefined,isOverlappingWith()
returnsundefined
.Solution
Member
Shape.width
should not be used as an indicator for resize, it should have a single task as a dimension. It is better that a new, different member variable is used for refresh indication.doRefresh
in NodeBase, which is set inNode._reset()
NodeBase.needRefresh()
for all relevant shapesImage
andCircularImage
do not have this adjustmentThis can be tested in example
examples/network/events/interactionEvents.html
, where a line of codeis added to show the output of
getNodeAt()
during aclick
event.All shape types have been tested using this page; however, this test code has been removed.