bsplosion
bsplosion

Reputation: 2886

GoJS: Prevent specific region from being occupied by nodes in a ForceDirectedLayout

I'd like to have a specific coordinate area reserved for a legend in my GoJS diagram which is using a ForceDirectedLayout for node layouts. However, the legend shape doesn't affect the layout of nodes when applied as a Part, and using a placeholder Node unfortunately doesn't allow placement based on document coordinates. This leads to the legend content overlapping node content depending on how the forces are randomly applied.

How can you create a region in a ForceDirectedLayout which affects/applies forces to nodes as they're being calculated but in turn either isn't a node or is a node with a fixed position and no links?

Ideally it'd be possible to define a specific Rect in the diagram which isn't accessible for nodes, or have a Part which can apply forces. The closest I could find to defining a specific rect is the total bounding box via the layout's boundsComputation which would just narrow the area and not allow for cutting a specific region out.

Here's a specific illustration of the challenge, where the legend will generally overlap node content since there's no force to repel the nodes:

General sketch of problem

It appears it may be possible with ForceDirectedLayout.isFixed based on the following, but it's unclear how one would go about setting a specific node position on that basis: https://forum.nwoods.com/t/maintain-existing-graph-shape-when-appending-items/7159/6

Upvotes: 0

Views: 254

Answers (1)

Walter Northwoods
Walter Northwoods

Reputation: 4146

You could try a variation of this class:

class FixedForceDirectedLayout extends go.ForceDirectedLayout {
  isFixed(v) {
    return v.node.category === "Fixed";
  }
  electricalCharge(v) {
    return (v.node.category === "Fixed") ? 300 : this.defaultElectricalCharge;
  }
}

You may want to fiddle with the value returned by the electricalCharge method.

To install, just set Diagram.layout to an instance of this custom ForceDirectedLayout class.

const myDiagram =
  $(go.Diagram, "myDiagramDiv",
    {
      layout: new FixedForceDirectedLayout(),
      . . .

Now where you want there to be an empty area, put a node of category "Fixed" covering that area. For example,

myDiagram.nodeTemplateMap.add("Fixed",
  $(go.Node,
    { background: "whitesmoke" },  // just for demo purposes, so you can see it
    new go.Binding("position", "bounds", b => go.Rect.parse(b).position),
    new go.Binding("desiredSize", "bounds", b => go.Rect.parse(b).size)
  ));

Example model data:

myDiagram.model = new go.GraphLinksModel(
  [
    { category: "Fixed", bounds: "20 20 100 100" },
    . . .

Upvotes: 0

Related Questions