I’m using genogram layout but in certain cases I want to do a hybrid normal linking (where individuals are twins). Here’s my regular orthogonal linking:
The orthogonal is working great but I’m not sure how I can link two nodes in this manner:
Yet maintain the orthogonal everywhere else. Basically all horizontal lines remain horizontal but in the case of twins I don’t want a vertical line to the node but these angled nodes. Any help is much appreciated.
You can see the father and Uncle 1 are twins and Son 1 and Son 2 are twins. I have no idea how to get the triangle shaped link lines in place as pictured in the original question. Here’s my code finds and assigns twins:
Pedigree.prototype.setupTwins = function setupTwins(diagram){
var model = diagram.model;
var nodeDataArray = model.nodeDataArray;
console.log(nodeDataArray);
for (var i = 0; i < nodeDataArray.length; i++) {
var data = nodeDataArray[i];
var key = data.key;
var twin = data.twin_data;
if(twin){
if(twin.twin_key){
var twinKey = twin.twin_key * 1;
var link = this.findTwin(diagram, key, twinKey);
if(link === null){ //we need to setup the link
// add a label node for the marriage link
var mlab = {
s: "TwinLinkLabel"
};
model.addNodeData(mlab);
// add the link itself, also referring to the label node
var mdata = {
from: key,
to: twinKey,
labelKeys: [mlab.key],
category: "Twin"
};
model.addLinkData(mdata);
}
}
}
}
};
Pedigree.prototype.findTwin = function(diagram, a, b){
var nodeA = diagram.findNodeForKey(a);
var nodeB = diagram.findNodeForKey(b);
console.log(nodeA, nodeB);
if (nodeA !== null && nodeB !== null) {
var it = nodeA.findLinksBetween(nodeB); // direction matters
console.log("The links: ", it);
while (it.next()) {
var link = it.value;
console.log(link);
// Link.data.category === "Twin" means it's a twin relationship
if (link.data !== null && link.data.category === "Twin") return link;
}
}
return null;
};
I need to link them to the parent/child horizontal line and make them point upwards to the midpoint between the two nodes. I have no idea how to change that behavior. Thanks again for your help! Also, I can set it all up on a jsfiddle instance if that’s helpful…
Thank you thank you Walter! I’ve got it linking the twins nicely now. There were some issues with multiple siblings that are not twins/multiple births but I’ve got a sorting algorithm working now. Any ideas on how to get that horizontal line in the case of identical twins? I’ve tried adding another link type but cannot get that horizontal line drawing correctly.
Well, one possibility, if you are willing to add a new link just to connect identical twins, would be to connect the mid-points of the twins’ links with their parents. Something like the green links in this sample: Links to Links.
This just involves adding a link label node to each parent link, and then adding an identical-twin link between those two link label nodes.
But if you don’t want to have such additional links, an implementation gets more complicated.
Yeah, I think adding a new link to connect identical twins will work fine. I’ve added the following:
/*
setupMultiBirths
Description: function that setups up all identical twin births
Params: object diagram
Returns: void
*/
Pedigree.prototype.setupMultiBirths = function setupMultiBirths(diagram) {
var model = diagram.model;
var nodeDataArray = model.nodeDataArray;
for (var i = 0; i < nodeDataArray.length; i++) {
var data = nodeDataArray[i];
if (data.mb_data !== undefined) {
for(var mb in data.mb_data){
mb = parseInt(mb);
var key = data.key;
var type = data.mb_data[mb];
if(type === "IDENTICAL"){
var link = this.findIdentical(diagram, key, mb);
if (link === null) {
// add a label node for the marriage link
var mlab = {
s: "LinkLabel"
};
model.addNodeData(mlab);
// add the marriage link itself, also referring to the label node
var mdata = {
from: data.key,
to: mb,
labelKeys: [mlab.key],
category: "linkToLink"
};
model.addLinkData(mdata);
}
}
}
}
}
};
/*
findIndentical
Description: function that finds a link between two people that share identical genetic data (twins)
Params: object diagram, key (int) of one person, key(int) of other person
Returns: Object Link
*/
Pedigree.prototype.findIdentical = function findIndentical(diagram, a, b){
var nodeA = diagram.findNodeForKey(a);
var nodeB = diagram.findNodeForKey(b);
if (nodeA !== null && nodeB !== null) {
var it = nodeA.findLinksBetween(nodeB); // in either direction
while (it.next()) {
var link = it.value;
// Link.data.category === "Marriage" means it's a marriage relationship
if (link.data !== null && link.data.category === "linkToLink") return link;
}
}
return null;
};
And the following linkTemplate:
// This template shows links connecting with label nodes as green and arrow-less.
myDiagram.linkTemplateMap.add("linkToLink", $("Link", {
relinkableFrom: false,
relinkableTo: false
}, $("Shape", {
stroke: "green",
strokeWidth: 1.5
})));
I think I’m missing the link label node to each parent link. Do you have any suggestions on how to add that? Thanks again for all of your help!
I’m getting an error: “TypeError: Cannot read property ‘Ea’ of undefined” when trying to model.addLinkData
Yes, you need to add a label Node to each twin’s parent Link, and then add a Link to connect those new label Nodes.
You do not need to modify the model to add such label Nodes and parent Link-connecting Links. Say that the two twins that you want to connect in this manner are in variables T1 and T2:
var T1 = ... twin Node 1 ...
var T2 = ... twin Node 2 ...
var TPL1 = T1.findTreeParentLink();
var TPL2 = T2.findTreeParentLink();
var TLN1 = $(go.Node);
TLN1.labeledLink = TPL1;
myDiagram.add(TLN1);
var TLN2 = $(go.Node);
TLN2.labeledLink = TPL2;
myDiagram.add(TLN2);
var TL = $(go.Link, { isLayoutPositioned: false }, $(go.Shape));
TL.fromNode = TLN1;
TL.toNode = TLN2;
myDiagram.add(TL);
Note that this only involves the diagram, not the model. You could also do this by modifying the model, but you can see in that LinksToLinks sample.
Pedigree.prototype.setupMultiBirths = function setupMultiBirths(diagram) {
var model = diagram.model;
var nodeDataArray = model.nodeDataArray;
for (var i = 0; i < nodeDataArray.length; i++) {
var data = nodeDataArray[i];
if (data.mb_data !== undefined) {
for(var mb in data.mb_data){
mb = parseInt(mb);
var key = data.key;
var type = data.mb_data[mb];
if(type === "IDENTICAL"){
var link = this.findIdentical(diagram, key, mb);
if (link === null) {
var T1 = diagram.findNodeForKey(key);
var T2 = diagram.findNodeForKey(mb);
var TPL1 = T1.findTreeParentLink();
var TPL2 = T2.findTreeParentLink();
var TLN1 = $g(go.Node);
TLN1.labeledLink = TPL1;
diagram.add(TLN1);
var TLN2 = $g(go.Node);
TLN2.labeledLink = TPL2;
diagram.add(TLN2);
var TL = $g(go.Link, { isLayoutPositioned: false }, $g(go.Shape));
TL.fromNode = TLN1;
TL.toNode = TLN2;
diagram.add(TL);
}
}
}
}
}
};
But that’s drawing an interesting pedigree. It’s adding the link between parent links on a different line. Here’s the link being drawn (along with a very out of whack pedigree). Mother and Aunt 1 are identical twins:
Ah, you also need to declare that all “Marriage” links are not Link.isTreeLink. Basically, Node.findTreeParentLink needs to know to ignore all “Marriage” links. Just set { isTreeLink: false } on the “Marriage” Link.
Furthermore, I think you need to position the label nodes on the parent links so that they are at the mid-point of the last segment – close to the child. You can do that for each label node as follows:
var TLN1 = new go.Node();
TLN1.labeledLink = TPL1;
TLN1.segmentIndex = -1;
TLN1.segmentFraction = 0.5;
diagram.add(TLN1);
I haven’t tried this code, so you might want to adjust the segmentFraction to your liking.
We’re getting there! So I’ve adjusted my label nodes as follows:
var T1 = diagram.findNodeForKey(key);
var T2 = diagram.findNodeForKey(mb);
var TPL1 = T1.findTreeParentLink();
var TPL2 = T2.findTreeParentLink();
var TLN1 = $g(go.Node);
TLN1.labeledLink = TPL1;
TLN1.segmentIndex = -2;
TLN1.segmentFraction = .5;
diagram.add(TLN1);
var TLN2 = $g(go.Node);
TLN2.labeledLink = TPL2;
TLN2.segmentIndex = -2;
TLN2.segmentFraction = .5;
diagram.add(TLN2);
var TL = $g(go.Link, {
isLayoutPositioned: false
}, $g(go.Shape, {
strokeWidth: 2,
stroke: "blue"
}));
TL.fromNode = TLN1;
TL.toNode = TLN2;
diagram.add(TL);
And adjusted my marriage links as follows:
myDiagram.linkTemplateMap.add("Marriage", // for marriage relationships
$g(go.Link, {
selectable: false,
isTreeLink: false
}, $g(go.Shape, {
strokeWidth: 2,
stroke: "red"
})));
Works great on non-married people. However…assigning a married person as a twin results in the following (marriage links are red, ident twin links are blue):
Since there’s now a third kind of Link, I’m wondering if some of the other code is broken because it was assuming only two kinds of links: parent and marriage.
I just looked at it, but I cannot figure out what might be causing the problem.
Have you added label nodes to each of the parent-child links that connect with twins? Did you make sure those label nodes and the link between them are declared isLayoutPositioned: false?