Scale coordinates in the direction of a vector in TikZ

Rather than the usual xscale and yscale, which scale in the direction of the canonical vectors, I'm thinking on scaling on the direction of a fixed vector, and I haven't seen this in the manual options, can this be done?

EDIT: I'll clarify the transformation. If you have a set in the plane, scaling by n in the direction of x dilates the x coordinate by a factor of n, that is, the dimension x is multiplied by n, while the y dimension remains intact.

Likewise, scaling in the direction of v by a factor of n multiplies the dimension that is in the direction of v by n, and leaves the perpendicular dimension intact.

In general, you can imagine doing this by taking the vector v and completing it to an orthonormal base of the space, and then, in this new base, scale only the coordinate corresponding to v. This is what is done when xscaling or yscaling.

As the following image shows, in general, you can take a hyperplane in n-dimensional space that is perpendicular to v, and the n-1 dimensions lying in this hyperplane remain intact, while the dimension perpendicular to it is scaled.

In this example, I scaled by a factor of 2 perpendicularly to the plane.

EDIT2: As requested, I show a minimal code of the shape.

\coordinate (A) at (-4,3);
\coordinate (B) at (4,3);
\coordinate (C) at (4,-3);
\coordinate (D) at (-4,-3);
\draw (A) to (B) to [bend left] (C) to (D) to [bend left] cycle;

• How is that different from a scaling in x and y directions with appropriate coefficients? The coefficients are just to components of the vector.
– user121799
May 1, 2018 at 19:08
• @marmot Maybe scale is not the proper term and dilate would be more appropriate, but for instance, if you scale a square in either the x or the y direction you get rectangles, meanwhile, if you scale in a diagonal direction you should get some kind of rhomboid. May 1, 2018 at 19:11
• @F.Webber Is dilate used to make distances shorter? scale is. May 1, 2018 at 19:13
• Well, of course, if you give a precise definition of your transformation, someone could tell you how to implement it. But at this point I don't understand what you're asking, sorry.
– user121799
May 1, 2018 at 19:14
• @marmot Sorry for the confusion, I clarified the transformation. May 1, 2018 at 19:26

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)}
\xdef\RotostretchAngle{\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}

• That's equivalent to \begin{scope}[cm={a,b,c,d,(0,0)}] May 1, 2018 at 19:47
• That is precisely it, I'm pretty grateful for the answer. Edit: I'll try percusse's comment. May 1, 2018 at 19:47
• @percusse I fully agree, but I thought the "challenge" was to determine a, b, c and d. Anyway, I added an explanation on what Mathematica did.
– user121799
May 1, 2018 at 19:54
• You're welcome. I'll delete now my comments because they're no longer useful from now on.
– Kpym
May 2, 2018 at 16:11

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}