6

I usually write macros when I need to draw many times a kind of figure. In the final document, I call the macro and then I always apply two transformations : a scale transformation to fit in the space I've left, and a rotation so that my students are not facing exactly the same situation. Unfortunately, labels won't be well positioned after a rotation.

Here is what I get using the anchor below left and midway,below after different rotations.

labels not well-positioned after different rotations

Here is what I would like to have.

labels well-positioned even after different rotations

Here is what I would like to avoid if I'm scaling the draw.

what I don't want to see after scaling

Here is the code I'm using to try to solve my problem:

\documentclass[margin=.5cm]{standalone}
\usepackage{tikz}

\newcommand{\myDraw}[2]{% \myDraw{rotation angle}{scale factor}
\begin{minipage}[t]{2.5cm}
\centering \footnotesize $\theta=#1^{\circ}$, $k=#2$ \medskip\\
  \begin{tikzpicture}[rotate=#1,scale=#2]
    \draw (0,1) -- (0,0) node[below left] {$O$} -- (1,0) node[midway,below] {$1$};
  \end{tikzpicture}
\end{minipage}}

\begin{document}
\myDraw{0}{0.5}
\myDraw{45}{0.7}
\myDraw{90}{1}
\myDraw{135}{1.3}
\myDraw{180}{1.5}
\end{document}

I thought of manually positioning my label, but then the distance between the label and the point would be affected by a scale transformation and I lose the ability of using convenient keywords like midway and pos=.

I could also use the key transform shape as a node option, but then the text would be rotated and scaled.

If you think my question doesn't reflect my issue, you are very welcome to edit it (I had a hard time to figure out how to explain my problem).

4
  • If you don’t use scale but x=2cm, y=2cm you can use the transform shape option on the O node. — What would be a “pleasant position”? Aug 16, 2013 at 16:03
  • Indeed, the text is not scaled anymore but it'is still rotated. I mean by "peasant position" that relative position of the label evolves according to the rotation of the whole figure.
    – remjg
    Aug 16, 2013 at 16:07
  • is it only rotation that you wish to apply as a transform?
    – percusse
    Aug 18, 2013 at 20:08
  • Actually I want to apply both rotation and scale, so my question is still confusing. I'm will edit my question, I must say it doesn't make much sense written like this.
    – remjg
    Aug 19, 2013 at 14:43

3 Answers 3

6

The Anchor key defines two coordinate with the current transformation matrix active (scaling, rotating, whatever) and from that calculates the angle between these coordinates in the canvas for an un-transformed node.

The code for the Anchor style was inspired by another answer of mine (which could be simplified) to Small text near arrow tips.

The Anchor style probably works best with the circle or the ellipse shape.
The rectangle shape is very nonuniform.

Of course, you can define styles like Below Left/.style={Anchor=45} if you want to use directions for the current transformation matrix.


The code uses \pgfcoordinate which is quick version of \pgfnode for a coordinate. Instead of the \pgfcoordinates we could have written

\path  (0,0) coordinate (qrr@origin)
      (#1:1) coordinate (qrr@direct);

The matrix transformation is then reset (so that we are back in coordinate system that has the same orientation as our canvas in which we place the un-transformed node) with \pgftransformreset.

The macro \pgfmathanglebetweenpoints is not documented in the PGF manual. It is defined in pgfmathcalc.code.tex after a block that warns

% *** The following commands DO NOT WORK without the rest of PGF ***
%
% (a dumping ground for stuff that doesn't really belong anywhere else)

The instruction for \pgfmathanglebetweenpoints read

% \pgfmathanglebetweenpoints
%
% Define \pgfmathresult as the angle between points #1 and #2
% Should get the quadrants right as well.

In the linked answer of mine I did practically the same as that macro but in a manual manner (and maybe not as precise).

The TeX group is necessary so that \pgftransformreset doesn’t affect the actual placement of the node. The \pgfmath@smuggleone (or its @-less version \pgfmathsmuggle) is then used to “smuggle” \pgfmathresult out of the group. The \pgfmath@smuggleone macro is used very often in PGF math. It’s definition in pgfmathutil.code.tex is

% \pgfmath@smuggleone
%
% Smuggle a macro outside a group.
%
% Changed by TT: Speedup by insisting, that smuggleone is directly
% followed by \endgroup
%
\def\pgfmath@smuggleone#1\endgroup{%
  \expandafter\endgroup\expandafter\def\expandafter#1\expandafter{#1}}  

\let\pgfmathsmuggle=\pgfmath@smuggleone

The advance over \globaling it is that it only affects one group and not all.

Code

\documentclass[margin=.5cm]{standalone}
\usepackage{tikz}
\tikzset{
  Anchor/.code=%
    \begingroup
      \pgfcoordinate{qrr@origin}{\pgfpointorigin}%
      \pgfcoordinate{qrr@direct}{\pgfpointpolarxy{#1}{1}}%
      \pgftransformreset
      \pgfmathanglebetweenpoints{\pgfpointanchor{qrr@origin}{center}}{\pgfpointanchor{qrr@direct}{center}}%
      \pgfmathsmuggle\pgfmathresult
    \endgroup
    \tikzset{anchor/.expanded=\pgfmathresult}%
}
\newcommand{\myDraw}[2]{% \myDraw{rotation angle}{scale factor}
\begin{minipage}[t]{2.5cm}
\centering \footnotesize $\theta=#1^{\circ}$, $k=#2$ \medskip\\
  \begin{tikzpicture}[rotate=#1,scale=#2]
    \draw (0,1) -- (0,0) node[Anchor=45] {$O$} -- (1,0) node[midway,Anchor=90] {$1$};
  \end{tikzpicture}
\end{minipage}}

\begin{document}
\myDraw{0}{0.5}
\myDraw{45}{0.7}
\myDraw{90}{1}
\myDraw{135}{1.3}
\myDraw{180}{1.5}
\end{document}

Output

enter image description here

10
  • Wow! This is really a great answer, exactly what I was looking for... Thank you! I must admit I don't understand most of it though!
    – remjg
    Aug 21, 2013 at 12:59
  • Let say that I want my anchor angle to be 45° and that the current transformation is a rotation of 100°. Then, before \pgftransformreset, the point qrr@direct polar angle is 145° since the current transformation matrix is applied. After \pgftransformreset, the transformation matrix is reset in the scope so \pgfpointanchor can't apply the inverse transformation matrix. Hence, the angle computed stays 145°. This value is then passed on to the anchor/.expanded handler. Am I right? I'm not used to pgf but I really want to understand what's going on here.
    – remjg
    Aug 21, 2013 at 15:07
  • @remjg That’s the gist of it, yes. One could probably also calculate the rotation from the entries of transformation matrix but this looks much easier (and basically does it). Aug 21, 2013 at 18:25
  • Also, I would like to ask you about the meaning of \pgfinterruptboundingbox and \endpgfinterruptboundingbox. I've removed them without seeing any differences. As far as I've understood, the bounding box is the rectangle that is uses to clip the draw for the rendering. Using the previous commands, you want that the points qrr@direct and qrr@origin (used to compute the anchor angle) not to be considered as part of the draw?
    – remjg
    Aug 21, 2013 at 19:05
  • @remjg Yes, in your case both points already lie in the current bounding box but this may not be true for every diagram. Change \pgfpointpolarxy{#1}{1} to \pgfpointpolarxy{#1}{10} and there should be a notable difference. Aug 21, 2013 at 19:27
3

I do not know if this will satisfy you, the question is not very precise. I added the library calc to determine the position of the text. The texts "O" is then positioned relative to the origin, same for "I".

\documentclass[margin=.5cm]{standalone}
\usepackage{tikz}
\usetikzlibrary{calc}

\newcommand{\myDraw}[2]{% \myDraw{rotation angle}{scale factor}
\begin{minipage}[t]{2.5cm}
\centering \footnotesize $\theta=#1^{\circ}$, $k=#2$ \medskip\\
  \begin{tikzpicture}[rotate=#1,scale=#2]
    \draw (0,1) -- (0,0) coordinate (o) -- (1,0) node[midway,name=i] {$1$};
    \node (O) at ($(o)+(-0.5em,-0.5em)$) {$O$};
    \node (I) at ($(i)+(0,-0.5em)$) {$1$};
  \end{tikzpicture}
\end{minipage}}

\begin{document}
\myDraw{0}{0.5}
\myDraw{45}{0.7}
\myDraw{90}{1}
\myDraw{135}{1.3}
\myDraw{180}{1.5}
\end{document}

enter image description here

another solution

Here's another solution, I use here the intersection between a line and a node (insensitive to scale) to position the text "O", the text "1" it is positioned at the intersection of the line from "O" and "i".

\documentclass[margin=.5cm]{standalone}
\usepackage{tikz}
\usetikzlibrary{calc,positioning}

\newcommand{\myDraw}[2]{% \myDraw{rotation angle}{scale factor}
\begin{minipage}[t]{2.5cm}
\centering \footnotesize $\theta=#1^{\circ}$, $k=#2$ \medskip\\
  \begin{tikzpicture}[rotate=#1,scale=#2]
    \draw (0,1) -- (0,0) coordinate (o) -- (1,0) coordinate[midway,name=i];
\node[circle,minimum size=2em](circle) at (o){};
\path(o) -- (-2,-2)coordinate(b);
\path (intersection of o--b and circle) coordinate (oo)node{O};
\path (oo) -| (i) node[pos=0.5]{1};
  \end{tikzpicture}
\end{minipage}}

\begin{document}
\myDraw{0}{0.5}
\myDraw{45}{0.7}
\myDraw{90}{1}
\myDraw{135}{1.3}
\myDraw{180}{1.5}
\end{document}

there is a problem with 180 °!enter image description here

2
  • Your solution is working well for rotation, there is only an issue with scaling. I read it too fast and at first I thought you were manually adapting the label position for each case... In conclusion, I've been looking for a solution for the last few days that you had already given to me (at least for the coordinate keyword).
    – remjg
    Aug 20, 2013 at 13:27
  • 1
    Since I've edited my question many times in the meantime, I've edited your answer to reflect the current MWE... Hope you don't mind.
    – remjg
    Aug 20, 2013 at 13:30
2

I've eventually came up with a solution to my problem:

\documentclass[margin=.5cm]{standalone}
\usepackage{tikz}

\newcommand{\myDraw}[2]{% \myDraw{rotation angle}{scale factor}
\begin{minipage}[t]{2.5cm}
\centering \footnotesize $\theta=#1^{\circ}$, $k=#2$ \medskip\\
  \begin{tikzpicture}[rotate=#1,scale=#2]
    \draw (0,1) -- (0,0) coordinate (O) -- (1,0) coordinate[midway] (I);
    \begin{scope}[scale=1/#2]
      \draw (O) ++ (-0.25,-0.25) node {$O$};
      \draw (I) ++ (0,-0.25) node {$1$};
    \end{scope}
  \end{tikzpicture}
\end{minipage}}

\begin{document}
\myDraw{0}{0.5}
\myDraw{45}{0.7}
\myDraw{90}{1}
\myDraw{135}{1.3}
\myDraw{180}{1.5}
\end{document}

Result

Among the things I've learned, I can name a point inside a path using coordinate like this:

\draw (0,1) -- (0,0) coordinate (O);

I can also name a node with the keyword name= so I still can use some convenient options like midway or pos=:

\draw (0,0) -- (1,0) node[midway,name=I] {};

But thanks to a comment of Qrrbrbirlbel below, I know now that I can simply use the previous options with coordinate:

\draw (0,0) -- (1,0) coordinate[midway] (I);

Once I've done that, I need to manually position my labels and apply the inverse scale factor:

\begin{scope}[scale=1/#2]
  \draw (O) ++ (-0.25,-0.25) node {$O$};
  \draw (I) ++ (0,-0.25) node {$1$};
\end{scope}

So two issues remain:

  • The scale factor must be passed as an argument to my macro.
  • I need to manually position my labels which means that I can't use convenient anchors like below left).
3
  • 1
    You can still do node[midway] (I) {} to name the node, but why did use you a node here? A coordinate as in the other case would be better: coordinate[midway] (I). By the way, if you insert the coordinate right after the -- the midway setting is used automatically (it’s the default setting). Aug 21, 2013 at 10:44
  • I've been reading and reading again the PGF/TikZ documentation but I've never seen such possibilities. Thank you for the tip, I'll edit my answer!
    – remjg
    Aug 21, 2013 at 12:36
  • Take a look at section 16.9 “Placing Nodes on a Line or Curve Implicitly” in the 2.10 version of the PGF manual (pp. 193ff.). Aug 24, 2013 at 5:18

You must log in to answer this question.

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