Scale coordinates in the direction of a vector in TikZ

OK, here we go then. And I agree with @samcarter of course.

\documentclass{article}
\usepackage{amsmath}
\usepackage{tikz}
\begin{document}
The pgfmanual provides \verb|\pgftransformcm|, see e.g.\ section~103.2.2. All
one has to do is to cook up the transformation matrix. Assume we want to scale
by a factor $\xi$ in $y$--direction. The transformation matrix is then given by
\begin{align}
 T_y&~=~\begin{pmatrix}
  1 & 0\\ 0 & \xi
 \end{pmatrix}\;.
\end{align}
If one is to choose a different direction, one only has to sandwich $T$ between
a rotation matrix $\Omega$ and its inverse,
\begin{align}
 T_\theta&~=~\Omega\cdot T_y\cdot \Omega^{-1}\;,
\end{align}
where
\begin{align}
 \Omega~=~
 \begin{pmatrix}
  \cos\theta & \sin\theta\\ -\sin\theta & \cos\theta
 \end{pmatrix}\;.
\end{align}
The result of this exercise is 
\begin{align}
 T_\theta&~=~\begin{pmatrix}
 \frac{1}{2} (\xi-(\xi-1)\, \cos (2 \theta
   )+1) & (\xi-1) \cos (\theta ) \sin
   (\theta ) \\
 (\xi-1) \cos (\theta ) \sin (\theta )
   & \frac{1}{2} (\xi+(\xi-1) \cos (2
   \theta )+1)\end{pmatrix}\;.
\end{align}
Below is an example in which $\xi=2$ and $\theta=30^\circ$.

\def\myx{3}
\def\myTheta{30}
\begin{tikzpicture}
\draw (0,0) rectangle (2,2);
\begin{scope}
\pgftransformcm{(1 + \myx - (-1 + \myx)*cos(2*\myTheta))/2}{% 
(-1 + \myx)*cos(\myTheta)*sin(\myTheta)}{%
(-1 + \myx)*cos(\myTheta)*sin(\myTheta)}{% 
(1 + \myx + (-1 + \myx)*cos(2*\myTheta))/2}{\pgfpoint{0cm}{0cm}} 
\draw (0,0 ) rectangle (2,2);
\end{scope}
\end{tikzpicture}
\end{document}

enter image description here

ADDENDUM: Motivated by @percusse's comment and Kpym's answer, I wrote a quick style, which also uses "active transformations", i.e. the interpretation of the rotation angle is arguably more intuitive.

\documentclass{article}
\usepackage{tikz}
\tikzset{rotostretch/.style 2 args={cm={(1 + #1 - (-1 + #1)*cos(2*#2))/2, 
-(-1 + #1)*cos(-#2)*sin(#2),
-(-1 + #1)*cos(-#2)*sin(#2), 
(1 + #1 + (-1 + #1)*cos(2*#2))/2,(0,0)}}}
\begin{document}
\begin{tikzpicture}
\draw[red] (0,0) rectangle (2,2);
\draw[rotostretch={2}{-30}] (0,0 ) rectangle (2,2);
\end{tikzpicture}
\end{document}

enter image description here

ANOTHER ADDENDUM: Based on Kpym's older answer. Similar to Kpym's newer answer but without \makeatletter. (I am not saying that it is bad to use \makeatletter.) Here is a directive stretch along which can be used with any vector, similar to Kpym's answer, who had this first.

\documentclass[tikz,border=3.14pt]{standalone}
\usetikzlibrary{calc}
\tikzset{rotostretch/.style 2 args={cm={(1 + #1 - (-1 + #1)*cos(2*#2))/2, 
-(-1 + #1)*cos(-#2)*sin(#2),
-(-1 + #1)*cos(-#2)*sin(#2), 
(1 + #1 + (-1 + #1)*cos(2*#2))/2,(0,0)}}}
\tikzset{stretch along/.code={\path let \p1=#1 in \pgfextra{%
\pgfmathsetmacro{\RotostretchAngle}{-atan2(\y1,\x1)}
\pgfmathsetmacro{\RotostretchRadius}{veclen(\y1,\x1)/28.3465}
\typeout{\RotostretchAngle,\RotostretchRadius}
\xdef\RotostretchRadius{\RotostretchRadius}
\xdef\RotostretchAngle{\RotostretchAngle}};
\pgfkeysalso{/tikz/rotostretch={\RotostretchRadius}{\RotostretchAngle}}}}

\begin{document}
\begin{tikzpicture}
\draw[red] (0,0) rectangle (2,2);
\draw[stretch along={(2,1)}] (0,0 ) rectangle (2,2);
\end{tikzpicture}
\end{document}

To scale in direction alpha we can simply rotate, then scale in x-direction, and then rotate back. For example to scale by 2 in direction 45° we can simply apply [rotate=45,xscale=2,rotate=-45] (the most right operation is executed first).

I define two styles scale angle direction = <angle><ratio> and scale vector direction = <vector point><ratio>. The second style simply calculate the angle argument of the vector and then call the first one.

\documentclass[tikz,border=7pt]{standalone}
\makeatletter
\tikzset{
  scale angle direction/.style 2 args ={
    % scale in direction #1 by #2
    rotate={#1},
    xscale=#2,
    rotate={#1*(-1)}
  },
  scale vector direction/.code 2 args={
    % get the angle argument of point #1
    \tikz@scan@one@point\pgfutil@firstofone#1\relax
    \pgfmathatantwo{\the\pgf@y}{\the\pgf@x}
    \let\tempangle=\pgfmathresult
    % scale in direction \tempangle by #2
    \pgfkeysalso{scale angle direction={\tempangle}{#2}}
  }
}
\makeatother
\begin{document}
  \begin{tikzpicture}
    \draw[red] rectangle (1,1) coordinate (A);
    \draw[scale vector direction={(A)}{2}] rectangle (1,1);
  \end{tikzpicture}
\end{document}

enter image description here

Tags:

Tikz Pgf