透视变换

透视变换

以下图片非常养眼: 花式罗盘图形

是使用以下代码生成的:

\documentclass[a4paper]{article}
\usepackage{tikz}
\usetikzlibrary{shapes.arrows}
\usetikzlibrary{shadows.blur}

\makeatletter

\def\sin@alpha{0.9}
\def\cos@alpha{0.006} % scaled with eye distance... never mind.

\def\kappa{0.551784} % control point inset for almost-circles

\def\pgf@pos@transform#1#2{%
  \ifpgf@pt@identity%
  \else%
    \pgf@pt@temp=#1%
    #1=\pgf@pt@aa#1%
    \advance#1 by\pgf@pt@ba#2%
    #2=\pgf@pt@bb#2%
    \advance#2 by\pgf@pt@ab\pgf@pt@temp%
  \fi%
  \advance#1 by\pgf@pt@x%
  \advance#2 by\pgf@pt@y%
  \pgfmathsetmacro{\denominator}{1+\cos@alpha*#2)}%
  \pgfmathparse{#1/\denominator}%
  #1=\pgfmathresult pt%
  #2=\sin@alpha#2%
  \pgfmathparse{#2/\denominator}%
  #2=\pgfmathresult pt%
}

\makeatother

\begin{document}
\begin{center}
  \begin{tikzpicture}[yshift=5cm]
    \draw[help lines] (-5,-5) grid (5,5);

    \def\r{5}
    \draw[thick,draw opacity=0.3,ball color=blue!20,blur shadow]
    (\r,0) .. controls(\r,\kappa*\r) and (\kappa*\r,\r) .. (0,\r)
    .. controls(-\kappa*\r,\r) and (-\r,\kappa*\r) .. (-\r,0)
    .. controls(-\r,-\kappa*\r) and (-\kappa*\r,-\r) .. (0,-\r)
    .. controls(\kappa*\r,-\r) and (\r,-\kappa*\r) .. (\r,0) -- cycle;

    \foreach \r in {1,2,3,4} {
      \draw[draw opacity=0.3] (\r,0) .. controls(\r,\kappa*\r) and (\kappa*\r,\r) .. (0,\r)
      .. controls(-\kappa*\r,\r) and (-\r,\kappa*\r) .. (-\r,0)
      .. controls(-\r,-\kappa*\r) and (-\kappa*\r,-\r) .. (0,-\r)
      .. controls(\kappa*\r,-\r) and (\r,-\kappa*\r) .. (\r,0) -- cycle;
    }

    \node[arrow box,rotate=45,arrow box arrows={north:6cm,east:6cm,south:6cm,west:6cm},
          draw,top color=blue!20,
          arrow box shaft width=5mm,arrow box head extend=2mm,arrow box head indent=1mm,
          minimum width=5mm,minimum height=5mm,blur shadow] at (0,0) {};
  \end{tikzpicture}
\end{center}
\end{document}

(对于模糊的阴影,你必须得到pgf-blur 包来自 CTAN。否则只需将模糊阴影更改为阴影)

网格、椭圆和箭头的代码完全独立于透视变换。这是通过对低级\pgf@pos@transform宏进行一些令人讨厌的操作来处理的。到目前为止一切顺利。但是...

  • 必须使用贝塞尔样条线来模拟圆,因为在将圆转换为样条线之前已经完成了坐标变换。
  • 对于任何椭圆、圆弧、圆角等都是同样如此。
  • 在这种情况下,变换控制点可以得到与变换后的贝塞尔样条曲线足够接近的近似值,但这只是运气好而已。一般来说,任何曲线可能都必须分解成几段才能得到更好的近似值。
  • 设置透视变换确实很低级。

关于如何正确执行此操作,您有什么建议吗?也许可以加点装饰?

答案1

我编写了一些代码,用于比 TikZ 目前更“正确”地处理 3D 坐标。有趣的是,它已经在 TeX-SX 启动板包中了,尽管(直到现在)它还不是答案的一部分。它就是包tikz3d.dtx。它的想法有两个方面:

  1. 更高级的 3D 坐标处理。特别是,比 TikZ 目前记住所有三个坐标的时间更长。这样做的目的是,如果你这样做,(1,2,3) -- +(3,4,5)那么 TikZ第一的将坐标转换为二维坐标,并然后进行相对计算。如果从 3D 到 2D 的转换是线性的,那么这样做没有问题,但是如果您想进行透视变换,那么这不是线性的,因此转换为 2D 然后添加会产生错误的结果。

  2. 修改从 3D 到 2D 的转换的能力。TikZ 可以处理线性 3D 到 2D 的转换,但有许多有趣的转换不是线性的。此代码允许指定任意转换。我用透视变换构建了它,但任何事情都可以做。因为坐标被记住为 3D 而不是 2D,所以在选择转换时具有更大的灵活性。

最大的缺点是,与 TikZ 一样,这仅适用于坐标,而不适用于实际路径。但是,要正确转换路径将涉及相当多的工作,因为例如,贝塞尔曲线的透视变换不是贝塞尔曲线。由于 TikZ 的输出格式最多只能处理三次贝塞尔曲线,因此需要通过一系列贝塞尔三次曲线来近似投影。

另一方面,直线的透视投影仍然是直线,需要相当大的透视效果才能使贝塞尔曲线明显变形而偏离贝塞尔曲线。以下是几个示例:曲线是通过投影坐标然后绘制生成的贝塞尔曲线来完成的。连接在一起的点是曲线真正应该看起来的样子(有点)。如您所见,没有太大区别。

投影贝塞尔曲线

基本形状确实需要进行一些修改才能考虑 3D 坐标,所以我有一些方法可以做到这一点。圆和圆弧不需要太多修改,但对于矩形,我创建了一条to路径来处理它。

这里还有一些示例(来自tikz3d_test.texTeX-SX 启动板项目中的文件)。

\begin{tikzpicture}[3d/perspective eye={5,5,-10}]
\draw (3d cs:0,0,0) to[3d rectangle] (3d cs:7,7,0);
\draw (3d cs:0,0,0) to[3d rectangle] (3d cs:7,0,7);
\draw (3d cs:7,7,7) to[3d rectangle] (3d cs:0,7,0);
\draw (3d cs:7,7,7) to[3d rectangle] (3d cs:0,0,7);
\end{tikzpicture}

具有透视的 3D 立方体

节点大小不变

\begin{tikzpicture}[3d/perspective eye={5,5,10}]
\foreach \y in {0,.5,...,5}
  \draw (3d cs:0,\y,1) -- (3d cs:0,\y,-31);
  \draw[ultra thick] (3d cs:0,0,0) -- +(0,1) node[thin,fill=white,draw,text width=5em,rounded corners,align=center,anchor=south] {How big is this sign?};
  \draw[ultra thick] (3d cs:0,0,-25) -- +(0,1) node[thin,fill=white,draw,text width=5em,rounded corners,align=center,anchor=south] {How big is this sign?};
\end{tikzpicture}

带节点的 3D 坐标

TeX-SX 启动板项目中有更多示例。

相关内容