18

I'm creating an interactive family tree creator, unlike more simpler versions which are simple pedigree charts/trees.

The requirements for mine (based on familyecho.com) are:

  • multiple partners vs just a simple 2 parent to 1 child that you normally see.
  • multiple siblings
  • partners dont necessarily need to have children
  • there doesn't always have to be a parent "pair", there can just be a single father/mother

The problem I'm encountering is: I'm generating the offsets based on the "current" node/family member and when I go past the first generation with say, 2 parents, it overlaps.

Example of the overlap as well as partner not being drawn on the same X axis:

enter image description here

Here is the actual app and main js file where I'm having the issue. And here is a simplified jsfiddle I created that demonstrates the parent/offset issue though I really have to solve overlapping for this in general, in addition to making sure partners are drawn on the same x axis as other partners.

How can I go about solving this and possible future overlapping conflicts? Do I need some sort of redraw function that detects collisions and adjusts the offsets of each block upon detecting? I'm trying to make it seamless so there's a limited amount of redrawing done.

An example of calculating offset relative to the "context" or current node:

var offset = getCurrentNodeOffset();

                        if ( relationship == RELATIONSHIPS.PARTNER ) {
                            var t = offset.top; // same level
                            var l = offset.left + ( blockWidth + 25 );
                        } else {
                            var t = offset.top - (blockHeight + 123 ); // higher
                            var l = offset.left - ( blockWidth - 25 );
                        }
Community
  • 1
  • 1
meder omuraliev
  • 171,706
  • 64
  • 370
  • 423
  • 1
    To the question of overlapping grandpa/grandma, all you have to do is stretch the distance between the parents in question, perhaps? – Rodrigo Dec 05 '15 at 03:43
  • 1
    Have you tried laying out nodes from the top down instead of the current bottom up approach? You're getting an overlap because (at least in the JavaScript example) you're positioning the parents based upon the position of the child. That fails because it's not taking sibling nodes into account when it adds the new parents, it just sort of dumps them in there and hopes for the best. – aroth Dec 05 '15 at 03:58
  • @Rodrigo - I believe I tried that but ran into something else. I'll try to recreate it in another fiddle. – meder omuraliev Dec 05 '15 at 04:15
  • @aroth - Currently it's just bottom up and I believe I might have to have a function that is fired on each new node addition, it'll check for collisions and/or respace each nodes. I actually tried to redo this all in d3js using a top to bottom approach but I ran into similar issues, so I might as well just tackle them here. – meder omuraliev Dec 05 '15 at 04:16
  • Here's my quick and dirty fix for collisions on node additions (should fix the main problem, but will of course cause some vertical-alignment issues...which can be worked out separately): http://jsfiddle.net/efLbbt01/10/ – aroth Dec 05 '15 at 04:33
  • I believe the easiest way is to "walk" the whole tree, measuring total height and width. Then you'll be able to draw the tree properly. – Rodrigo Dec 05 '15 at 04:39
  • @Rodrigo - do you mean to each node and excluding lines? Every block is 100x90 px, the canvas will be 5000x5000 but I might make it larger than that. – meder omuraliev Dec 05 '15 at 04:41
  • Yes, you'll only know the size of the canvas after you iterate the whole tree. – Rodrigo Dec 05 '15 at 05:53
  • Wouldn't it just be multiplying the number of total nodes by a 100x90 block? – meder omuraliev Dec 05 '15 at 06:04
  • @aroth - nice but how would you make it so mother+father are next to each other always? – meder omuraliev Dec 05 '15 at 14:10
  • @meder - By making the rendering less stateless. The example implementation on jsfiddle just kind of adds nodes to the DOM and hopes for the best. The way I'd approach this sort of thing in practice would be to maintain the tree-structure explicitly in memory, and have it available when rendering so that I could have more context with respect to how the node I'm working with relates to other nodes. Though also, I imagine that there must be preexisting JS libraries for nicely rendering directed acyclic graphs/trees? – aroth Dec 05 '15 at 14:26
  • @aroth - Not any that I've seen for my specific use. Including goJS, jTree, google charts, and others. The closest one might be http://gojs.net/latest/samples/genogram.html – meder omuraliev Dec 05 '15 at 14:30
  • @aroth - what I was going to do is I think similar to your explanation. Instead of drawing each node individually, traverse through the entire tree on each node addition and draw each node or adjust (existing) depending on surrounding + generation but it gets complex I think because if I go say, generation by generation, there might be an inconsistency vertically on the Y axis if that makes any sense. Would you recommend start at the top of the tree and go down generation by generation? – meder omuraliev Dec 05 '15 at 14:44

3 Answers3

18

I'm going to give a complicated answer, and that's because this situation is more complicated than you seem aware of. Graph layout algorithms are an active field of research. It's easy to attempt a simpler-than-general algorithm and then have it fail in spectacular ways when you make unwarranted, and usually hidden, assumptions.

In general, genetic inheritance graphs are not planar (see Planar Graphs on Wikipedia). Although uncommon, it certainly happens that all the ancestral relationships are not filled by unique people. This happens, for example, when second cousins have children.

Another non-planar situation can occur in the situation of children from non-monogamous parents. The very simplest example is two men and two women, each pairing with children (thus at least four). You can't lay out even the four parent pairs in one rank without curved lines.

These are only examples. I'm sure you'll discover more as you work on your algorithm. The real lesson here is to explicitly model the class of relationship your algorithm is able to lay out and to have verification code in the algorithm to detect when the data doesn't meet these requirements.

The question you are actually asking, though, is far more basic. You're having basic difficulties because you need to be using a depth-first traversal of the graph. This is the (easiest) full version of what it means to lay out "from the top down" (in one of the comments). This is only one of many algorithms for tree traversal.

You're laying out a directed graph with (at least) implicit notion of rank. The subject is rank 0; parents are rank 1; grandparents at rank 2. (Apropos the warnings above, ranking is not always unique.) Most of the area of such graphs is in the ancestry. If you don't lay out the leaf nodes first, you don't have any hope of succeeding. The idea is that you lay out nodes with the highest rank first, progressively incorporating lower-ranked nodes. Depth-first traversal is the most common way of doing this.

I would treat this as a graph-rewriting algorithm. The basic data structure is a hybrid of rendered subgraphs and the underlying ancestry graph. A rendered subgraph is a (1) a subtree of the whole graph with (1a) a set of progeny, all of whose ancestors are rendered and (2) a collection of rendering data: positions of nodes and lines, etc. The initial state of the hybrid is the whole graph and has no rendered subgraphs. The final state is a rendered whole graph. Each step of the algorithm converts some set of elements at the leaf boundary of the hybrid graph into a (larger) rendered subgraph, reducing the number of elements in the hybrid. At the end there's only one element, the render graph as a whole.

eh9
  • 7,036
  • 17
  • 41
  • Yup you're right I did come to this realization. Though I do have trouble digesting the last 2 paragraphs of what you said. Is it possible to come up with a simpler explanation? – meder omuraliev Dec 05 '15 at 18:49
  • @meder I've added some references and expanded the last paragraphs. – eh9 Dec 08 '15 at 14:28
  • Is there any chance you can explain with this data how you would traverse it using a depth first travel? I read up on the concept - but still kind of confused. So you said lay out the nodes with highest rank but incorporate lower ranked nodes - isn't this a breadth-first approach? Here is a sample tree - how would you traverse it? http://i.imgur.com/o1OunBa.png . Would you start out with the top most rank "john" "raymond "betty" and go downwards? Or do you start with one of the 3 ancestors and go down until the bottom most child? – meder omuraliev Dec 25 '15 at 00:15
  • http://static.arounds.org/tree-new/ That's my attempt rendering it by "level" so far. The "root" is level 0. Anything below is -1, anything above is greater than 0. Is this how you suggest rendering it? I'll obviously need to really refactor it to take into account positions of "children" relative to their parents. – meder omuraliev Dec 25 '15 at 01:08
  • The images and data you've added deserve to be separate questions. Addressing them properly is not best done in comments. Link a new question back here, and then add a comment here with the follow-on question. – eh9 Dec 29 '15 at 15:12
  • will do after I attempt a DFS one more time. Thanks! – meder omuraliev Dec 29 '15 at 15:32
  • I've added a new question here: http://stackoverflow.com/questions/34539786/rendering-a-dynamically-created-family-graph-with-no-overlapping-using-a-depth-f . Would appreciate if you had a chance to look. Thank you. – meder omuraliev Dec 31 '15 at 01:36
3

Since you are already using Family Echo, I'd suggest you look at how they develop their online family tree diagram, since they seem to have solved your problem.

When I enter your sample diagram into Family Echo, I can build a nice looking tree that seems to be what you are looking for with no cross over.

enter image description here

Although they are creating their diagrams with html and css, you can add the people to their diagrams one by one and then inspect where the boxes are being placed in terms of the left and top pixel locations of each element.

enter image description here

If I had more expertise in JavaScript, I would have tried building up some code to replicate some of what Family Echo is doing, but I'm afraid that's not my mojo.

lkessler
  • 19,414
  • 31
  • 125
  • 196
  • The code is really too obfuscated to understand. There's a lot of math going on and there are iframes as well. It's not so easy to grok how familyecho does it. Hence asking basically how to do it in a similar manner. – meder omuraliev Dec 05 '15 at 18:06
  • Then rather than trying to reinvent the wheel, why not attempt to use some javascript code that many others built for this. For example, there seem to be a few decent ones you might be able to use as is or modify at GitHub, e.g.: https://github.com/chandlerprall/FamilyTreeJS or https://github.com/wamacdonald89/ftree.js or https://github.com/djfun/geneajs – lkessler Dec 05 '15 at 18:37
  • Also see this related question on stackoverflow which might have some ideas for you: http://stackoverflow.com/questions/5639142/javascript-php-family-tree-builder-with-multiple-parents – lkessler Dec 05 '15 at 18:41
  • 1
    I've researched pretty much all relevant SO questions and projects out there and none seemed to fit the needs. https://github.com/djfun/geneajs may seem like a good start. – meder omuraliev Dec 05 '15 at 18:43
  • 1
    The link to http://philogb.github.io/jit/static/v20/Jit/Examples/Hypertree/example1.html - This would require extensive customization to it my chart needs which would basically be the same amount of work/redoing what I'm at – meder omuraliev Dec 05 '15 at 18:44
  • I wouldn't recommend non-standard or 3-D rendering of family tree diagrams. Most people (myself included) don't like them. – lkessler Dec 05 '15 at 18:49
  • 1
    Well if you do come across a chart you see that resembles the one I need, let me know! I've been searching but haven't yet. – meder omuraliev Dec 05 '15 at 18:56
2

you'll have to adjust all the branches off the node that you affect, each branch will have to recalculate the position of its nodes, and each node will have to be recalculated locally reaching the leaves.You calculated once the leaves are going to have to recalculate all the way to backing up, all that recursively. It's like a real tree, when you add physically branch to trunk ... the other branches move alone to leave some space, all sheets are automatically reset, so you have to imagine. And simulate this process in your diagram. Processes each branch reaches each leaf, and recalculates up to recompute the modified node neighbors. (one level above you started) That is not easy or single job to do.

  • Can you elaborate on what you mean by "single job to do"? – meder omuraliev Dec 12 '15 at 00:14
  • may be you found this useful: https://github.com/mbostock/d3/wiki/Gallery thirdten galery #30 (Collapsible Tree Layout): http://bl.ocks.org/robschmuecker/7880033 –  Jan 12 '16 at 21:43
  • Or this (Collapsible Force Layout): http://mbostock.github.com/d3/talk/20111116/force-collapsible.html in same gallery: https://github.com/mbostock/d3/wiki/Gallery –  Jan 12 '16 at 21:44