我想画一条曲线的切线,我知道有一些优雅的解决方案可以做到这一点(例如。如何在 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 box
TikZ 库可以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
元素即切线的解决方案。
装饰
phSTrajectory
采用一个参数(点的数量)并P_i
沿曲线和U_i
切线单位向量的尖端创建等距的点。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
给…curveattime
s。(从技术上讲,这确实做了两次工作,因为 s…curveattime
还返回了与 一起使用的角度sloped
。)
我使用的是 PGFMath,\fpeval
因为我遇到了基本的 PGFmath 问题。
这可能可以转换以便使用fpu
库,但我对它没有太多经验。
与除显式贝塞尔曲线之外的任何其他路径一起使用pos angle
都不会产生任何效果。“显式”表示它仅适用于.. controls
或各种out
/in
选项。
虽然arc
s 在最低级别上被绘制为贝塞尔曲线,但它们使用不同的计时器并且不会处理角度计时器。
最后,我添加了一个节点来显示 PGF 认为的切线角度,不出所料,它有点偏离。
要使用它,只需沿曲线放置一个坐标、节点或图片,然后使用 而不是pos
(或其隐式版本,如midway
)pos angle
。如果您希望节点或图片也有角度,请使用sloped
和allow 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}