Unconnected link end snapping

Hi-

I was having trouble getting the unconnected ends of links to snap to grid. I found this thread:

The temporary link is indeed snapping per our diagrams settings, but when the mouse is released, the real link reverts back to the actual mouse location. I don’t see anywhere that I have modified the tool further. Could you give some insight as to what may be my issue?

thank you

I just installed the code that you referred to:

  function SnappingRelinkingTool() {
    go.RelinkingTool.call(this);
  }
  go.Diagram.inherit(SnappingRelinkingTool, go.RelinkingTool);

  SnappingRelinkingTool.prototype.doMouseMove = function() {
    var e = this.diagram.lastInput;
    var grid = this.diagram.grid;
    e.documentPoint = e.documentPoint.copy().snapToGridPoint(grid.gridOrigin, grid.gridCellSize);
    e.viewPoint = e.diagram.transformDocToView(e.documentPoint);
    go.RelinkingTool.prototype.doMouseMove.call(this);
  }

  SnappingRelinkingTool.prototype.doMouseUp = function() {
    var e = this.diagram.lastInput;
    var grid = this.diagram.grid;
    e.documentPoint = e.documentPoint.copy().snapToGridPoint(grid.gridOrigin, grid.gridCellSize);
    e.viewPoint = e.diagram.transformDocToView(e.documentPoint);
    go.RelinkingTool.prototype.doMouseUp.call(this);
  }

and I replaced the standard Diagram.toolManager.relinkingTool and enabled disconnected links:

          relinkingTool: new SnappingRelinkingTool(),
          "relinkingTool.isUnconnectedLinkValid": true,

Now it appears to snap the end of a link as expected, and it stays there when I let go.

Could you tell me how to reproduce the problem?

I also have the tool:

function SnappingRelinkingTool() {
	go.RelinkingTool.call(this);
}

go.Diagram.inherit(SnappingRelinkingTool, go.RelinkingTool);

SnappingRelinkingTool.prototype.doMouseMove = function () {

	var diagram = this.diagram;
	if (diagram.toolManager.draggingTool.isGridSnapEnabled === true) {
		var e = this.diagram.lastInput;
		var grid = this.diagram.grid;
		e.documentPoint = e.documentPoint.copy().snapToGridPoint(grid.gridOrigin, grid.gridCellSize);
		e.viewPoint = e.diagram.transformDocToView(e.documentPoint);
	}
	go.RelinkingTool.prototype.doMouseMove.call(this);
}

and my diagram:

var diagram = $$( go.Diagram, canvasId, {

   
    commandHandler: $$( DrawCommandHandler ),
    //"ChangedSelection" : updateMultiSelectionPart,
    "grid.gridCellSize": new go.Size( 30, 20 ),
    "undoManager.isEnabled": true,
    "undoManager.maxHistoryLength": -1,
    "panningTool.isEnabled": true,
    "dragSelectingTool.isEnabled": false,
    "draggingTool.dragsLink": true,
    "draggingTool.isGridSnapEnabled": true,
    linkReshapingTool: $$(SnapLinkReshapingTool),
    relinkingTool: new SnappingRelinkingTool(),
    "relinkingTool.isUnconnectedLinkValid": true,
    "animationManager.isEnabled": false,
} );

I apologize that I don’t know exactly what is different between ours. I am just coming back into this project. I will look again tomorrow to see what else differs. Thanks for your help

I wasn’t able to identify what could be doing this. Is there something in the link template or a property on the base linking tool that could be preventing it from working?

The link goes back to the mouse instead of where the temporary link is when go.RelinkingTool.prototype.doDeactivate.call(this); happens.

What is your link template?

var linkTemplate =
        $$(go.Link, // the whole link panel
            {
            	selectable: true,
            	resizeObjectName: 'SHAPE',
            	selectionAdornmentTemplate: linkSelectionAdornmentTemplate,
            	resizeAdornmentTemplate: linkSelectionAdornmentTemplate,
            	relinkableFrom: true,
            	relinkableTo: true,
            	reshapable: true,
            	routing: go.Link.AvoidsNodes,
            	resegmentable: true,
            	curve: go.Link.None,
            	corner: 10,
            	smoothness: 1
            },
            new go.Binding('zOrder', 'zOrder').makeTwoWay(),
            new go.Binding('layerName', 'layerName').makeTwoWay(),
            new go.Binding('points', 'points').makeTwoWay(),
				new go.Binding('location', 'loc', go.Point.parse),
				new go.Binding('position', 'pos', go.Point.parse),
            new go.Binding('desiredSize', 'size', go.Size.parse).makeTwoWay(go.Size.stringify),
            new go.Binding('selectable', 'selectable'),
            new go.Binding('reshapable', 'reshapable'),
				new go.Binding('visible', 'visible'),
        $$(go.Shape, {
        	isPanelMain: true, stroke: "black", strokeWidth: 11
        },
                new go.Binding('strokeWidth', 'width', calculateLinkWidth1),
                new go.Binding('stroke', 'color', calculateLinkGradient1)//.makeTwoWay(),
				),
        $$(go.Shape, {
        	isPanelMain: true, stroke: "#5e5e5e", strokeWidth: 9
        },
                new go.Binding('strokeWidth', 'width', calculateLinkWidth2),
                new go.Binding('stroke', 'color', calculateLinkGradient2)//.makeTwoWay(),
				),
        $$(go.Shape, {
        	isPanelMain: true, stroke: "#8c8c8c", strokeWidth: 7
        },
                new go.Binding('strokeWidth', 'width', calculateLinkWidth3),
                new go.Binding('stroke', 'color', calculateLinkGradient3)//.makeTwoWay(),
				),
        $$(go.Shape, {
        	isPanelMain: true, stroke: "#b2b2b2", strokeWidth: 5
        },
                new go.Binding('strokeWidth', 'width', calculateLinkWidth4),
                new go.Binding('stroke', 'color', calculateLinkGradient4)//.makeTwoWay(),
				),
            $$(go.Shape,
                {
                	isPanelMain: true,
                	name: 'SHAPE',
                	fill: 'lightblue',
                	stroke: '#99ccff',
                	strokeWidth: 3,
                },
                new go.Binding('desiredSize', 'size', go.Size.parse).makeTwoWay(go.Size.stringify),
                new go.Binding('geometryString', 'geo').makeTwoWay(),
                new go.Binding('strokeWidth', 'width'),//.makeTwoWay(),
                new go.Binding('stroke', 'color'), //.makeTwoWay(),
                new go.Binding('geometryString', 'angle', SetGeometryStringFromAngle).makeTwoWay(SetAngleFromGeometryString),
                new go.Binding('strokeDashArray', 'strokeDashArray')
            ),


            // Set the arrows to default to Standard. The "toArrow/fromArrow" attribute set to 
            // empty string makes the arrow to not show.  The fromArrow will always have the 
            // word Backward in the name (i.e. Triangle, BackwardTriangle, etc.). Standard is 
            // the exception.
            $$( go.Shape,
                { name: 'TOARROW', toArrow: '', fill: null, scale: 2, stroke: '#000' }, new go.Binding( 'toArrow' ), new go.Binding( 'fill', 'toArrowFill' ), new go.Binding( 'scale', 'toArrowScale' ), new go.Binding( 'stroke', 'toArrowStroke' ) ),
            $$( go.Shape,
                { name: 'FROMARROW', fromArrow: '', fill: null, scale: 2, stroke: '#000' }, new go.Binding( 'fromArrow' ), new go.Binding( 'fill', 'fromArrowFill' ), new go.Binding( 'scale', 'fromArrowScale' ), new go.Binding( 'stroke', 'fromArrowStroke' ) )
        );
    return linkTemplate;
}

The shapes of increasing width give the link a gradient effect. (They represent pipes).

I’m not sure what the cause of the problem is. However, I see a lot of suspicious Bindings in your link template.

Bindings of Link.location and Link.position are not advisable – the Binding on Link.points will determine the location and position of the Link.

Bindings of Link.desiredSize is also inadvisable – the Binding on Link.points and the other objects on the link will determine the size of the Link. I could see this causing the error that you are observing, although that’s just a guess of mine.

Bindings of Shape.desiredSize and Shape.geometryString (twice!?)
are also inadvisable for Shapes that are the link path, because Links automatically determine the Geometry for the main path Shapes. This is another possible cause of the problem that you have.

Thanks for the input Walter. I removed those bindings and it still did not work. I tried implementing the tool on one of the provided samples, specifically Draggable Link, and I was still seeing the same behavior as in our project. it looks as follows:

    <script async="" src="./Draggable Link__files/analytics.js.download"></script><script src="./Draggable Link__files/go.js.download"></script>
<script src="./Draggable Link__files/goSamples.js.download"></script><script src="./Draggable Link__files/highlight.js.download"></script><script src="./Draggable Link__files/jquery.min.js.download"></script><script src="./Draggable Link__files/bootstrap.min.js.download"></script><link type="text/css" rel="stylesheet" href="./Draggable Link__files/bootstrap.min.css"><link type="text/css" rel="stylesheet" href="./Draggable Link__files/highlight.css"><link type="text/css" rel="stylesheet" href="./Draggable Link__files/main.css">  <!-- this is only for the GoJS Samples framework -->
<script id="code">
  function SnappingRelinkingTool() {
    go.RelinkingTool.call(this);
  }
  go.Diagram.inherit(SnappingRelinkingTool, go.RelinkingTool);

  SnappingRelinkingTool.prototype.doMouseMove = function() {
    var e = this.diagram.lastInput;
    var grid = this.diagram.grid;
    e.documentPoint = e.documentPoint.copy().snapToGridPoint(grid.gridOrigin, grid.gridCellSize);
    e.viewPoint = e.diagram.transformDocToView(e.documentPoint);
    go.RelinkingTool.prototype.doMouseMove.call(this);
  }
  function init() {
    if (window.goSamples) goSamples();  // init for these samples -- you don't need to call this
    var $ = go.GraphObject.make;  // for conciseness in defining templates

    myDiagram =
      $(go.Diagram, "myDiagramDiv",  // must name or refer to the DIV HTML element
        {
          grid: $(go.Panel, "Grid",
                  $(go.Shape, "LineH", { stroke: "lightgray", strokeWidth: 0.5 }),
                  $(go.Shape, "LineH", { stroke: "gray", strokeWidth: 0.5, interval: 10 }),
                  $(go.Shape, "LineV", { stroke: "lightgray", strokeWidth: 0.5 }),
                  $(go.Shape, "LineV", { stroke: "gray", strokeWidth: 0.5, interval: 10 })
                ),
          allowDrop: true,  // must be true to accept drops from the Palette
          "draggingTool.dragsLink": true,
          "draggingTool.isGridSnapEnabled": true,
          "linkingTool.isUnconnectedLinkValid": true,
          "linkingTool.portGravity": 20,
          relinkingTool: new SnappingRelinkingTool(),
          "relinkingTool.isUnconnectedLinkValid": true,
          "relinkingTool.portGravity": 20,
          "relinkingTool.fromHandleArchetype":
            $(go.Shape, "Diamond", { segmentIndex: 0, cursor: "pointer", desiredSize: new go.Size(8, 8), fill: "tomato", stroke: "darkred" }),
          "relinkingTool.toHandleArchetype":
            $(go.Shape, "Diamond", { segmentIndex: -1, cursor: "pointer", desiredSize: new go.Size(8, 8), fill: "darkred", stroke: "tomato" }),
          "linkReshapingTool.handleArchetype":
            $(go.Shape, "Diamond", { desiredSize: new go.Size(7, 7), fill: "lightblue", stroke: "deepskyblue" }),
          rotatingTool: $(TopRotatingTool),  // defined below
          "rotatingTool.snapAngleMultiple": 15,
          "rotatingTool.snapAngleEpsilon": 15,
          "undoManager.isEnabled": true
        });

(the rest of the file is unchanged)

Ah, there’s a missing override of doMouseUp:

  function SnappingRelinkingTool() {
    go.RelinkingTool.call(this);
  }
  go.Diagram.inherit(SnappingRelinkingTool, go.RelinkingTool);

  SnappingRelinkingTool.prototype.doMouseMove = function() {
    var e = this.diagram.lastInput;
    var grid = this.diagram.grid;
    e.documentPoint = e.documentPoint.copy().snapToGridPoint(grid.gridOrigin, grid.gridCellSize);
    e.viewPoint = e.diagram.transformDocToView(e.documentPoint);
    go.RelinkingTool.prototype.doMouseMove.call(this);
  }

  SnappingRelinkingTool.prototype.doMouseUp = function() {
    var e = this.diagram.lastInput;
    var grid = this.diagram.grid;
    e.documentPoint = e.documentPoint.copy().snapToGridPoint(grid.gridOrigin, grid.gridCellSize);
    e.viewPoint = e.diagram.transformDocToView(e.documentPoint);
    go.RelinkingTool.prototype.doMouseUp.call(this);
  }

Thank you! that was it. I am good now.