Grouping using PolygonDrawing sample

Using PolygonDrawing sample i am trying to draw a group.So in finishShape method, i am making the node as group and the node inside the group as a member group.But The member node is not staying inside the group.When i am dragging the group or the member node, they are moving along not together.

GroupTemplate:

myDiagram.groupTemplate =
GO(go.Group,
new go.Binding(“location”, “loc”, go.Point.parse).makeTwoWay(go.Point.stringify),
GO(go.Panel, “Auto”,{ name: “PANEL” },
GO(go.Shape,
{ name: “SHAPE”, fill: “lightgray”, strokeWidth: 2},
new go.Binding(“angle”).makeTwoWay(),
new go.Binding(“geometryString”, “geo”).makeTwoWay(),
new go.Binding(“fill”),
new go.Binding(“stroke”),
new go.Binding(“strokeWidth”)
),
GO(go.Placeholder, { padding: 100 }),
)
);

finishShape method :

finishShape() {
var diagram = this.diagram;
var shape:go.Shape = this.temporaryShape();
if (shape !== null && this.archetypePartData() !== null) {
if (!diagram.lastInput.isTouchEvent) this.removeLastPoint();
var tempgeo = shape.geometry;

//getting the nodes inside the group.
 var objectInsideShape =  this.diagram.findObjectsIn(shape.actualBounds,
                    function (x: any) {
                        var part = x.part;
                        if ((part instanceof go.Link || part instanceof go.Node) && !(part instanceof go.Group) ) {
                          console.log(part.data)
                                 return part;
                        }
                        return null;
                    },
                    null, true);

this.objectsInShape = objectInsideShape
// require 3 points (2 segments) if polygon; 2 points (1 segment) if polyline
if (tempgeo.figures.first().segments.count >= (this.isPolygon() ? 2 : 1)) {
var viewpt = diagram.viewportBounds.position;
var geo = tempgeo.copy();
if (this.isPolygon()) {
var segs = geo.figures.first().segments;
var seg = segs.elt(segs.count-1);
seg.isClosed = true;
}
// create the node data for the model
var d = diagram.model.copyNodeData(this.archetypePartData());

//creating the group
   d["isGroup"] = true; 

// adding data to model creates the actual Part
diagram.model.addNodeData(d);
var part = diagram.findPartForData(d);
//creating member node
objectInsideShape.each((e:go.Part) => e.data[“group”] = part.data[“uuid”]);

// assign the position for the whole Part
var pos = geo.normalize();
pos.x = viewpt.x - pos.x - shape.strokeWidth / 2;
pos.y = viewpt.y - pos.y - shape.strokeWidth / 2;
part.position = pos;
// assign the Shape.geometry
var shape1:any = part.findObject(“SHAPE”);
if (shape1 !== null) {
shape1.geometry = geo;
}
this.transactionResult = this.name;
}
}
}
this.stopTool();
};

so,the diagramJson is changing. i think the panel is not updating.Thats why when i am dragging the group,the member nodes are not moving.

shape.actualBounds will be in its containing Panel’s coordinate system, not document coordinates. Use shape.part.actualBounds instead.

You cannot just modify some data property – GoJS cannot know that the property was modified. So you need to call http://gojs.net/latest/api/symbols/GraphLinksModel.html#setGroupKeyForNodeData in order to add existing node data to a group data object. That’s at the model level. At the diagram level you can just set Node.containingGroup.

Thanks for the reply.
But i have another issue.Using PolygonDrawing,i am drawing the shape for the group.But panel is coming as a Rectangle or square only.

so, I want the panel same as the Shape in the figure.

You are setting the SHAPE.geometry, so the group ought to be showing that shape.
Can you describe the situation more precisely?

As,I followed PolygonDrawing sample, so it changing the shape.I understood that i am changing the shape and not panel.
But I am not getting any way to change the panel.
So when we make group using ctrl+g, panel and shape size is same and i think any nodes are in group means it is in the panel.So when i drag any node inside group,it is always inside the shape/panel/group.
But here there are some gap between panel and shape,so when i am dragging a node inside group(shape),it is dragging out upto the pannel,so coming out from shape and then becoming ungroup.

I am having a hard time understanding the situation that you are trying to describe.

Might the problem be that your Group’s Shape.fill needs to be set to a color or to “transparent”?

Actually i want the shape(black color box) and panel(blue color box) size same, e.g. ‘L’ size in my figure.
Because:
1.Then it will looks good.

2.Say node A,B,C are inside group.

Now problem is coming if i drag any Node towards top-right corner.

because there is a empty space.So Nodes are going out of Shape.

When Node A goes out of the shape and reach the empty space it is becoming ungroup.

SO, think if both the shape and panel is same size then this problem will not come.

That blue rectangle is the default selection Adornment. If the Group were not selected, the user would still be able to do exactly same movement resulting in exactly the same removal from the group.

But if you want the selection Adornment to be different, or if you do not want a selection Adornment at all, you can do that. Read more at http://gojs.net/latest/intro/selection.html.

No,I want the shape and panel size same e.g ‘L’ in the figure.

GroupTemplate :
myDiagram.groupTemplate =
GO(go.Group, “Vertical”,
new go.Binding(“location”, “loc”, go.Point.parse).makeTwoWay(go.Point.stringify),
{
selectionObjectName: “SHAPE”,
mouseDrop: finishDrop
},
GO(go.Panel, “Auto”,{ name: “PANEL”,background:“lightgray” },
GO(go.Shape,
{ fill: “lightblue”, stroke: “black”, strokeWidth: 2, name: “SHAPE” },
new go.Binding(“stroke”, “color”).makeTwoWay(),
new go.Binding(“geometryString”, “geo”).makeTwoWay(),
),
GO(go.Placeholder, { padding: 50 })
)
);

I know in the PolygonDrawing sample it is changing the Shape.So the Shape is changing but Panel is coming squre or rectangle.So, is there any way i can change the panel size also like the shape.

http://gojs.net/latest/intro/selection.html discusses several possibilities. The easiest might be to not use a selection Adornment at all.

But,I dont want selection.I just want to change the panel as shape.Like the shape can we change the panel structure.?

Panels do not draw anything, unless you set the panel’s background. So panels do not have any inherent shape, although they do have an actualBounds.

I still think that if you are concerned that the blue rectangle (the selection handle) is confusing to the user, that you set selectionAdorned: false and change the appearance of the group when it is selected.

Ok.I have changed the selectionAdornmentTemplate

Now it looks like :

.But how to solve my main problem i.e. stopping the object going out of the shape.??

OK, now your problem is that the user might drag a member node outside of the group in any direction, yes?

Have you seen all of the examples of customizing the Part.dragComputation? There is one, stayInGroup, that gets you part of what you want. You just have to extend that function so that it works with the actual Shape.geometry, not assuming it is always rectangular. You can call Diagram.findObjectAt to see if a particular point is inside the Group. GoJS does not provide a general intersection predicate that takes two arbitrary shapes.

Yes.
I am trying ‘dragComputation: stayInGroup’. I am getting the group shape but if i use
gshape.getDocumentPoint(go.Spot.TopLeft);
gshape.getDocumentPoint(go.Spot.BottomRight);
it is not reflecting any change.It is returning the topLeft and bottomRight point for rectangle shape only.
I am not getting any idea how to change the logic for getting the geometry area points.

That is why I suggested calling findObjectAt. You will need to make sure the predicate to that method ignores the node(s) that the user is dragging.

This might be instructive: http://gojs.net/latest/samples/dragUnoccupied.html

I am not getting any idea of how to write the logic in stayInGroup with Diagram.findObjectAt.
Can you please give any idea what logic i should write for keeping the node inside any type of shape??

If I get some free time later today, I might be able to work on this for you.

What have you implemented as your Node.dragComputation function so far?

I have done like that:

function stayInGroup(part, pt, gridpt) {

        var grp = part.containingGroup;
        if (grp === null) return pt;
       
        var gshape = grp.findObject("GSHAPE");           
        if (gshape === null) return pt;
        var sab = gshape.actualBounds;

        // get the bounds of the part being dragged
        var bnds = part.actualBounds;
        var loc = part.location;

        function navig(obj) {
            var npart = obj.part;
            if (npart === part) return null;
            if (npart instanceof go.Link) return null;
            return npart;
        }

        function pred(obj) {
            if(obj != part) return true;
        }

        var diagram = part.diagram;
        var gro = diagram.findObjectAt(pt, navig, pred);  
        if(gro instanceof go.Group){
            return pt;
        }
        return loc;
   }

It is working but not fully.When i am trying to drag a node outside of the group,it is not going out but mouse is going out of group.And then if i drop my mouse outside the group,the node is going there and becoming ungroup.

<!DOCTYPE html>
<html>
<head>
<title>Stay In Group With Non-Rectangular Shape </title>
<!-- Copyright 1998-2017 by Northwoods Software Corporation. -->
<meta charset="UTF-8">
<script src="go.js"></script>
<script id="code">
  function init() {
    var $ = go.GraphObject.make;

    var GroupMargin = new go.Margin(5);

    myDiagram =
      $(go.Diagram, "myDiagramDiv",
        {
          initialContentAlignment: go.Spot.Center,
          "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;

      // allow dragging a Node out of a Group if the Shift key is down
      //if (part.diagram.lastInput.shift) return pt;

      // assume all Groups, and only Groups, are in the "Background" Layer
      var backlayer = part.diagram.findLayer("Background");

      // look at four corners of the PART's bounds, expanded by the desired GroupMargin
      var b = part.actualBounds.copy().addMargin(GroupMargin);
      // and moved to the proposed location PT
      b.x += pt.x - part.location.x;
      b.y += pt.y - part.location.y;

      function onlyGroupShape(obj) {
        if (obj.part !== grp) return null;  // ignore all other Groups
        if (obj.name !== "SHAPE") return null;  // ignore all other objects within Group
        return obj;  // only return the main SHAPE of the Group
      }

      // now make sure all four proposed corners are inside the PART's Group's Shape;
      // if any corner is outside the shape, leave the PART at its current location
      var corner = new go.Point(b.x, b.y);
      if (backlayer.findObjectAt(corner, onlyGroupShape) === null) return part.location;
      corner.x = b.x + b.width;
      if (backlayer.findObjectAt(corner, onlyGroupShape) === null) return part.location;
      corner.y = b.y + b.height;
      if (backlayer.findObjectAt(corner, onlyGroupShape) === null) return part.location;
      corner.x = b.x;
      if (backlayer.findObjectAt(corner, onlyGroupShape) === null) return part.location;

      // all four corners will be inside the Group's Shape -- OK
      // NOTE: this will not be as smooth as a dragComputation function that finds the nearest
      // location where the PART will be fully inside the Group's Shape, but that's harder to implement.
      return pt;
    }

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

    myDiagram.groupTemplate =
      $(go.Group, "Vertical",
        { locationObjectName: "SHAPE", layerName: "Background", selectionAdorned: false },
        new go.Binding("location").makeTwoWay(),
        $(go.TextBlock, { font: "bold 11pt sans-serif" },
          new go.Binding("text", "key")),
        $(go.Shape,
          {
            name: "SHAPE", fill: "whitesmoke",
            geometryString: "F1 M0 0 L100 0 100 100 200 100 200 200 0 200 z"
          },
          new go.Binding("fill", "isSelected", function(sel) { return sel ? "dodgerblue" : "whitesmoke"; }).ofObject())
      );

    myDiagram.model = new go.GraphLinksModel([
      { key: "Alpha" },
      { key: "Beta", group: "Epsilon", location: new go.Point(20, 70) },
      { key: "Gamma", group: "Epsilon", location: new go.Point(40, 120) },
      { key: "Delta" },
      { key: "Epsilon", isGroup: true, location: new go.Point(10, 60) }
    ],[
      { from: "Alpha", to: "Beta" },
      { from: "Beta", to: "Gamma" },
      { from: "Gamma", to: "Delta" }
    ]);
  }
</script>
</head>
<body onload="init()">
  <div id="myDiagramDiv" style="border: solid 1px black; width:100%; height:600px"></div>
</body>
</html>