Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

The first two lines do just what I want:

\node (F) at (1,1) {$F$};

\draw (0,2) to [out=-90] (F);

That is, it draws an edge downward from the point (0,0) and bends it to hit (F), entering the node at roughly 135 degrees. I don't have to specify in=135.

But the next line uses the same ~135 degree value for in, when I want it to have a value around 45 degrees and not specify it!

\draw (2,2) to [out=-90] (F);

How do I tell tikz to compute the in value again rather than use the previous one?

Here's a minimum working example, modified now that it's clear I was a "victim of circuses":

\documentclass{article}
\usepackage{tikz}

\begin{document}
  \begin{tikzpicture}
    \node [circle, draw=black] (F) at (1,1) {$F$};
    \draw (0,2) to [out=-90] (F);
    \draw (0.7,2) to [out=-90, in=110] (F);
    \draw (2,2) to [out=-90, in=45] (F);
    \draw (F) to [out=-135, in=90] (0,0);
    \draw (F) to [out=-45, in=90] (2,0);
  \end{tikzpicture}
\end{document}
share|improve this question
    
I've inserted the image, and will delete my comments. You can do the same to keep this space clean. –  Werner Jan 3 '12 at 2:50
add comment

2 Answers 2

up vote 9 down vote accepted

You are a victim of circuses.

The fact that you "don't have to specify in=135" is a coincidence. And the conclusion that in the second case TikZ is "us[ing] the previous one" is false. The observed behaviour is all due to two little lines in the file tikzlibrarytopaths.code.tex which read:

\def\tikz@to@out{45}
\def\tikz@to@in{135}

These mean that if you don't specify an out then it is automatically set to 45, similarly if you don't specify an in it is automatically set to 135. (These only have effect if something selects the curve variant of the to path rather than the default. Specifying one of in or out is sufficient.)

There is a relative option which sets the angles relative to a line between the start and ending coordinates. So if you use relative,in=180 (as the line is oriented from the source to the target) then the correct angle is chosen for the in line. However, this makes the out angle also relative, which isn't what you seem to want.

So here's a little code I wrote that sets the in and out automatically if you don't set them yourself. The key, auto ends, has to be given before any overriding out or in because it can't tell the difference between the user saying out=45 and the default value. However, this does not seem much of a hardship.

What it does is to set both in and out to be empty at call time. Then when the path is processed, any that are still empty are set to the angle between the start and end coordinates.

\documentclass{article}
%\url{http://tex.stackexchange.com/q/39903/86}
\usepackage{tikz}

\makeatletter
\tikzset{
  auto ends/.code={%
    \def\tikz@to@switch@on{\let\tikz@to@path=\tikz@to@curve@path@rel}%
    \tikz@to@switch@on
    \let\tikz@to@in=\pgfutil@empty
    \let\tikz@to@out=\pgfutil@empty
  }
}

\def\tikz@to@curve@path@rel{%
  [every curve to]
  \pgfextra{%
    \ifx\tikz@to@out\pgfutil@empty
     \let\relin@toto=\tikztotarget
    \ifx\tikz@to@in\pgfutil@empty
    \else
     \tikz@to@modify\relin@toto\tikz@to@in
    \fi
     \tikz@scan@one@point\pgfutil@firstofone(\relin@toto)\relax
     \pgf@xa=\pgf@x
     \pgf@ya=\pgf@y
     \tikz@scan@one@point\pgfutil@firstofone(\tikztostart)\relax
     \advance\pgf@xa by -\pgf@x
     \advance\pgf@ya by -\pgf@y
     \pgfmathtruncatemacro\tikz@to@out{int(atan2(\pgf@xa,\pgf@ya))}%
    \fi
    \ifx\tikz@to@in\pgfutil@empty
     \let\relin@tofrom=\tikztostart
    \ifx\tikz@to@out\pgfutil@empty
    \else
     \tikz@to@modify\relin@tofrom\tikz@to@out
    \fi
     \tikz@scan@one@point\pgfutil@firstofone(\relin@tofrom)\relax
     \pgf@xa=\pgf@x
     \pgf@ya=\pgf@y
     \tikz@scan@one@point\pgfutil@firstofone(\tikztotarget)\relax
     \advance\pgf@xa by -\pgf@x
     \advance\pgf@ya by -\pgf@y
     \pgfmathtruncatemacro\tikz@to@in{int(atan2(\pgf@xa,\pgf@ya))}%
    \fi
    \iftikz@to@relative
     \tikz@to@compute@relative
    \else
     \tikz@to@compute
    \fi
  }
  \tikz@computed@path \tikztonodes
}


\makeatother

\begin{document}
  \begin{tikzpicture}
    \node [circle, draw=black] (F) at (1,1) {$F$};
    \draw (0,2) to [auto ends,out=-90] (F);
    \draw (2,2) to [auto ends,out=-90] (F);
    \draw (2,-2) to [auto ends,out=90] (F);
    \draw (-2,-2) to [auto ends] (F);
  \end{tikzpicture}
\end{document}

Here's the result:

tikz to path with automatic angles


Marc van Dongen and I have slightly different interpretations of the request. Here's an adaptation of the above code that allows for syntax of the form:

\draw[every to/.style={auto ends}] (0,2) to [out=-90]  (F) to[intermediate,in=-90] (2,2);

The reason for the needed code is that if one writes something like:

\draw (0,0) to[out=90,in=45] (F) to[out=90,in=45] (0,2);

then the result is not the same as writing

\draw (0,0) to[out=90,in=45] (F) (F) to[out=90,in=45] (0,2);

Witness:

to paths

The point is that a to path constructs a new piece of a path which is appended to the current path. So in the above, the construction results in something like .. controls (some point) and (some other point) .. (the target). Now (the target) might well have been modified from what was originally specified on the path. But the source was not mentioned in the additional part (this makes sense for concatenating paths segments). So the result of two successive to paths is something like:

.. controls (1st cntrl) and (2nd cntrl) .. (1st modified target) .. controls (3rd cntrl) and (4th cntrl) .. (2nd modified target);

In particular, the source for the second to path is not what was specified in the original \draw command but is what the first to path said it was. Most of the time this is exactly what is wanted. However, to do the original poster's path in one go, this isn't what is wanted. There, what is wanted is the second behaviour where the result would be:

.. controls (1st cntrl) and (2nd cntrl) .. (1st modified target) (1st original target) .. controls (3rd cntrl) and (4th cntrl) .. (2nd modified target);

To achieve this automatically, we need to reinsert the original target back in the stream. However, we can't do this automatically at the end of the first to path since we don't know that there is a second to path on its way. All we can do at the end of the first one is to save the original target just in case it is needed again. Then at the start of the second, we can reuse it (if we want) to restart the path again at the correct place.

Here's the modified code, with an example. As written, it is tied in to the auto ends key, but it could easily be extracted to a separate set of keys. The difficulty is that the standard to paths would need patching to to insert the correct move to.

\documentclass{standalone}
%\url{http://tex.stackexchange.com/q/39903/86}
\usepackage{tikz}

\makeatletter
\tikzset{
  auto ends/.code={%
    \def\tikz@to@switch@on{\let\tikz@to@path=\tikz@to@curve@path@rel}%
    \tikz@to@switch@on
    \let\tikz@to@in=\pgfutil@empty
    \let\tikz@to@out=\pgfutil@empty
    \let\relin@start=\pgfutil@empty
  },
  intermediate/.code={
    \let\relin@start=\relin@target
  },
}

\newif\ifrelin@restartnext
\newif\ifrelin@restart

\def\tikz@to@curve@path@rel{%
  [every curve to]
  \pgfextra{%
    \global\let\relin@target=\tikztotarget
    \ifx\relin@start\pgfutil@empty
    \else
    \let\tikztostart=\relin@start
    \fi
    \ifx\tikz@to@out\pgfutil@empty
     \let\relin@toto=\tikztotarget
    \ifx\tikz@to@in\pgfutil@empty
    \else
     \tikz@to@modify\relin@toto\tikz@to@in
    \fi
     \tikz@scan@one@point\pgfutil@firstofone(\relin@toto)\relax
     \pgf@xa=\pgf@x
     \pgf@ya=\pgf@y
     \tikz@scan@one@point\pgfutil@firstofone(\tikztostart)\relax
     \advance\pgf@xa by -\pgf@x
     \advance\pgf@ya by -\pgf@y
     \pgfmathtruncatemacro\tikz@to@out{int(atan2(\pgf@xa,\pgf@ya))}%
    \fi
    \ifx\tikz@to@in\pgfutil@empty
     \let\relin@tofrom=\tikztostart
    \ifx\tikz@to@out\pgfutil@empty
    \else
     \tikz@to@modify\relin@tofrom\tikz@to@out
    \fi
     \tikz@scan@one@point\pgfutil@firstofone(\relin@tofrom)\relax
     \pgf@xa=\pgf@x
     \pgf@ya=\pgf@y
     \tikz@scan@one@point\pgfutil@firstofone(\tikztotarget)\relax
     \advance\pgf@xa by -\pgf@x
     \advance\pgf@ya by -\pgf@y
     \pgfmathtruncatemacro\tikz@to@in{int(atan2(\pgf@xa,\pgf@ya))}%
    \fi
    \iftikz@to@relative
     \tikz@to@compute@relative
    \else
     \tikz@to@compute
    \fi
    \ifx\relin@start\pgfutil@empty
    \else
    \let\relin@computed@path=\tikz@computed@path
    \edef\tikz@computed@path{(\relin@start)\tikz@computed@path}
    \fi
  }
  \tikz@computed@path \tikztonodes
}


\makeatother

\begin{document}
  \begin{tikzpicture}
    \node [circle, draw=black] (F) at (1,1) {$F$};
\draw[every to/.style={auto ends}] (0,2) to[out=-90] (F) to[intermediate,in=-90] (2,2);
  \end{tikzpicture}
\end{document}

Result:

tikz to path with intermediate node

share|improve this answer
    
The original poster asked for a solution where he could specify two end points and an intermediate point. In your solution, the intermediate point has to be specified twice, which is a form of over-specification that can (and should) be avoided because it will lead to user errors in the long run. –  Marc van Dongen Jan 3 '12 at 9:41
    
@MarcvanDongen That's not my reading of the question. Maybe it's ambiguous. I read it as that the last code (the MWE) would be fine if the in=45 could be omitted. –  Loop Space Jan 3 '12 at 9:53
    
That's fine, but can you make the solution better? In my solution I also implemented the requirements but I had to revert to a new command, which is awkward. It would be much nicer if you could write \draw (source) to[via={intermediate node specification},in=degree] (destination);. I lacked the TikZ skills to do this. (There still is hope I will some time learn.:-) –  Marc van Dongen Jan 3 '12 at 10:05
    
@MarcvanDongen I've added an option for the intermediate part, together with an explanation of why it doesn't just work. –  Loop Space Jan 3 '12 at 10:51
1  
Thanks for that. I'm not convinced a solution isn't possible. –  Marc van Dongen Jan 3 '12 at 12:41
show 14 more comments

You have the simple solution :

\documentclass{article}
\usepackage{tikz}

\begin{document}
   \begin{tikzpicture}
    \node [circle, draw=black] (F) at (1,1) {$F$};
    \draw (0,2) to [out=-90] (F);
    \draw (F) to [in=-90] (2,2) ;
  \end{tikzpicture} 
\end{document}  

enter image description here

share|improve this answer
    
This does qualify as a solution if you always have the same in and out values. In general, this is not the case. For example, if you want a different in value, you have to make two changes, in the code. –  Marc van Dongen Jan 3 '12 at 7:11
    
Here we have the same values but if you have different values I use out=45 and out=135 with (F) and If you need four defined values, I think the best it's to write them. –  Alain Matthes Jan 3 '12 at 7:23
    
Surely it's not better to repeat them. First of all you make errors when you copy the values. Second, you may make errors when you're computing the ``opposite' in and out values: if one in=1.432565626764 then you don't want to compute the other in by hand. It's not a convenient API. Even if you want to change the intermediate point, you have to make two edit operations. (Not great for maintenance.) –  Marc van Dongen Jan 3 '12 at 7:38
    
This happens to work because of the choice of angles. The defaults for in and out are 135 and 45 respectively. –  Loop Space Jan 3 '12 at 9:23
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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