8

I am trying to rotate a rectangle drawn in tikz. Many questions on this site were helpful about tikz rotations but for me personally I seem to be misusing one of the parameters, as my intended center of rotation (red dot in the figure below) doesn't seem to be the center of rotation for the rectangle. I want the rectangle to be centered over the red dot, but angled relative to it's horizontal center. The rotation angle looks okay, just not rotated about the correct point (as depicted below).

enter image description here

\documentclass{article}

\usepackage[%
    papersize={100mm,100mm},
]{geometry}
\usepackage{atbegshi}
\usepackage{tikz}
\usetikzlibrary{calc}
\usetikzlibrary{positioning}
\usetikzlibrary{backgrounds}
\usetikzlibrary{automata}

%http://tex.stackexchange.com/questions/229279/how-can-i-make-a-perfect-page-grid-that-fits-my-page-for-measuring-purposes-in-t
\newcommand{\showgrid}{%
    \begin{tikzpicture}
    [
        shift={(current page.center)},
        x=1mm,y=1mm,
        overlay,
        remember picture,
        inner sep=0pt,
        outer sep=0pt,
        minor line/.style={help lines, draw=gray!25, on background layer},
        major line/.style={help lines, draw=gray},
    ]
    \pgfmathtruncatemacro\xmaxstep{\paperwidth/1mm}% calculate needed steps in x direction
    \pgfmathtruncatemacro\ymaxstep{\paperheight/1mm}% calculate needed steps in y direction
    % Vertical lines (events in distinct columns)
    \foreach \step in {0,...,\xmaxstep} {
        \pgfmathsetmacro\gridlineconfig{ifthenelse(equal(int(mod(\step,5)),0),"major line","minor line")}%
        \draw [\gridlineconfig] ($(current page.north west) + (\step mm,0)$) -- ($(current page.south west) + (\step mm,0)$);
    }
    % Horizontal lines (events in distinct rows)
    \foreach \step in {0,...,\ymaxstep} {
        \pgfmathsetmacro\gridlineconfig{ifthenelse(equal(int(mod(\step,5)),0),"major line","minor line")}%
        \draw [\gridlineconfig] ($(current page.north west) - (0,\step mm)$) -- ($(current page.north east) - (0,\step mm)$);
        %\node [anchor=north] at ($ (current page.north west) + (\step mm,0) $) {}; % add text coordinates along the top
        %\node [anchor=west] at ($ (current page.north west) - (0,\step mm) $) {};  % add text coordinates along the LHS
    }
    \end{tikzpicture}
}
\tikzset{dot/.style={circle,fill=#1,inner sep=0,minimum size=4pt}}

\begin{document}\thispagestyle{empty}

\showgrid

\begin{tikzpicture}%[overlay,remember picture,every node/.style={fill=red,inner sep=0pt,outer sep=0pt}]%
[
shift={(current page.center)},
x=1mm,y=1mm,
overlay,
remember picture
]

\node [dot=black] (Point1) at (0,0) {};

\node [dot=red] (ExpectedCentre) at ($(Point1.center) + (-0.5 in,0)$) {};


\draw[black, line width=0.55mm, rotate around={45:($(Point1.center) + (-0.5 in,0)$)}] ($(Point1.center) + (-0.5 in - 0.5 mm, 5 mm)$) rectangle ($(Point1.center) + (-0.5 in + 0.5 mm, -5 mm)$);

\end{tikzpicture}
\end{document}

UPDATE

I've been trying to implement CFR's solution below, but I have still been unable to implement a successful rotation about a point that is not (0,0) The MWE has been trimmed further:

\documentclass[tikz,border=10pt,multi]{standalone}
\usetikzlibrary{calc}
\tikzset{dot/.style={circle,fill=#1,inner sep=0,minimum size=4pt}}
\begin{document}
\begin{tikzpicture}[x=1mm,y=1mm]

\node [dot=black] (Point2) at (2,1) {};

\path ($(Point2.center) + (-0 in,0)$) coordinate (c) node [dot=red] {};

\draw[black, line width=0.55mm, rotate around={45:(c)}] ($(0,0) + ({-0 in - 0.5 mm}, 5 mm)$) rectangle ($(0,0) + ({-0 in + 0.5 mm}, -5 mm)$);

\end{tikzpicture}
\end{document}
EngBIRD
  • 3,985

2 Answers2

9

I'm not sure why you made the MWE so complicated. As far as I can tell, the basic problem is the use of the named coordinate, which is not transformed as part of the rotation of the coordinate system. If that is replaced by the appropriate coordinates, which are subject to the transformation, then everything works as expected.

The following first shows the problem, to demonstrate the sufficiency of the MWE, and second the resolution, when points not influenced by the rotation are replaced by points which are.

problem and resolution

That is, (Point1), for example, is not affected by the coordinate transformation set with rotate around, while (0,0) is.

\documentclass[tikz,border=10pt,multi]{standalone}
\usetikzlibrary{calc}
\tikzset{dot/.style={circle,fill=#1,inner sep=0,minimum size=4pt}}
\begin{document}
\begin{tikzpicture}
  [
    x=1mm,
    y=1mm,
  ]
  \node [dot=black] (Point1) at (0,0) {};
  \path ($(Point1.center) + (-0.5 in,0)$)  node (n)  [dot=red] {};
  \draw[gray, line width=0.55mm] ($(Point1.center) + ({-0.5 in - 0.5 mm}, 5 mm)$) rectangle ($(Point1.center) + ({-0.5 in + 0.5 mm}, -5 mm)$);
  \draw[black, line width=0.55mm, rotate around={45:(n)}] ($(Point1.center) + ({-0.5 in - 0.5 mm}, 5 mm)$) rectangle ($(Point1.center) + ({-0.5 in + 0.5 mm}, -5 mm)$);
  \begin{scope}[yshift=-20mm]
    \node [dot=black] (Point2) at (0,0) {};
    \path ($(Point2.center) + (-0.5 in,0)$) coordinate (c) node [dot=red] {};
    \draw[gray, line width=0.55mm] ($(Point2.center) + ({-0.5 in - 0.5 mm}, 5 mm)$) rectangle ($(Point2.center) + ({-0.5 in + 0.5 mm}, -5 mm)$);
    \draw[black, line width=0.55mm, rotate around={45:(c)}] ($(0,0) + ({-0.5 in - 0.5 mm}, 5 mm)$) rectangle ($(0,0) + ({-0.5 in + 0.5 mm}, -5 mm)$);
  \end{scope}
\end{tikzpicture}
\end{document}

UPDATE

To see what's going on, it is easiest to concentrate on a single point. Here's an illustration:

rotate around demo

This is not what TikZ does, but it is useful to think of it as if it did the following. Suppose we ask it to rotate a path around (2,1) by 45 degrees.

  1. Figure out where the untransformed objects would be.
  2. Now move to (2,1).
  3. From here, figure out where each point on the untransformed path is as a polar coordinate i.e. (<angle>:<distance from here>).
  4. For each calculated polar coordinate, increase <angle> by 45 degrees.
  5. Now use the resulting coordinates in place of the original ones to construct the path.

Code:

\documentclass[tikz,border=10pt,multi]{standalone}
\tikzset{
  dot/.style={circle, fill=#1, inner sep=0, minimum size=4pt},
  mini dot/.style={circle, inner sep=0pt, minimum size=2pt, pos=1, fill, node contents={}},
}
\begin{document}
\begin{tikzpicture}[x=3mm,y=3mm]
  \path (2,1) coordinate (c) node [dot=red] {};
  \draw[black, opacity=.5, line width=0.55] (-.5, 5) rectangle (.5, -5);
  \draw[black, opacity=.5, line width=0.55, rotate around={45:(c)}] (-.5, 5) rectangle (.5, -5);
  \draw [blue]
    (c) edge   node (p1) [mini dot] ++({atan((5-1)/(.5-2))}:-{sqrt((5-1)^2+(.5-2)^2)})
    -- ++({atan((5-1)/(.5-2))+45}:-{sqrt((5-1)^2+(.5-2)^2)})  node (q1) [mini dot]
    ;
  \draw [blue, ->] (p1) arc ({atan((5-1)/(.5-2))}:{atan((5-1)/(.5-2))+45}:-{sqrt((5-1)^2+(.5-2)^2)}) ;
\end{tikzpicture}
\end{document}

EDIT

Here's an example which tries to answer both the 'update' to the question (i.e. the new question being smuggled in as part of 'the' question) and the question Stefan asked about why you might want your named coordinates to stay put.

Consider, then, the following example.

colourful example of rotations and scopes

This is drawn using three series of rotate arounds. Each roughly circular set of objects is created using 8 rotations around the central point. None of these central points are the origin. Within each series, the 8 rotations differ in the angle of rotation. Between each series, the rotations differ in the point about which the rotation is done.

For the first and third of the series, named coordinates or nodes are defined as part of each rotation.

The positions created within the third series (at the top) are then used to add the green parts of the picture in the background. Part of this is done within the rotational scopes. However, the large central green object is drawn later and relies crucially on knowing where the defined points are within the overall picture and independent of the rotational scope. This is much easier --- and, I think, more natural --- than trying to compensate for the original rotation when drawing the green central bit later.

\PassOptionsToPackage{rgb,x11names,dvipsnames,svgnames}{xcolor}
\documentclass[tikz,border=10pt,multi]{standalone}
\usetikzlibrary{backgrounds,hobby}
\begin{document}
\begin{tikzpicture}
  \coordinate (o) at (0,0);
  \coordinate (g) at ([yshift=-40mm]o);
  \foreach \i/\j in {(-35mm,0)/a,(35mm,0)/b,(0,40mm)/c} \path \i coordinate (\j) ;
  \foreach \i [count=\n from 0, evaluate=\n as \c using {\n < 5 ? 100*(1-\n/4) : 100*(\n/4 - 1 ) }] in {0,45,...,315}
  {
    \begin{scope}[rotate around={\i:(-35mm,0)}]
      \path [inner color=white, outer color=WildStrawberry!\c!Purple4] (-35mm,0) -- ++(-20:20mm) arc (-20:20:-20mm) node (a\n) [midway, shift=(\i:2.5mm), circle, ball color=WildStrawberry!\c!Purple4] {} -- cycle;
    \end{scope}
    \begin{scope}[rotate around={\i:(35mm,0)}]
      \path [outer color=blue!\c!cyan, inner color=white] (35mm,0) -- ++(-20:25mm) arc (-20:20:25mm) -- cycle;
    \end{scope}
    \begin{scope}[rotate around={\i:(0,40mm)}]
      \path [inner color=white, outer color=WildStrawberry!\c!Gold1] (0,40mm) -- ++(-20:20mm) arc (-160:-200:20mm) node (c\n) [midway, shift=(\i:2.5mm), circle, ball color=WildStrawberry!\c!Gold1] {} -- cycle;
      \scoped[on background layer] {\path [draw=Green4, bottom color=Green4, top color=Green1!25] (c\n.\i) [out=-75,in=-20] to (c) [out=20, in=75] to (c\n.\i);}
    \end{scope}
  }
  \begin{scope}[on background layer]
    \path  (c5.-120) ++(-90:4mm) coordinate (c5d);
    \path  (c7.-60) ++(-90:4mm)  coordinate (c7d);
    \path [top color=white, bottom color=Green4, middle color=Green1] ([yshift=-5mm]c6.-90)
    [
      closed,
      curve through={
        ([tension out=5, tension in=-1]c7d)
        ([tension out=4, tension in=5]10mm,15mm)
        (5mm,-15mm)
        ([xshift=1mm]g)
        ([xshift=-1mm]g)
        (-5mm,-15mm)
        ([tension in=4, tension out=5]-10mm,15mm)
        ([tension in=5, tension out=-1]c5d)
      }
    ] to cycle;
  \end{scope}
\end{tikzpicture}
\end{document}
cfr
  • 198,882
  • 1
    Thanks, I am reading your solution, but first wanted to leave an apology for my overly complicated MWE. I trimmed from my complete code (which needed the grid as I was learning how the tikz coordinates work by trial and error), but somehow had it in my head that the grid would be necessary to to see the proportional offsets and consistency with the (0,0) origin of rotation. Sadly, even that wasn't really clear enough so I added the red dot, and that's when the grid truly became redundant and unnecessary. – EngBIRD Jun 14 '16 at 13:29
  • No problem. I understand. I was just a bit confused by the grid because I started out assuming it must be needed, but couldn't see why. So I said that partly to explain why I'd pared the example down and why I thought the reduced version still showed the problem, as I understood it. – cfr Jun 14 '16 at 15:26
  • Thanks, if I could please bother you further, I am having trouble figuring out how the -20 mm shift works. I assume there would have been a trigonometric with 45 degrees and 0.5in. How did you come up with the 20 mm to place it inline with the original black origin (0,0)? – EngBIRD Jun 14 '16 at 15:34
  • I didn't. The shift is just to move the second case down so it doesn't overlap the original example. It isn't aligning anything else. – cfr Jun 14 '16 at 15:37
  • Interestingly this works when the Point being centered is (0,0) or is reset as an origin using a local scope i.e. \begin{scope}[shift={($(Point2.center)+(-0.5in,0)$)}]. Does this mean the center of rotation is always assumed to be (0,0)? I updated my original question with an offset example that remains off center to Point2 when it is not (0,0). – EngBIRD Jun 14 '16 at 18:26
  • @EngBIRD Please see update and edit above. I don't understand what you are expecting, but I'm pretty sure it does not correspond to what TikZ thinks you should expect, if you see what I mean. – cfr Jun 15 '16 at 01:35
  • Thanks! I don't even think AMAZING!!! can begin to describe the clarity, detail, and depth is illustrated here. I look forward to fully exploring this code! – EngBIRD Jun 15 '16 at 02:39
4

As I understand it the coordinate at (1,0) and the position (1,0) are not treated the same way in Tikz. The position can be moved around and e.g. be part of rotations or scope, while the coordinate is fix and does not move unless told explicitly. So, all movement of the coordinate must be applied directly to it, and not the surrounding. The following code has helped me understand how to treat them:

\documentclass[border=5]{standalone}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}
  %%%% Rotate
  \draw[very thin] (-1.2,-1.2) rectangle node[above=1.2cm]{\tiny rotate} +(3.4,2.4);
  \coordinate (Zero) at (0,0);
  \coordinate (nonZero) at (1,0);
  %%
  \draw[red,fill=red] (Zero) circle (1pt) node [above]{\tiny(0,0)};
  \draw[fill=black] (nonZero) circle (1pt) node [above]{\tiny(1,0)};
  %%
  \foreach \angle in {0,10,...,300}{
    \draw[blue,rotate=\angle] (nonZero) circle (2pt);
    \draw[green,rotate=\angle] (1,0) circle (2pt);
    \draw[red] ([rotate=\angle]nonZero) circle (3pt);
  };
  %% Rotate around
  \draw[very thin] (4-1.2,-1.2) rectangle node[above=1.2cm]{\tiny rotate around} +(3.4,2.4);
  \coordinate (Four) at (4,0);
  \coordinate (Five) at (5,0);
  %% 
  \draw[red,fill=red] (Four) circle (1pt) node [above]{\tiny(4,0)};
  \draw[fill=black] (Five) circle (1pt) node [above]{\tiny(5,0)};
  %% 
  \foreach \angle in {0,10,...,300}{
    \draw[blue,rotate around={\angle:(Five)}] (Four) circle (2pt);
    \draw[green,rotate around={\angle:(Five)}] (4,0) circle (2pt);
    \draw[red] ([rotate around={\angle:(Five)}]Four) circle (3pt);
  };
\end{tikzpicture}
\end{document}

The output is then: enter image description here

And the explanation:

In the left box I use rotate to rotate around zero. There are two reference points in the box, zero, that is red with coordinate (Zero), and one that is black with coordinate (nonZero). Then in the loop I first try to rotate the coordinate nonZero which all and up at (1,0). Since it is a coordinate it is already fixed and will not rotate along with the surrounding. Then rotating (1,0) works fine and gives the green circles. If the movement is instead applied directly to the coordinate, as in the third row, it will move as expected.

Similar movement can be applied to rotate around, which is shown in the right box, and the second loop.

I have at this point just accepted the fact of the difference, but if someone can explain why, you are very welcome.

StefanH
  • 13,823
  • (1,0) is a coordinate specification. Isn't the difference between a named coordinate, which TikZ assumes should remain where it is, and a directly specified one, which is not in any particular place until TikZ figures out where it is, taking the surrounding context into account? That is, when you use (Zero), you are referring to an already determined location. When you use (0,0), the location is to-be-determined. – cfr Jun 14 '16 at 15:29
  • Yes, that is how I understand it too. But even if I use a predefined coordinate in a surrounding, e.g. rotate, I might like to rotate the whole picture. It does become a bit confusing if some coordinates are fixed and some move. – StefanH Jun 14 '16 at 16:01
  • It isn't that some are fixed and some move. It is that some already exist and some don't. The former already have a location. The latter don't. Imagine fixing things onto a board. There are the things you've already pinned on and the ones you have yet to pin. That's the difference. – cfr Jun 14 '16 at 16:18
  • OK. But then if you view the board as the surrounding and you want to rotate it, some of the points are pinned not only on the board but also to the floor. This clearly explain why it works as it does. But then, why is it defined like this? Is there some situations when it is an advantage, or is it more an implementation aspect? – StefanH Jun 14 '16 at 20:17
  • Aren't there cases where you want things to stay put? Usually, if you've named a point, it is so you can use that point again later e.g. to draw an arrow to it or something. I don't know if that's why or not, but it is certainly handy! – cfr Jun 14 '16 at 20:59
  • See my edit above for an example where it is much easier for named points to stay put. It would, in fact, be very inconvenient - and quite unnatural - if such points routinely shifted according to the local transformational context. At least, so I think ;). – cfr Jun 15 '16 at 00:39
  • Thanks for your answer and your continued discussion. I have found it very informative! – EngBIRD Jun 15 '16 at 02:41
  • When explicit coordinates are passed they also pass through the transformation matrix. Named coordinates are references to canvas coordinates that are already established so they are just looked-up and hence immune to further transformations. – percusse Jun 15 '16 at 06:47
  • I see your point. My problem was roughly that I saw the coordinate name as a variable, rather than a constant. The idea to set a coordinate within a scope environment, and use it outside as a reference point can be quite handy. – StefanH Jun 15 '16 at 08:16