Hi -
I was curious if you could point me in the right direction to implement a bounding box that would encompass everything you have selected - specifically I want this in the event that you have more than one object selected.
thank you,
Caleb
Hi -
I was curious if you could point me in the right direction to implement a bounding box that would encompass everything you have selected - specifically I want this in the event that you have more than one object selected.
thank you,
Caleb
Take a look at: Minimal GoJS Sample with Multi Selection Box
Note that the Diagram.computePartsBounds ignores Links in its computation. If you care about Links, you’ll need to compute the union of the Part.actualBounds yourself.
What would it take for us to use multi select box example and do the following:
I used another popular diagramming tool to provide an example.
So in IMAGE 1 below we have 8 squares that are all selected and contained in a bonding box with a rotation handle at the top center of the bounding box.
We were able to rotate the bounding box and all the squares rotated around the center point of the bounding box.
Then we grabbed the right bottom corner of the bounding box, stretched it to the bottom right and caused the bounding box to resize, along with all the squares relative to the stretching of the bounding box. The outcome in pictured in IMAGE 2.
We would also like to use GoJS to implement the same capability with groups.
IMAGE 1
IMAGE 2
So basically you want the functionality already offered by http://gojs.net/latest/extensions/RotateMultipleTool.js (and demonstrated in http://gojs.net/latest/extensions/FloorPlanEditor.html), but with a single rotation handle and surrounding box. Is that correct?
I would guess that it wouldn’t be too hard to extend the RotateMultipleTool to do that.
Walter. How are you. Thanks for the response.
Mostly true. But what I found with the Floor Plan Editor was that the multiple objects were all resizing but not within a confine of a bounding box, and also they were overlapping each other when they got big enough. What we are looking for is that all the objects resize and remain the same relative distance between each other. So it would seem there is a bit of positional movement going on as well.
Look forward to hearing back. Have a great evening sir.
Wait – I thought you were asking about rotating, not resizing, multiple nodes.
You don’t have to use either the RotateMultipleTool or the ResizeMultipleTool extension, or you can modify either or both of them to do what you want.
I am not sure I understand your response.
We could discuss this outside of the forum if need be.
Hello all. Please let me know if we can communicate outside of the forum to clarify your last response.
You can send email, gojs at nwoods.com
I’m digging around the docs for similar functionality, can someone point me to code example for this?
As normal, the complete source code is in the page itself. Just do a “View Page Source” command in your browser.
Thank you!
When you drag one of the nodes in the selection, the selection of nodes moves as a group. We would like the MultiSelectionPart to also move (it currently repositions after the selection is placed). I’m digging through the docs and have not yet come across what I need. Can you point me in the right direction?
I’m not sure if the position needs to be recalculated or if there is a setting that needs to be toggled.
I think the customer who wanted that wanted the original bounds to be shown so that it’s clear to the user where the Nodes and Links came from.
If you want that special MultiSelectionPart to be moved along, the DraggingTool would need to be customized.
<!DOCTYPE html>
<html>
<head>
<title>Minimal GoJS Sample with Multi Selection Box</title>
<!-- Copyright 1998-2025 by Northwoods Software Corporation. -->
<meta name="description"
content="An almost minimal diagram using a very simple node template and the default link template." />
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<div id="myDiagramDiv" style="border: solid 1px black; width:400px; height:400px"></div>
This draws a box around all selected Parts that dynamically updates as the selection collection changes.
<script src="../latest/release/go.js"></script>
<script id="code">
class MultiSelectionDraggingTool extends go.DraggingTool {
constructor(init) {
super();
if (init) Object.assign(this, init);
}
computeEffectiveCollection(parts, options) {
const map = super.computeEffectiveCollection(parts, options);
if (MultiSelectionPart.visible) {
map.set(MultiSelectionPart, new go.DraggingInfo(MultiSelectionPart.position.copy()));
}
return map;
}
}
myDiagram = new go.Diagram("myDiagramDiv", { // create a Diagram for the DIV HTML element
draggingTool: new MultiSelectionDraggingTool(), // install custom DraggingTool
"undoManager.isEnabled": true, // enable undo & redo
"ChangedSelection": updateMultiSelectionPart,
"ModelChanged": e => {
if (e.isTransactionFinished) updateMultiSelectionPart();
}
});
// a single Part that surrounds all selected Parts when the Diagram.selection.count > 1
const MultiSelectionPart =
new go.Part({ layerName: "Tool", selectable: false, pickable: false, visible: false, copyable: false })
.add(
new go.Shape({ stroke: "blue", strokeWidth: 2.5, fill: null })
);
myDiagram.add(MultiSelectionPart);
function updateMultiSelectionPart() {
const diagram = MultiSelectionPart.diagram;
if (diagram === null) return;
let selnodes = 0; // only count selected non-links
const it = diagram.selection.iterator;
while (it.next()) {
const part = it.value;
if (part instanceof go.Link) continue;
selnodes++
if (selnodes > 1) break;
}
MultiSelectionPart.visible = selnodes > 1;
if (MultiSelectionPart.visible) {
const b = diagram.computePartsBounds(diagram.selection);
const shape = MultiSelectionPart.elt(0);
b.grow(shape.strokeWidth + 2, shape.strokeWidth, shape.strokeWidth, shape.strokeWidth + 2);
MultiSelectionPart.move(b.position);
shape.desiredSize = b.size;
}
}
// define a simple Node template
myDiagram.nodeTemplate =
new go.Node("Auto") // the Shape will go around the TextBlock
.add(
new go.Shape("RoundedRectangle", { strokeWidth: 0 })
// Shape.fill is bound to Node.data.color
.bind("fill", "color"),
new go.TextBlock({ margin: 8 }) // some room around the text
// TextBlock.text is bound to Node.data.key
.bind("text", "key")
);
// create the model data that will be represented by Nodes and Links
myDiagram.model = new go.GraphLinksModel(
[
{ key: "Alpha", color: "lightblue" },
{ key: "Beta", color: "orange" },
{ key: "Gamma", color: "lightgreen" },
{ key: "Delta", color: "pink" }
],
[
{ from: "Alpha", to: "Beta" },
{ from: "Alpha", to: "Gamma" },
{ from: "Beta", to: "Beta" },
{ from: "Gamma", to: "Delta" },
{ from: "Delta", to: "Alpha" }
]);
</script>
</body>
</html>