32

In TikZ accessing the coordinate values of a node can be done using the let syntax of the calc library or using the PGF command \pgfpointanchor. However, this values are dimensions/lengths in points and not the values of the used coordinate system. For example, I like to label certain points and nodes in the graph with their logical coordinate values, e.g. in a standard tikzpicture, i.e. with x=1cm,y=1cm in effect, a point at (1,2) should be labeled with (1,2) not with (28.4527pt,56.9055pt). The nodes are not positioned manually, but using the various relative positioning techniques of TikZ, so I don't know the coordinates myself. This is about normal 2-D XY coordinate systems with orthogonal X and Y axes.

The way I do this at the moment is to get the length of a unit vector in points and divide the coordinate lengths with it to calculate the corresponding value of the used coordinate system. This works OK, but of course results in rounding errors, e.g. 1.9998 instead of 2. I then use the \num function of siunitx to round the numbers to a fitting number of digits.

My question: Is there a better way to achieve this for arbitrary TikZ coordinates?
I mean something more user-friendly and maybe provided by TikZ itself (I didn't found anything in the manual). Especially: Can TikZ/PGF round the number as well without an extra package? A guess TikZ converts coordinates always quickly to length, so that rounding errors are unavoidable (except using x=1pt,y=1pt maybe).

Here my proof-of-concept solution. It is very inflexible at the moment because it only works for nodes.

\documentclass[png]{standalone}
\usepackage{tikz}
\usetikzlibrary{intersections}
\usepackage[round-mode=places]{siunitx}

\makeatletter
\newcommand\xcoord[2][center]{{%
    \pgfpointxy{1}{1}%
    \@tempdima=\pgf@x
    \pgfpointanchor{#2}{#1}%
    \@tempdimb=\pgf@x
    \pgfmathparse{\@tempdimb/\@tempdima}%
    \num{\pgfmathresult}%
}}
\newcommand\ycoord[2][center]{{%
    \pgfpointxy{1}{1}%
    \@tempdima=\pgf@y
    \pgfpointanchor{#2}{#1}%
    \@tempdimb=\pgf@y
    \pgfmathparse{\@tempdimb/\@tempdima}%
    \num{\pgfmathresult}%
}}
\makeatother

\begin{document}
\begin{tikzpicture}
    \draw [help lines] (0,0) grid [step=1] (2,4);
    \draw [name path=A] (0,0) -- (2,4);
    \draw [name path=B] (2,0) -- (0,4);
    \draw [name intersections={of=A and B}]
        (intersection-1)
        node [right] {(\xcoord{intersection-1},\ycoord{intersection-1})}
        circle (2pt);
\end{tikzpicture}
\end{document}

Result

Martin Scharrer
  • 262,582
  • What other things (other than nodes) would you want to use the function for? I can't think of a way to refer to a coordinate other than through a node. – Jake May 15 '11 at 18:38
  • @Jake: Any numeric (x,y) coordinate or node anchor points for example. Here coordinate doesn't need to be a \coordinate. – Martin Scharrer May 15 '11 at 18:54

3 Answers3

26

PGF has the \pgfmathprintnumber macro that allows you to print and format numbers, including rounding. To round to two decimal digits, you would use \pgfmathprintnumber[precision=2]{\pgfmathresult}, for example.

The unit vectors are stored in \pgf@xx (for the x-component of the x unit vector) and \pgf@yy (the y-component of the y unit vector). So your function could be shortened to

\makeatletter
\newcommand\xcoord[2][center]{{%
    \pgfpointanchor{#2}{#1}%
    \pgfmathparse{\pgf@x/\pgf@xx}%
    \pgfmathprintnumber{\pgfmathresult}%
}}
\newcommand\ycoord[2][center]{{%
    \pgfpointanchor{#2}{#1}%
    \pgfmathparse{\pgf@y/\pgf@yy}%
    \pgfmathprintnumber{\pgfmathresult}%
}}
\makeatother
Jake
  • 232,450
  • Thanks, that's exactly what I was looking for to solve the rounding part of my question. I gives me (1,2) in the above example. – Martin Scharrer May 15 '11 at 17:56
  • Thanks for updating your question to include \pgf@xx and \pgf@yy. They make my live simpler. I saw there are also \pgf@xy and \pgf@yx, but there are zero; for orthogonal coordinate systems I guess. – Martin Scharrer May 15 '11 at 19:30
  • Are you sure that it is safe to use \pgf@x and \pgf@y inside \pgfmathparse{...}. – Martin Scharrer May 15 '11 at 20:14
  • @Martin: I don't see why it wouldn't be. If the \pgfmathparse follows the \pgfpointanchor directly, \pgf@x should hold the correct value. It's used in a \pgfmathparse expression in tikzlibrarygraphs.code.tex, though there it is preceded by \the. – Jake May 15 '11 at 20:38
  • @Jake: It should be save as long \pgfmathparse doesn't change it before reading it. I had a look in tikzlibrarygraphs.code.tex and can't find any use of it in the current version (2.10). – Martin Scharrer May 16 '11 at 11:54
  • @Martin: I'm using the development version of PGF. It's in lines 1638 and 1643 of v 1.17 2010/12/17 of tikzlibrarygraphs.code.tex: \pgfmathparse{#2+\the\pgf@x} – Jake May 16 '11 at 13:18
  • Sorry, I forgot the accept this answer. I wanted to wait a day to allow for alternative answers... – Martin Scharrer May 26 '11 at 13:25
  • @Martin: No worries! I wouldn't mind if you unaccept it and maybe edit your question to include the requirement of a solution working with arbitrary coordinates more explicitly. – Jake May 26 '11 at 13:28
  • Just a note: this needs slightly more complicated math when the x and y vectors aren't horizontal/vertical. – Caramdir Jun 20 '11 at 18:32
  • @jake I used your functions, until I came to a scaled tikzpicture and then it was off. I now have added a factor for the scaling, but I am looking for a method to make it universal. That would mean the xcoord/ycoord would take the picture's scaling into account through a tikz/pgf variable. – Louis Jul 20 '11 at 10:57
  • @Louis: Please do not use answer posts to address other users. I converted your post to a comment. Unfortunately, new users with a reputation lower than 50rep are not allowed to post comments everywhere, just to own questions and their answers. Please feel free to ask any follow-up questions as new question ("Ask Question" button above) and link back to this answer from their. – Martin Scharrer Jul 20 '11 at 11:03
10

Here is another method to extract coordinate values from a node or an anchor. Its advantage is to provide these values ​​taking account of the current origin.

\makeatletter
\def\extractcoord#1#2#3{
  \path let \p1=(#3) in \pgfextra{
    \pgfmathsetmacro#1{\x{1}/\pgf@xx}
    \pgfmathsetmacro#2{\y{1}/\pgf@yy}
    \xdef#1{#1} \xdef#2{#2}
  };
}
\makeatother

Example:

enter image description here

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc}
\makeatletter
\def\extractcoord#1#2#3{
  \path let \p1=(#3) in \pgfextra{
    \pgfmathsetmacro#1{\x{1}/\pgf@xx}
    \pgfmathsetmacro#2{\y{1}/\pgf@yy}
    \xdef#1{#1} \xdef#2{#2}
  };
}
\makeatother
\pagestyle{empty}
\begin{document}
\begin{tikzpicture}
  \draw[help lines] (0,0) grid (3,4);
  \begin{scope}[shift={(1,1)}]
    \coordinate (A) at (0,0);
    \coordinate (B) at (2,3) ;
    \coordinate (C) at ($(A)!.5!(B)$);
    \fill[red] (A) circle(2pt);
    \fill[red] (B) circle(2pt);
    \fill[blue] (C) circle(2pt);
    \extractcoord\x\y{C}
  \end{scope}
  \extractcoord\xb\yb{C}
\end{tikzpicture}

Inner scope: $(\x,\y)$\par
Global scope: $(\xb,\yb)$\par
Inner scope (fixed precision):
$(\pgfmathprintnumber[precision=2]{\x},\pgfmathprintnumber[precision=2]{\y})$\par
\end{document}
Paul Gaborit
  • 70,770
  • 10
  • 176
  • 283
3

Dividing a dimension by 1cm and transforming the result into a scalar will give you the dimension's numeric value in centimetres. Combining this with TikZ's \let construct for coordinates (which requires the loading of TikZ's calc library), your example can be simplified as follows.

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{calc,intersections}

\begin{document}

\begin{tikzpicture} \draw [help lines] (0,0) grid [step=1] (2,4); \draw [name path=A] (0,0) -- (2,4); \draw [name path=B] (2,0) -- (0,4); \draw [name intersections={of=A and B}] let \p1=(intersection-1), \n1={scalar(\x1/1cm)}, \n2={scalar(\y1/1cm)} in (\p1) node [right] {(\n1,\n2)} circle (2pt); \end{tikzpicture}

\end{document}

Getting the numeric values of a coordinate's components


The code above doesn't format the printed numbers explicitly. Here's a version with formatting.

The following command, \scalar, takes as mandatory argument a mathematical expression of the kind that can be processed by \pgfmathparse (of the pgfmath package), and strips away its dimension, if any, returning a pure numeric value. It then formats this value using \pgfmathprintnumber (of the pgf package), setting its options to \scalar's optional argument.

\newcommand{\scalar}[2][]{%
   \pgfmathscalar{#2}%
   \pgfmathprintnumber[#1]{\pgfmathresult}%
}

For example, the following command, whose name \logv is an abbreviation of "logical value", the expression appearing in the title of your post, accepts a dimension, and returns its numeric value in centimeters (TikZ's default unit) rounded to 2 decimal places.

\newcommand{\logv}[1]{\scalar[precision=2,fixed zerofill]{#1/1cm}}

Using these tools, and keeping in mind that the tikz package loads both the pgf and the pgfmath packages automatically, my answer above can be rewritten as follows.

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

\newcommand{\scalar}[2][]{% \pgfmathscalar{#2}% \pgfmathprintnumber[#1]{\pgfmathresult}% } \newcommand{\logv}[1]{\scalar[precision=2,fixed zerofill]{#1/1cm}}

\begin{document} \begin{tikzpicture} \draw [help lines] (0,0) grid [step=1] (2,4); \draw [name path=A] (0,0) -- (2,4); \draw [name path=B] (2,0) -- (0,4); \draw [name intersections={of=A and B}] let \p1=(intersection-1) in (\p1) node [right] {(\logv{\x1},\logv{\y1})} circle (2pt); \end{tikzpicture} \end{document}

Adding number formatting to the original answer

Evan Aad
  • 11,066