TikZ 中类似 Gimp 的透视变换

TikZ 中类似 Gimp 的透视变换

我是 TikZ 的新手,所以请原谅我在这篇文章中表达的任何愚蠢行为。

Gimp 有一个非常好用的透视变换工具,它显示一个 3x3 矩阵,其值会随着使用该工具拖动而变化。本文底部的图片显示了我使用此 GIMP 工具的一个示例(从上到下,图片依次是我绘制的一个圆圈、透视变换后的圆圈以及我用来变换圆圈的 GIMP 工具的屏幕截图)。

有人知道 TikZ 中是否有类似的工具,或者知道如何编写这样的工具吗?理想情况下,我正在寻找以下形式的东西

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

(如果有一种工具可以实现这一功能并且采用不同的格式,那就太好了。)

因此,执行本文底部图片中所操作的示例可能是这样的:

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

我查看了一些帖子,试图弄清楚如何做到这一点 - 我发现最接近我所寻找的帖子是:透视变换。然而,我不太明白这个例子中的代码,包括它是否只是为了这个例子而制作的,或者它是否可以用作我正在寻找的通用工具。

感谢您的帮助!以下是我提到的图片:

在此处输入图片描述

在此处输入图片描述

在此处输入图片描述

答案1

荒谬的计算成本,使用我的答案的线性变化绘制不同形状的文本,网格由绘制最小 x 轴

如上所述,消失点不能位于物体的正上方,但我想,通过巧妙地利用\rotatebox前后变换,就可以获得消失点。

修订的解决方案(垂直加深度缩短)

我意识到我原来的解决方案(如下)缩短了物体的垂直测量值,但没有缩短物体沿线到消失点的长度。这通常是不可察觉的,直到物体被渲染到接近消失点的位置。然后,它就变得清晰了。

因此,在这次修订中,我缩短了物体的高度和深度(这在第二行转换后图像的右侧图像中最为明显)。我还将切片减少到 150,因为否则,我会超出一些 LaTeX 或 PDF 限制。

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

在此处输入图片描述

原始解决方案(仅限垂直缩短)

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

在此处输入图片描述

在此处输入图片描述

的参数(我应该将其重命名{200}为)是对象的切片数。可以通过减少它来加快编译速度,但代价是分辨率降低,引入更多的阶梯式步骤。我建议在编译时将切片数设置为较低,直到最终输出符合要求。\parabtext\lineartext

例如,将其简化为{20}以下结果:

在此处输入图片描述

答案2

我认为像 Asymptote 这样的东西可能比基于 TikZ 的任何东西更有利于实现这一点,尽管tikz-3dplot在简单情况下可以有助于伪造 3D。

这是一个基于 Charles Staats 的教程的非常不熟练的例子。

\documentclass{article}
\usepackage{asymptote}
\begin{document}
\begin{figure}
\centering
% Charles Staats: tutorial: page 78
\begin{asy}
  settings.outformat = "pdf";
  settings.render = 8;
  import three;
  currentprojection = perspective(4*(0,10,2),up=Y);
  size(4cm, 0);
  surface s = surface(reverse(scale(2)*unitcircle) ^^ unitcircle);
  draw(s, black, light=nolight);
\end{asy}
\end{figure}
\end{document}

要编译,请使用pdflatex(或选择的引擎)。然后asy在生成的.asy.TeX Live 上运行asy,尽管我的不起作用,所以我使用了 Linux 发行版打包的版本。然后pdflatex再次运行。(显然,这可以使用您选择的工具自动完成,并适合您的特定系统。)

结果如下。

渐近线

这看起来可能并不令人印象深刻,但重点在于 Asymptote 的强大功能和灵活性,它可以配置和组合一系列不同的变换和投影。例如,投影orthographic可能比上面的版本更有用perspective

关键点在于,与 TikZ 不同,Asymptote 了解 3D。它处理 3D 对象,而不必在 2D 中伪造它们,这使得它很容易改变对我来说,它的缺点是很多不如 TikZ 熟悉!

以下是 TikZ 中一些选定的标准二维转换:

\documentclass[tikz,border=10pt,multi]{standalone}
\begin{document}
\begin{tikzpicture}
  \draw [line width=1mm] circle (2.5mm);
  \begin{scope}[yscale=1.5, xshift=7mm]
    \draw [line width=1mm] circle (2.5mm);
  \end{scope}
  \begin{scope}[cm={1,0,-1,2,(1,1)}, xshift=10mm]
    \draw [line width=1mm] circle (2.5mm);
  \end{scope}
  \begin{scope}[transform canvas={yscale=3, rotate=3}, xshift=15mm]
    \draw [line width=1mm] circle (2.5mm);
  \end{scope}
\end{tikzpicture}
\end{document}

转型

tikz-3dplot参阅更多可能性。

答案3

非线性变换有点慢,不能用于文本,但这是一个概念验证的想法。原始坐标系的两个“源”角以及四个“目标”角任何非线性绘图都是完成的,但是图片或范围的任何变换。源角始终是未变换矩形的西南角和西北角(按此顺序);绘图应在这些角内。四个目标角始终从西南逆时针指定。

我不知道代码在“激进”变换、旋转或倾斜时会如何表现。可能很糟糕。

\documentclass[tikz,border=5]{standalone}
\usepgfmodule{nonlineartransformations}
\makeatletter
\def\tikz@scan@transform@one@point#1{%
  \tikz@scan@one@point\pgf@process#1%
  \pgf@pos@transform{\pgf@x}{\pgf@y}}
\tikzset{%
  grid source opposite corners/.code args={#1and#2}{%
   \pgfextract@process\tikz@transform@source@southwest{%
     \tikz@scan@transform@one@point{#1}}%
   \pgfextract@process\tikz@transform@source@northeast{%
     \tikz@scan@transform@one@point{#2}}%
  },
  grid target corners/.code args={#1--#2--#3--#4}{%
   \pgfextract@process\tikz@transform@target@southwest{%
     \tikz@scan@transform@one@point{#1}}%
   \pgfextract@process\tikz@transform@target@southeast{%
     \tikz@scan@transform@one@point{#2}}%
   \pgfextract@process\tikz@transform@target@northeast{%
     \tikz@scan@transform@one@point{#3}}%
   \pgfextract@process\tikz@transform@target@northwest{%
     \tikz@scan@transform@one@point{#4}}%
  }
}

\def\tikzgridtransform{%
  \pgfextract@process\tikz@current@point{}%
  \pgf@process{%
    \pgfpointdiff{\tikz@transform@source@southwest}%
      {\tikz@transform@source@northeast}%
  }%
  \pgf@xc=\pgf@x\pgf@yc=\pgf@y%
  \pgf@process{%
    \pgfpointdiff{\tikz@transform@source@southwest}{\tikz@current@point}%
  }%
  \pgfmathparse{\pgf@x/\pgf@xc}\let\tikz@tx=\pgfmathresult%
  \pgfmathparse{\pgf@y/\pgf@yc}\let\tikz@ty=\pgfmathresult%
  %
  \pgfpointlineattime{\tikz@ty}{%
    \pgfpointlineattime{\tikz@tx}{\tikz@transform@target@southwest}%
      {\tikz@transform@target@southeast}}{%
    \pgfpointlineattime{\tikz@tx}{\tikz@transform@target@northwest}%
      {\tikz@transform@target@northeast}}%
}


\begin{document}
\begin{tikzpicture}
\draw (0,0) grid (6,6);
\fill [even odd rule, opacity=0.5] 
  (3,3) circle [radius=2] circle [radius=1];
\begin{scope}[shift=(0:8),
  grid source opposite corners={(0,0) and (6,6)},
  grid target corners={(1,1) -- (3,2) -- (7,6) -- (-1,6)}]
\pgftransformnonlinear\tikzgridtransform
\draw [red] (0,0) grid (6,6);
\fill [red, even odd rule, opacity=0.5] 
  (3,3) circle [radius=2] circle [radius=1];
\end{scope}
\begin{scope}[shift=(270:8),
  grid source opposite corners={(0,0) and (6,6)},
  grid target corners={(1,1) -- (6,0) -- (5,4) -- (1,6)}]
\pgftransformnonlinear\tikzgridtransform
\draw [green] (0,0) grid (6,6);
\fill [green, even odd rule, opacity=0.5] 
  (3,3) circle [radius=2] circle [radius=1];
\end{scope}
\begin{scope}[shift={(8,-8)},
  grid source opposite corners={(0,0) and (6,6)},
  grid target corners={(1,1) -- (7,0) -- (5,8) -- (3,8)}]
\pgftransformnonlinear\tikzgridtransform
\draw [blue] (0,0) grid (6,6);
\fill [blue, even odd rule, opacity=0.5] 
  (3,3) circle [radius=2] circle [radius=1];
\end{scope}
\end{tikzpicture}
\end{document}

在此处输入图片描述

答案4

从不同角度看同一个圆圈。

\documentclass{article}
\usepackage{tikz}
\usepackage{tikz-3dplot}

\begin{document}

\tdplotsetmaincoords{60}{110}
%
\pgfmathsetmacro{\rvec}{.8}
\pgfmathsetmacro{\thetavec}{30}
\pgfmathsetmacro{\phivec}{60}
%
\begin{tikzpicture}[scale=5,tdplot_main_coords]
\tdplotsetcoord{P}{\rvec}{\thetavec}{\phivec}
\tdplotdrawarc[red,line width=3mm]{(0,0,0)}{0.5}{0}{360}{}{}
\tdplotsetthetaplanecoords{\phivec}
\tdplotdrawarc[tdplot_rotated_coords,green,line width=3mm]{(0,0,0)}{0.5}{0}%
{360}{}{}
\tdplotsetthetaplanecoords{\thetavec}
\tdplotdrawarc[tdplot_rotated_coords,blue,line width=3mm]{(0,0,0)}{0.5}{0}%
{360}{}{}
\end{tikzpicture}
\end{document}

在此处输入图片描述

相关内容