6

Consider this tree, where the node, C1, is missing. The missing node may occur at any tier of the tree, and nodes may be present at one or more tiers below the missing node.

enter image description here

What are the syntax to draw the edges for B1 -> D1 and B1 -> D2, as shown in red, to align the vertical section of these edges with the vertical section of the edges below node C2? The fork sep key specifies the l-distance between the parent anchor and the fork, but I don't know how to use it.

This is the MWE:

\documentclass[12pt,crop=true,border=1cm]{standalone}
\usepackage[edges]{forest}
\begin{document}

\begin{forest}
  for tree={
    grow'=0,
    draw,
    forked edges,
    text width=10mm,
    minimum height=5mm,
    parent anchor=east,
    child anchor=west,
    text centered,
  }
    [A,tier=level1
      [B1,tier=level2,name=B1
%        [C1 would be here, but it is missing
          [D1,tier=level4,name=D1
            [E1,tier=level5]
            [E2,tier=level5]
          ]
          [D2,tier=level4,name=D2
            [E3,tier=level5]
            [E4,tier=level5]          
          ]
%        ]
      ]
      [B2,tier=level2
        [C2,tier=level3
          [D3,tier=level4
            [E5,tier=level5]
            [E6,tier=level5]          
          ]
          [D4,tier=level4
            [E7,tier=level5]
            [E8,tier=level5]          
          ]
        ]
      ]
    ]
    % Draw the edges with the fork at the desired level.
    % The fork sep key specifies the "The l-distance between the parent anchor and the fork." 
    % See Forest manual version  July 14, 2017, p81
    \draw[red,thick] (B1.parent anchor) -- +(57pt,0) |- (D1.child anchor);
    \draw[red,thick] (B1.parent anchor) -- +(57pt,0) |- (D2.child anchor);
\end{forest}


\end{document}

UPDATE

Testing an expanded MWE with the fork sep code from @marmot revealed some shortfalls

With two nodes (C1 and C3) at tier=level3, and a third node C2 is missing, the edge shape resembles a folder structure rather than being forked.

enter image description here

Removing C2 and D5 results in the tree no longer correctly representing the structure.

enter image description here

While I expect that an automated solution is going to rely on using the fork sep key, @marmot's solution appears to be close, but perhaps something is still missing.

The solution posted by @Zarko drew edges between grandparent and child nodes using orthogonal coordinates to define the location of the fork. This is a robust and versatile approach. @Zarko, please put back your answer.

UPDATE 2

This may be a robust solution. It uses (a) @marmot's fork sep code, (b) identifies the missing nodes as coordinates without drawing the node shape i.e. [,draw=none,name=C2 and (c) uses a \draw command to join node.child anchor to node.parent anchor. For example: \draw (C2.child anchor) -- (C2.parent anchor);

\documentclass[12pt,crop=true,border=1cm]{standalone}
\usepackage[edges]{forest}
\forestset{
  declare dimen={my fork sep}{0.5em},
  my forked edge'/.style={
    edge={rotate/.option=!parent.grow},
    edge path'={let \noexpand\p1=($(.child anchor)-(!u.parent anchor)$) in
    (!u.parent anchor) -- ++(\noexpand\x1-\forestoption{my fork sep},0) |- (.child anchor)},
  },
  my forked edge/.style={
    on invalid={fake}{!parent.parent anchor=children},
    child anchor=parent,
    my forked edge',
  },
  my forked edges/.style={for nodewalk={#1}{my forked edge}},
  my forked edges/.default=tree,
}
\begin{document}

\begin{forest}
  for tree={
    grow'=0,
    draw,
    my forked edges,my fork sep=8pt,
    text width=10mm,
    minimum height=5mm,
    parent anchor=east,
    child anchor=west,
    text centered,
  }
    [A,tier=level1
      [B1,tier=level2,name=B1
        [C1% would be here, but it is missing
          [D1,tier=level4,name=D1a
            [E1,tier=level5]
            [E2,tier=level5]
          ]
          [D2,tier=level4,name=D2a
            [E3,tier=level5]
            [E4,tier=level5]
          ]
          [D3,tier=level4,name=D3a
            [E5,tier=level5]
            [E6,tier=level5]
          ]
        ]
        [,draw=none,name=C2                           
          [D4,tier=level4,name=D1b
            [E7,tier=level5]
            [E8,tier=level5]
          ]
          [,draw=none,name=D5 
            [E9,tier=level5]
            [E10,tier=level5]
          ]                                   
        ]                                     
      ]
      [B2,tier=level2
        [C3,tier=level3
          [D6,tier=level4
            [E11,tier=level5]
            [E12,tier=level5]
          ]
          [D7,tier=level4
            [E13,tier=level5]
            [E14,tier=level5]
          ]
        ]
      ]
    ]
    \draw (C2.child anchor) -- (C2.parent anchor);
    \draw (D5.child anchor) -- (D5.parent anchor);
\end{forest}

\end{document}

enter image description here

  • It is now really hard to understand your question because the code for your extended example no longer demonstrates the problem you wanted help with. Please post answers in the answer space and not in the question space! – cfr Jul 21 '18 at 0:19
  • forked edges should not be within for tree. Either forked edges or for tree={forked edge}. – cfr Jul 21 '18 at 0:21
3

I don't know what the extended version of the question was as the question now is an answer rather than a question.

For this reason, my aim was to do two things:

  • answer the non-extended question;

  • reproduce the result in the extended answer-question.

Basically, I want to propose something much simpler, which does not require drawing anything in, doesn't require a variant on the forked edges style (though it does use a wrapper around the original version of the style) and let's you specify the missing nodes as just empty nodes.

In addition, the assignment of tiers is automated and the use of relative anchors makes the code a bit more flexible.

The wrapper style is called, very boringly, my tree and is defined thus:

\forestset{
  my tree/.style={
    forked edges,

Note that forked edges should not be within the scope of for tree. Either use forked edge within that scope or, as here, keep forked edges outside it.

    for tree={
      grow'=0,
      draw,
      text width=10mm,
      minimum height=5mm,
      text centered,
      tier/.option=level,

This automates the levels, since the level completely determines the desired tier.

    },
    delay={
      where content={}{content=\phantom{X},draw=none, child anchor=children}{}

If you want to miss a node, leave the node empty. This code will then add a phantom X (to save messing around trying to figure out the right height to give it - apparently the other nodes exceed the 5mm minimum). The node will have the standard assigned width, but won't be drawn. Changing the child anchor, however, has the effect of making it look as if no node is there. Essentially, this runs the branch from the parent all the way through the centre of the node, which is just what would happen if you had a coordinate on the far side, rather than a regular width node.

In some cases, you need a coordinate, but here, it's easier not to go that route. If you really want true coordinates rather than look-alikes, I would just move the coordinate later in before drawing tree.

    },
  },
}

That's it.

Now, we can write

\begin{forest}
  my tree
    [A
      [B1
       [
          [D1
            [E1]
            [E2]
          ]
          [D2
            [E3]
            [E4]          
          ]
       ]
      ]
      [B2
        [C2
          [D3
            [E5]
            [E6]          
          ]
          [D4
            [E7]
            [E8]          
          ]
        ]
      ]
    ]
\end{forest}
\begin{forest}
  my tree,
    [A
      [B1
        [
          [D1
            [E1]
            [E2]
          ]
          [D2
            [E3]
            [E4]
          ]
          [D3
            [E5]
            [E6]
          ]
        ]
        [                        
          [D4
            [E7]
            [E8]
          ]
          [
            [E9]
            [E10]
          ]                                   
        ]                                     
      ]
      [B2
        [C3
          [D6
            [E11]
            [E12]
          ]
          [D7
            [E13]
            [E14]
          ]
        ]
      ]
    ]
\end{forest}

to produce

result with simplified code

\documentclass[12pt,border=10pt]{standalone}
\usepackage[edges]{forest}
\forestset{
  my tree/.style={
    forked edges,
    for tree={
      grow'=0,
      draw,
      text width=10mm,
      minimum height=5mm,
      text centered,
      tier/.option=level,
    },
    delay={
      where content={}{content=\phantom{X},draw=none, child anchor=children}{}
    },
  },
}
\begin{document}
\begin{forest}
  my tree
    [A
      [B1
       [
          [D1
            [E1]
            [E2]
          ]
          [D2
            [E3]
            [E4]          
          ]
       ]
      ]
      [B2
        [C2
          [D3
            [E5]
            [E6]          
          ]
          [D4
            [E7]
            [E8]          
          ]
        ]
      ]
    ]
\end{forest}
\begin{forest}
  my tree,
    [A
      [B1
        [
          [D1
            [E1]
            [E2]
          ]
          [D2
            [E3]
            [E4]
          ]
          [D3
            [E5]
            [E6]
          ]
        ]
        [                        
          [D4
            [E7]
            [E8]
          ]
          [
            [E9]
            [E10]
          ]                                   
        ]                                     
      ]
      [B2
        [C3
          [D6
            [E11]
            [E12]
          ]
          [D7
            [E13]
            [E14]
          ]
        ]
      ]
    ]
\end{forest}
\end{document}
  • Thank you. Your insight regarding Forest questions is remarkable. I think I understand your solution. Where you wrote delay={… child anchor=children}, in the context of my question, children is actually the grandchild node because the edge passes through the child node? This methods permits drawing dendrograms, which are graphical devices used in classification and taxonomy. Thank you for a wonderful answer. – Ross Jul 21 '18 at 1:08
  • @Ross No. It is an anchor of the invisible node. It isn't a node at all. It is an anchor (a coordinate, I guess). Just like .east or .west. But these anchors are sensitive to the direction of the tree's growth. So .children is an anchor on the invisible node in the direction of that node's children. This is actually also the value parent anchor has, as that's the default for forked edges. So, basically, the undrawn node is there, but parent anchor=child anchor, so the edge from the parent meets the edge to the children. – cfr Jul 21 '18 at 2:36
  • @Ross As for insight, that's just the effect of writing prooftrees, I think. Plus some features of current Forest were added for prooftrees, so I had the benefit of some one-to-one tuition in chat from Forest's author during their development :-). – cfr Jul 21 '18 at 2:40
2

I am not a forest expert but I could look up the definition of the forked edges in the forest manual and use it to define a "new" version in which the distance of the fork to the children (rather than parent) is fixed. EDIT: Moved the my forked edges out of for tree, big thanks to @cfr!

\documentclass[12pt,crop=true,border=1cm]{standalone}
\usepackage[edges]{forest}
%\usetikzlibrary{calc}
\forestset{
  declare dimen={my fork sep}{0.5em},
  my forked edge'/.style={
    edge={rotate/.option=!parent.grow},
    edge path'={let \noexpand\p1=($(.child anchor)-(!u.parent anchor)$) in 
    (!u.parent anchor) -- ++(\noexpand\x1-\forestoption{my fork sep},0) |- (.child anchor)},
  },
  my forked edge/.style={
    on invalid={fake}{!parent.parent anchor=children},
    child anchor=parent,
    my forked edge',
  },
  my forked edges/.style={for nodewalk={#1}{my forked edge}},
  my forked edges/.default=tree,
}
\begin{document}

\begin{forest}
my forked edges,
  for tree={
    grow'=0,
    draw,
    my fork sep=8pt,
    text width=10mm,
    minimum height=5mm,
    parent anchor=east,
    child anchor=west,
    text centered,
  }
    [A,tier=level1
      [B1,tier=level2,name=B1
%        [C1 would be here, but it is missing
          [D1,tier=level4,name=D1
            [E1,tier=level5]
            [E2,tier=level5]
          ]
          [D2,tier=level4,name=D2
            [E3,tier=level5]
            [E4,tier=level5]          
          ]
%        ]
      ]
      [B2,tier=level2
        [C2,tier=level3
          [D3,tier=level4
            [E5,tier=level5]
            [E6,tier=level5]          
          ]
          [D4,tier=level4
            [E7,tier=level5]
            [E8,tier=level5]          
          ]
        ]
      ]
    ]
\end{forest}
\end{document}

enter image description here

  • As usual, you demonstrate a high level of skill with tikz-based packages. This looks like the way to go. I'll do some testing and wait to see if there are other suggestions. Thank you. – Ross Jul 20 '18 at 14:31
  • Thank you @marmot. I did some more testing and included the results in my question. – Ross Jul 20 '18 at 16:02
  • I think I have been able to tweak your code to get the correct tree. Many thanks for your help, and all your wonderful answers. – Ross Jul 20 '18 at 16:52
  • @cfr Thanks a lot! (again ;-) I guess there might be a way to use some average to make it work in the general case, but you already provided a much simpler solution (+1 of course) so it is pointless to try that out. – marmot Jul 21 '18 at 7:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.