I want sample code of shift node to avoid node overlap without reset all layout:
Simple Shifting of Nodes to Avoid Overlapping with Groups (gojs.net)
Issue:
Note: I am using Tree Layout.
I want sample code of shift node to avoid node overlap without reset all layout:
Simple Shifting of Nodes to Avoid Overlapping with Groups (gojs.net)
Issue:
Note: I am using Tree Layout.
So right now are you using a TreeLayout with Layout.isOngoing set to false? And you are showing additional details of a node, causing it to change size?
What are the constraints of what you want to allow to move? It seems to me that it would be natural to move the “BUILD_CONSOLE_MSG” node and all of the nodes to the right of there, towards the right far enough to avoid the overlap with the just-expanded node. But if you move those nodes, you’d want to move the parents too, and their parents, etc, along with their children and their children in turn. So it seems to me that you need to perform a real tree layout again, and the easiest way to accomplish that is by setting Layout.isOngoing back to its default value of true.
I want to move only overlapping nodes, after setting Layout.isOngoing to true
its changing the draggable node position set them to original position, I don’t want to set draggable nodes on original position, just want to move draggable node toward right or bottom of expandable node if they overlap.
Note: Not want large difference in structure after node expansion(only adjust overlapping nodes or expandable node).
What’s wrong with just calling the function in that sample?
// This function moves any nodes that overlap with the given Part, either rightwards or downwards.
// Currently it is only called on Groups, but it could work for any Node.
function shiftNodes(part) {
const diagram = part.diagram;
if (diagram === null) return;
part.ensureBounds();
const b = part.actualBounds;
const overlaps = diagram.findObjectsIn(b,
x => { const p = x.part; return (p.isTopLevel && p instanceof go.Node) ? p : null; },
node => node !== part && !node.isMemberOf(part),
true);
let dx = 0;
let dy = 0;
const shiftsXY = new go.Set();
const shiftsX = new go.Set();
const shiftsY = new go.Set();
overlaps.each(node => {
const r = node.actualBounds;
if (r.contains(b.right, b.bottom)) {
dx = Math.max(dx, b.right - r.left);
dy = Math.max(dy, b.bottom - r.top);
shiftsXY.add(node);
} else if (b.contains(r.left, r.bottom)) {
dx = Math.max(dx, b.right - r.left);
shiftsX.add(node);
} else if (b.contains(r.right, r.top)) {
dy = Math.max(dy, b.bottom - r.top);
shiftsY.add(node);
}
});
if (dx > 0) diagram.moveParts(shiftsX, new go.Point(dx+10, 0), false);
if (dy > 0) diagram.moveParts(shiftsY, new go.Point(0, dy+10), false);
if (dx > 0 && dy > 0) diagram.moveParts(shiftsXY, new go.Point(dx+10, dy+10), false);
}
Or maybe the code in this sample? Automatically Shift Nodes Aside Righward to Make Room
it’s work, but if there is lots of neighbor nodes near to panel expandable node and no space to move right then overlapping happened.
Is there any way to move expandable node to free space without disturbing diagram structure/layout.
Try this code. Double-click on a node in order to toggle its size. The doubleClick event handler will call findUnoccupiedRect to search for an empty area below the node. (Empty of Nodes that are Node.avoidable.)
You’ll probably want to replace this findUnoccupiedRect function with something that produces better results according to your own preferences.
<!DOCTYPE html>
<html>
<head>
<title>Minimal GoJS Sample</title>
<!-- Copyright 1998-2024 by Northwoods Software Corporation. -->
</head>
<body>
<div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:400px"></div>
<script src="https://unpkg.com/gojs"></script>
<script id="code">
// Find an unoccupied rectangular area of a given size within a given area.
// DIAG provides the Diagram in which to search.
// BOUNDS specifies the area to search for empty space (no Avoidable Nodes) of at least size SIZE.
// SIZE specifies the minimum width and height for the empty area.
// ANGLE specifies the direction in which to search (0, 90, 180, 270)
// SKIP is an optional Node that should be ignored when checking for existing Avoidable Nodes in the area.
function findUnoccupiedRect(diag, bounds, size, angle, skip) {
if (!skip) skip = null;
const r = new go.Rect(bounds.x, bounds.y, size.width, size.height);
let a, b, ma, mb, da, db;
if (angle === 0) {
a = bounds.left; ma = Infinity; /*bounds.right - size.width;*/ da = 8;
b = bounds.top; mb = bounds.bottom - size.height; db = 8;
} else if (angle === 90) {
a = bounds.top; ma = Infinity; /*bounds.bottom - size.height;*/ da = 8;
b = bounds.left; mb = bounds.right - size.width; db = 8;
} else if (angle === 180) {
a = bounds.right - size.width; ma = -Infinity; /*bounds.left;*/ da = -8;
b = bounds.top; mb = bounds.bottom - size.height; db = 8;
} else if (angle === 270) {
a = bounds.bottom - size.height; ma = -Infinity; /*bounds.top;*/ da = -8;
b = bounds.left; mb = bounds.right - size.width; db = 8;
} else {
throw new Error("unknown angle for findUnoccupiedRect: " + angle);
}
let s = b;
for (; (da > 0) ? a < ma : a > ma; a += da) {
if (angle === 0 || angle === 180) {
r.x = a;
} else {
r.y = a;
}
for (; (db > 0) ? b < mb : b > mb; b += db) {
if (angle === 0 || angle === 180) {
r.y = b;
} else {
r.x = b;
}
const empty = diag.isUnoccupied(r, skip);
if (empty) return r;
}
b = s;
}
return null;
}
const myDiagram =
new go.Diagram("myDiagramDiv", {
layout: new go.TreeLayout({ angle: 90, isOngoing: false })
});
var DetailsTemplate =
new go.Panel("TableRow", {
stretch: go.GraphObject.Horizontal
})
.bind("portId", "name")
.add(
new go.TextBlock({
height: 16,
font: "14px sans- serif",
stretch: go.GraphObject.Fill,
overflow: go.TextBlock.OverflowEllipsis
})
.bind("text", "rulename")
);
myDiagram.nodeTemplate =
new go.Node("Auto", {
desiredSize: new go.Size(50, 25),
doubleClick: (e, node) => {
e.diagram.commit(diag => {
node.desiredSize = node.desiredSize.isReal() ? new go.Size(NaN, NaN) : new go.Size(50, 25);
node.ensureBounds(); // force synchronous re-measurement of width and height
if (!node.desiredSize.isReal()) { // assume big -- gotta search
const nb = node.actualBounds;
const found = findUnoccupiedRect(diag,
new go.Rect(nb.x, nb.y, nb.width + 10, 1000), // look in column below the node
node.actualBounds.size, // look for an area at least the node's size
90, node); // look downward
if (found) node.move(found.position);
}
});
}
})
.add(
new go.Shape("RoundedRectangle", { fill: "orange", strokeWidth: 0 }),
new go.Panel("Table")
.add(
new go.Panel("Auto", { row: 0, margin: 8, stretch: go.GraphObject.Horizontal })
.add(
new go.TextBlock({ stretch: go.GraphObject.Fill })
.bind("text")
),
new go.Panel("Table", { row: 1,
stretch: go.GraphObject.Horizontal,
padding: 2,
//columnSpan: 2,
itemTemplate: DetailsTemplate,
minSize: new go.Size(100, NaN),
defaultRowSeparatorStroke: go.Brush.lighten("grey"),
background: "#fef5d9"
})
.bind("itemArray", "ruledetails")
)
);
myDiagram.model = new go.GraphLinksModel({
linkFromPortIdProperty: "fid",
linkToPortIdProperty: "tid",
nodeDataArray: [
{ key: 1, text: "Alpha Alpha", color: "lightblue",
ruledetails:
[
{ name: "1", rulename: "one" },
{ name: "2", rulename: "two" },
{ name: "3", rulename: "table much wider than header" },
]
},
{ key: 2, text: "Header much wider than table", color: "orange",
ruledetails:
[
{ name: "0", rulename: "zero" },
{ name: "1", rulename: "one" }
]
},
{ key: 3, text: "min:100", color: "pink",
ruledetails:
[
{ name: "0", rulename: "zero" },
{ name: "1", rulename: "one" }
]
}
],
linkDataArray: [
{ from: 1, to: 2 },
{ from: 1, to: 3 }
]
});
</script>
</body>
</html>
Hi,
it’s worked for me.
it also working inside group node, but whenever node moves to empty space inside group node then layout is getting auto reset.
but I don’t want to auto reset layout.
Sample Group Node:
Note: I am using groupTemplateMap in my code.
That’s probably because moving the node caused the containing group to change size, which by default invalidates the layout responsible for the nodes and links including that group, which results in that layout being performed again at the end of the transaction.
Did you want layouts to happen automatically whenever nodes or links are added to or removed from the diagram or group? If not, you could set Layout.isOngoing to false both on the Diagram.layout and on every Group.layout.
Read more about this issue at: Layouts | GoJS
Hi
I am set the Layout.isOngoing to false both on the Diagram.layout and on every Group.layout.
For Group Node: on subGraphExpandedChanged I am applying findUnoccupiedRect related code.
it’s worked for me but,
1.whenever I am expanding node for 1st time overlapping happened and after that I am collapsing node.
2.On 2nd time expanding node, node move toward empty space.
Issue1: on 1st expand group node overlaps.
1st expand overlapping happened:
2nd Expand node moves:
Issue2: after applying Layout.isOngoing to false I have lost nodes present inside group node.
Every Part must have a real Part.location (and GraphObject.position) for it to be seen in a Diagram. Otherwise, where should it be shown?
Normally the location (or position) of every Node is provided either via a data Binding or due to some code setting it, such as a Layout.
So you are depending on a layout happening on a group’s subgraph when the group is expanded. OK, then don’t set Layout.isOngoing at all, but just set Group.layoutConditions to go.LayoutConditions.Standard & ~go.LayoutConditions.GroupLayout & ~go.LayoutConditions.NodeSized
. I’m not sure about exactly what combinations of LayoutConditions flags you want to use – you may need to use a different set of flags.