I want to integrate link to link into dynamic ports diagram. Can you please provide any help/reference in related to my query?
OK, so make a copy of the DynamicPorts.html sample, and replace the definition of the Link template with the following code:
// an orthogonal link template, reshapable and relinkable
myDiagram.linkTemplate =
$(CustomLink, // defined below
{
routing: go.Link.AvoidsNodes,
corner: 4,
curve: go.Link.JumpGap,
reshapable: true,
resegmentable: true,
relinkableFrom: true,
relinkableTo: true,
doubleClick: createJunction,
mouseDrop: function(e, link) {
var labelnode = null;
e.diagram.selection.each(function(p) {
if (p instanceof go.Node && p.category === "LinkLabel") labelnode = p;
});
if (labelnode !== null) attachJunction(link, labelnode, labelnode.location);
}
},
new go.Binding("points").makeTwoWay(),
$(go.Shape, { stroke: "#2F4F4F", strokeWidth: 2 })
);
function createJunction(e, link) { // Creates Junction node when user double clicks on a link
e.handled = true;
e.diagram.startTransaction("Link Label");
var label = { category: "LinkLabel" }; // Create data for label node -- the junction Node
e.diagram.model.addNodeData(label);
var labelnode = e.diagram.findNodeForData(label); // Finds the created node from the NodeData
attachJunction(link, labelnode, e.documentPoint);
e.diagram.commitTransaction("Link Label");
}
function attachJunction(link, labelnode, pos) {
labelnode.labeledLink = link; // set the labeledLink for the junction node
var index = link.findClosestSegment(pos); // Finds the index of the segment that is closest to POS
labelnode.segmentIndex = index;
var startPoint = link.points.elt(index); // Grabs the point at the start and end of the segment
var endPoint = link.points.elt(index + 1);
// Finds diff between pos and startPoint divided by diff of endPoint and startPoint
if (Math.abs(startPoint.x - endPoint.x) < 0.1) { // Segment is horizontal
labelnode.segmentFraction = (pos.y - startPoint.y) / (endPoint.y - startPoint.y);
} else { // Segment is vertical
labelnode.segmentFraction = (pos.x - startPoint.x) / (endPoint.x - startPoint.x);
}
}
// This is the template for a label node on a link.
// This node supports user-drawn links to and from the label node.
// It is actually larger than it appears, so it can be selected and deleted.
myDiagram.nodeTemplateMap.add("LinkLabel",
$(go.Node,
{
layerName: "Foreground", // always have link label nodes in front of Links
background: "transparent",
width: 12, height: 12,
locationSpot: go.Spot.Center,
movable: false, // but becomes movable when it's not a label on a Link
deletable: false, // but becomes deletable when there's no connected link
// only deletable when it's unconnected
linkConnected: function(node, link, port) {
node.deletable = false;
},
linkDisconnected: function(node, link, port) {
node.deletable = (node.linksConnected.count === 0);
}
},
// only movable when it's a label Node of a Link
new go.Binding("movable", "labeledLink", function(link) { return link === null; }).ofObject(),
new go.Binding("segmentIndex").makeTwoWay(), // remember where this label belongs on the link
new go.Binding("segmentFraction").makeTwoWay(),
$(go.Shape, "Circle",
{ // a junction node appears as just a small black circle
position: new go.Point(3, 3), // center the circle in the Node
width: 6, height: 6,
fill: "blue", strokeWidth: 0,
portId: "",
fromLinkable: true, toLinkable: true, cursor: "pointer"
})
));
This adds a “LinkLabel” template that is a junction node. Double click on an orthogonal link to add a new junction node as a label on the link.
But the CustomLink class needs changes to handle the new kind of node that now can exist in the diagram. So update these overrides:
/** @override */
CustomLink.prototype.computeEndSegmentLength = function(node, port, spot, from) {
var esl = go.Link.prototype.computeEndSegmentLength.call(this, node, port, spot, from);
var other = this.getOtherPort(port);
if (port !== null && other !== null && node.category !== "LinkLabel" && other.part.category !== "LinkLabel") {
var thispt = port.getDocumentPoint(this.computeSpot(from));
var otherpt = other.getDocumentPoint(this.computeSpot(!from));
if (Math.abs(thispt.x - otherpt.x) > 20 || Math.abs(thispt.y - otherpt.y) > 20) {
var info = this.findSidePortIndexAndCount(node, port);
var idx = info[0];
var count = info[1];
if (port._side == "top" || port._side == "bottom") {
if (otherpt.x < thispt.x) {
return esl + 4 + idx * 8;
} else {
return esl + (count - idx - 1) * 8;
}
} else { // left or right
if (otherpt.y < thispt.y) {
return esl + 4 + idx * 8;
} else {
return esl + (count - idx - 1) * 8;
}
}
}
}
return esl;
};
/** @override */
CustomLink.prototype.hasCurviness = function() {
if (isNaN(this.curviness)) return true;
return go.Link.prototype.hasCurviness.call(this);
};
/** @override */
CustomLink.prototype.computeCurviness = function() {
if (isNaN(this.curviness)) {
var fromnode = this.fromNode;
var tonode = this.toNode;
if (fromnode.category !== "LinkLabel" && tonode.category !== "LinkLabel") {
var fromport = this.fromPort;
var fromspot = this.computeSpot(true);
var frompt = fromport.getDocumentPoint(fromspot);
var toport = this.toPort;
var tospot = this.computeSpot(false);
var topt = toport.getDocumentPoint(tospot);
if (Math.abs(frompt.x - topt.x) > 20 || Math.abs(frompt.y - topt.y) > 20) {
if ((fromspot.equals(go.Spot.Left) || fromspot.equals(go.Spot.Right)) &&
(tospot.equals(go.Spot.Left) || tospot.equals(go.Spot.Right))) {
var fromseglen = this.computeEndSegmentLength(fromnode, fromport, fromspot, true);
var toseglen = this.computeEndSegmentLength(tonode, toport, tospot, false);
var c = (fromseglen - toseglen) / 2;
if (frompt.x + fromseglen >= topt.x - toseglen) {
if (frompt.y < topt.y) return c;
if (frompt.y > topt.y) return -c;
}
} else if ((fromspot.equals(go.Spot.Top) || fromspot.equals(go.Spot.Bottom)) &&
(tospot.equals(go.Spot.Top) || tospot.equals(go.Spot.Bottom))) {
var fromseglen = this.computeEndSegmentLength(fromnode, fromport, fromspot, true);
var toseglen = this.computeEndSegmentLength(tonode, toport, tospot, false);
var c = (fromseglen - toseglen) / 2;
if (frompt.x + fromseglen >= topt.x - toseglen) {
if (frompt.y < topt.y) return c;
if (frompt.y > topt.y) return -c;
}
}
}
}
}
return go.Link.prototype.computeCurviness.call(this);
};
CustomLink.prototype.getLinkPoint = function(node, port, spot, from, ortho, othernode, otherport, result) {
if (ortho && node.category === "LinkLabel") {
var link = node.labeledLink;
if (link) {
var idx = node.segmentIndex;
var p1 = link.getPoint(idx);
var p2 = link.getPoint(idx + 1);
var othpt = otherport.getDocumentPoint(go.Spot.Center);
if (Math.abs(p1.x - p2.x) < 0.1) {
return port.getDocumentPoint((othpt.x > p1.x) ? go.Spot.Right : go.Spot.Left, result);
} else {
return port.getDocumentPoint((othpt.y > p1.y) ? go.Spot.Bottom : go.Spot.Top, result);
}
}
}
return go.Link.prototype.getLinkPoint.call(this, node, port, spot, from, ortho, othernode, otherport, result);
}
Basically the changes are only executing the old CustomLink code when the nodes are not junction nodes (i.e. category is “LinkLabel”), plus a new override for Link.getLinkPoint.
Thanks for your reply.
As i have integrated this in to my system and it is working fine on double click on any point on link.
Can it be work with single click like there will be no need to double click, just make link from any point to any point?
What changes do you think you would have to make to have a click rather than a double click on a link be what creates a junction label node? What have you tried? Have you read Get Started with GoJS and then all of the relevant pages of the Introduction, starting at GoJS Introduction -- Northwoods Software?
I am going with double click functionality now but when i load json in dynamic ports diagram with links to links enabled then dots are not on the proper place on the links .
Example -
What information is being saved for each junction node (a.k.a. label node)?
{ “class”: “go.GraphLinksModel”,
“copiesArrays”: true,
“copiesArrayObjects”: true,
“linkFromPortIdProperty”: “fromPort”,
“linkToPortIdProperty”: “toPort”,
“nodeDataArray”: [
{“key”:1, “uniquekey”:“1-3-1”, “image”:“http://beta.brstdev.com/Cable/wp-content/uploads/2018/10/list3.png”, “label”:“A”, “showname”:“DC1F\n4 contact”, “name”:“DC1F\n4 contact\nA”, “leftArray”:[], “topArray”:[], “bottomArray”:[], “rightArray”:[ {“portColor”:“#000000”, “portId”:“right0”},{“portColor”:“#000000”, “portId”:“right1”},{“portColor”:“#000000”, “portId”:“right2”},{“portColor”:“#000000”, “portId”:“right3”} ], “loc”:“-182 45”},
{“key”:-4, “uniquekey”:“1-3-3”, “image”:“http://beta.brstdev.com/Cable/wp-content/uploads/2018/10/list2.png”, “label”:“B”, “showname”:“BH1M\n6 contact”, “name”:“BH1M\n6 contact\nB”, “leftArray”:[ {“portColor”:“#000000”, “portId”:“left0”},{“portColor”:“#000000”, “portId”:“left1”},{“portColor”:“#000000”, “portId”:“left2”},{“portColor”:“#000000”, “portId”:“left3”},{“portColor”:“#000000”, “portId”:“left4”},{“portColor”:“#000000”, “portId”:“left5”} ], “topArray”:[], “bottomArray”:[], “rightArray”:[], “loc”:“409.99999999999994 -6”},
{“key”:3, “uniquekey”:“1-3-2”, “image”:“http://beta.brstdev.com/Cable/wp-content/uploads/2018/10/list2.png”, “label”:“C”, “showname”:“BH1M\n6 contact”, “name”:“BH1M\n6 contact\nC”, “leftArray”:[ {“portColor”:“#000000”, “portId”:“left0”},{“portColor”:“#000000”, “portId”:“left1”},{“portColor”:“#000000”, “portId”:“left2”},{“portColor”:“#000000”, “portId”:“left3”},{“portColor”:“#000000”, “portId”:“left4”},{“portColor”:“#000000”, “portId”:“left5”} ], “topArray”:[], “bottomArray”:[], “rightArray”:[], “loc”:“121 351.9999999999999”},
{“key”:-5, “uniquekey”:“1-3-4”, “image”:“http://beta.brstdev.com/Cable/wp-content/uploads/2018/10/list3.png”, “label”:“D”, “showname”:“DC1F\n4 contact”, “name”:“DC1F\n4 contact\nD”, “leftArray”:[ {“portColor”:“#000000”, “portId”:“left0”},{“portColor”:“#000000”, “portId”:“left1”},{“portColor”:“#000000”, “portId”:“left2”},{“portColor”:“#000000”, “portId”:“left3”} ], “topArray”:[], “bottomArray”:[], “rightArray”:[], “loc”:“467.99999999999994 379.9999999999999”},
{“category”:“LinkLabel”, “key”:-6, “segmentIndex”:3, “segmentFraction”:0.7517857142857143},
{“category”:“LinkLabel”, “key”:-7, “segmentIndex”:2, “segmentFraction”:0.5961538461538461}
],
“linkDataArray”: [
{“from”:1, “to”:-4, “fromPort”:“right0”, “toPort”:“left2”, “points”:[-96,18,-82,18,120,18,120,-15,290,-15,324,-15]},
{“from”:1, “to”:-5, “fromPort”:“right1”, “toPort”:“left1”, “points”:[-96,36,-70,36,-68,36,-68,36,212,36,212,371,360,371,382,371]},
{“from”:1, “to”:3, “fromPort”:“right2”, “toPort”:“left4”, “points”:[-96,54,-78,54,-28.5,54,-28.5,379,-11,379,35,379]},
{“from”:-7, “to”:-6, “fromPort”:“”, “toPort”:“”, “points”:[-25.5,247.75,-25.5,237.75,-25.5,159.375,142.5,159.375,142.5,49,142.5,39]}
]}
Notice that there is no information in the model associating links with label nodes. And notice what the Links to Links sample does: it sets GraphLinksModel.linkLabelKeysProperty.
For example:
myDiagram.model =
$(go.GraphLinksModel,
{
linkLabelKeysProperty: "labelKeys",
nodeDataArray:
[ . . .
Or in the JSON-formatted text, add this line:
"linkLabelKeysProperty": "labelKeys",
and for any links that have label nodes, add a property like this:
{"from":1, "to":2, "labelKeys":[ 5 ]},
What will be the value of labelkeys?
I have this type of linkdataarray -
{“key”: “1-3-1-5”, “from”: 1, “to”: 3, “fromPort”: “right0”, “toPort”: “left0”, “right”: 1, “left”: 1}
Basically, an Array of node keys.
Okay but for link that has been created from middle of one link to another has empty labelKeys but others fills automatically and that giving error while editing json
“from”:-6, “to”:-4, “labelKeys”:[], “fromPort”:“”, “toPort”:“”
Error -
Uncaught TypeError: Cannot read property ‘x’ of undefined
at CustomLink.getLinkPoint ((index):1103)
at CustomLink.W.computePoints (go.js:1557)
at CustomLink.W.updateRoute.W.hn (go.js:1554)
at CustomLink. (go.js:1539)
at CustomLink.g.np (go.js:1119)
at fj (go.js:978)
at E.BA (go.js:761)
at yk (go.js:760)
at $k (go.js:900)
at Wh (go.js:757)
I cannot reproduce that problem. When I edit to get this:
The resulting model is:
{ "class": "GraphLinksModel",
"linkLabelKeysProperty": "labelKeys",
"nodeDataArray": [
{"key":1, "text":"Alpha", "color":"lightblue", "loc":"0 0"},
{"key":2, "text":"Beta", "color":"orange", "loc":"100, -50"},
{"key":3, "text":"Gamma", "color":"lightgreen", "loc":"-52.00000000000003 58"},
{"key":4, "text":"Delta", "color":"pink", "loc":"128.99999999999991 73"},
{"key":5, "category":"LinkLabel", "segmentIndex":2, "segmentFraction":0.4},
{"category":"LinkLabel", "key":-6, "segmentIndex":2, "segmentFraction":0.8466665649414062},
{"category":"LinkLabel", "key":-7, "segmentIndex":3, "segmentFraction":0.24946229996219757},
{"category":"LinkLabel", "key":-8, "segmentIndex":1, "segmentFraction":0.7237442225626071},
{"category":"LinkLabel", "key":-9, "segmentIndex":2, "segmentFraction":0.4718813758397271}
],
"linkDataArray": [
{"from":1, "to":2, "labelKeys":[ 5 ]},
{"from":5, "to":4, "labelKeys":[ -6 ]},
{"from":3, "to":4, "labelKeys":[ -7,-8 ]},
{"from":-6, "to":-7, "labelKeys":[ -9 ]},
{"from":-9, "to":-8, "labelKeys":[]}
]}
Certainly any Link that does not have any label Nodes can have the “labelKeys” property be an empty Array.