57

It is easy to produce a path between two nodes consisting of a single horizontal and a single vertical line (from the manual):

\begin{tikzpicture}
  \draw (0,0) node(a) [draw] {A}  (1,1) node(b) [draw] {B};
  \draw (a.north) |- (b.west);
  \draw[color=red] (a.east) -| (2,1.5) -| (b.north);
\end{tikzpicture}

However, what if I wanted the same short syntax to produce a path between (a) and (b) using a horizontal line, a vertical line, and a second horizontal line. It isn't to difficult to calculate the intermediate points, but how could the following syntax be defined?

\begin{tikzpicture}
  \draw (0,0) node(a) [draw] {A}  (3,1) node(b) [draw] {B};
  \draw (a.east) -|- (b.west);
\end{tikzpicture}
1
  • 1
    Do you mean something like \begin{tikzpicture} \draw (0,0) node(a) [draw] {A} (3,1) node(b) [draw] {B}; \draw (a.east) -- ++(1,0) |- (b.west); \end{tikzpicture}
    – percusse
    Feb 21, 2012 at 23:48

4 Answers 4

61

This answer extends Marc van Dongen's anwser.

The two styles are -|- and |-| with default value 0.5 to position the intermediate point (so, default is middle point).

enter image description here

\documentclass[margin=2mm]{standalone}
\usepackage{tikz}
\usetikzlibrary{calc}
\tikzset{
  -|-/.style={
    to path={
      (\tikztostart) -| ($(\tikztostart)!#1!(\tikztotarget)$) |- (\tikztotarget)
      \tikztonodes
    }
  },
  -|-/.default=0.5,
  |-|/.style={
    to path={
      (\tikztostart) |- ($(\tikztostart)!#1!(\tikztotarget)$) -| (\tikztotarget)
      \tikztonodes
    }
  },
  |-|/.default=0.5,
}
\begin{document}
\begin{tikzpicture}[thick]
  \draw[->,blue] (2.1,1.1) to[|-|] (1.1,0.1);
  \draw[->] (2,1) to[-|-] (1,0);

  \draw[->,blue] (3.1,0.1) to[|-|] (4.1,1.1);
  \draw[->] (3,0) to[-|-] (4,1);

  \begin{scope}[yshift=-1.5cm]
    \draw[->,red] (2.1,1.1) to[|-|=.2] (1.1,0.1);
    \draw[->,orange] (2,1) to[-|-=.2] (1,0);

    \draw[->,red] (3.1,0.1) to[|-|=.8] (4.1,1.1);
    \draw[->,orange] (3,0) to[-|-=.8] (4,1);
  \end{scope}
\end{tikzpicture}
\end{document}
2
  • 1
    Well, I must say: This pretty much want I wanted. Nice, touch to allow for something other than 0.5 i.e. the middle point. Simple syntax, didn't know that this was possible. Good answer!
    – DrJay
    Dec 11, 2012 at 18:02
  • Works perfect with the trees library using edge from parent path={(\tikzparentnode) to[|-|] (\tikzchildnode)}.
    – n4pK
    Jan 21, 2020 at 15:05
69
+500

A previous version of this answer explained the paths.ortho library that was published in my pgf repo on GitHub. The library is now part of my tikz-ext package which should be used instead (and has a proper manual).

With the ext.paths.ortho library that is possible. It works by re-writing and intercepting the main TikZ parser. (Which is also the reason this may possibly break with an TikZ update or another library but I hope it won't.)

This library introduces six path operators:

  • Zig-Zag
    • |-| and
    • -|-;
  • Zig-Zig
    • r-ud,
    • r-du,
    • r-lr and
    • r-rl.

The following keys exist (when these are used directly with the path operation, the prefix ortho/ should be dropped, e.g. -|-[ratio=.3]).

  • ortho/spacing=<ratio>

    This sets the ratio for the middle part part of the Zig-Zag connection. Values under 0 and over 1 will lead to lines that look more like the Zig-Zig paths.

  • ortho/distance=<distance>

    Instead of ratio an absolute distance can be used instead, this will be measured from the starting point when <distance> ≥ 0 and from the targeting point otherwise.

  • ortho/from center=<true or false> (default true)

    When nodes get connected the placement of the middle part of the Zig-Zag and the Zig-Zig (see below) connections will be calculated from the border of these nodes. The middle part of the connections will be calculated from the nodes’ center if this key is set to true.

New timers are setup for both the Zig-Zag and the Zig-Zig connections, these can be configured through the following keys.

  • ortho/spacing=<number> (default 4)

    This affects the the position of the kinks that will be at 1/<number> (default 0.25) and <number>−1/<number> (default 0.75). The position 0.5 will always be at the center of the middle part. Positions 0.0 and 1.0 will be at the start and at the end respectively.

    When <number> is set to 0 the position

    • -1 will be at the start,
    • 0 will be at the first kink,
    • 1 will be at the second kink,
    • 2 will be at the end and
    • 0.5 will still be the center of the middle part.
  • ortho/middle 0 to 1 is an alternative to ortho/spacing=0.

The Zig-Zig connections are explained on my answer to How to draw a return arrow from node-3 to node-1.


For to and edge connections the keys

  • horizontal vertical horizontal for -|- and
  • vertical horizontal vertical for |-|

amongst others are defined. Previously, I've used |-| and -|- for these shortcuts but they conflict with arrows specification which is why these are not defined by default anymore. However, they can easily be defined by

\tikzset{ortho/install shortcuts}

amongst others.

Code (Question)

\documentclass[tikz,border=5pt]{standalone}
\usetikzlibrary{ext.paths.ortho}
\begin{document}
\begin{tikzpicture}[thick, ->]
\draw[blue] (2.1, 1.1) |-| (1.1, 0.1);
\draw       (2  , 1  ) -|- (1  , 0  );

\draw[blue] (3.1, 0.1) |-| (4.1, 1.1);
\draw       (3  , 0  ) -|- (4  , 1  );

\begin{scope}[yshift=-1.5cm, ortho/ratio=.2]
  \draw[red]    (2.1, 1.1) |-| (1.1, 0.1);
  \draw[orange] (2  , 1  ) -|- (1  , 0  );

  \draw[red]    (3.1, 0.1) |-|[ratio=.8] (4.1, 1.1);
  \draw[orange] (3  , 0  ) -|-[ratio=.8] (4  , 1  );
\end{scope}
\end{tikzpicture}
\end{document}

Output (Question)

enter image description here

6
  • 7
    This is just excellent, something I have missed for years drawing complex diagrams with TikZ. Have you considered trying to get this into the TikZ CVS (so that it will continue to work with future updates of TikZ)? Till Tantau hasn't been seen at tex.se so far, but Mark Wilbrow and Christian Feuersänger are both around and may be willing to help you in this respect.
    – Daniel
    Jun 6, 2013 at 8:53
  • 5
    I also think this should go in to the CVS. Tikz-uml has also a similar construction but this is way cleaner. However a minor problem is that it has to be freed from the etoolbox-related content. TikZ developers are very careful about that.
    – percusse
    Jun 6, 2013 at 12:33
  • @percusse That’s not a problem. The original draft didn’t used etoolbox but just re-defined the needed parsing macros. The \patchcmd version makes it a little bit more compatible if TikZ changes. But if it would be implemented there will be no re-definition but just one (ever-growing?) handler. But the code is still not complete or bug-free. For once I wanted to implement a switch that calculates the distances/ratios from the center of the nodes. Also the border is not always the correct one for certain values and combinations. (But that also happens with |-/-|.) Jun 6, 2013 at 12:56
  • 1
    Please consider making this more generally available before TikZ is dead and buried. I appreciate you may (still?) want to improve the code, but if you've not got around to it in 2.5 years, you probably aren't going to do so any time soon. Meanwhile, the code essentially sits on a shelf. If you contribute it, other people may help by sending you patches. If you include a TO DO, people will know the direction you'd like to go in. Perfection is a worthy aim, but so is availability. Pleeeeaaaase. I know how tempting it is to want to wait, tweak... but....
    – cfr
    Feb 15, 2016 at 23:38
  • 1
    I'd like to see it on CTAN. It really won't get generally used or be generally available otherwise. & I don't fear TikZ will die.
    – cfr
    May 14, 2016 at 20:46
26

You can do this with a to path. The following solution is intended to highlight the steps that are needed. The solution can draw connections in any direction, not just vertical/horizontal connections, and therefore generalises all other solutions presented thus far.

Also note the solution doesn't include an animated picture, so you can focus on the provided output without any risk of being distracted by the animation:-).

The solution shows a nice application of the not-too-frequently used projection modifier ($(a)!(b)!(c)$), which projects the point (b) onto the infinite line through (a) and (c).

The reader may add their own favourite auxiliary keys for drawing a horizontal-vertical-horizontal connection, or a vertical-horizontal-vertical connection.

\documentclass{scrartcl}
\usepackage{tikz}
\usetikzlibrary{calc}

\makeatletter
% The parsing is done relative to the key "/my connection/,
% which is not part of the user-defined API
\pgfkeys{/my connection/.cd,
         % Save the angle value.
         angle/.store in=\mycon@angle,
         % Save the ratio value.
         ratio/.store in=\mycon@ratio,
         % The draw key triggers the drawing. Should be last.
         draw/.style={/tikz/to path={
         let % \n{a} is the angle.
             \n{a}=\mycon@angle,
             % \n{r} is the ratio.
             \n{r}=\mycon@ratio,
             % \p{s} is the start of the connection.
             \p{s}=(\tikztostart),
             % \p{t} is the target of the connection.
             \p{t}=(\tikztotarget),
             % \p{sa} is some point on the line from \p{s} in direction \n{a}.
             \p{sa}=($(\p{s})+(\n{a}:1)$),
             % \p{st} is some point on the line from \p{t} in direction \n{a}.
             \p{st}=($(\p{t})+(\n{a}:1)$),
             % \p{proj s} is the projection of \p{s} on the line through \p{t} and \p{st}.
             \p{proj s}=($(\p{t})!(\p{s})!(\p{st})$),
             % \p{proj t} is the projection of \p{t} on the line through \p{s} and \p{sa}.
             \p{proj t}=($(\p{s})!(\p{t})!(\p{sa})$)
         in    % Second point of connection.
               -- ($(\p{s})!\n{r}!(\p{proj t})$)
               % Third point of connection.
               -- ($(\p{proj s})!\n{r}!(\p{t})$)
               % Last point of connection.
               -- (\p{t})
            \tikztonodes}}}
% The "connection" key is part of the user-defined API key.
% It has two sub-keys: ratio and angle, with default values 0.5
% and 0 respectively. We use the connection key to set the defaults,
% override the user-provided values for the keys (if any), and then
% draw the connection.
\tikzset{connection/.style={/my connection/.cd,ratio=0.5,angle=0,#1,draw}}
\makeatother

\begin{document}

\begin{tikzpicture}
\draw (0,0) coordinate(a) node[anchor=north]{$a$}
      (6,3) coordinate(b) node[anchor=south]{$b$};
\draw[->]      (a) to[connection] (b);
\draw[blue,->] (b) to[connection={ratio=0.25,angle=30}] (a);
\draw[red,->]  (a) to[connection={ratio=0.25,angle=90}] (b);
\end{tikzpicture}
\end{document}

sloped connections

4
  • Yes, I could do that. As stated it is easy to calculate the intermediate points (or point). But the question was wether it is possible to define the syntax: -|-
    – DrJay
    Feb 22, 2012 at 0:16
  • @MarcvanDongen I suggest some addition in my community answer... Dec 10, 2012 at 15:00
  • Allow me some criticism: All solutions with a to path are only able to place the nodes on one part of the whole zig-zag line (here: the last one) without further modification. This is one of the major reason why I created the library in the first place; the other one is that these kind of questions had come up once a week—I’m exaggerating, of course—when I created the udlr part. Jun 6, 2013 at 5:31
  • @Qrrbrbirlbel If I understand you correctly, you're saying that it should be possible to treat the connection path as a whole so it's possible to compute points along the path. If not, please explain further. Otherwise, where does the OP make this request?
    – user10274
    Jun 6, 2013 at 9:08
4

Try this:

\begin{tikzpicture}
\path (0,0) node(a) [draw] {A} -- (3,1) node(b) [draw] {B} coordinate[pos=0.5](inter);
\draw (a) -| (inter) |- (b) ;
\end{tikzpicture}
1
  • Hi, I get your point, but it does not answer the question!
    – DrJay
    Sep 11, 2014 at 22:50

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .