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