39

I would like to apply a transformation to some text so that it is slanted following a trapezoidal shape, such as the famous opening text of the Star Wars franchise:

enter image description here

This seems tricky to do since it is not an affine transformation. I have tried googling around but without luck.

Empty MWE:

\documentclass{article}
\newcommand{\starwarsbox}[1]{\fbox{#1}}

\begin{document}
\begin{center}
\starwarsbox{
    \parbox{0.8\linewidth}
    {
    Star wars effect demo.
    \begin{itemize}
        \item Lots of text here
        \item And environments such as \texttt{itemize}, too!
    \end{itemize}
    }
}
\end{center}
\end{document}

Any ideas? Thanks!

  • 6
    Welcome to TeX.SX and may the force be with you! – Andrew Apr 1 '16 at 8:10
  • 1
    Asymptote can do proper three dimensional rendering of labels in perspective. You would need to put the labels on a horizontal plane and get the 3D eye in the right place. – Thruston Apr 1 '16 at 8:21
  • 1
    TikZ has \pgftransformnonlinear{...}. This might be necessary here. – Henri Menke Apr 1 '16 at 8:58
  • Pst-solides3d from the pstricks family, should be able to do that. The documentation shows examples of text on the faces of a cube. – Bernard Apr 1 '16 at 8:59
  • 2
    @Bernard Text on a cube face is not, of itself, sufficient. Isometric drawings have slant, but not vanishing-point perspective (for example, tex.stackexchange.com/questions/228579/… or tex.stackexchange.com/questions/192620/…) – Steven B. Segletes Apr 1 '16 at 9:43
25

This all starts with Bruno's \slantbox (Shear transform a "box") and applies it letter by letter. For reference, the slant value supplied to Bruno's routine is the tangent of the slant angle.

I am EDITing to better explain what I do here. The key is to define a vanishing point and a coordinate system, and to know your current location relative to it.

Having done the coding last night when I was tired, I foolishly defined a left-handed system, where x is the current horizontal location relative to the vanishing point, and y is downward distance on the page from the vanishing point. Thus, there are some extra negatives needed in the trig calculations to undo the effects of the left-handed system.

The left margin of the perspective view is defined by the line connecting the vanishing point to the coordinates (\xvpref, \ypvref), specified relative to the vanishing point. Thus changing the value of \xvpref changes how wide the text is, while changing \yvpref will change the vertical location of the horizon. In my original approach (first line of text in foreground), that was also where the 1st line of text began. In the corrected approach (last line of text in the foreground), the starting point is at (\xvp, \ypv)

The \nextline input, is processed through \nextword and \nextletter in order to process the line one letter at a time, accounting for spaces. Once each letter is isolated, the tangent from its location back to the vanishing point is calculated inside of \nextletter with an \FPdiv. That tangent will determine the slant.

In addition to the slant, the magnification of the letter needs to be determined. The magnification is determined in \setmagf, solely as a function of (and proportional to) the y-distance from the vanishing point (so that a given line is all at the same magnification). Once the slant and magnification of the letter are known, these parameters are passed to \shslbox by way of \nextletter. The macro \shslbox merely scales the letter and passes it to Bruno's \slantbox.

As each letter is rendered, the x value is updated (in \xvp), which will affect the tangent slope to the vanishing point and thus the slant for the subsequent letter.

The \nextline macro, in addition to processing the letters in each line, has to perform additional bookkeeping. The \parallax macro is called to register the current location on the vanishing left margin by way of the proper \hspace. The \setmagf macro is called to determine the mag factor for this line of text. A \vspace is called to shift the line up (original approach) or down (corrected approach), accounting for the mag factor in the process. The revised value of y, naturally, also affects the tangent to the vanishing point.

No margination is performed on the right side, which remains ragged, and must be manually adjusted to suit. This can be done by shifting whole words to adjacent lines, or by padding words with \, to increase interword gaps.

CORRECTED APPROACH (last line of text in foreground)

\documentclass{article}
\usepackage{fp,amssymb,graphicx,xcolor}
\makeatletter
\def\strippt{\strip@pt}
\makeatother
\newsavebox\foobox
\newcommand\slbox[2]{%
  \slantbox[\result]{#2}%
}%
\newcommand{\slantbox}[2][30]{%
        \mbox{%
        \global\sbox{\foobox}{\textcolor{yellow}{#2}}%
        \hskip\wd\foobox
        \pdfsave
        \pdfsetmatrix{1 0 #1 1}%
        \llap{\usebox{\foobox}}%
        \pdfrestore
}}
\newcommand\shslbox[3]{\slbox{#1}{\scalebox{#3}{#2}}}
\newlength\yvp
\newlength\xvp
\newlength\yvpref
\newlength\xvpref
\yvpref 50pt\relax
\xvpref -75pt\relax
\xvp=\xvpref\relax
\yvp=.3\yvpref\relax
\newcommand\nextletter[1]{%
  \FPdiv{\result}{\strippt\xvp}{-\strippt\yvp}%
  \shslbox{\result}{#1}{\themagf}%
  \addtolength{\xvp}{\wd\foobox}%
}
\newcommand\nextword[1]{\nextwordhelp #1\relax}
\def\nextwordhelp#1#2\relax{%
  \nextletter{#1}%
  \ifx\relax#2\relax\sbox{\foobox}{\scalebox{\themagf}{~}}\addtolength{\xvp}{\wd\foobox}\usebox{\foobox}%
  \else\nextwordhelp#2\relax\fi%  
}
\newcommand\nextline[1]{%
  \parallax\setmagf\nextlinehelp #1 \relax \relax%
  \vspace{-\dimexpr\baselineskip-\themagf\baselineskip}\par%
  \addtolength\yvp{\themagf\baselineskip}\xvp\xvpref\relax%
  \ignorespaces%
}
\def\nextlinehelp#1 #2\relax{%
  \nextword{#1}%
  \ifx\relax#2\relax\else\nextlinehelp #2\relax\fi%
}
\newcommand\parallax{%
  \FPdiv{\result}{\strippt\yvp}{\strippt\yvpref}%
  \FPsub{\result}{1}{\result}%
  \hspace{-\result\xvpref}\addtolength{\xvp}{-\result\xvpref}%
}
\def\setmagf{\global\FPdiv{\themagf}{\strippt\yvp}{\strippt\yvpref}} 
\parindent 0pt

\begin{document}
\pagecolor{black}\sffamily\bfseries%

\nextline{In a galaxy, far, far away, the Empire}
\nextline{struck back.  Our hero, Luke Skywalker}
\nextline{nearly died, but didn't.  Meanwhile,}
\nextline{R2D2 and C3PO were stuck on the}
\nextline{planet Tatoine.}
\nextline{~}
\nextline{The evil Darth Vader was plotting}
\nextline{terrible deeds.}

\end{document}

enter image description here

ORIGINAL APPROACH (first line of text in foreground)

\documentclass{article}
\usepackage{fp,amssymb,graphicx,xcolor}
\makeatletter
\def\strippt{\strip@pt}
\makeatother
\newsavebox\foobox
\newcommand\slbox[2]{%
  \slantbox[\result]{#2}%
}%
\newcommand{\slantbox}[2][30]{%
        \mbox{%
        \global\sbox{\foobox}{\textcolor{yellow!50}{#2}}%
        \hskip\wd\foobox
        \pdfsave
        \pdfsetmatrix{1 0 #1 1}%
        \llap{\usebox{\foobox}}%
        \pdfrestore
}}
\newcommand\shslbox[3]{\slbox{#1}{\scalebox{#3}{#2}}}
\newlength\yvp
\newlength\xvp
\newlength\yvpref
\newlength\xvpref
\yvpref 50pt\relax
\xvpref -50pt\relax
\xvp=\xvpref\relax
\yvp=\yvpref\relax
\newcommand\nextletter[1]{%
  \FPdiv{\result}{\strippt\xvp}{-\strippt\yvp}%
  \shslbox{\result}{#1}{\themagf}%
  \addtolength{\xvp}{\wd\foobox}%
}
\newcommand\nextword[1]{\nextwordhelp #1\relax}
\def\nextwordhelp#1#2\relax{%
  \nextletter{#1}%
  \ifx\relax#2\relax\sbox{\foobox}{\scalebox{\themagf}{~}}\addtolength{\xvp}{\wd\foobox}\usebox{\foobox}%
  \else\nextwordhelp#2\relax\fi%  
}
\newcommand\nextline[1]{\setmagf\nextlinehelp #1 \relax \relax%
  \vspace{-\dimexpr\baselineskip+\themagf\baselineskip}\par%
  \addtolength\yvp{-\themagf\baselineskip}\xvp\xvpref\relax%
  \parallax\ignorespaces%
}
\def\nextlinehelp#1 #2\relax{%
  \nextword{#1}%
  \ifx\relax#2\relax\else\nextlinehelp #2\relax\fi%
}
\newcommand\parallax{%
  \FPdiv{\result}{\strippt\yvp}{\strippt\yvpref}%
  \FPsub{\result}{1}{\result}%
  \hspace{-\result\xvpref}\addtolength{\xvp}{-\result\xvpref}%
}
\def\setmagf{\global\FPdiv{\themagf}{\strippt\yvp}{\strippt\yvpref}} 
\parindent 0pt

\begin{document}
\pagecolor{black}
\nextline{In a galaxy, far, far away}
\nextline{the Empire struck back.}
\nextline{Our hero Luke Skywalker}
\nextline{nearly died, but didn't}
\nextline{Meanwhile, R2D2 and}
\nextline{C3PO were stuck on}
\nextline{the planet Tatoine.}
\end{document}

enter image description here

Merely changing the value of \yvpref from 50pt to 30pt changes the fade angle:

enter image description here

Increasing the negative value of \xvpref from -50pt to -80pt allows more text to be included on a line. Of course, adjustment of the text specification is required, as it is input line by line. Also, \yvpref was set to 50pt.

enter image description here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.