10

It seems that, when drawing an edge between two nodes, TikZ defaults to aligning the edge with the 'center' anchors of the two nodes. More precisely, the edge points from the center of one node to the center of the other node, and the endpoints of the edge sit on the boundary of the nodes. An example is the black arrow in the following diagram.

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{matrix}

\begin{document}

\begin{tikzpicture}
  \matrix (m) [matrix of math nodes,row sep={4em,between origins},column sep={5em,between origins},nodes={anchor=mid}]{
    A^B & A_B \\
        & E   \\};
  \draw[->] (m-1-1) -- (m-1-2);
  \draw[->,red] (m-1-1.mid) -- (m-2-2.mid);
\end{tikzpicture}

\end{document}

result picture

On the other hand, the red arrow joins the 'mid' anchors of two nodes. I would like to shorten the red arrow to make it start and end at the boundaries of the nodes (like the black arrow does). Nevertheless, I want the desired shortened arrow to stay on the same line as the above red arrow, and to still have an arrowhead (so simply clipping does not seem to do the trick).

Is there a simple way to shorten the red arrow as requested? Does anyone know how TikZ does it for the black arrow?

7
  • It would be interesting to give a minimal example! Commented Apr 8, 2011 at 16:53
  • Altermundus' intersection example works with arrows. Also you could use the shorten >=... syntax.
    – Caramdir
    Commented Apr 9, 2011 at 3:07
  • For commutative diagrams, you should either add something like [text height=1.5ex, text depth=0.25ex] or [nodes={anchor=center}] to the matrix, so that the arrows are not slanted, see the two example in tex.stackexchange.com/questions/3892
    – Caramdir
    Commented Apr 9, 2011 at 3:12
  • @Caramdir: Unfortunately, anchoring the nodes at the center causes the text of different nodes to not be vertically aligned. Also, using the options 'text height' and 'text depth' causes the arrows to behave poorly with respect to the actual border of the text in the nodes. Commented Apr 9, 2011 at 3:16
  • 1
    To answer your question about how TikZ calculates the black arrow: There is a special test in the code of line to (and similar commands). For details see texmf/tex/generic/pgf/frontendlayer/tikz/tikz.code.tex. For line to, the code is in \tikz@@lineto, which (in v2.10) starts at line 2187.
    – Caramdir
    Commented Apr 9, 2011 at 21:12

3 Answers 3

4

I'm not sure to understand exactly the question . I understand that : you want an edge between two nodes but you want to draw the edge only between the intersections of the edges and the shapes of the nodes.

With intersections (the problem is to make a choice between the points) but in the second method I just fill the node's background.

\documentclass{scrartcl}
\usepackage{tikz}
\usetikzlibrary{positioning,intersections}
\begin{document}

\begin{tikzpicture}
  \node[name path= c1] (foo)  at (0,0)[circle] {$x$};
  \node[name path= c2] (bar) at (2,2)[circle] {$x+1$};
  \path[name path=line] (foo.south) -- (bar.center);
  \fill [name intersections={of=c1 and line, name=i, total=\t}]
 [red, opacity=0.5, every node/.style={left=.25cm, black, opacity=1}] 
 \foreach \s in {1,...,\t}{(i-\s) circle (2pt) node {}};
  \fill [name intersections={of=c2 and line, name=j, total=\t}]
 [red, opacity=0.5, every node/.style={left=.25cm, black, opacity=1}] 
 \foreach \s in {1,...,\t}{(j-\s) circle (2pt) node {}};   
  \draw[red] (i-1) -- (j-1);  
\end{tikzpicture} 
\begin{tikzpicture}
  \node[name path= c1] (foo)  at (0,0)[circle] {$x$};
  \node[name path= c2] (bar) at (2,2)[circle] {$x+1$};
  \path[name path=line] (foo.south) -- (bar.center);
  \path [name intersections={of=c1 and line, name=i, total=\t}] ;
  \path [name intersections={of=c2 and line, name=j, total=\t}];  
  \draw[red] (i-1) -- (j-1);  
\end{tikzpicture}
\begin{tikzpicture}
  \node (foo)  at (0,0)[circle] {\hphantom{$x$}};
  \node (bar) at (2,2)[circle] {\hphantom{$x+1$}};
  \draw[red] (foo.south) -- (bar.center);
  \node[circle,fill=white] at (foo.center){$x$} ;
  \node[circle,fill=white] at (bar.center){$x+1$} ; 
\end{tikzpicture}  
\end{document}

enter image description here

5
  • Although this won't work if the background is complicated. Could you make the circles transparent and then draw the line under them?
    – Seamus
    Commented Apr 8, 2011 at 15:12
  • @Seamus Yes but it's possible to use layers if the background is complicated but the problem is the same with the clip. You may need another clip zone ! example you can't add ` \path[clip] (1,1) circle(.2cm); ` Commented Apr 8, 2011 at 15:30
  • 1
    @Seamus Intersection is easy if the edge starts and ends at the origins of the shapes. In this case we can use \pgfpointborderellipse or \pgfpointborderrectangle (Points on Borders of Objects in the pgfmanual) Commented Apr 8, 2011 at 15:38
  • In order to solve my modified question above, how would one change this answer to instead use the border (not drawn) of a math node (as in the question)? Commented Apr 9, 2011 at 5:52
  • @Ricardo You need only to remove the draw option in the style of nodes. I modified my version .You can also remove the red circle, I will add an example. Commented Apr 9, 2011 at 7:08
5

Thinking about this mathematically, it is going to be very hard to do properly. I mean, so that \draw[->] (a) -- (b); draws an arrow from a towards the desired point on b and stops at the boundary with the arrowhead. To do that, one is going to have to calculate the point on the boundary where the line from a to the anchor of b reaches the boundary of b and that will depend on many parameters.

Here's a slightly different approach, which is presented more as a "proof of concept" than as usable code. The idea is that for a simple shape such as a rectangle, one can define some auxiliary nodes whose boundary coincides with the boundary of the original node when seen from certain directions but which are centred at the correct place. The trick is then to pick the right one of these auxiliary nodes upon approach (this is the main bit that I haven't thought how to do).

\documentclass{standalone}
\usepackage{tikz}

\makeatletter
\newlength\relocate@minx
\newlength\relocate@maxx
\newlength\relocate@miny
\newlength\relocate@maxy
\def\relocatenode#1#2{%
  \path (#1.south east);
  \pgfgetlastxy{\relocate@east}{\relocate@south}
  \path (#1.north west);
  \pgfgetlastxy{\relocate@west}{\relocate@north}
  \path (#1) ++(#2);
  \pgfgetlastxy{\relocate@x}{\relocate@y}
  \pgfmathsetlength{\relocate@minx}{2 * min(\relocate@east - \relocate@x, \relocate@x - \relocate@west)}
  \pgfmathsetlength{\relocate@maxx}{2 * max(\relocate@east - \relocate@x, \relocate@x - \relocate@west)}
  \pgfmathsetlength{\relocate@miny}{2 * min(\relocate@north - \relocate@y, \relocate@y - \relocate@south)}
  \pgfmathsetlength{\relocate@maxy}{2 * max(\relocate@north - \relocate@y, \relocate@y - \relocate@south)}
  \path (#1) ++(#2) node[minimum width=\relocate@minx,minimum height=\relocate@miny] (#1-minx-miny) {};
  \path (#1) ++(#2) node[minimum width=\relocate@maxx,minimum height=\relocate@miny] (#1-maxx-miny) {};
  \path (#1) ++(#2) node[minimum width=\relocate@minx,minimum height=\relocate@maxy] (#1-minx-maxy) {};
  \path (#1) ++(#2) node[minimum width=\relocate@maxx,minimum height=\relocate@maxy] (#1-maxx-maxy) {};
}
\makeatother
\begin{document}
\begin{tikzpicture}[every path/.style={->}]
\node[draw,align=left] (a) at (0,0) {hello world\\greetings mars};
\relocatenode{a}{1,.3}
\fill (a) circle (2pt);
\fill (a) ++(1,.3) circle (2pt);

\path (a) ++(1,.3) node (b) {};

\foreach \pt in {30,60,...,360} {
  \draw (a) ++(\pt:5) -- (a);
}

\foreach \pt in {0,10,...,80} {
  \draw[red] (a) ++(\pt:5) -- (a-minx-miny);
}
\foreach \pt in {90,100,...,170} {
  \draw[red] (a) ++(\pt:5) -- (a-maxx-miny);
}
\foreach \pt in {180,190,...,260} {
  \draw[red] (a) ++(\pt:5) -- (a-maxx-maxy);
}
\foreach \pt in {270,280,...,360} {
  \draw[red] (a) ++(\pt:5) -- (a-minx-maxy);
}
\end{tikzpicture}
\end{document}

Result:

relocated node

1
  • That was a very good interpretation of my ultimate goals, even though I did not state them. Indeed, I do want to draw arrows and not just segments, in the way you state. Commented Apr 9, 2011 at 1:11
3

Is this the sort of thing you want? I've replaced the code with an example closer to the sort of thing mentioned in the question.

\documentclass[border=1cm]{standalone}
\usepackage{tikz}
\usetikzlibrary{positioning}
\begin{document}
\begin{tikzpicture}
  \node (a) {A};
  \node (b)  [below=of a] {B};
  \node (c)  [right=of a] {C};
  \node (d)  [right=of b] {D};
  \begin{scope}
    \clip (a.north east) -- (a.east) -- (a.south east) -- (c.south west) -- (c.west) -- (c.north west) -- (a.north east);
    \draw (a.mid) -- (c.mid);
    \draw[red] (a)--(c);
  \end{scope}
  \begin{scope}
    \clip (a.south) -- (a.south east) -- (a.east) --(d.north) -- (d.north west) -- (d.west) -- (a.south);
    \draw (a.mid) -- (d.mid);
    \draw[red] (a)--(d);
  \end{scope}
\end{tikzpicture}
\end{document}

Better image

The trick is to clip to a box around the relevant anchors, but keep the clip within a scope so the whole picture doesn't get clipped...

10
  • I think that the line to the lower node ought to stop where it first hits the boundary. (Incidentally, I see horrible black lines on the right and lower edge of your picture but don't see them in the code.) Commented Apr 8, 2011 at 12:17
  • No. That unites two anchors, allowing it to intercept the boundaries of the nodes somewhere in the middle. I do not want that. Also, the case I have in mind would have the anchors placed within the nodes (I specifically mentioned the 'mid' anchor in my question). Commented Apr 8, 2011 at 12:25
  • @Andrew yes, I used the standalone environment, and then wasn't very good at selecting an area for a screenshot, so I caught some of the border...
    – Seamus
    Commented Apr 8, 2011 at 12:32
  • 1
    For the first one you could also use the mid east and mid west anchors instead of clipping.
    – Caramdir
    Commented Apr 8, 2011 at 16:27
  • 1
    See the “Shape Library” section of the TikZ manual (chapter 48 in v2.10). Very useful are base and base east/west.
    – Caramdir
    Commented Apr 8, 2011 at 16:40

You must log in to answer this question.

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