3

Trying to implement a handy way to check if a TikZ point is right or left of another one, I came up with the following MWE.

\documentclass[border=1mm, tikz]{standalone}

\makeatletter
% prints 1 if #1.center is right than #2.center, 0 otherwise
\newcommand{\isRight}[2]{
    %\pgfpointdiff{a}{b} gives b-a
    \pgfpointdiff{\pgfpointanchor{#2}{center}}{\pgfpointanchor{#1}{center}}
    \pgfmathparse{greater(\pgf@x,0)}\pgfmathresult
}

% 1 if #1.center is left than #2.center, 0 otherwise -> result in \pgfmathresult
\newcommand{\checkIfLeft}[2]{
    %\pgfpointdiff{a}{b} gives b-a
    \pgfpointdiff{\pgfpointanchor{#2}{center}}{\pgfpointanchor{#1}{center}}%
    \pgfmathparse{less(\pgf@x,0)}
}
\makeatother

\begin{document}

    \begin{tikzpicture}
        \node[draw] (A) at (0,0) {A};
        \node[draw] (B) at (1,0) {B};

        \checkIfLeft{A}{B}
        \ifnum\pgfmathresult=1
            \node at (0.5,1){A is left of B};
        \fi

        %\ifnum\isRight{B}{A}=1
        %    \node at (0.5,-1){B is right of A};
        %\fi
    \end{tikzpicture}
\end{document}

but I am quite unsatisfied with it. What I would like to have is something that may be used in a \ifnum ... \fi construct, as in the commented code (and I would like not to use packages beyond what pgf loads).

Here my questions:

  1. In the commented code, what is exactly happening in the \ifnum expansion, which makes the compilation fail?
  2. How can I fix the \isRight command in order to be able to use it together with \ifnum?
  3. If question 2 is tricky, how can I affect the \ifnum expansion to achieve what I wish?
  4. Is there in pgf a smarter/more straightforward way to check if a TikZ point is left/right of another point? My final goal would be to draw something only if a point is right/left of another.

Bonus question:

  1. How (and where) is \pdfstrcmp{}{} implemented, since it is harmless to use it in a \ifnum ... \fi construct?
  • your commands are not expandable (they assign a number to \pgfmathresult) and will never work in a \ifnum. \pdfstrcmp is a primitive. – Ulrike Fischer Apr 2 at 12:46
  • Thanks for the comment. I find it a bit cryptic, though. Do you want to say that I should give up and what I got so far is the best I can have? The fact that \pdfstrcmp is a primitive clarifies why I was not finding its implementation... I should have had a look to the pdftex manual before. – Axel Krypton Apr 2 at 13:14
  • You will have to give up one part - either \ifnum or pgfmath commands. You could implement something working with \ifnum by using e.g. zrefsavepos (needs two compilations) but not with \pgfmathparse. – Ulrike Fischer Apr 2 at 13:24
6

Here is some low level code based on this answer that do not use calc.

\documentclass[tikz,border=7pt]{standalone}
\newif\ifleft
\makeatletter
\def\isleft(#1)of(#2)?{%
  \tikz@scan@one@point\pgfutil@firstofone(#1)\relax%
  \pgf@xa=\pgf@x%
  \tikz@scan@one@point\pgfutil@firstofone(#2)\relax%
  \ifdim\pgf@xa<\pgf@x\relax\lefttrue\else\leftfalse\fi
}
\makeatother

\begin{document}
    \begin{tikzpicture}[nodes=circle]
        \node[draw] (A) at (0,0) {A};
        \node[draw] (B) at (2,1) {right of A};
        \isleft(A)of(B)?
        \path (A) \ifleft edge[latex-] (B) \else edge[-latex] (B) \fi ;
        \foreach~in{1,...,70}
          \path[ultra thin] ({180*rand}:1) coordinate(N)
            \pgfextra{\isleft(N)of(A)?}
            \ifleft [red] \else [blue] \fi node[scale=2]{.}
            (N) \ifleft edge[latex-] (A) \else edge[-latex] (A) \fi;
    \end{tikzpicture}
\end{document}

enter image description here

  • I like your idea a lot! Indeed it is perfect for my needs, up to a tiny adjustment. The comments to the question clarify the other points. – Axel Krypton Apr 3 at 8:55
6

How about

\documentclass[border=1mm, tikz]{standalone}
\usetikzlibrary{calc}
\tikzset{if left/.style n args={4}{insert path={%
let \p1=($#1-#2$) in  \ifdim\x1<0pt
#3 
\else 
#4 
\fi}}}

\begin{document}

    \begin{tikzpicture}
        \node[draw] (A) at (0,0) {A};
        \node[draw] (B) at (1,0) {B};
        \path[if left={(A)}{(B)}{(0.5,1) node {A is left of B}}{}];
        \path[if left={(B)}{(A)}{(0.5,-1) node {B is left of A}}{}];
    \end{tikzpicture}
\end{document}

enter image description here

  • This is for sure a good idea and I indeed thought about something in this direction at the beginning. However, if I have to draw a lot (nodes, paths, for-loops, etc.) in the if-else branches, then this approach is not handy. – Axel Krypton Apr 3 at 8:42
  • @AxelKrypton I actually beg to disagree. You can definitely use this in loops and so on. Can you please tell me a scenario in which you think this has disadvantages? – marmot Apr 3 at 13:46
  • @AxelKrypton Just add \path foreach \X in {1,...,100} {(rnd*360:1) coordinate(aux) [if left={(aux)}{(A)}{ (A) edge[red,-latex] (aux)}{(A) edge[blue,-latex] (aux)}]}; to my code. I did not have to adjust anything. You can pick whatever answer you like. But making statements about my code that do not apply just to make Kpym happy is IMHO not useful for other users who have a similar problem and want a simple solution without any \makeatletter and so on stuff. All the foreach's of your own post can be done in the very same way. – marmot Apr 3 at 20:26
  • In my comment I actually recognized that your approach is good, I do not know why you think my comment is against you in favour of Kpym. I simply said that if I have to draw a lot, like many nodes, paths, for loops, whatever then your tikzstyle is not as handy as having an if-clause. – Axel Krypton Apr 4 at 12:55
  • And this does not mean that your solution would not work, just that having something very similar to a \if ... \fi block makes the conditional code stand out and this in my opinion also increases readability. It was simply better in my use case. – Axel Krypton Apr 4 at 13:07
0

Just for future record, I want to share a tiny elaboration of what Kpym suggested in his answer. Just to avoid having the \newif, it is possible to supply two more arguments to the macro. Of course, having the \ifleft gives versatility in the usage (as nicely demonstrated in the Kpym's answer), but maybe this is not really needed (like in my case).

\documentclass[border=1mm, tikz]{standalone}

\makeatletter
\long\def\IfLeft(#1)of(#2)#3#4{%
  \tikz@scan@one@point\pgfutil@firstofone(#1)\relax%
  \pgf@xa=\pgf@x%
  \tikz@scan@one@point\pgfutil@firstofone(#2)\relax%
  \ifdim\pgf@xa<\pgf@x\relax#3\else#4\fi
}
\makeatother

\begin{document}

    \begin{tikzpicture}
        \node[draw] (A) at (0,0) {A};
        \node[draw] (B) at (1,0) {B};
        \node[draw] (C) at (2,0) {C};

        \IfLeft(A)of(B){%
            \foreach \n in {10,20,...,90}{
                \fill[red!\n!yellow] (0.5,0) ++(\n/100,0.5) circle(1mm);
            }
        }{%
            \foreach \n in {10,20,...,90}{
                \fill[black!\n!gray] (0.5,0) ++(\n/100,-0.5) circle(1mm);
            }
        }

        \IfLeft(C)of(B){%
            \foreach \n in {10,20,...,90}{
                \fill[black!\n!gray] (0.5,0) ++(\n/100,0.5) circle(1mm);
            }
        }{%
            \foreach \n in {10,20,...,90}{
                \fill[blue!\n!magenta] (0.5,0) ++(\n/100,-0.5) circle(1mm);
            }
        }
    \end{tikzpicture}
\end{document}

 

    enter image description here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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