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.

How do I combine the !.5! operator with the -| operator?

I want a node that has x-coordinate equal to the average of x coordinates of nodes A and B, and a y-coordinate equal to the average the y coordinates of nodes C and D.

share|improve this question
add comment

5 Answers 5

up vote 17 down vote accepted

I don't think you can directly mix the ($...!...!...$) and |- syntax. Instead, I would suggest using a \path ... node construct to get the desired result:

\documentclass{article}

\usepackage{tikz}
\usetikzlibrary{calc}

\begin{document}
\begin{tikzpicture}
\node (A) at (0,1) {A};
\node (B) at (1,1.5) {B};
\node (C) at (2,0) {C};
\node (D) at (2.5,-2) {D}; 
\path ($(A)!0.5!(B)$) |- node[red] {X} ($(C)!0.5!(D)$);
\end{tikzpicture}
\end{document}

share|improve this answer
1  
Oh, but you can mix them ... –  Loop Space Sep 28 '11 at 11:53
add comment

Looking at the code, Jake is right in spirit but not in fact. It is possible to mix calc stuff (ie $...$ calculations) with intersection stuff (ie (... |- ...) things) but the way that it is done involves a bit of trickery and I would be wary of this changing, so I do not recommend this method.

To know how this works, we have to know a bit about how TikZ parses coordinates. There are so many ways of specifying a coordinate that TikZ has to know about! So it looks for clues. One of the first clues is as to whether the coordinate (after the opening parenthesis) has a dollar. If it does, it hands it off to the calc library (assuming that it is loaded). If not, it goes on to other tests. One of these other tests looks for the strings |- or -|. If it finds one of these two then it knows that it is looking at an intersection. At this point it assumes that the syntax is something like (coordinate |- coordinate). The two coordinates might be quite general (nodes, lengths, all that sort of thing) so it very sensibly sends them back into the parser for reprocessing. This is a new instance so it is theoretically possible for these to involve calc expressions. The difficulty is that if the first involves a calc expression then it starts with a dollar, but that was what the parser looked for at the very start!

So, our task (should we choose to accept it) is to figure out a way to specify a coordinate of the form (calc-coord |- calc-coord) in such a fashion that the initial parser does not see the opening dollar of the first coordinate but that when it gets reinserted into the parser then it is exposed. It turns out that this is possible because at some point, some groupings are ungrouped. So we can wrap the coordinates in groups to hide them from the first iteration, exposing them at the second. We simply have to get the number of groups right. Another issue is that since calc expressions often involve parentheses, we want to hide these from the initial parser so that it doesn't get confused as to where the end of the coordinate lies.

At the end of all that, a little experimenting shows that we need two groups for the first coordinate and just one for the second. Hence:

\node at ({{$(A)!0.5!(B)$}} -| {$(C)!.5!(D)$}) {X};

does what it says on the tin.

The reason that I really do not recommend this is that if the implementation changes, the number of groupings might change; and it is even feasible that this would no longer work.

For that reason, I also have a solution for you which uses pure calc syntax. We can do this because the calc library has its own syntax for projections. This method avoids creating any additional coordinates, and means that it is all part of a single coordinate expression (which means that it is a bit more robust than Jake's method since no paths are involved, it is therefore safer inside a more complicated path). However, I'm still a little wary of recommending this solution since it is a complicated one and I'd rather go for the simpler solution with a few extra coordinates lying around as it would make it easier to work out what is going on when re-reading the code six months later. But for the sake of the challenge, here it is:

\node at ($($(A)!0.5!(B)$)!($(C)!0.5!(D)$)!($(A)!.5!(B)+(0,2)$)$) {Y};

In short, we project the coordinate at ($(C)!0.5!(D)$) orthogonally onto the line from ($(A)!0.5!(B)$) to ($(A)!0.5!(B)+(0,2)$).

Here's some sample code with both of the above. I've put one below and one above to show them separately. The rectangle is to show that these are at the right places.

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

\begin{document}
\begin{tikzpicture}
\node (A) at (0,0) {A};
\node (B) at (2,2) {B};
\node (C) at (4,4) {C};
\node (D) at (6,6) {D}; 
\draw[blue] ($(A)!.5!(B)$) |- ($(C)!.5!(D)$) |- ($(A)!.5!(B)$);
\node[red] at ({{$(A)!0.5!(B)$}} -| {$(C)!.5!(D)$}) {X};
\node[red] at ($($(A)!0.5!(B)$)!($(C)!0.5!(D)$)!($(A)!.5!(B)+(0,2)$)$)  {Y};
\end{tikzpicture}
\end{document}

Result:

projections and intersections

(My filename for this hack is tikzcomplicatedcalc. I thought you might like to know that.)

share|improve this answer
3  
Hats off! Excellent detective work, and well written. This could be a blog post. –  Jake Sep 28 '11 at 12:01
add comment

I did it in pieces:

\coordinate (AB) at ($(A)!.5!(B)$);
\coordinate (CD) at ($(C)!.5!(D)$);
\coordinate (X) at (AB |- CD);
share|improve this answer
2  
+1 for its simplicity in reading the code. –  Peter Grill Sep 28 '11 at 15:40
add comment

I may be late to the party, but there are other possibilities to get to that special point.

The first one uses the let … in syntax that defined the two points \p1 and \p2 with the $calc$ syntax and uses than \x1 (x value of \p1) and \y2 (y value of \p2) for the node declaration.

The other solution has defined |-/ -| styles/codes (?) that take two arguments, two points, to be correct. The code segment saves the x value of one point to \tempx and the y value of the other point to \tempy.

The advantage of these solutions is that you can re-use the calculated values in the same path (let … in) or until they get redefined (|-/-|).

One could write additional styles/codes/macros that define extra macros instead of \tempx and \tempy so that they can get re-used after another set of values were calculated.

Code

\documentclass[border=2pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{calc}

\makeatletter
\def\getxy#1#2{%
  \tikz@scan@one@point\pgfutil@firstofone(#1)\relax%
  \xdef\tempx{\the\pgf@x}%
  \tikz@scan@one@point\pgfutil@firstofone(#2)\relax%
  \xdef\tempy{\the\pgf@y}%
}
\makeatother

\tikzset{
  |-/.code 2 args={\getxy{#1}{#2}},
  -|/.code 2 args={\getxy{#2}{#1}}
}

\begin{document}
\begin{tikzpicture}
\node (A) at (2*rand,2*rand) {A};
\node (B) at (2*rand,2*rand) {B};
\node (C) at (2*rand,2*rand) {C};
\node (D) at (2*rand,2*rand) {D};

\path let \p1=($(A)!0.5!(B)$),
          \p2=($(C)!0.5!(D)$) in node[blue] at (\x1,\y2) {X};

\node[green, |-={$(A)!0.5!(B)$}{$(C)!0.5!(D)$}] at (\tempx,\tempy) {Y};
\node[yellow,-|={$(C)!0.5!(D)$}{$(A)!0.5!(B)$}] at (\tempx,\tempy) {Z};
\end{tikzpicture}
\end{document}

Output

Output

share|improve this answer
add comment

I may be even later to the party :-), but there is still some option that has not been discussed yet: Using the perpendicular coordinate system directly instead of its abbreviation form. That is, to write

(perpendicular cs: horizontal line through={expA}, vertical line through={expB})

instead of (expA -| expB), with expA and expB being coordinate expressions in calc syntax. Thereby we do neither need helper points nor do we relay on the obscure parser internals investigated by Andrew.

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

\begin{document}
\begin{tikzpicture}
\node (A) at (0,0) {A};
\node (B) at (2,2) {B};
\node (C) at (4,4) {C};
\node (D) at (6,6) {D}; 
\draw[blue] ($(A)!.5!(B)$) |- ($(C)!.5!(D)$) |- ($(A)!.5!(B)$);
\node[red] at (perpendicular cs: horizontal line through={($(A)!0.5!(B)$)}, vertical line through={($(C)!.5!(D)$)}) {X};
\end{tikzpicture}
\end{document}

There is a generalized lesson behind this: Remember that TikZ has a common syntax for all coordinate systems. Coordinates are specified (see §13 of the PGF Manual) by:

(<coordinate system> cs: <param1>=<value1>, <param2>=<value2>, ...)

For convenience, TiKZ specifies a lot of "magic" operators (such as -| and |-) that internally select a different coordinate system. However, especially in combination with the calc library, this can become pretty difficult to parse and so we all sooner or later come to the point where an seemingly correct and "logical" coordinate expression is refused by TikZ with some really weird error message.

In these cases it often helps to simplify the job of the parser by refactoring parts of the expression into the general coordinate syntax.

enter image description here

share|improve this answer
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.