How to make Diagram rectangle exclusive for each other at all

I have Diagram(official sample here: like this:

As it be, if I keep dragging “Harry” towards “Slytherin”, it will make “Gryffindor” move towards it too.

And at last, it will become like:

I need “Slytherin” and “Gryffindor” to be exclusive at all, no matter how “Harry” moves. If “Harry” moves out of his school, that would be not allowed.

Any help would be appreciated, thank you.

So you want to make sure Slytherin and Gryffindor do not overlap? But you want to allow Harry to be moved by the user? And you want to limit Harry’s movement to stay within Hogwarts, but allow him to go into Slytherin (“stay within school”)? Did you want to allow Harry to be dragged out of Gryffindor? And it’s OK if the bounds of each house changes, so long as they do not overlap? Or should the bounds of each house be fixed?

This is a bit complex. I guess I do not understand the relationships that you want to model. Do the house groups represent abstract membership or physical area?

The most general answer is to override methods of DraggingTool. But in some cases it is sufficient to specify the Part.dragComputation function on the Node template. And in the easiest cases you can just set or bind the Part.minLocation and maxLocation.

I just want Harry to stay within his house(No group-changing), but not to make Gryffindor getting overlapped with Slytherin by moving himself just like this gif showed below:

You see, at last the Gryffindor and Slytherin do overlapped by moving Harry.

How did I to stop Gryffindor and Slytherin to get overlapped by moving Harry?

Also when Gryffindor gets more children, and it will be like:

The question still remains, How did I to stop Gryffindor and Slytherin to get overlapped by moving Gryffindor 's students(children)?

Another terminology correction: to avoid confusing the “children” of parent in trees and the “children” of groups, we only refer to nodes in groups as “members”. So “children” only are in trees. And Harry and Hermione are members of Gryffindor.

Try the LimitedDraggingTool in this sample:

<!DOCTYPE html>
  <meta charset="UTF-8">
  <title>Custom Dragging Avoiding Overlapping Groups</title>
  <meta name="description" content="Demonstrates a custom DraggingTool that prevents moved or resized Nodes, including unselected Groups, from overlapping unmoving Nodes." />
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <!-- Copyright 1998-2020 by Northwoods Software Corporation. -->
  <script src="go.js"></script>
  <script id="code">
    // The custom DraggingTool that disallows moving nodes such that their containing groups
    // do not overlap their rectangular bounds with unchanging nodes or groups.
    function LimitedDraggingTool() {;
      this._stationary = new go.Set();  // undragged Nodes or Groups that will not change bounds during the drag
      this._changing = new go.Set();  // undragged Groups that might change bounds during the drag
      this._lastOK = new go.Point();  // in document coordinates
    go.Diagram.inherit(LimitedDraggingTool, go.DraggingTool);

    // Just once for each drag compute the _stationary and _changing Sets.
    LimitedDraggingTool.prototype.doActivate = function() {;

      // collect unmoving nodes
      // Future optimization: only consider stationary nodes that are "near" to the dragged nodes
      var stationary = this._stationary;
      var dragged = this.draggedParts;  // already computed, includes member nodes of moving groups
      // NOTE: dragged is a Map<Part, DraggingInfo>, and some of those Parts will be Links
      this.diagram.nodes.each(function(n) {
        // obviously nodes being dragged aren't stationary
        if (dragged.has(n)) return;
        // if N is a Group and is an (indirect) container of a dragged Part, N cannot be assumed to be stationary
        if (n instanceof go.Group &&
            dragged.any(function(kvp) {
              return !(kvp.key instanceof go.Link) && kvp.key.isMemberOf(n);
            })) {

      // collect groups whose bounds may change due to the drag that are not
      // selected or members of selected nodes but might still change bounds
      var changing = this._changing;
      this.diagram.nodes.each(function(n) {
        // ignore stationary and dragged parts
        if (stationary.has(n) || dragged.has(n)) return;

      // assume nodes start are initially not overlapping
      this._lastOK = this.diagram.firstInput.documentPoint.copy();

    // Cleanup
    LimitedDraggingTool.prototype.doDeactivate = function() {;

    // See if there are now any undesired overlaps.
    LimitedDraggingTool.prototype.hasOverlaps = function() {
      var sit = this._stationary.iterator;
      while ( {
        var st = sit.value;
        var stb = st.actualBounds;
        // see if any dragged part intersects with any stationary part
        var dit = this.draggedParts.iteratorKeys;
        while ( {
          var dr = dit.value;
          if (dr instanceof go.Link) continue;
          if (dr.actualBounds.intersectsRect(stb)) {
            return true;
        // see if any changing part intersects with any stationary part
        var cit = this._changing.iterator;
        while ( {
          var ch = cit.value;
          if (!st.isMemberOf(ch) && ch.actualBounds.intersectsRect(stb)) {
            return true;
      return false;

    // Try to move normally and see if that causes any overlaps.
    // If it does, move back to the last known OK point.
    LimitedDraggingTool.prototype.doMouseMove = function() {;
      var e = this.diagram.lastInput;
      var overlaps = this.hasOverlaps();
      if (overlaps) {
        e.documentPoint = this._lastOK;
        e.viewPoint = e.diagram.transformDocToView(e.documentPoint);;
      } else {
        this._lastOK = e.documentPoint.copy();

    // Try to move normally, but go back if it causes overlaps.
    // Always call the super .doMouseUp method.
    LimitedDraggingTool.prototype.doMouseUp = function() {
      // first try moving to mouse point;
      var e = this.diagram.lastInput;
      var overlaps = this.hasOverlaps();
      if (overlaps) {
        // failed -- move to last known safe point
        e.documentPoint = this._lastOK;
        e.viewPoint = e.diagram.transformDocToView(e.documentPoint);;

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

      myDiagram =
        $(go.Diagram, "myDiagramDiv",
          { // install custom DraggingTool
            draggingTool: new LimitedDraggingTool(),
            "undoManager.isEnabled": true

      myDiagram.nodeTemplate =
        $(go.Node, "Auto",
          new go.Binding("location", "loc"),
            locationSpot: go.Spot.Center,
            toEndSegmentLength: 30,
            fromEndSegmentLength: 30
          $(go.Shape, "Rectangle",
            { fill: "white", desiredSize: new go.Size(30, 30) }),
            { margin: 4 },
            new go.Binding("text", "key"))

      myDiagram.linkTemplate =
          { curve: go.Link.Bezier, toShortLength: 2 },
          $(go.Shape, { toArrow: "Standard" })

      myDiagram.groupTemplate =
        $(go.Group, "Spot",
            toSpot: go.Spot.AllSides, // links coming into groups at any side
            toEndSegmentLength: 30,
            fromEndSegmentLength: 30
          $(go.Panel, "Auto",
            $(go.Shape, "Rectangle",
              { parameter1: 14, fill: "rgba(255,0,0,0.10)" }),
              { padding: 16 })
              alignment: go.Spot.TopLeft,
              alignmentFocus: new go.Spot(0, 0, -4, -4),
              font: "Bold 10pt Sans-Serif"
            new go.Binding("text", "key"))

      myDiagram.model = new go.GraphLinksModel(
        [ // node data
          { key: "A", loc: new go.Point(320, 100) },
          { key: "B", loc: new go.Point(420, 200) },
          { key: "C", group: "Psi", loc: new go.Point(250, 225) },
          { key: "D", group: "Omega", loc: new go.Point(270, 325) },
          { key: "E", group: "Phi", loc: new go.Point(120, 225) },
          { key: "F", group: "Omega", loc: new go.Point(200, 350) },
          { key: "G", loc: new go.Point(180, 450) },
          { key: "Chi", isGroup: true },
          { key: "Psi", isGroup: true, group: "Chi" },
          { key: "Phi", isGroup: true, group: "Psi" },
          { key: "Omega", isGroup: true, group: "Psi" }
        [  // link data
          { from: "A", to: "B" },
          { from: "A", to: "C" },
          { from: "A", to: "C" },
          { from: "B", to: "B" },
          { from: "B", to: "C" },
          { from: "B", to: "Omega" },
          { from: "C", to: "A" },
          { from: "C", to: "Psi" },
          { from: "C", to: "D" },
          { from: "D", to: "F" },
          { from: "E", to: "F" },
          { from: "F", to: "G" }
<body onload="init()">
  <div id="sample">
    <div id="myDiagramDiv" style="border: solid 1px black; height: 560px; display: inline-block; vertical-align: top; width: 70%"></div>

This is just the Navigation sample,, augmented with the custom LimitedDraggingTool.

Thank u very much! Problem solved!