Working with different graph states

I am trying to understand what the best way is to work with different states of a graph in Go.js.

For example, when the page loads and the graph has not been touched, I have the following:

Once a node has been selected, the graph looks like this:

Two questions, when a user clicks away from the graph (i.e. on the document somewhere), what is the best way to set the graph back to its original state (i.e. the first screen shot above). I currently have this code but wondering if there is a better way:

        // when the user clicks on the background of the Diagram, remove all highlighting
        myDiagram2.click = function(e) {
         myDiagram2.startTransaction("no highlighteds");
         myDiagram2.clearHighlighteds();

        myDiagram2.nodes.each(function (currentNode) {
	  var shape = currentNode.findObject("SHAPE");
	  shape.stroke = "#009BE1";
	  shape.fill = "#FFF";
	  var text = currentNode.findObject("TEXT");
	  text.stroke = "black";
	  var placeholderText = currentNode.findObject("PLACEHOLDERTEXT");
	  if (placeholderText){
		 placeholderText.stroke = "black";
	  }
	
	   currentNode.findLinksOutOf().each(function (node){
		var arrow = node.findObject("ARROW");
		    arrow.stroke = "#009BE1";
		var arrowHead = node.findObject("ARROWHEAD");
		    arrowHead.stroke = "#009BE1";
			arrowHead.fill = "#009BE1";	
	 })
  })

    myDiagram2.commitTransaction("no highlighteds");
};

I also need to decipher between states for the hovering functionality. The mouseEnter and mouseLeave styling needs to be different for when the page is initially loaded before a node selection has been made, and then once a selection has been made. For example, the screen shot below shows what a hovered upon node looks like after a selection has been made:

Notice that “CHESTFIELD HOLDING” has the bright blue border around it after it has been hovered upon, this look is great for the original, clean graph (i.e. the first screen shot above) but once a node selection has been made I need to hover state to match the other nodes with the faded out colors.

I guess what I’m wondering is how can I apply mouse events to an initial graph and then change the styling once a graph node has been selected.

My current mouseEnter and mouseLeave code for the nodeTemplate looks like this

         myDiagram.nodeTemplate = 
              $(go.Node, "Auto",
                       {
                         mouseEnter: mouseEnter,
                         mouseLeave: mouseLeave,
                       })

     function mouseEnter (e, obj){
       var shape = obj.findObject("SHAPE");
       shape.fill = "#DDF8BF";
       shape.stroke = "#77B03B";
       var text = obj.findObject("TEXT");
        text.stroke = "black"; 
      }

     function mouseLeave (node){
     var shape;
     var text;
      // check to see if the current node is selected or is highlighted and if it is then apply these styles
    if (node.isSelected || node.isHighlighted){
	    shape = node.findObject("SHAPE");
	    shape.fill = "#E9F9D7"; 
	    shape.stroke = "#388E3C";
	    text = node.findObject("TEXT");
	    text.stroke = "black";
      //if the node is not selected or highlighted apply these styles
      } else {
	    shape = node.findObject("SHAPE");
	    shape.fill = "#FFF";
	    shape.stroke = "#009BE1";
	    text = node.findObject("TEXT");
	    text.stroke = "black"; 
       }
      }

Thanks for all the help.

If you don’t want to depend on mouse events to drive what is highlighted, implement a “ChangedSelection” DiagramEvent listener to make some layers partly opaque or to restore their opacity, depending on whether Diagram.selection.count is zero or not. That listener should also be responsible for highlighting the desired nodes and links that are connected with the selected nodes and links. Do not depend on any mouse events.

But if you do want to depend on mouse events to determine what is highlighted and which layers are opaque, that’s OK too. Then you should not depend on whether nodes are selected or highlighted or not. See the Friend Wheel sample, Friend Wheel, which demonstrates this technique.

Hey Walter, I had the same functionality before hand with a click event on the node calling showConnections, and then I had a separate click handler on the diagram itself (myDiagram.click). But now I am using ChangedSelection on the diagram that is calling showConnections:

function showConnections(e, obj) {
  // var diagram = node.diagram;
  myDiagram2.startTransaction("highlight");
   // remove any previous highlighting
   myDiagram2.clearHighlighteds();

   if (myDiagram2.selection.count > 0) {
	//loop throug all nodes in the diagram and apply different styling 
	myDiagram2.nodes.each(function (indNode) {
		//if the currently selected node
		if(indNode.isSelected) {
			indNode.findLinksOutOf().each(function(l) { l.isHighlighted = true; });
			indNode.findNodesOutOf().each(function(n) { n.isHighlighted = true; });
		//all the other nodes in the diagram that is not selected or highlighted
		} else if (!indNode.isHighlighted) {
			//RoundedRectangle shape
			var shape = indNode.findObject("SHAPE");
			shape.stroke = "rgba(183, 227, 246, 0.7)";
			//Text of each node
			var text = indNode.findObject("TEXT");
			text.stroke = "rgba(0,0,0, 0.5)"; 	
			//Placeholder for the sub-graph
			var placeholderText = indNode.findObject("PLACEHOLDERTEXT");
			if(placeholderText){
				placeholderText.stroke = "rgba(0,0,0, 0.5)";	
				}
		} 
		
		if (!indNode.isSelected){
			indNode.findLinksOutOf().each(function (currentNode) {
					var arrow = currentNode.findObject("ARROW");
					arrow.stroke = "rgba(128,128,128, 0.1)";
					var arrowHead = currentNode.findObject("ARROWHEAD");
					arrowHead.stroke = "rgba(128,128,128, 0.1)";
					arrowHead.fill = "rgba(128,128,128, 0.1)";	
			})
		}
	})
} else {
	myDiagram2.nodes.each(function (currentNode) {
	var shape = currentNode.findObject("SHAPE");
	shape.stroke = "#009BE1";
	shape.fill = "#FFF";
	var text = currentNode.findObject("TEXT");
	text.stroke = "black";
	var placeholderText = currentNode.findObject("PLACEHOLDERTEXT");
	if (placeholderText){
		placeholderText.stroke = "black";
	}
	
	currentNode.findLinksOutOf().each(function (node){
		var arrow = node.findObject("ARROW");
		    arrow.stroke = "#009BE1";
		var arrowHead = node.findObject("ARROWHEAD");
		    arrowHead.stroke = "#009BE1";
			arrowHead.fill = "#009BE1";	
	})
})
}
myDiagram2.commitTransaction("highlight");

}

Is this the best way to go about it, considering very high amounts of nodes (100,000 +)?

Also, I need some sort of mouseEnter and MouseLeave event so that I can show a hover state when the user is hovering over a node, this needs to be different from when a node is actually selected, so I would need mouseLeave and mouseHover wouldn’t I?

I just need to apply different styles to those mouseEnter and mouseLeave depending on if a node has been selected. is that possible?

For example: “CHESTFIELD HOLDINGS” shows a currently hovered upon state:

Thanks

Thanks Walter…so I think I had similar functionality before (I was listening for a click event that was calling showConnections, and then had a click event on the diagram itself (myDiagram.click). But I have removed both of those and am now using a “ChangedSelection” that calls showConnections:

Does this seem like the most efficient way to do this considering there could potentially be a very large amount of nodes (100,000 + possibly)?

function showConnections(e, obj) {
   // var diagram = node.diagram;
    myDiagram2.startTransaction("highlight");
   // remove any previous highlighting
   myDiagram2.clearHighlighteds();

if (myDiagram2.selection.count > 0) {
	//loop throug all nodes in the diagram and apply different styling 
	myDiagram2.nodes.each(function (indNode) {
		//if the currently selected node
		if(indNode.isSelected) {
			indNode.findLinksOutOf().each(function(l) { l.isHighlighted = true; });
			indNode.findNodesOutOf().each(function(n) { n.isHighlighted = true; });
		//all the other nodes in the diagram that is not selected or highlighted
		} else if (!indNode.isHighlighted) {
			//RoundedRectangle shape
			var shape = indNode.findObject("SHAPE");
			shape.stroke = "rgba(183, 227, 246, 0.7)";
			//Text of each node
			var text = indNode.findObject("TEXT");
			text.stroke = "rgba(0,0,0, 0.5)"; 	
			//Placeholder for the sub-graph
			var placeholderText = indNode.findObject("PLACEHOLDERTEXT");
			if(placeholderText){
				placeholderText.stroke = "rgba(0,0,0, 0.5)";	
				}
		} 
		
		if (!indNode.isSelected){
			indNode.findLinksOutOf().each(function (currentNode) {
					var arrow = currentNode.findObject("ARROW");
					arrow.stroke = "rgba(128,128,128, 0.1)";
					var arrowHead = currentNode.findObject("ARROWHEAD");
					arrowHead.stroke = "rgba(128,128,128, 0.1)";
					arrowHead.fill = "rgba(128,128,128, 0.1)";	
			})
		}
	})
} else {
	myDiagram2.nodes.each(function (currentNode) {
	var shape = currentNode.findObject("SHAPE");
	shape.stroke = "#009BE1";
	shape.fill = "#FFF";
	var text = currentNode.findObject("TEXT");
	text.stroke = "black";
	var placeholderText = currentNode.findObject("PLACEHOLDERTEXT");
	if (placeholderText){
		placeholderText.stroke = "black";
	}
	
	currentNode.findLinksOutOf().each(function (node){
		var arrow = node.findObject("ARROW");
		    arrow.stroke = "#009BE1";
		var arrowHead = node.findObject("ARROWHEAD");
		    arrowHead.stroke = "#009BE1";
			arrowHead.fill = "#009BE1";	
	})
})
}
myDiagram2.commitTransaction("highlight");

}

I also need some sort of mouseEnter and mouseLeave event to show a user is hovering over a current node (not selected). For example, in the below screen shot I am showing a group of selected nodes, a currently hovered upon node with a tooltip, and a node that was hovered and then left (bright blue border, white fill)

All the above is great, I just need the node with the bright blue border to go back to the faded out look when the mouseLeave takes place when there is an active selected node, otherwise I want that bright blue border. Is there some kind of check I can do in the mouseEnter / mouseLeave function to see if there is a current selection (myDiagram.selection.count) doesn’t seem to work there.

Yes, you can use mouseEnter/mouseLeave as well as selection changes as events to react to. Just make sure mouseLeave exactly undoes what mouseEnter did.

I thought you were confusing the two (mouse vs selection), and that would cause bugs such as what you noticed.

Regarding performance, if you actually create 100,000 nodes, you’ll have other problems besides this. But you can get around most of those problem by virtualization. (In which case there won’t be that many nodes to iterate over.) However, you will not be able to perform a LayeredDigraphLayout, even a virtualized one, on 100,000 nodes.

Sorry I’m probably not being clear but how do I decipher between the two states of the graph…when mouseEnter is called, how do I apply two different sets of styles to the same node depending on if the graph has been touched or not. That’s what I’m confused about, I’m trying to apply two different sets of styling on the same node with the mouseLeave:

Can I do something like this:

function mouseLeave (node) {
      if(myDiagram.isDirty || myDiagram.hasBeenTouched) {
              //the graph has a currently selected node, apply these styles to the node
         } else {
              //the graph is in its original state, apply these styles
         }
}

Anything like that? Thanks again

I don’t see what the problem is. You just need to figure out what possible states a node can have and make sure you switch correctly.

Hmmm Ok…are those states built into Go.js then? How does the state of an untouched node change when a selection has been made on a different node?

The state of the diagram itself would change wouldn’t it? If I select the “WHEAT” node from my screen shot above, how does the state of the other nodes (FARMER HOLDING, for example) change?

If the other nodes state changes, then I can try checking that state in my mouseLeave.

No, states are not built into GoJS, other than properties such as Part.isSelected and Part.isHighlighted.

I suppose we could provide a visual state manager, in which you could declaratively specify the states and their respective visual stylings, and GoJS could provide automatic transitions between states. But the basic functionality is actually not too hard to implement yourself.

Here’s an example of theming: Flowchart Styled. You can diff the page source with Flowchart.

Alright sounds good, I’ll take a look at those examples but I have it looking the way I want on mouseLeave with this:

function mouseLeave(node){
var shape;
var text;
if (myDiagram2.selection.count > 0) {
	if(node.isSelected || node.isHighlighted) {
		shape = node.findObject("SHAPE");
		shape.fill = "#E9F9D7"; 
		shape.stroke = "#388E3C";
		text = node.findObject("TEXT");
		text.stroke = "black";
	//if the node is not selected or highlighted apply these styles
	} else {
		shape = node.findObject("SHAPE");
		shape.fill = "#FFF"; 
		shape.stroke = "rgba(183, 227, 246, 0.7)";
		text = node.findObject("TEXT");
		text.stroke = "rgba(0,0,0, 0.5)";
	}
} else {
  //there is currently no selections in the graph
		shape = node.findObject("SHAPE");
		shape.fill = "#FFF";
		shape.stroke = "#009BE1";
		text = node.findObject("TEXT");
		text.stroke = "black";
	}
}

Just making sure I’m not making it harder then it needs to be and using built in functionality when I can.