![TikZ 中类似 Gimp 的透视变换](https://linux22.com/image/337535/TikZ%20%E4%B8%AD%E7%B1%BB%E4%BC%BC%20Gimp%20%E7%9A%84%E9%80%8F%E8%A7%86%E5%8F%98%E6%8D%A2.png)
我是 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}