Dragging tool grid snap multiple nodes selected

Hi,

Given the following example:

<!DOCTYPE html>
<html>
  <head>
    <title>Snap to grid</title>
    <meta charset="UTF-8" />
  </head>

  <body onload="init()" style="margin: 0; padding: 0; overflow: hidden">
    <div id="app">
      <div
        id="diagram"
        style="border: solid 1px black; width: 100%; height: calc(100vh)"
      ></div>
    </div>

    <script src="https://unpkg.com/gojs"></script>
    <script id="code">
      function init() {
        var $ = go.GraphObject.make; // for conciseness in defining templates

        myDiagram = $(
          go.Diagram,
          "diagram", // create a Diagram for the DIV HTML element
          {
            initialScale: 2.0,
            "draggingTool.isGridSnapEnabled": true,
            "grid.visible": true,
          }
        );

        // define a simple Node template
        myDiagram.nodeTemplate = $(
          go.Node,
          "Auto", // the Shape will go around the TextBlock
          new go.Binding("location", "location"),
          $(
            go.Shape,
            "RoundedRectangle",
            { strokeWidth: 0, fill: "white" },
            // Shape.fill is bound to Node.data.color
            new go.Binding("fill", "color")
          ),
          $(
            go.TextBlock,
            { margin: 8, font: "bold 14px sans-serif", stroke: "#333" }, // Specify a margin to add some room around the text
            // TextBlock.text is bound to Node.data.key
            new go.Binding("text", "key")
          )
        );

        // but use the default Link template, by not setting Diagram.linkTemplate

        // create the model data that will be represented by Nodes and Links
        myDiagram.model = new go.GraphLinksModel([
          {
            key: "Alpha",
            color: "lightblue",
            location: new go.Point(0, 0),
          },
          { key: "Beta", color: "orange", location: new go.Point(195, 0) },
        ]);
        myDiagram.model.linkKeyProperty = "key";
      }
    </script>
  </body>
</html>

When you select both nodes and start dragging over the grid, each nodes snaps individually.

snap

Would it be possible to snap the selected collection synchronously based on a common point so that the original distance between the nodes would not be affected while dragging? We are using the GuidedDraggingTool and the same thing happens when the parts are being snapped to a guide.

Thanks!

You could override DraggingTool.doMouseMove to snap the Diagram.lastInput event’s documentPoint to the grid, and then call the super method.

export default class GuidedDraggingTool extends go.DraggingTool {
  public doMouseMove() {
    this.diagram.lastInput.documentPoint.snapToGridPoint(this.diagram.grid.gridOrigin, this.diagram.grid.gridCellSize);
    super.doMouseMove();
  }
}

This does not seem to work

In general it is forbidden to modify the values of any Point, Size, Rect, Margin, or Spot that is returned by getting any property. Think of instances of those classes being immutable once “used”.

Try assigning InputEvent.documentPoint.

export default class GuidedDraggingTool extends go.DraggingTool {
  public doMouseMove() {
    this.diagram.lastInput.documentPoint = this.diagram.lastInput.documentPoint
      .copy()
      .snapToGridPoint(this.diagram.grid.gridOrigin, this.diagram.grid.gridCellSize);
    super.doMouseMove();
  }
}

It appears to give the same result

Hmmm, we’ll look into this later.

1 Like

This works for me:

  $(go.Diagram, "myDiagramDiv",
    {
      "draggingTool.isGridSnapEnabled": true,
      "draggingTool.doMouseMove": function() {
        const p = this.diagram.lastInput.documentPoint.copy();
        const grid = this.diagram.grid;
        p.snapToGridPoint(grid.gridOrigin, grid.gridCellSize);
        this.diagram.lastInput.documentPoint = p;
        go.DraggingTool.prototype.doMouseMove.call(this);
      },
      . . .

(Using modern class syntax shouldn’t matter.)

I added your code to the example:

<!DOCTYPE html>
<html>
  <head>
    <title>Snap to grid</title>
    <meta charset="UTF-8" />
  </head>

  <body onload="init()" style="margin: 0; padding: 0; overflow: hidden">
    <div id="app">
      <div
        id="diagram"
        style="border: solid 1px black; width: 100%; height: calc(100vh)"
      ></div>
    </div>

    <script src="https://unpkg.com/gojs"></script>
    <script id="code">
      function init() {
        var $ = go.GraphObject.make; // for conciseness in defining templates

        myDiagram = $(
          go.Diagram,
          "diagram", // create a Diagram for the DIV HTML element
          {
            initialScale: 2.0,
            "draggingTool.isGridSnapEnabled": true,
            "draggingTool.doMouseMove": function () {
              const p = this.diagram.lastInput.documentPoint.copy();
              const grid = this.diagram.grid;
              p.snapToGridPoint(grid.gridOrigin, grid.gridCellSize);
              this.diagram.lastInput.documentPoint = p;
              go.DraggingTool.prototype.doMouseMove.call(this);
            },
            "grid.visible": true,
          }
        );

        // define a simple Node template
        myDiagram.nodeTemplate = $(
          go.Node,
          "Auto", // the Shape will go around the TextBlock
          new go.Binding("location", "location"),
          $(
            go.Shape,
            "RoundedRectangle",
            { strokeWidth: 0, fill: "white" },
            // Shape.fill is bound to Node.data.color
            new go.Binding("fill", "color")
          ),
          $(
            go.TextBlock,
            { margin: 8, font: "bold 14px sans-serif", stroke: "#333" }, // Specify a margin to add some room around the text
            // TextBlock.text is bound to Node.data.key
            new go.Binding("text", "key")
          )
        );

        // but use the default Link template, by not setting Diagram.linkTemplate

        // create the model data that will be represented by Nodes and Links
        myDiagram.model = new go.GraphLinksModel([
          {
            key: "Alpha",
            color: "lightblue",
            location: new go.Point(0, 0),
          },
          { key: "Beta", color: "orange", location: new go.Point(195, 0) },
        ]);
        myDiagram.model.linkKeyProperty = "key";
      }
    </script>
  </body>
</html>

But the problem does not go away:

snap-wrong

You see that when I start dragging, the Beta node snaps first and not simultaneously with the Alpha node. Am I missing something?

I do get the behavior that when first starting to move all selected nodes that are not already aligned to grid points are moved so that they are aligned. But after that I find that all nodes move together by equal amounts, maintaining their distances relative to each other.

I suppose you could modify the override so that it doesn’t do any move unless it moves a node to a different cell. That would avoid the initial making-sure-everything-is-aligned until the first move to a different cell.

But there’s no way to avoid nodes that are not aligned to move relative to other selected nodes that are aligned, soon or later.