使用 tikz 以指定角度绘制曲线的切线

使用 tikz 以指定角度绘制曲线的切线

我想画一条曲线的切线,我知道有一些优雅的解决方案可以做到这一点(例如。如何在 TikZ 中绘制路径上任意点的切线)然而,这些方法通常将切线绘制在由其指定的点处相对位置在曲线上。相反,我想要做的是给出一个指定的角度并让 tikz 找到位置本身。(我知道可能会有多个,但就我的目的而言,我没有这个问题。)

以下是 MWE:

\documentclass[tikz]{standalone}
\usepackage{tikz}

\begin{document}
\begin{tikzpicture}
    \draw (0,6) -- (0,0) -- (9,0);
    \draw[blue] (0,5) to[out=-5,in=100] (8,0);
    \begin{scope}[shift={(5.4,3.6)}]
        \draw[red,rotate=-30] (-2,0) -- (2,0);
        \draw (0,0) circle (3pt);
    \end{scope}
\end{tikzpicture}
\end{document}

本质上,我想要的是如何自动搜索“shift”参数,我在这里只是通过反复试验来判断。

答案1

bezier bounding boxTikZ 库可以bbox执行这些类型的计算,但它并不用于此目的,因此需要一些技巧来提取坐标。

\documentclass[tikz, border=1cm]{standalone}
\usetikzlibrary{bbox, intersections}
\begin{document}
\begin{tikzpicture}
\newcommand{\mya}{-30}
\draw (0,6) -- (0,0) -- (9,0);
\draw[blue] (0,5) to[out=-5, in=100] (8,0);
\begin{scope}[local bounding box=lbb, bezier bounding box]
\path[name path=curve, rotate=-\mya] (0,5) to[out=-5, in=100] (8,0);
\path[name path=max] ([yshift=-0.001pt]lbb.north west) -- ([yshift=-0.001pt]lbb.north east);
\path[name intersections={of=max and curve}] (intersection-1) coordinate(c);
\end{scope}
\begin{scope}[transform canvas={rotate=\mya}]
\draw[red] (c) +(-2,0) -- +(2,0);
\draw (c) circle[radius=3pt];
\end{scope}
\end{tikzpicture}
\end{document}

有切点和线的曲线

答案2

在此处输入图片描述

我提出了一种使用装饰和pic元素即切线的解决方案。

  1. 装饰phSTrajectory采用一个参数(点的数量)并P_i沿曲线和U_i切线单位向量的尖端创建等距的点。

  2. pic 元素tangent接受两个参数,即我们要寻找的切线角度(它一定存在) 和点数,并绘制切线(近似值)。

图中,红色部分是您绘制的 -30 度切线,黑色部分是对应的图片。我用粗红色添加了斜率为 -65 的切线。

评论我标记了相应的点(实心圆圈)以便进行目视验证。

代码

\documentclass[11pt, margin=.5cm]{standalone}
\usepackage{tikz}
\usetikzlibrary{math, calc, decorations.markings}

\begin{document}
\tikzset{%
  phSTrajectory/.style={% nb of steps
    decoration={%
      markings,
      mark=between positions 0 and 1 step 1/#1 with {
        \tikzmath{%
          {
            \path (0, 0) coordinate[name=P_\pgfkeysvalueof{%
              /pgf/decoration/mark info/sequence number}];
            \path (1, 0) coordinate[name=tipU_\pgfkeysvalueof{%
              /pgf/decoration/mark info/sequence number}];
          };
        }
      }
    },
    postaction=decorate
  },
  pics/tangent/.style 2 args={% angle / numbrer of points
    code={
      \tikzmath{%
        integer \j;
        real \a;
        \j = -1;
        coordinate \M;
        for \i in {1, ..., #2}{%
          \M = ($(tipU_\i) -(P_\i)$);
          \a = {atan2(\My, \Mx)};
          if \a<-#1 then {%
            if \j==-1 then {%
              \j = \i -1;
              {
                \draw ($(P_\j)!-1!(tipU_\j)$) -- ($(P_\j)!1!(tipU_\j)$);
                \filldraw[blue] (P_\j) circle (1pt);
              };
            } else {};
          } else {};
        };
      }
    }
  }
}
\begin{tikzpicture}[evaluate={\N = int(220);}]
  \draw (0, 6) -- (0, 0) -- (9, 0);
  \draw[blue, phSTrajectory={\N}] (0, 5) to[out=-5, in=100] (8, 0);
  \path pic {tangent={30}{\N}};
  \path pic[red, very thick] {tangent={65}{\N}};

  \begin{scope}[shift={(5.4, 3.6)}]
    \draw[red, rotate=-30] (-2, 0) -- (2, 0);
    \draw (0, 0) circle (3pt);
  \end{scope}
\end{tikzpicture}
\end{document}

答案3

你可以强制曲线T通过已知角度的切点。像这样:

\documentclass[tikz]{standalone}

\begin{document}
\begin{tikzpicture}
  \coordinate (T) at (5.4,3.6);
  \draw (0,6) -- (0,0) -- (9,0);
  \draw[blue] (0,5) to[out=-5,in=150] (T) to [out=-30,in=100] (8,0);
  \draw[red] (T) ++ (150:3) --++ (-30:6);
  \draw (T) circle (3pt);
\end{tikzpicture}
\end{document}

在此处输入图片描述

答案4

正如我的评论所建议的,这里有一个数学解决方案,其工作原理如下\pgfpointcurveattime\pgftransformcurveattime但与…atangle

对于 TikZ,我使用pos angle = <angle>它来将曲线计时器切换为曲线角度计时器。

这个答案的核心是\pgfpointcurveatangle@哪个会“返回”时间然后s\@time会将其转发…curveatangle…curveattimes。(从技术上讲,这确实做了两次工作,因为 s…curveattime还返回了与 一起使用的角度sloped。)

我使用的是 PGFMath,\fpeval因为我遇到了基本的 PGFmath 问题。
这可能可以转换以便使用fpu库,但我对它没有太多经验。


与除显式贝塞尔曲线之外的任何其他路径一起使用pos angle都不会产生任何效果。“显式”表示它仅适用于.. controls或各种out/in选项。
虽然arcs 在最低级别上被绘制为贝塞尔曲线,但它们使用不同的计时器并且不会处理角度计时器。

最后,我添加了一个节点来显示 PGF 认为的切线角度,不出所料,它有点偏离。


要使用它,只需沿曲线放置一个坐标、节点或图片,然后使用 而不是pos(或其隐式版本,如midwaypos angle。如果您希望节点或图片也有角度,请使用slopedallow upside down

在您的例子中,我添加了一个名为的图片cs,其中只放置了三个名为 和 的坐标<name><name>-x其中<name>-y<name>图片的名称)。pic cs然后可以使用样式来移动和旋转坐标系,就像您在图片中绘图一样。

然后你就可以像这样绘制切线:

\draw[blue] (0,5) to[out=-5,in=100]
  pic[pos angle=-30, sloped] (a) {cs} (8,0);
\draw[pic cs=a, red] (-2,0) -- (2,0);
\draw (a) circle[radius=3pt];

不要忘记, 否则allow upside down你的坐标系可能会被旋转。

pics/cs/.prefix style={/tikz/allow upside down, /tikz/sloped}

您可以使每张cs图片自动倾斜和颠倒。

代码

\documentclass[tikz,convert]{standalone}
\makeatletter
%% Utils
\def\pgfextractxy#1{\pgf@process{#1}\pgfgetlastxy}
\newcommand*\fpUnlessIf[1]{
  \edef\pgfmathresult{\fpeval{#1}}%
  \ifnum\pgfmathresult=1 \expandafter\@gobble
                    \else\expandafter\@firstofone\fi}
\newcommand*\fpsetmacro[2]{\edef#1{\fpeval{#2}}\typeout{#1 = #2}}
%% PGF
\def\pgfpointcurveatangle@#1#2#3#4#5{%
  \def\@time{1}\fpsetmacro\@tan{tand(#1)}%
  \pgfextractxy{#2}\@Ax\@Ay \pgfextractxy{#3}\@Bx\@By
  \pgfextractxy{#4}\@Cx\@Cy \pgfextractxy{#5}\@Dx\@Dy
  \fpsetmacro\@divisor{
    -3*\@Bx*\@tan+3*\@Cx*\@tan-\@Dx*\@tan-\@Ax*\@tan -\@Ay+3*\@By-3*\@Cy+\@Dy}%
  \fpUnlessIf{\@divisor==0}{% divide by 0?
    \fpsetmacro\@divnosqrt{%% calculate dividend (no sqrt part)
      -2*\@Bx*\@tan+\@Cx*\@tan+\@Ax*\@tan-\@Ay+2*\@By-\@Cy}%
    \fpsetmacro\@divsqrt{%% calculate divident (sqrt part)
      (-4*\@Bx*\@tan+2*\@Cx*\@tan+2*\@Ax*\@tan -2*\@Ay+4*\@By-2*\@Cy)^2
      -4*(\@Bx*\@tan-\@Ax*\@tan+\@Ay-\@By)
      *(3*\@Bx*\@tan-3*\@Cx*\@tan+\@Dx*\@tan-\@Ax*\@tan+\@Ay-3*\@By+3*\@Cy-\@Dy)}%
    \fpUnlessIf{\@divsqrt<0}{% sqrt(neg)?
      \fpsetmacro\@time{(.5*sqrt(\@divsqrt)+\@divnosqrt)/\@divisor}%
      \fpUnlessIf{\@time>=0&&\@time<=1}{%
        \fpsetmacro\@time{(-.5*sqrt(\@divsqrt)+\@divnosqrt)/\@divisor}}%
    }}}
\def\pgfpointcurveatangle#1#2#3#4#5{%
  \pgfpointcurveatangle@{#1}{#2}{#3}{#4}{#5}%
  \pgfpointcurveattime{\@time}{#2}{#3}{#4}{#5}}
\def\pgftransformcurveatangle#1#2#3#4#5{%
  \pgfpointcurveatangle@{#1}{#2}{#3}{#4}{#5}%
  \pgftransformcurveattime{\@time}{#2}{#3}{#4}{#5}}
% TikZ
\newif\iftikz@angletimer
\def\tikz@timer@curve{%
  \iftikz@angletimer
    \pgftransformcurveatangle{\tikz@time@angle}{\tikz@timer@start}
     {\tikz@timer@cont@one}{\tikz@timer@cont@two}{\tikz@timer@end}%
  \else
    \pgftransformcurveattime{\tikz@time}{\tikz@timer@start}
      {\tikz@timer@cont@one}{\tikz@timer@cont@two}{\tikz@timer@end}%
  \fi}%
\tikzset{
  pos angle/.code=%
    \edef\tikz@time@angle{#1}%
    \ifx\tikz@time@angle\pgfutil@empty\else
      \pgfmathsetmacro\tikz@time@angle{\tikz@time@angle}\fi
    \tikz@angletimertrue,
  pos/.append code=\tikz@angletimerfalse}
\makeatother
\usetikzlibrary{calc}
\tikzset{
  cs/.pic={
   \coordinate (-x) at (1,0) coordinate (-y) at (0,1) coordinate () at (0,0);},
  pic cs/.style={shift={(#1)}, x={($(#1-x)-(#1)$)}, y={($(#1-y)-(#1)$)}}}
\begin{document}
\begin{tikzpicture}
    \draw (0,6) -- (0,0) -- (9,0);
    \draw[blue] (0,5) to[out=-5,in=100]
      pic[pos angle=-30, sloped] (a) {cs} (8,0);
    \draw[pic cs=a, red] (-2,0) -- (2,0);
    \draw (a) circle[radius=3pt];
    \node[below left] at (a) {
          \pgfmathanglebetweenpoints{\pgfpointanchor{a}{center}}
                                    {\pgfpointanchor{a-x}{center}}
          \pgfmathresult};
\end{tikzpicture}
\end{document}

输出

在此处输入图片描述

相关内容