# Automatically line breaking text, or extending arrows in tikz

Is it possible to automatically make the text line break so that it does not overflow into the nodes? An alternative is to have the nodes automatically moved farther away to accommodate the longer text (keeping as much as possible to the way that I am setting up the nodes, I would like to avoid manual coordinate entry as much as possible).

While this is sometimes manually possible with the text width option, that also has the effect of making the text off center in some cases, and should not be necessary.

MWE:

\documentclass[12pt]{article}
\usepackage{tikz}
\usetikzlibrary{automata,positioning,arrows}
\begin{document}
\begin{tikzpicture}[shorten >=2pt,node distance=4cm,on grid,auto]
\node[state] (Rn) [above right =of Rd] {Run};
\node[state] (Bl) [below right =of Rd] {Blocked};
\node[state] (Nr) [below right =of Rn] {Dead};
\path[->]
(Rd)    edge  node[sloped, anchor=center, above] {Given a timeslice}     (Rn)
(Rd)    edge  node[sloped, anchor=center, below] {Asks for I/O}          (Bl)
(Rd)    edge  node {Gets killed by kernel} (Nr)
(Bl)    edge  node {Out of memory}         (Nr);
\end{tikzpicture}
\end{document}

Update: Example of issues with text width. Notice how the center shifts, even though anchor = center (text width = 3cm and text width = 2.7cm were used).

-
text width is kind of the right way in my opinion but I don't fully understand your complaint about it. Can you elaborate a little more? You can also try to align=left to increase the line break effect. –  percusse Feb 22 '13 at 8:51
@percuße updated –  soandos Feb 22 '13 at 9:06
+1 for Times lice! :-D –  morbusg Feb 22 '13 at 9:07

It is actually feasible to do inline calculations of the radius and enforce the text width to do this.

The method needed is the TikZ let commands (which is enabled by the library calc).

It basically allows you to do arithmetic on coordinates in the path:

An example from the manual is this:

\coordinate (a) at (rnd,rnd);
\coordinate (b) at (3-rnd,3-rnd);
\draw (a) -- (b);
\node (c) at (1,2) {x};
\draw let \p1 = ($(a)!(c)!(b) - (c)$),
\n1 = {veclen(\x1,\y1)}

As coordinates a and b are located at random, it was impossible to align the size of the circle draw, without using arithmetic to solve the problem of drawing a circle which just touches the line between a and b.

What let basically does is allow to access x and y coordinates of specific points and do operations on them. And together with the calc library you have, virtually, endless possibilities to align, calculate lengths etc.

In your case, you need to calculate the length of the vector between them, minus the radius of the nodes.

\path[->]
let \p1 = ($(Rd)-(Bl)$), % Save length between centers of the nodes
\p2 = ($(Rd)-(Rd.south)+(Bl)-(Bl.south)$), % Calculate the radius
\n1 = {veclen(\x1,\y1)-veclen(\x2,\y2)} in % The allowed text width
(Rd) edge node[sloped,anchor=center,align=center,above,text width=\n1]
{Given a timeslice} (Rn) ...

This will automatically adjust to the length between the two nodes.

An example at different node distance is this:

\foreach \myx in {2.5cm,3cm,4cm,5cm} {
\begin{tikzpicture}[shorten >=2pt,node distance=\myx,on grid,auto]
\node[state] (Rn) [above right =of Rd] {Run};
\path[->]
let \p1 = ($(Rd)-(Rn)$),
\p2 = ($(Rd)-(Rd.south)+(Rn)-(Rn.south)$),
\n1 = {veclen(\x1,\y1)-veclen(\x2,\y2)} in
(Rd) edge node[sloped,anchor=center,align=center,above,text width=\n1]
{Given a timeslice} (Rn);
\end{tikzpicture}
}

Which produces the following figure:

In your case, you can even create a new command:

\def\mylet#1#2{%
let \p1 = ($(#1)-(#2)$),%
\p2 = ($(#1)-(#1.south)+(#2)-(#2.south)$), %
\n1 = {veclen(\x1,\y1)-veclen(\x2,\y2)} in %
}

However, this limits you to recreate the \path command on each connection made (notice that you need the align=center for alignment on the path correctly, this is because when the text is too small the text width is too large), try and play with node distance and see the results.

\begin{tikzpicture}[shorten >=2pt,node distance=4cm,on grid,auto
,every node/.append style={align=center}]
\node[state] (Rn) [above right =of Rd] {Run};
\node[state] (Bl) [below right =of Rd] {Blocked};
\node[state] (Nr) [below right =of Rn] {Dead};
\path[->] \mylet{Rd}{Rn} (Rd)
edge node[sloped,anchor=center,above,text width=\n1]
{Given a timeslice} (Rn);
\path[->] \mylet{Rd}{Bl}
(Rd) edge node[sloped,anchor=center,below,text width=\n1]
\path[->] \mylet{Rd}{Nr}
(Rd) edge node[text width=\n1]
{Gets killed by kernel} (Nr);
\path[->] (Bl) edge node {Out of memory} (Nr);
\end{tikzpicture}

# Exemplified

So how does the let command work for TikZ?

Consider the following:

\coordinate (a) at (1,0);
\draw (a) -- ++(2,0);

This will create a coordinate called a and then draw from a to plus two units in the x-direction.
The exact same drawing can be realized like this:

\coordinate (a) at (1,0);
\draw[thick,dashed] let \p1 = (a) in (\p1) -- ++(2,0);

For now this is obviously not the best way (the former is much clearer and shorter). However, this allows to do arithmetic on x and y coordinates for specific coordinates (in this case the point a or (1,0) can be used in calculations of subsequent points.

The best example is probably to step through the solution supplied to you:

let \p1 = ($(Rd)-(Bl)$)

As in the previous example we let \p1 become a coordinate which is calculated within the outermost parenthesis. Within those we encounter the form:

$(Rd)-(Bl)$

this is a typical TikZ command to substract the coordinate Rd from Bl. In this case these coordinates refers to nodes, and in such cases they correspond to the center-point of the node, i.e. the center of the circle. So if Rd and Bl where defined as this:

\node (Rd) at (1,1) {Rd};
\node (Bl) at (4,4) {Bl};

now we can calculate the vector pointing from Bl to Rd by subtracting Bl from Rd:

\draw (0,0) -- ($(Rd)-(Bl)$); % Will draw (0,0) -- (-3,-3)

So in the example I gave you, we would have \p1 = (-3,-3). The following command \p2 = ($(Rd)-(Rd.south)+(Bl)-(Bl.south)$) takes the points show in the following picture and calculates, simultaneously, the r_1 and r_2 vectors and adding them.

Now we have the vector going from the middle of one node, to the middle of the other node AND we have the vector that has a length of both nodes radius's.
Now we can calculate the length between the two nodes by: subtracting the radius vector from the node center vectors.

\n1 = {veclen(\x1,\y1)-veclen(\x2,\y2)}

If it makes more sense, you could also have done:

...
let \p1 = ($(Rd)-(Rn)$), % Vector between centers
\p2 = ($(Rd)-(Rd.south)$), % Vector with length of Rd radius
\p3 = ($(Rn)-(Rn.south)$), % Vector with length of Rn radius
%     (length between centers) - (radius of Rd) - (radius of Rn)
\n1 = {veclen(\x1,\y1)-veclen(\x2,\y2)-veclen(\x3,\y3)}
in ...
-

The option text width can be used to give the width of the box in which the text has to be typeset, and this causes automatic line-breaking (and perhaps hyphenation) in that box.

However, since the text by default is flush-left inside that box, even if the box is correctly centered on the line, the text could appear slightly shifted to the left.

\begin{tikzpicture}
\node[circle, draw] (A) at (0,0) {A};
\node[circle, draw] (B) at (4,0) {B};
\draw (A) -- (B) node[text width=3cm, above, midway]
{Some long text which will be wrapped};
\end{tikzpicture}

Shading the box behind the text helps to understand this effect:

\begin{tikzpicture}
\node[circle, draw] (A) at (0,0) {A};
\node[circle, draw] (B) at (4,0) {B};
\draw (A) -- (B) node[text width=3cm, above, midway, fill=black!10]
{Some long text which will be wrapped};
\end{tikzpicture}

This problem can be alliviated centering the text inside the box. However this will disable hyphenation and will try to make all resulting lines of the same length. You can manually insert linebreaks with \\ though.

\begin{tikzpicture}
\node[circle, draw] (A) at (0,0) {A};
\node[circle, draw] (B) at (4,0) {B};
\draw (A) -- (B) node[text width=3cm, above, midway, fill=black!10, align=center]
{Some long text which will be wrapped};
\end{tikzpicture}

Finally, the next code shows a technique to automatically compute an adequate width for the text box depending on the distance among nodes:

\usetikzlibrary{calc}

\def\connectwithlongtext#1#2#3#4{ % line options, start, end, text
\draw[#1]
let
\p1 = ($(#2)-(#3)$),
\n1 = {0.6*veclen(\x1,\y1)}
in  (#2) -- (#3)
node[fill=black!5, midway, above, align=center, text width=\n1, sloped]
{#4};

}
\begin{tikzpicture}
\node[draw,circle] (A) at (0,0) {A};
\node[draw] (B) at (3,2) {B};
\node[draw] (C) at (4.5, -3) {C};
\connectwithlongtext{->}{A}{B}{Lorem ipsum and blah, blah, blah};
\connectwithlongtext{->}{A}{C}{Lorem ipsum and blah, blah, blah};
\end{tikzpicture}

-