Modify the LinkShiftingTool constructor:
function LinkShiftingTool() {
go.Tool.call(this);
this.name = "LinkShifting";
// these are archetypes for the two shift handles, one at each end of the Link:
var h = new go.Shape();
h.figure = "Circle";
h.desiredSize = new go.Size(8, 8);
h.fill = "red";
h.stroke = null;
h.cursor = "pointer";
h.segmentIndex = 0;
h.segmentOffset = new go.Point(8, 0);
h.segmentOrientation = go.Link.OrientAlong;
/** @type {GraphObject} */
this._fromHandleArchetype = h;
h = new go.Shape();
h.figure = "Circle";
h.desiredSize = new go.Size(8, 8);
h.fill = "red";
h.stroke = null;
h.cursor = "pointer";
h.segmentIndex = -1;
h.segmentOffset = new go.Point(-8, 0);
h.segmentOrientation = go.Link.OrientAlong;
/** @type {GraphObject} */
this._toHandleArchetype = h;
// transient state
/** @type {GraphObject} */
this._handle = null;
/** @type {List} */
this._originalPoints = null;
}
go.Diagram.inherit(LinkShiftingTool, go.Tool);
Modify the LinkShiftingTool.updateAdornments method:
LinkShiftingTool.prototype.updateAdornments = function(part) {
if (part === null || !(part instanceof go.Link)) return; // this tool only applies to Links
var link = part;
// show handles if link is selected, remove them if no longer selected
var category = "LinkShiftingFrom";
var adornment = null;
if (link.isSelected && !this.diagram.isReadOnly) {
var selelt = link.selectionObject;
if (selelt !== null && link.actualBounds.isReal() && link.isVisible() &&
selelt.actualBounds.isReal() && selelt.isVisibleObject()) {
var spot = link.computeSpot(true);
adornment = link.findAdornment(category);
if (adornment === null) {
adornment = this.makeAdornment(selelt, false);
adornment.category = category;
link.addAdornment(category, adornment);
}
}
}
if (adornment === null) link.removeAdornment(category);
category = "LinkShiftingTo";
adornment = null;
if (link.isSelected && !this.diagram.isReadOnly) {
var selelt = link.selectionObject;
if (selelt !== null && link.actualBounds.isReal() && link.isVisible() &&
selelt.actualBounds.isReal() && selelt.isVisibleObject()) {
var spot = link.computeSpot(false);
adornment = link.findAdornment(category);
if (adornment === null) {
adornment = this.makeAdornment(selelt, true);
adornment.category = category;
link.addAdornment(category, adornment);
}
}
}
if (adornment === null) link.removeAdornment(category);
};
Modify the LinkShiftingTool.doReshape method:
LinkShiftingTool.prototype.doReshape = function(pt) {
var ad = this._handle.part;
var link = ad.adornedObject.part;
var fromend = ad.category === "LinkShiftingFrom";
var port = null;
if (fromend) {
port = link.fromPort;
} else {
port = link.toPort;
}
var portb = new go.Rect(port.getDocumentPoint(go.Spot.TopLeft),
port.getDocumentPoint(go.Spot.BottomRight));
var lp = link.getLinkPointFromPoint(port.part, port, port.getDocumentPoint(go.Spot.Center), pt, fromend);
var spot = new go.Spot((lp.x - portb.x) / (portb.width || 1), (lp.y - portb.y) / (portb.height || 1));
if (fromend) {
link.fromSpot = spot;
} else {
link.toSpot = spot;
}
};
Then add this custom Link class:
function DirectLink() {
go.Link.call(this);
}
go.Diagram.inherit(DirectLink, go.Link);
DirectLink.prototype.computePoints = function() {
var fromport = this.fromPort;
if (fromport === null) return false;
var toport = this.toPort;
if (toport === null) return false;
var fromnode = this.fromNode;
var tonode = this.toNode;
var fromspot = this.computeSpot(true, fromport); // link's Spot takes precedence, if defined
var tospot = this.computeSpot(false, toport); // link's Spot takes precedence, if defined
var selfloop = fromport === toport;
var ortho = this.isOrthogonal;
var bezier = (this.curve === Link.Bezier);
if (!selfloop && !ortho && !bezier && this.adjusting === go.Link.None) {
var pA = this.getLinkPoint(fromnode, fromport, fromspot, true, false, tonode, toport); // must be newly allocated result
var pB = this.getLinkPoint(tonode, toport, tospot, false, false, fromnode, fromport); // must be newly allocated result
this.clearPoints();
this.addPoint(pA);
this.addPoint(pB);
return true;
} else {
return go.Link.prototype.computePoints.call(this);
}
}
And set up the LinkShiftingTool:
myDiagram.toolManager.mouseDownTools.add($(LinkShiftingTool));
And use the new DirectLink class:
myDiagram.linkTemplate =
$(DirectLink,
{ relinkableFrom: true, relinkableTo: true },
$(go.Shape),
$(go.Shape, { toArrow: "OpenTriangle" })
);