介绍

介绍

我一直在尝试构建一个宏\parabola{...}来使用 TikZ 绘制一条经过 3 个坐标的抛物线,但没有成功。

例如

\parabola{A}{B}{C}

A将绘制插入 (x,y) 坐标 ( )、( B) 和 ( )的抛物线C。我还想指定曲线的样式、绘图域等。

我发现的主要问题是我无法弄清楚如何以无量纲形式(给定某个单位)获取给定坐标的值。

答案1

介绍

这是一个老问题,但之前的所有答案都有局限性:主要局限性是所有使用plot. 和plot命令都会产生多个三次曲线。但要绘制抛物线,一条二次(三次)曲线就足够了。

一些解释

任何抛物线都可以用二次贝塞尔曲线绘制,因此也可以用三次贝塞尔曲线绘制。
(当且仅当 ,具有控制点的三次曲线A,B,C,D绘制二次曲线AD=3BC。)

在此处输入图片描述

t(1-t)上的“标准”抛物线[0,1]可以用 来绘制\draw (0,0) .. controls (1/3,1/3) and (2/3,1/3) .. (1,0);

在此处输入图片描述

两点之间的每条抛物线都可以通过从这个“标准”抛物线进行仿射变换获得。利用这一点,我们可以定义一种parabola through使用单个贝塞尔曲线绘制所需抛物线的样式。此样式可以与to或一起使用edge,方式如下(A) to[parabola through={(B)}] (C)

代码

的定义parabola through是:

\makeatletter
\def\pt@get#1#2{
  \tikz@scan@one@point\pgfutil@firstofone#2\relax%
  \csname pgf@x#1\endcsname=\pgf@x%
  \csname pgf@y#1\endcsname=\pgf@y%
}
\tikzset{
  parabola through/.style={
    to path={{[x={(\pgf@xc,\pgf@yc)}, y=\parabola@y, shift=(\tikztostart)]
      -- (0,0) .. controls (1/3,1/3) and (2/3,1/3) .. (1,0) \tikztonodes}--(\tikztotarget)}
  },
  parabola through/.prefix code={
    \pt@get{a}{(\tikztostart)}\pt@get{b}{#1}\pt@get{c}{(\tikztotarget)}%
    \advance\pgf@xb by-\pgf@xa\advance\pgf@yb by-\pgf@ya%
    \advance\pgf@xc by-\pgf@xa\advance\pgf@yc by-\pgf@ya%
    \pgfmathsetmacro\parabola@y{(\pgf@yc-\pgf@xc/\pgf@xb*\pgf@yb)%
      /(\pgf@xb-\pgf@xc)*\pgf@xc}%
  }
}
\makeatother

注意:我们可以通过使用库来避免\makeatletter/\makeatother和 all @s 。letcalc

我们可以用(A) to[parabola through={(B)}] (C)

  • 在抛物线存在的每一个情况下,当三个 x 坐标不同时,
  • 该点B可以位于绘制区域之外,
  • 这可以是具有位于其上的节点的通用路径的一部分。

示例 1:

\tikz\draw[help lines] (0,0) grid (4,3)
  (0,0) edge[parabola through={(3,2)},
    red,thick,fill=blue,fill opacity=.21] (4,1);

在此处输入图片描述

示例 2(完整 MWE):

\documentclass[tikz,border=7pt]{standalone}
\makeatletter
\def\pt@get#1#2{
  \tikz@scan@one@point\pgfutil@firstofone#2\relax%
  \csname pgf@x#1\endcsname=\pgf@x%
  \csname pgf@y#1\endcsname=\pgf@y%
}
\tikzset{
  parabola through/.style={
    to path={{[x={(\pgf@xc,\pgf@yc)}, y=\parabola@y, shift=(\tikztostart)]
      -- (0,0) .. controls (1/3,1/3) and (2/3,1/3) .. (1,0) \tikztonodes}--(\tikztotarget)}
  },
  parabola through/.prefix code={
    \pt@get{a}{(\tikztostart)}\pt@get{b}{#1}\pt@get{c}{(\tikztotarget)}%
    \advance\pgf@xb by-\pgf@xa\advance\pgf@yb by-\pgf@ya%
    \advance\pgf@xc by-\pgf@xa\advance\pgf@yc by-\pgf@ya%
    \pgfmathsetmacro\parabola@y{(\pgf@yc-\pgf@xc/\pgf@xb*\pgf@yb)%
      /(\pgf@xb-\pgf@xc)*\pgf@xc}%
  }
}
\makeatother
\begin{document}
  \begin{tikzpicture}
    \draw[help lines] (-1,-1) grid (3,3);
    % variations of the point "through"
    \foreach \y in {-1,-.9,...,1}
      \draw[green] (-1,1) node[black]{.}
        to[parabola through={(0,\y)}] node[black]{.}
          node[black,at end]{.} (1,.5);
    % variations of a boundary point
    \foreach \y in {1.5,1.7,...,3}
      \draw[purple] (-1,2) node[black]{.}
        to[parabola through={(0,2)}] node[black]{.}
          node[black,at end]{.} (1,\y);
    % variations of a point "trough" outside the drawn part
    \foreach \y in {-1,-0.5,...,3}{
      \draw[red,thick] (.5,1) node[black]{.}
        to[parabola through={(3,\y)}] node[black]{.}
          node[black,at end]{.} (2,1);
      \draw[dashed,blue] (.5,1) node[black]{.}
        to[parabola through={(2,1)}] node[black]{.}
          node[black,at end]{.} (3,\y);
    }
  \end{tikzpicture}
\end{document}

在此处输入图片描述

与内置抛物线操作相比

TikZ 提供了parabola路径操作。但是设计得不是很好:

  • 应该在0和1之间(0,0) parabola (1,1)画一条抛物线t^2。它画了一条接近这条抛物线的三次曲线,但并不完全相同,实际上它画的是(0,0) .. controls (.5,0) and (0.8875,0.775) .. (1,1),但确切的曲线是(0,0) .. controls (1/3,0) and (2/3,1/3) .. (1,1) (不清楚为什么不使用这条曲线)
  • 与选项一起使用时bend,它使用两个三次曲线来近似抛物线,但只需一条就足以绘制精确的抛物线,
  • 与选项一起使用时bend=<point>,如果点选择不好,曲线就不是抛物线。

有一种情况,当弯曲点(极值点)位于起点或终点时,原始抛物线使用起来更简单(即使没有画出一条完整的抛物线):(0,0) parabola (2,4)比更简单(0,0) to[parabola through={(1,1)}] (2,4)

答案2

1)fp 的变体:

\documentclass{article}
\usepackage{tikz,fp}
\FPmessagesfalse
\FPdebugfalse

\makeatletter  
\tikzset{%
  parabola through/.style={
    to path={%
      \pgfextra{%
        \tikz@scan@one@point\pgfutil@firstofone(\tikztostart)\relax 
        \FPeval\xa{\pgf@sys@tonumber{\pgf@x}/28.45274}   
        \FPeval\ya{\pgf@sys@tonumber{\pgf@y}/28.45274}   
        \tikz@scan@one@point\pgfutil@firstofone#1\relax
        \FPeval\xb{\pgf@sys@tonumber{\pgf@x}/28.45274}   
        \FPeval\yb{\pgf@sys@tonumber{\pgf@y}/28.45274}   
        \tikz@scan@one@point\pgfutil@firstofone(\tikztotarget)\relax
        \FPeval\xc{\pgf@sys@tonumber{\pgf@x}/28.45274}   
        \FPeval\yc{\pgf@sys@tonumber{\pgf@y}/28.45274}   
        \FPeval\pb@a{(\ya*(\xb-\xc)+\yb*(\xc-\xa)+\yc*(\xa-\xb))/%
        ((\xa-\xb)*(\xa-\xc)*(\xb-\xc))}
        \FPeval\pb@b{(\ya*(\xc+\xb)*(\xc-\xb)+\yb*(\xa+\xc)*(\xa-\xc)+\yc*(\xb+\xa)*(\xb-\xa))/((\xa-\xb)*(\xa-\xc)*(\xb-\xc))} 
        \FPeval\pb@c{(\ya*\xb*\xc*(\xb-\xc)+\yb*\xa*\xc*(\xc-\xa)+\yc*\xa*\xb*(\xa-\xb))/((\xa-\xb)*(\xa-\xc)*(\xb-\xc))} 

    \draw plot[domain=\xa:\xc]  (\x,{\pb@a*(\x*\x)+\pb@b*\x+\pb@c}) ;
    }(\tikztotarget)
    }
  }
}
\makeatother
\begin{document}

\begin{tikzpicture}
\draw [help lines] (-3,-1) grid (7,4);  
\draw (-3,0) to[parabola through={(-2,2)}]%
  (0,-1) to[parabola through={(2,4)}] (4,0) to[parabola through={(5,3)}] (7,0);     
\end{tikzpicture}

\end{document} 

2)来自 maeshtro 的 gnuplot 答案

\documentclass{article}
\usepackage{tikz}
\makeatletter
\tikzset{%
  parabola through/.style={
    to path={%
      \pgfextra{%
        \tikz@scan@one@point\pgfutil@firstofone(\tikztostart)\relax 
        \edef\xa{\pgf@sys@tonumber{\pgf@x}}   
        \edef\ya{\pgf@sys@tonumber{\pgf@y}}   
        \tikz@scan@one@point\pgfutil@firstofone#1\relax
        \edef\xb{\pgf@sys@tonumber{\pgf@x}}   
        \edef\yb{\pgf@sys@tonumber{\pgf@y}}   
        \tikz@scan@one@point\pgfutil@firstofone(\tikztotarget)\relax
        \edef\xc{\pgf@sys@tonumber{\pgf@x}}   
        \edef\yc{\pgf@sys@tonumber{\pgf@y}}   

\draw plot[domain=\xa/28.45274:\xc/28.45274] function{
  \ya/28.45274*((x*28.45274-\xb)*(x*28.45274-\xc))/((\xa-\xb)*(\xa-\xc))+
  \yb/28.45274*((x*28.45274-\xa)*(x*28.45274-\xc))/((\xb-\xa)*(\xb-\xc))+
  \yc/28.45274*((x*28.45274-\xa)*(x*28.45274-\xb))/((\xc-\xa)*(\xc-\xb))
};
    }(\tikztotarget)
    }
  }
}
\makeatother
\begin{document}


\begin{tikzpicture}
    \draw [help lines] (-3,-1) grid (7,4);  
  \draw (-3,0) to[parabola through={(-2,2)}] (0,-1) to[parabola through={(2,4)}] (4,0) to[parabola through={(5,3)}] (7,0);     
\end{tikzpicture}

\end{document} 

在此处输入图片描述

答案3

这是一个解决方案。它本质上解决了从二次插值获得的线性方程。但需要注意的是,它远非完美。特别是由于 tikz 计算限制,对可能的点有很强的限制:计算中的数字必须足够小。这显然不是 tikz 的用途。使用其他方法获取系数会更好(sagetex 或 asymptote 或其他)。

let至少它是在 tikz 路径中使用的一个好例子。

我希望计算足够清楚。系数 A、B 和 C 是二次多项式 Ax^2 + Bx + C 的系数。

必须按照 x 坐标的升序输入点,以确保代码正常工作。

代码如下

\documentclass{article}

\usepackage{tikz}
\usetikzlibrary{calc}

\begin{document}

\begin{tikzpicture}
\coordinate (1) at (0.1,0.2);
\coordinate (2) at (0.2,0.7);
\coordinate (3) at (0.4,-0.3);

\draw let \p1 = (1),
          \p2 = (2),
          \p3 = (3),
          \n{denom} = {(\x1 - \x2)*(\x1 - \x3)*(\x2-\x3)},
          \n{A} = {(\x3*(\y2-\y1) + \x2*(\y1-\y3) + \x1*(\y3-\y2))/\n{denom}},
          \n{B} = {(\x3*\x3*(\y1-\y2) + \x2*\x2*(\y3-\y1)+\x1*\x1*(\y2-\y3))/\n{denom}},
          \n{C} = {(\x2*\x3*(\x2-\x3)*\y1 + \x3*\x1*(\x3-\x1)*\y2 + \x1*\x2*(\x1-\x2)*\y3)/\n{denom}} in
          plot[domain=\x1:\x3] (\x,{\n{A}*\x*\x+\n{B}*\x + \n{C}});

\end{tikzpicture}

\end{document}

答案4

你可以依赖 来\pgfmathparse始终返回 中的任何给定长度pt。看一下输出:

\pgfmathparse{12cm+1pt}
\pgfmathresult

\pgfmathparse{1pt}
\pgfmathresult

输出结果为:

342.43306
1.0

有了它,你几乎可以通过转移到(现在无量纲的)单位来完成所有的计算pt。然而,你应该对非常大的数字保持警惕。

为了避免这种情况,您可以依赖fpu单位pgf

% Preamble
\usepgflibrary{fpu}

\begin{document}

\pgfkeys{/pgf/fpu,/pgf/fpu/output format=fixed}
\pgfmathparse{12cm+1pt}
\pgfmathresult

\pgfmathparse{1pt}
\pgfmathresult

342.43306000000000
1.0000000000

然后您就可以访问始终位于一个特定单元中的数字!

相关内容