Inside Parts shouldn't move on Group resize

when I try the above snippet, though the group does not shrink but it allows me to first overlap boundaries of the adjacent group and then further pass over its boundary, when each time I try to keep going further into the adjacent group by the touching handle, though only by a whisker each time.

Try setting GroupMargin to a zero Margin. If you are allowing your groups to touch each other, clearly having a Margin of five isn’t suitable for your app.

I have kept margin only between the group boundaries and the nodes it contains, I tried setting to new go.Margin(0) , but it doesn’t work. It still allows my group to go inside another group with touching boundaries.

bound

I tried the following model along with the above code, but with GroupMargin set to a zero Margin:

    myDiagram.model = new go.GraphLinksModel([
      { key: "Alpha", group: "Epsilon", location: new go.Point(20, 70) },
      { key: "Beta", group: "Epsilon", location: new go.Point(40, 120) },
      { key: "Gamma", location: new go.Point(50, 200) },
      { key: "Delta", location: new go.Point(150, 100) },
      { key: "Epsilon", isGroup: true, location: new go.Point(10, 60), desiredSize: new go.Size(100, 100) },
      { key: "Alpha2", group: "Epsilon2", location: new go.Point(150, 170) },
      { key: "Beta2", group: "Epsilon2", location: new go.Point(140, 220) },
      { key: "Epsilon2", isGroup: true, location: new go.Point(111, 140), desiredSize: new go.Size(100, 100) }
    ],[
      { from: "Alpha", to: "Beta" },
      { from: "Beta", to: "Gamma" },
      { from: "Alpha", to: "Delta" }
    ]);

Note how the position of the “Epsilon2” group has an X value of 111 – that’s the position + width + strokeWidth of the “Epsilon” group, so that the two groups are touching each other but the strokes do not overlap.

If you change the X value of “Epsilon2” to be 110, the two group strokes are coincident, causing the _checkIntersections predicate to indicate that there is a problem. That causes the LimitedGroupResizingTool to use the original bounds, but it does so without accounting for the strokeWidth. You’ll need to change the code to account for the stroke width.

I understand, I tried accounting for the strokeWidth in the _lastOK variable, but still facing the same issue, does it needs to be accounted elsewhere?
this._lastOK = this.adornedObject.getDocumentBounds().addMargin(new go.Margin(2)); , where 2 is my strokeWidth.
Any Leads?

<!DOCTYPE html>
<html>
<head>
<title>Limited Group Resizing</title>
<!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
<meta charset="UTF-8">
<script src="go.js"></script>
<script id="code">
  // This is a custom ResizingTool that works on Groups and limits resizing so
  // that the group does not become smaller than needed to cover its members and
  // so that the group does not overlap any non-member nodes.
  // This assumes that Groups do not have a Placeholder and are not rotated.
  // This code requires version 2.1.8 or later.
  function LimitedGroupResizingTool() {
    go.ResizingTool.call(this);
    this._lastOK = new go.Rect();  // in document coordinates
  }
  go.Diagram.inherit(LimitedGroupResizingTool, go.ResizingTool);

  LimitedGroupResizingTool.prototype.computeMinSize = function() {
    var group = this.adornedObject.part;
    if (group instanceof go.Group) {
      // remember a "safe" bounds
      this._lastOK = this.adornedObject.getDocumentBounds().subtractMargin(GroupMargin);
      // Compute the minimum area needed by the members, as if there were a Placeholder.
      // This ignores ResizingTool.minSize and the adornedObject.minSize;
      // you can change this code to respect those properties if you want to.
      var membnds = group.diagram.computePartsBounds(group.memberParts);
      //membnds.addMargin(GroupPadding);
      membnds.unionPoint(this.oppositePoint);
      return membnds.size;
    } else {
      return go.ResizingTool.prototype.computeMinSize.call(this);
    }
  }

  LimitedGroupResizingTool.prototype._checkIntersections = function(r, group) {
    // this is slightly more efficient than findPartsIn
    var results = this.diagram.findObjectsIn(r,
      // ignore all Links and temporary Parts such as Adornments
      function(obj) {
        var p = obj.part;
        if (p.layer.isTemporary) return null;
        if (p instanceof go.Link) return null;
        return p;
      },
      // don't include the Group itself nor any of its member Nodes
      function(node) { return node !== group && !node.isMemberOf(group) },
      true);
    return results.count > 0;
  }

  LimitedGroupResizingTool.prototype.computeResize = function(newPoint, spot, min, max, cell, reshape) {
    var newRect = go.ResizingTool.prototype.computeResize.call(this, newPoint, spot, min, max, cell, reshape);
    var group = this.adornedObject.part;
    if (group instanceof go.Group) {
      // assume not rotated; use document coordinates
      var tl = this.adornedObject.getDocumentPoint(newRect.position);
      var br = this.adornedObject.getDocumentPoint(new go.Point(newRect.right, newRect.bottom));
      if (this._checkIntersections(new go.Rect(tl, br).addMargin(GroupMargin), group)) {
        return this._lastOK;  // too big! use last safe bounds
      } else {
        this._lastOK = newRect.copy();  // remember for future use
      }
    }
    return newRect;
  }

  LimitedGroupResizingTool.prototype.resize = function(newr) {
    var group = this.adornedObject.part;
    if (group instanceof go.Group) {
      var obj = this.adornedObject;
      obj.desiredSize = newr.size;
      group.ensureBounds();
      var pt = this.adornedObject.getDocumentPoint(this.handle.alignment.opposite());
      group.location = group.location.copy().subtract(pt).add(this.oppositePoint);
    } else {
      go.ResizingTool.prototype.resize.call(this, newr);
    }
  }
  // end of LimitedGroupResizingTool

  var GroupStrokeWidth = 2;
  var GroupMargin = new go.Margin(GroupStrokeWidth/2);
  var GroupPadding = new go.Margin(GroupStrokeWidth + 5);

  function init() {
    var $ = go.GraphObject.make;

    myDiagram =
      $(go.Diagram, "myDiagramDiv",
        { // replace the standard ResizingTool
          resizingTool: new LimitedGroupResizingTool(),
          "undoManager.isEnabled": true
        });

    // this is a Part.dragComputation function for limiting where a Node may be dragged
    function stayInGroup(part, pt, gridpt) {
      // don't constrain top-level nodes
      var grp = part.containingGroup;
      if (grp === null) return pt;
      // try to stay within the background Shape of the Group
      var back = grp.resizeObject;
      if (back === null) return pt;
      // allow dragging a Node out of a Group if the Shift key is down
      //if (part.diagram.lastInput.shift) return pt;
      var p1 = back.getDocumentPoint(go.Spot.TopLeft);
      var p2 = back.getDocumentPoint(go.Spot.BottomRight);
      var b = part.actualBounds;
      var loc = part.location;
      // no placeholder -- just assume some Margin
      var m = GroupPadding;
      // now limit the location appropriately
      var x = Math.max(p1.x + m.left, Math.min(pt.x, p2.x - m.right - b.width - 1)) + (loc.x - b.x);
      var y = Math.max(p1.y + m.top, Math.min(pt.y, p2.y - m.bottom - b.height - 1)) + (loc.y - b.y);
      return new go.Point(x, y);
    }

    myDiagram.nodeTemplate =
      $(go.Node,
        { dragComputation: stayInGroup },
        new go.Binding("location").makeTwoWay(),
        $(go.TextBlock, new go.Binding("text", "key"))
      );

    myDiagram.groupTemplate =
      $(go.Group, "Vertical",
        {
          resizable: true, resizeObjectName: "SHAPE",
          selectionObjectName: "SHAPE",
          locationObjectName: "SHAPE"
        },
        new go.Binding("location").makeTwoWay(),
        $(go.TextBlock, { font: "bold 11pt sans-serif" },
          new go.Binding("text", "key")),
        $(go.Shape, { name: "SHAPE", fill: "whitesmoke", strokeWidth: GroupStrokeWidth },
          new go.Binding("desiredSize").makeTwoWay())
      );

    myDiagram.model = new go.GraphLinksModel([
      { key: "Alpha", group: "Epsilon", location: new go.Point(20, 70) },
      { key: "Beta", group: "Epsilon", location: new go.Point(40, 120) },
      { key: "Gamma", location: new go.Point(0, 200) },
      { key: "Delta", location: new go.Point(150, 0) },
      { key: "Epsilon", isGroup: true, location: new go.Point(10, 60), desiredSize: new go.Size(100, 100) },
      { key: "Alpha2", group: "Epsilon2", location: new go.Point(150, 170) },
      { key: "Beta2", group: "Epsilon2", location: new go.Point(140, 220) },
      { key: "Epsilon2", isGroup: true, location: new go.Point(110, 140), desiredSize: new go.Size(100, 100) }
    ],[
      { from: "Alpha", to: "Beta" },
      { from: "Beta", to: "Gamma" },
      { from: "Alpha", to: "Delta" }
    ]);
  }
</script>
</head>
<body onload="init()">
  <div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:600px"></div>
  This sample uses a Group template that does not use a Placeholder, but allows the user
  to resize the group.  Resizing of a group will not allow the group to resize smaller than its members,
  nor to grow larger to cover any non-member nodes.
  Moving member nodes is limited to stay within the group.
</body>
</html>

Cool, issue fixed. Thank you once again.