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

2
  • 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
    Commented May 15, 2011 at 18:38
  • @Jake: Any numeric (x,y) coordinate or node anchor points for example. Here coordinate doesn't need to be a \coordinate. Commented May 15, 2011 at 18:54

3 Answers 3

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
11
  • 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. Commented May 15, 2011 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. Commented May 15, 2011 at 19:30
  • Are you sure that it is safe to use \pgf@x and \pgf@y inside \pgfmathparse{...}. Commented May 15, 2011 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
    Commented May 15, 2011 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). Commented May 16, 2011 at 11:54
11

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}
4

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

You must log in to answer this question.

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