[Tex/LaTex] Gimp-like perspective transform in TikZ

tikz-pgf

I am brand new to TikZ, so please forgive any foolishness I express in this post.

Gimp has a very nice perspective transform tool, which shows a 3×3 matrix with values that change as you drag using the tool. An example of me using this GIMP tool is shown in the images at the bottom of this post (from top to bottom, the images are a circle that I drew, a perspective-transformed circle, and a screenshot of the GIMP tool I used to transform the circle.

Does anyone know if there is a similar tool, or know how to code one, in TikZ? Ideally I am looking for something that would be of the form

\perspectiveTransform{<shape name or draw command>}{<perspective transform matrix parameters>}

(If there is a tool that does just this that takes a different format, that would be great too.)

So, an example that does what is done in the pictures at the bottom of this post might be something like:

\perspectiveTransform{
    \draw[line width = 10.0pt] (0,0) circle (1); %or name of shape
}
{
    1.0, 1.4, 0.0
    0.0, 1.4, 0.0
    0.0, 0.004, 1.0;
    %these are roughly the matrix values that showed up while using the GIMP tool.
}

I've looked at a few posts trying to figure out how to do this – the one I've found that seems to come closest to what I'm looking for is here: Perspective transform. I don't really understand the code in this example though, including whether it is made for just this example, or whether it can be employed as a general tool, like I'm looking for.

Thanks for any help! Here are the images I mentioned:

enter image description here

enter image description here

enter image description here

Best Answer

At a ridiculous computational cost, using a linear variation of my answer at Draw Text in different shapes, with grid supplied by Drawing minimal xy axis.

As written, the vanishing point cannot be directly above the object, but I would image with clever use of \rotatebox before and after the transformation, it could be obtained.

REVISED SOLUTION (vertical plus depth foreshortening)

I realized my original solution (below) foreshortened the vertical measure of the object, but did nothing to foreshorten the object along the line to the vanishing point. This often is unnoticeable, until the object is rendered close to the vanishing point. Then, it becomes clear.

So, in this revision, I foreshorten both the height and depth of the object (this is most obvious in the right hand image of the 2nd row of transformed images). I also reduced the slices to 150, because otherwise, I overflow some LaTeX or PDF limit.

\documentclass{article}
\usepackage{ifthen,trimclip,calc,fp,graphicx,xcolor}
\newsavebox\mytext
\newcounter{mycount}
\newlength\clipsize
\newcommand\parabtext[5][0]{%
  \edef\neck{#3}% percent to depress the amplitude
  \def\cuts{#4}% Number of cuts
  \savebox{\mytext}{\kern.2pt#5\kern.2pt}% TEXT
  \FPeval{\myprod}{1/cuts}%
  \clipsize=\myprod\wd\mytext\relax%
  \setcounter{mycount}{0}%
  \whiledo{\value{mycount}<\cuts}{%
    \stepcounter{mycount}%
    \edef\NA{\themycount}%
    \edef\NB{\the\numexpr\cuts-\themycount\relax}%
    \FPeval{\myprod}{\NA/\cuts}%
    \ifnum0#1=0\relax%
      \FPeval{\myprod}{1 - \neck*(\myprod)}%
    \else%
      \FPeval{\myprod}{1 - \neck*(1-\myprod)}%
    \fi%
    \FPmul{\myprodB}{\myprod}{\myprod}%
    \scalebox{\myprod}[1]{\clipbox{%
      \value{mycount}\clipsize\relax{} %
      -1pt %
      \wd\mytext-\value{mycount}\clipsize-\clipsize\relax{} %
      -1pt%
    }{\raisebox{#2\dimexpr\ht\mytext-\myprodB\ht\mytext}{%
        \scalebox{1}[\myprodB]{\usebox{\mytext}}}}%
  }}%
}
%%%%%%%%%%
\usepackage[usestackEOL]{stackengine}
\usepackage{xcolor,graphicx,amssymb}
\setstackgap{L}{1cm}
\def\stacktype{L}
% DASHED LINE OF SPECIFIED LENGTH
% From morsburg at https://tex.stackexchange.com/questions/12537/
% how-can-i-make-a-horizontal-dashed-line/12553#12553
\def\solidfill{\cleaders\hbox to .1cm{\rule{.1cm}{1pt}}\hfill}
\def\dashfill{\cleaders\hbox to .2cm{\rule{.05cm}{.4pt}}\hfill}
\newcommand\dashline[1]{\hbox to #1{\dashfill\hfil}}
\newcommand\solidline[1]{\hbox to #1{\solidfill\hfil}}
\newcommand\DL{\textcolor{black!30}{\dashline{6.6cm}}}
\newcommand\SL{\textcolor{black}{\solidline{6.8cm}}\makebox[.2cm][r]{\arrowhead}}
\def\arrowhead{\raisebox{-2.6pt}{$\blacktriangleright$}}
%%%%%%%%%%
\begin{document}
\savestack\partA{\Longstack{\DL\\ \DL\\ \DL\\ \SL\\ \DL\\ \DL\\ \DL}}
\savestack\X{\stackinset{c}{}{c}{}{\Huge o}{\scalebox{.15}{\stackinset{c}{10pt}{t}{3pt}{$y$}{%
  \stackinset{r}{3pt}{c}{-10pt}{$x$}{%
    \stackon[-.5cm]{\partA}{\rotatebox{90}{\partA}}%
}}}}}
\centering%
%\def\X{\Huge Hot!}
\X\par
\parabtext{0}{.7}{150}{\X}\parabtext[1]{0}{.4}{150}{\X}\par
\parabtext{1.2}{.7}{150}{\X}\parabtext[1]{1.2}{1}{150}{\X}\par
\parabtext{.2}{.9}{150}{\X}\parabtext[1]{.425}{.7}{150}{\X}
\end{document}

enter image description here

ORIGINAL SOLUTION (vertical foreshortening only)

\documentclass{article}
\usepackage{ifthen,trimclip,calc,fp,graphicx,xcolor}
\newsavebox\mytext
\newcounter{mycount}
\newlength\clipsize
\newcommand\parabtext[5][0]{%
  \edef\neck{#3}% percent to depress the amplitude
  \def\cuts{#4}% Number of cuts
  \savebox{\mytext}{\kern.2pt#5\kern.2pt}% TEXT
  \FPeval{\myprod}{1/cuts}%
  \clipsize=\myprod\wd\mytext\relax%
  \setcounter{mycount}{0}%
  \whiledo{\value{mycount}<\cuts}{%
    \stepcounter{mycount}%
    \edef\NA{\themycount}%
    \edef\NB{\the\numexpr\cuts-\themycount\relax}%
    \FPeval{\myprod}{\NA/\cuts}%
    \ifnum0#1=0\relax%
      \FPeval{\myprod}{1 - \neck*(\myprod)}%
    \else%
      \FPeval{\myprod}{1 - \neck*(1-\myprod)}%
    \fi%
    \clipbox{%
      \value{mycount}\clipsize\relax{} %
      -1pt %
      \wd\mytext-\value{mycount}\clipsize-\clipsize\relax{} %
      -1pt%
    }{\raisebox{#2\dimexpr\ht\mytext-\myprod\ht\mytext}{%
        \scalebox{1}[\myprod]{\usebox{\mytext}}}}%
  }%
}
%%%%%%%%%%
\usepackage[usestackEOL]{stackengine}
\usepackage{xcolor,graphicx,amssymb}
\setstackgap{L}{1cm}
\def\stacktype{L}
% DASHED LINE OF SPECIFIED LENGTH
% From morsburg at https://tex.stackexchange.com/questions/12537/
% how-can-i-make-a-horizontal-dashed-line/12553#12553
\def\solidfill{\cleaders\hbox to .1cm{\rule{.1cm}{1pt}}\hfill}
\def\dashfill{\cleaders\hbox to .2cm{\rule{.05cm}{.4pt}}\hfill}
\newcommand\dashline[1]{\hbox to #1{\dashfill\hfil}}
\newcommand\solidline[1]{\hbox to #1{\solidfill\hfil}}
\newcommand\DL{\textcolor{black!30}{\dashline{6.6cm}}}
\newcommand\SL{\textcolor{black}{\solidline{6.8cm}}\makebox[.2cm][r]{\arrowhead}}
\def\arrowhead{\raisebox{-2.6pt}{$\blacktriangleright$}}
%%%%%%%%%%
\begin{document}
\savestack\partA{\Longstack{\DL\\ \DL\\ \DL\\ \SL\\ \DL\\ \DL\\ \DL}}
\savestack\X{\stackinset{c}{}{c}{}{\Huge o}{\scalebox{.15}{\stackinset{c}{10pt}{t}{3pt}{$y$}{%
  \stackinset{r}{3pt}{c}{-10pt}{$x$}{%
    \stackon[-.5cm]{\partA}{\rotatebox{90}{\partA}}%
}}}}}
\centering%
\X\par
\parabtext{0}{.7}{200}{\X}\parabtext[1]{0}{.4}{200}{\X}\par
\parabtext{1.2}{.7}{200}{\X}\parabtext[1]{1.2}{1}{200}{\X}\par
\parabtext{.2}{.9}{200}{\X}\parabtext[1]{.425}{.7}{200}{\X}
\end{document}

enter image description here

enter image description here

The {200} argument to \parabtext (which I should rename \lineartext) is the number of slices taken of the object. One can speed up the compilation by reducing it, but at the cost of resolution, introducing more stair-stepping. I recommend compiling with the slice count set low, until the final output is desired.

For example, reducing it to {20} gives this result:

enter image description here

Related Question