Mixing orthogonal and normal linking

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:

myDiagram.linkTemplate = // for parent-child relationships
        $g(go.Link, {
            routing: go.Link.Orthogonal,
            layerName: "Background",
            curviness: 10,
            selectable: false,
            fromSpot: go.Spot.Bottom,
            toSpot: go.Spot.Top
        }, $g(go.Shape, {
            strokeWidth: 2
        }, new go.Binding("strokeDashArray", "dash")));

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.

Use two link templates, one ortho and one not.

The Geonogram sample is already using multiple link templates (for marraige)

myDiagram.linkTemplateMap.add("Marriage", ...

For twins, you will have the link category to be of the different template.

Hi Simon, thanks for responding.

Ok, I’ve got twins linking now but it’s a little messed up:


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;
};

Here’s my link template for twins:

myDiagram.nodeTemplateMap.add("TwinLinkLabel", $g(go.Node, {
            selectable: false,
            width: 1,
            height: 1,
            fromEndSegmentLength: 50
        }));

myDiagram.linkTemplateMap.add("Twin", // for twin relationships
            $g(go.Link, {
                selectable: false
            }, $g(go.Shape, {
                strokeWidth: 5,
                stroke: "red"
        })));

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…

This might help you: http://gojs.net/temp/genogramTwins.html

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: http://gojs.net/latest/samples/linksToLinks.html.

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.

I went the non data model route:

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:

It’s supposed to look like the 2nd example below:

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):

Actually…it only works on the probands children. Everyone else and the pedigree is drawing the twins on the last generation row:

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.

You are probably correct. I’ll make a codepen with all my code.

The identical-twin links should also have isTreeLink: false.

Look at all of the code that depends on the category being some value.

Finally getting back to this. I’ve setup a codepen showing the issue: http://codepen.io/runningrandall/pen/wWJORj

I set isTreeLink:false on the identical links. Still have the same issue. Thanks again for looking at this.

This may take a while for me to address.

Hi Walter,

Just following up on this. Have you had a chance to look at that codepen? I really appreciate any help you can lend.

-Randall

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?

Yep, I’ve added label nodes to each of the parent-child links that connect with twins. They are declared as isLayoutPositioned: false.