Inside Parts shouldn't move on Group resize

This is only making the dragging of left side work but still using the area of the right as I am also using the group to limit shrinking like in Swim Lanes example and if an inside part is on the limit of the right boundary the shrink from left is also getting restricted.
Also the resize icon is not coming, not just from current update but from the starting of the resize override.

OK, I just tried the code I (eventually) gave you above, and it seems to work as I believe you were originally asking for.

If you want to limit how far the resize can go, you’ll want to override ResizingTool.computeResize. But this code is getting complicated enough that I suggest that you implement a separate class rather than depending on JavaScript overrides via setting properties. If you are using ES6 or TypeScript, that would be more natural anyway.

I am currently using the below code to compute a resizing limit and fix resizing (from left and top sides of the group) for my group at the same time. Although both event codes interfere to produce errors and additional issues if used together,
can there be a way both parts be used at once in a single event, either ‘computeResize’ or ‘Resize’, to provide me the desired functionality ?

"resizingTool.computeResize"(newPoint, spot, min, max, cell, reshape) {
        const group = this.adornedObject.part;
        var newrect = go.ResizingTool.prototype.computeResize.call(this, newPoint, spot, min, max, cell, reshape);
        var anyrect = go.ResizingTool.prototype.computeResize.call(this, newPoint, spot, new go.Size(0, 0), max, cell, reshape);

        if (newrect.width > anyrect.width || newrect.height > anyrect.height) {
          console.log("No Shrinking is allowed");
          group.diagram.currentTool.transactionResult = group.diagram.currentTool.name;
          group.diagram.currentTool.stopTool();
        }
        return newrect;
      },
"resizingTool.resize": function (newr) {
        var diagram = this.diagram;
        if (diagram === null) return;
        var obj = this.adornedObject;
        var part = obj.part;

        // calculate new location
        var angle = obj.getDocumentAngle();
        var sc = obj.getDocumentScale();

        var radAngle = Math.PI * angle / 180;
        var angleCos = Math.cos(radAngle);
        var angleSin = Math.sin(radAngle);

        var angleRight = (angle > 270 || angle < 90) ? 1 : 0;
        var angleBottom = (angle > 0 && angle < 180) ? 1 : 0;
        var angleLeft = (angle > 90 && angle < 270) ? 1 : 0;
        var angleTop = (angle > 180 && angle < 360) ? 1 : 0;

        var deltaWidth = newr.width - obj.naturalBounds.width;
        var deltaHeight = newr.height - obj.naturalBounds.height;

        var pos = part.position.copy();
        pos.x += sc * ((newr.x + deltaWidth * angleLeft) * angleCos - (newr.y + deltaHeight * angleBottom) * angleSin);
        pos.y += sc * ((newr.x + deltaWidth * angleTop) * angleSin + (newr.y + deltaHeight * angleLeft) * angleCos);

        obj.desiredSize = newr.size;
        go.Node.prototype.move.call(part, pos);
      }

When one needs to override multiple methods of a class, I suggest that you define a subclass (in this case of ResizingTool) and replace the standard ToolManager.resizingTool with an instance of your custom class. This is just a matter of code organization, but it may free your thinking when deciding how to solve your problem. Remember that all of the method overrides employed by the standard ResizingTool were needed in its implementation, so overriding more than one method is entirely plausible.

Your requirements are unclear enough and code complicated enough that I am unable to provide any specific advice. I do hope that you can debug your code.

Okay, so my specific requirement is…
I have multiple groups on a layout as shown in the question itself, I want to -
firstly, restrict the resizing of groups up until the boundary of some other group placed near to it. (max limit)
second, restrict downsizing of groups till the boundaries of the multiple nodes placed inside the group. (min limit)
third, I want to fix this particular resizing issue from left and top sides of the group, so as they work like i resize it from right and bottom sides.

Right now I am overloading computeMinSize, computeResize, and resize events of the resizingTool, Although they kind of work fine when used separately, but gives me additional issues when used together.
How can I club all points mentioned above in a single custom class ?

Given below is my code for reference -

"resizingTool.computeMinSize"() {
        const group = this.adornedObject.part;

        if (group.memberParts.count) {
          const membnds = group.diagram.computePartsBounds(group.memberParts);
          membnds.addMargin(parentctxt.GroupMargin);
          membnds.unionPoint(group.location);
          return membnds.size;
        }
        else {
          return new go.Size(50, 50); // returning min bounds
        }
      },
"resizingTool.computeResize"(newPoint, spot, min, max, cell, reshape) {
        const group = this.adornedObject.part;
        var newrect = go.ResizingTool.prototype.computeResize.call(this, newPoint, spot, min, max, cell, reshape);
        var anyrect = go.ResizingTool.prototype.computeResize.call(this, newPoint, spot, new go.Size(0, 0), max, cell, reshape);

        if (newrect.width > anyrect.width || newrect.height > anyrect.height) {
          console.log("No Shrinking is allowed");
          group.diagram.currentTool.transactionResult = group.diagram.currentTool.name;
          group.diagram.currentTool.stopTool();
        }
        return newrect;
      },
"resizingTool.resize": function (newr) {
        var diagram = this.diagram;
        if (diagram === null) return;
        var obj = this.adornedObject;
        var part = obj.part;

        // calculate new location
        var angle = obj.getDocumentAngle();
        var sc = obj.getDocumentScale();

        var radAngle = Math.PI * angle / 180;
        var angleCos = Math.cos(radAngle);
        var angleSin = Math.sin(radAngle);

        var angleRight = (angle > 270 || angle < 90) ? 1 : 0;
        var angleBottom = (angle > 0 && angle < 180) ? 1 : 0;
        var angleLeft = (angle > 90 && angle < 270) ? 1 : 0;
        var angleTop = (angle > 180 && angle < 360) ? 1 : 0;

        var deltaWidth = newr.width - obj.naturalBounds.width;
        var deltaHeight = newr.height - obj.naturalBounds.height;

        var pos = part.position.copy();
        pos.x += sc * ((newr.x + deltaWidth * angleLeft) * angleCos - (newr.y + deltaHeight * angleBottom) * angleSin);
        pos.y += sc * ((newr.x + deltaWidth * angleTop) * angleSin + (newr.y + deltaHeight * angleLeft) * angleCos);

        obj.desiredSize = newr.size;
        go.Node.prototype.move.call(part, pos);
      }

It seems to me that you’d want to customize the ResizingTool so that when resizing a Group, depending on the chosen handle, you could check ahead of time for how far that side (for a handle on a side) or those two adjacent sides (for a corner handle) can go. You can call Diagram.findPartsIn to find all of the Parts that are (partially) in a given Rect, which you can start from the group’s chosen side being resized.

That’s easy enough to do when the group is not rotated, but I notice that you seem to want to handle rotated groups, so testing for empty areas gets more complicated.

No, I don’t need any rotation for my groups. They can either be dragged (for which I have overloaded dragComputation which works just fine) or resized.

I am still unable to create what I desire here, when I override the “resizingTool.resize” with the below code is when my groups starts to overlap other groups since it does not have a calculation of that I believe (although the groups wont overlap if I do not override this method.), can you help ?

"resizingTool.doActivate": function() {
      go.ResizingTool.prototype.doActivate.call(this);
      this.originalPoint = this.adornedObject.getDocumentPoint(this.handle.alignment.opposite());
    },
"resizingTool.resize": function(newr) {
      var obj = this.adornedObject;
      var part = obj.part;

      obj.desiredSize = newr.size;

      part.ensureBounds();
      var pt = this.adornedObject.getDocumentPoint(this.handle.alignment.opposite());
      part.location = part.location.copy().subtract(pt).add(this.originalPoint);
    }

I don’t see any code where you are calling Diagram.findPartsIn to try to find the permissible area that the resized group may occupy. And you still haven’t defined a subclass of ResizingTool as I recommended. If you don’t ask a specific question about your code, it’s hard to help.

Ok, so after implementing your recommendations I was able to solve the problem of overlapping of groups while resizing. Have a look at the below code, can you firstly verify if my “temp” variable creation looks fine to you and will it be fool proof ?

this.floorPlan = $(Floorplan, {
      "resizingTool": new customResizingTool()
    });

function customResizingTool() {
  go.ResizingTool.call(this);
}
go.Diagram.inherit(customResizingTool, go.ResizingTool);

customResizingTool.prototype.computeMinSize = function () {
  const group = this.adornedObject.part;

  if (group.memberParts.count) {
    const membnds = group.diagram.computePartsBounds(group.memberParts);
    membnds.addMargin(new go.Margin(5));
    membnds.unionPoint(group.location);
    return membnds.size;
  }
  else {
    return new go.Size(50, 50); // returning min bounds
  }
};
customResizingTool.prototype.doActivate = function () {
  go.ResizingTool.prototype.doActivate.call(this);
  this.originalPoint = this.adornedObject.getDocumentPoint(this.handle.alignment.opposite());
};
customResizingTool.prototype.resize = function (newr) {
  var obj = this.adornedObject;
  var part = obj.part;

  var temp = this.diagram.findPartsIn(new go.Rect(new go.Point(obj.part.position.x + obj.actualBounds.position.x, obj.part.position.y + obj.actualBounds.position.y), new go.Point(obj.part.position.x + obj.actualBounds.size.width, obj.part.position.y + obj.actualBounds.size.height)), true);
  var groups = temp.filter(x => x.data.isGroup);
  if (groups.count > 1) {
    part.diagram.currentTool.stopTool();
  }
  else {
    obj.desiredSize = newr.size;
    part.ensureBounds();
    var pt = this.adornedObject.getDocumentPoint(this.handle.alignment.opposite());
    part.location = part.location.copy().subtract(pt).add(this.originalPoint);
  }
};

secondly, I can still see an issue where the top and left sides of the group does not respect computeMinSize (thats means the group can be downsized leaving the nodes contained in the group outside the group), and where right and bottom sides works fine.

Here you go:

<!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.
  function LimitedGroupResizingTool() {
    go.ResizingTool.call(this);
    this._oppositePoint = new go.Point();  // in document coordinates
    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) {
      this._oppositePoint = this.adornedObject.getDocumentPoint(this.handle.alignment.opposite());
      // remember a "safe" bounds
      this._lastOK = this.adornedObject.getDocumentBounds();

      // 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(GroupMargin);
      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), 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 GroupMargin = new go.Margin(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 = GroupMargin;
      // 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" },
          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(50, 200) },
      { key: "Delta", location: new go.Point(150, 100) },
      { key: "Epsilon", isGroup: true, location: new go.Point(10, 60) }
    ],[
      { 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>

Great, this works like I wanted it to be. Thank you.

Although there is only one issue I am noticing,
When two groups are touching boundaries and I try to further increase the size of one of these groups by holding any handle on the touching side, the group immediately reduces to a small box ignoring the computeMinSize .

Notice the value for this._lastOK assigned in computeMinSize. If the first computeResize finds that there are undesirable overlaps, it shrinks the group down to the bounds needed to cover only the member nodes.

If you can assume that all groups start off not overlapping any non-member nodes, then you should set this._lastOK to the initial bounds of the group’s adornedObject.

    this._lastOK = this.adornedObject.getDocumentBounds();

I have updated the code above.

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.