TikZ 中的简单曲线

TikZ 中的简单曲线

我发现自己需要在 TikZ 中绘制许多优雅的曲线路径。理想情况下,我只需指定一系列点,TikZ 会自行计算额外数据,以绘制一系列平滑通过这些点的漂亮曲线,也许还可以指定一个可选的“松散度”参数。但我能找到的绘制漂亮曲线的唯一方法是明确给出控制点,或者手动指定入角和出角。

我可以想出一个简单的算法来做到这一点,这肯定在 TikZ 的能力范围内:只需根据每对相邻线段之间的相对角度以简单的方式选择入角和出角。

类似的东西已经内置了吗?或者有人能发明一些东西来完成这个工作吗?

编辑:Jake 使用该功能提供了答案plot [smooth]。这几乎是完美的!但它不能满足我的需要,因为它不允许我在需要时手动指定切线角度,这在曲线的起点和终点尤其重要。我本以为这会是对现有plot [smooth]算法的自然而直接的补充:对于每个坐标,应该能够将可选角度指定为参数,如果提供,则将其视为该点曲线的切线角度。而且,在我们这样做的同时,允许沿路径修改张力也不会有什么坏处。

该算法的最小扩展只接受两个可选参数,即起点和终点的曲线切线。

答案1

您可以使用\draw plot [smooth] coordinates {<coordinate1> <coordinate2> <coordinate3> ...};与您描述的算法类似的算法的语法。

松散程度由参数控制tension。如果要闭合线条,可以使用[smooth cycle]代替smooth

\documentclass{article}

\usepackage{tikz}

\begin{document}
\begin{tikzpicture}
\draw [gray!50]  (0,0) -- (1,1) -- (3,1) -- (1,0)  -- (2,-1) -- cycle;
\draw [red] plot [smooth cycle] coordinates {(0,0) (1,1) (3,1) (1,0) (2,-1)};

\draw [gray!50, xshift=4cm]  (0,0) -- (1,1) -- (2,-2) -- (3,0);
\draw [cyan, xshift=4cm] plot [smooth, tension=2] coordinates { (0,0) (1,1) (2,-2) (3,0)};
\end{tikzpicture}
\end{document}

平滑算法非常简单:它设置支撑点,使得每个角的切线与从上一个角到下一个角的线平行。支撑点到角的距离在任一方向上都是相同的,并且与从上一个角到下一个角的距离成比例。用作tension支撑点距离的乘数。它不能沿曲线改变,也不能指定线的起始和终止角度。该算法可以在中pgflibraryplothandlers.code.tex找到\pgfplothandlercurveto

\documentclass{article}

\usepackage{tikz}
\usetikzlibrary{decorations.pathreplacing,shapes.misc}

\begin{document}
\begin{tikzpicture}
\tikzset{
    show curve controls/.style={
        decoration={
            show path construction,
            curveto code={
                \draw [blue, dashed]
                    (\tikzinputsegmentfirst) -- (\tikzinputsegmentsupporta)
                    node [at end, cross out, draw, solid, red, inner sep=2pt]{};
                \draw [blue, dashed]
                    (\tikzinputsegmentsupportb) -- (\tikzinputsegmentlast)
                    node [at start, cross out, draw, solid, red, inner sep=2pt]{};
            }
        }, decorate
    }
}

\draw [gray!50]  (0,0) -- (1,1) -- (3,1) -- (1,0)  -- (2,-1) -- cycle;
\draw [show curve controls] plot [smooth cycle] coordinates {(0,0) (1,1) (3,1) (1,0) (2,-1)};
\draw [red] plot [smooth cycle] coordinates {(0,0) (1,1) (3,1) (1,0) (2,-1)};

\draw [gray!50, xshift=4cm]  (0,0) -- (1,1) -- (3,-1) -- (5,1) -- (7,-2);
\draw [cyan, xshift=4cm] plot [smooth, tension=2] coordinates { (0,0) (1,1) (3,-1) (5,1) (7,-2)};
\draw [show curve controls,cyan, xshift=4cm] plot [smooth, tension=2] coordinates { (0,0) (1,1) (3,-1) (5,1) (7,-2)};
\end{tikzpicture}
\end{document}

这里是 plothandler 的一个稍微修改过的版本,它允许你使用 TikZ 键first support={<point>}和指定第一个和最后一个支撑点last support={<point>},其中可以是<point>任何 TikZ 坐标表达式,例如(1,2),,,(感谢 Andrew Stacey 对(1cm,2pt)(A.south west)([xshift=1cm] A.south west)提取 TikZ 中任意点的 x,y 坐标)。

absolute first support默认情况下,这些点被认为是相对于路径的第一个/最后一个点的坐标。您可以使用、absolute last support或键指定支撑点为绝对坐标absolute supports

 \documentclass{article}

\usepackage{tikz}
\usetikzlibrary{decorations.pathreplacing,shapes.misc}

\begin{document}
\begin{tikzpicture}
\tikzset{
    show curve controls/.style={
        decoration={
            show path construction,
            curveto code={
                \draw [blue, dashed]
                    (\tikzinputsegmentfirst) -- (\tikzinputsegmentsupporta)
                    node [at end, cross out, draw, solid, red, inner sep=2pt]{};
                \draw [blue, dashed]
                    (\tikzinputsegmentsupportb) -- (\tikzinputsegmentlast)
                    node [at start, cross out, draw, solid, red, inner sep=2pt]{};
            }
        }, decorate
    }
}

\makeatletter
\newcommand{\gettikzxy}[3]{%
  \tikz@scan@one@point\pgfutil@firstofone#1\relax
  \edef#2{\the\pgf@x}%
  \edef#3{\the\pgf@y}%
}

\newif\iffirstsupportabsolute
\newif\iflastsupportabsolute

\tikzset{
    absolute first support/.is if=firstsupportabsolute,
    absolute first support=false,
    absolute last support/.is if=lastsupportabsolute,
    absolute last support=false,
    absolute supports/.style={
        absolute first support=#1,
        absolute last support=#1
    },
    first support/.code={
        \gettikzxy{#1}{\pgf@plot@firstsupportrelx}{\pgf@plot@firstsupportrely}
    },
    first support={(0pt,0pt)},
    last support/.code={
        \gettikzxy{#1}{\pgf@plot@lastsupportrelx}{\pgf@plot@lastsupportrely}
    },
    last support={(0pt,0pt)}
}

\def\pgf@plot@curveto@handler@initial#1{%
  \pgf@process{#1}%
  \pgf@xa=\pgf@x%
  \pgf@ya=\pgf@y%
  \pgf@plot@first@action{\pgfqpoint{\pgf@xa}{\pgf@ya}}%
  \xdef\pgf@plot@curveto@first{\noexpand\pgfqpoint{\the\pgf@xa}{\the\pgf@ya}}%
  \iffirstsupportabsolute
    \pgf@xa=\pgf@plot@firstsupportrelx%
    \pgf@ya=\pgf@plot@firstsupportrely%
  \else
    \advance\pgf@xa by\pgf@plot@firstsupportrelx%
    \advance\pgf@ya by\pgf@plot@firstsupportrely%
  \fi
  \xdef\pgf@plot@curveto@firstsupport{\noexpand\pgfqpoint{\the\pgf@xa}{\the\pgf@ya}}%
  \global\let\pgf@plot@curveto@first@support=\pgf@plot@curveto@firstsupport%
  \global\let\pgf@plotstreampoint=\pgf@plot@curveto@handler@second%
}

\def\pgf@plot@curveto@handler@finish{%
  \ifpgf@plot@started%
    \pgf@process{\pgf@plot@curveto@second}
    \pgf@xa=\pgf@x%
    \pgf@ya=\pgf@y%
    \iflastsupportabsolute
      \pgf@xa=\pgf@plot@lastsupportrelx%
      \pgf@ya=\pgf@plot@lastsupportrely%
    \else
      \advance\pgf@xa by\pgf@plot@lastsupportrelx%
      \advance\pgf@ya by\pgf@plot@lastsupportrely%
    \fi
    \pgfpathcurveto{\pgf@plot@curveto@first@support}{\pgfqpoint{\the\pgf@xa}{\the\pgf@ya}}{\pgf@plot@curveto@second}%
  \fi%
}
\makeatother

\coordinate (A) at (2,-1);

\draw [gray!50]  (-1,-0.5) -- (1.5,1) -- (3,0);
\draw [
    cyan,
    postaction=show curve controls
] plot [
    smooth, tension=2,
    absolute supports,
    first support={(A)},
    last support={(A)}] coordinates { (-1,-0.5) (1.5,1) (3,0)};

\draw [
    yshift=-3cm,
    magenta,
    postaction=show curve controls
] plot [
    smooth, tension=2,
    first support={(-0.5cm,1cm)},
    last support={(0.5cm,1cm)}] coordinates { (-1,-0.5) (1.5,1) (3,0)};

\end{tikzpicture}
\end{document}

答案2

另一个选项可能是to操作。通过这个你可以指定角度,但它可能不如plotJake 提出的使用解决方案那么自动化。

\documentclass{minimal}

\usepackage{tikz}

\begin{document}
\begin{tikzpicture}
    \draw [ultra thick,red] (-2,2) to[out=45,in=115] (1,1) to[out=-180+115,in=10] (-5,-3);
\end{tikzpicture}
\end{document}

结果

答案3

我很惊讶没有人使用“弯曲”选项。以下是代码:

\documentclass[12pt]{article}
\usepackage{amsmath}
\usepackage{tikz}


\begin{document}

\begin{tikzpicture}
  \coordinate (O) at (0,0,0);
  \coordinate (A) at (3,0,0);

  \draw[] (O)--(A);
  \draw[color=red] (O) to [bend left=10] (A);
  \draw[color=red] (O) to [bend right=10] (A);
  \draw[color=blue] (O) to [bend left=30] (A);
  \draw[color=blue] (O) to [bend right=30] (A);
  \draw[color=green] (O) to [bend left=50] (A);
  \draw[color=green] (O) to [bend right=50] (A);
  \draw[color=yellow] (O) to [bend left=70] (A);
  \draw[color=yellow] (O) to [bend right=70] (A);
  \draw[color=orange] (O) to [bend left=90] (A);
\end{tikzpicture}


 \end{document}

这里是图: 使用弯曲到关节点

我用“lualatex”运行了这个

这是一个非常有用的功能。很多时候我们需要连接两个点,但点之间没有简单的方程或弧函数可用。在这种情况下,这非常方便。例如,查看以下帖子

球面上的曲线三角形

下面图片中的红色和蓝色三角形是用“弯曲”函数伪造的。很难找到代表它们的解析方程。如果没有“弯曲”函数,您将获得类似于此帖子中的等效图: 球体上的平边三角形

虽然使用“弯曲”连接两条曲线确实不如样条曲线那么平滑(除非保留曲率),但当我们想要制作某些不需要平滑度的曲线时,这恰恰是一个优点。我能想到的最好的例子是球体中的半月形或球体中的三角形,如上面的链接所示。

H。

答案4

这是一个仅提供链接的答案,因为坦率地说,这个链接不应该像现在这样被埋在评论中。自从提出这个问题以来,后来的一个类似问题刺激了新包的创建。您可以指定点,也可以指定角度、张力变化等,然后该库使用 Hobby 算法计算出平滑的曲线。它比 Ti 做得更好Zsmooth比手动找出控制点等要容易得多,并且通常可以使一些相当棘手的事情变得容易处理。

示例和图书馆介绍hobby在以下回复中介绍使用 Metapost 和 TikZ 通过一系列点绘制曲线

相关内容