与曲线相切的线(使用绘图或控件),从某点开始

与曲线相切的线(使用绘图或控件),从某点开始

我需要从某个点开始绘制与曲线平行的线。

曲线可以是使用 3 点绘制的曲线,如下面的示例所示,但也可以是一个使用命令绘制的曲线\draw () .. controls () and () .. ()。最好两种方式都可以。

这是我想要的图片:

在此处输入图片描述

解释

我有两条曲线,u_0 和 u_1,以及一个点 I_0。我想找到从该点 (I_0) 出发并与第一条曲线 (u_0) 相切的直线,以及从 (I_0) 出发并与第二条曲线 (u_1) 相切的直线。

然后,我想找到一条与(u_1)切线平行的线,并且这条线需要与(u_0)切线。如果线可以在不使用缩短命令的情况下在轴处开始和停止,那就更好了

目前,我通过反复试验找到了切线和平行线的起点和终点,但我有几十张这样的图要画,我需要一种更好的方法来做到这一点。

这是我的代码:

\documentclass{standalone}
\usepackage{tikz,pgfplots}
\usetikzlibrary{calc}

\begin{document}

\begin{tikzpicture}
    % axes
    \coordinate (origin) at (0,0);
    \draw (origin) -- (0,6.5) node[left]{$x_{2}$};
    \draw (origin) -- (9.5,0) node[below]{$x_{1}$};
    % u_0
    \node (e) at (2.2,4.9) {};
    \node (f) at (3,3) {};
    \node (g) at (5,2.3) {};
    \draw[thick] plot[smooth,tension=0.9] coordinates {(e) (f) (g)} node[right]{$u_{0}$};
    % u_1
    \node (h) at (1.5,4.2) {};
    \node (b) at (2.4,2.3) {};
    \node (j) at (4.4,1.6) {};
    \draw[thick] plot[smooth,tension=0.9] coordinates {(h) (b) (j)} node[right]{$u_{1}$};
    % first budget line (I_0 to p_0)
    \coordinate[label=left:{\scriptsize$I_{0}$}] (i0) at (0,4.65);
    \draw (i0) -- (8.1,0); 
    % second budget line (I_0 to p_1)
    \coordinate (d) at (4.67,0) {};
    \draw (i0) -- (d);
    % dashed line
    \draw[densely dashed,style={shorten >=2.8cm,shorten <=-4.64cm}] (2.68,3.3)   -- +($(i0)-(d)$);
\end{tikzpicture}
\end{document}

答案1

我认为这里可以利用一个装饰的想法。如手册中所示,装饰声明有一个特别有用的寄存器,它将装饰角度定义为特定装饰段的切线。所以我尝试了这个想法,结果如下:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{decorations}
\newcounter{loopcoun}
\setcounter{loopcoun}{0}
\makeatletter
\pgfdeclaredecoration{findtan}{initial}{
\state{initial}[width=1mm,next state=findit]{
}
\state{findit}[width=1mm]{
\ifnum\value{loopcoun}<1
        \pgfpointanchor{i0}{center}
        \pgf@xa = \pgf@x
        \pgf@ya = \pgf@y
        \pgfmathparse{ifthenelse(atan2(\pgf@xa,\pgf@ya)<\pgfdecoratedangle,1,}
        \ifx\pgfmathresult\@empty\relax
        \pgfmoveto{\pgfpointorigin}
        \pgflineto{\pgfpointanchor{i0}{center}}
        \setcounter{loopcoun}{1}
        \else
        \fi
\else\fi
\pgfusepath{stroke}
    }
\state{final}[1mm]{
\setcounter{loopcoun}{0}
}
}
\makeatother
\begin{document}
\begin{tikzpicture}
\draw[style=help lines] (0,0) grid[step=1cm] (8cm,5cm);
\node[fill,inner sep=1pt,circle] (i0) at (0,3) {};

\path[draw,decoration=findtan,postaction={decorate}] (2,3) .. controls (0,1) and (4,0) .. (5,1);
\path[red,draw,decoration=findtan,postaction={decorate}] (3,4) .. controls (2,1) and (5,2) .. (6,1);
\end{tikzpicture}
\end{document}

在此处输入图片描述

基本思想是 TikZ 开始在曲线上一点一点地行进(由width声明的选项给出\state)并执行由该特定状态的命令定义的一些事情。基本上,这定义了近似分辨率(如有必要,请降低!)。在这里,我计算切线的角度并将其与给定的点进行比较(i0)。如果它满足约束,我将绘制一条到该点的线并增加计数器,使得代码只执行一次。所以这是概念的证明。如果不增加计数器,它将提供所有满足条件的点。请注意,我们实际上在利用一些根本不应该这样表现的东西,所以会出现各种奇怪的结果。如果没有这样的点,至少它不会画任何东西,请摆弄一下位置(i0)看看是否有任何错误。

我写了一个稍微有点乱的代码,它似乎可以满足你的要求。这个想法是,你提供一些初始点,然后绘制指示特定切点的曲线。然后,如果有的话,它们将被收集为通用名称下的节点(c-#)。为此,我使用一些几何图形进行了一些繁琐的计算并绘制了线条。显然还有很多需要改进的地方,希望它能有所帮助。

编辑:曲线应该从左上角开始到右下角,因为代码标记了第一个合格点。可能存在许多缺点中的第一个……

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{decorations,calc}
\newcounter{loopcoun}
\newcounter{cocounter}
\setcounter{cocounter}{0}
\setcounter{loopcoun}{0}
\makeatletter

\pgfkeys{/tikz/find tangent/.style={decoration=findtan,depoint=#1,postaction=decorate}}
\pgfkeys{/tikz/depoint/.code= \edef\pgf@pointname{#1}}

\pgfdeclaredecoration{findtan}{initial}{
\state{initial}[width=1mm,next state=findit]{
}
\state{findit}[width=1mm]{
\ifnum\value{loopcoun}<1
        \pgfpointanchor{\pgf@pointname}{center}
        \pgf@xa = \pgf@x
        \pgf@ya = \pgf@y
        \pgfmathparse{ifthenelse(atan2(\pgf@xa,\pgf@ya)>\pgfdecoratedangle,1,0)}
        \ifnum\pgfmathresult>0
        \stepcounter{cocounter}{1}
        \pgfcoordinate{c-\the\value{cocounter}}{\pgfpointorigin}
        \setcounter{loopcoun}{1}
        \else
        \fi
\else\fi
\pgfusepath{stroke}
    }
\state{final}[1mm]{
\setcounter{loopcoun}{0}
}
}
\makeatother
\begin{document}
\begin{tikzpicture}
\draw[style=help lines] (0,0) grid[step=1cm] (9cm,5cm);
\node[fill,inner sep=1pt,circle] (p0) at (0,3) {};
\node[fill,inner sep=1pt,circle] (p1) at (0,4) {};

\draw[find tangent=p0] (2,3) .. controls (0,1) and (4,0) .. (5,1);
\path[find tangent=p0] (3,4) .. controls (2,1) and (5,2) .. (6,1);
\draw[red,find tangent=p1] (3,4) .. controls (2,1) and (5,2) .. (6,1);

\draw let \p1 = (p0), \p2 = (c-1),\n2 = {atan2(\x1-\x2,\y1-\y2)},\n3 = {-\y1/sin(\n2)} in (p0) -- ++(\n2:\n3); 
\draw let \p1 = (p0), \p2 = (c-2),\n2 = {atan2(\x1-\x2,\y1-\y2)},\n3 = {-\y1/sin(\n2)} in (p0) -- ++(\n2:\n3); 
\draw[dashed] let \p1 = (p1), \p2 = (c-3),\n2 = {atan2(\x1-\x2,\y1-\y2)},\n3 = {-\y1/sin(\n2)} in (p1) -- ++(\n2:\n3); 
\end{tikzpicture}
\end{document}

在此处输入图片描述

答案2

更新

用 Asymptote 可能可以,但用 TikZ 不行。你需要解一个方程来得到从I_0到 的第一个切线u_1,因为你不知道与曲线的交点在哪里。

我认为一种不用(或许有)曲线方程的好方法是使用视觉方式:

步骤1I_0我们尝试得到从到 的切线斜率的近似值u_1。该切线的方程为 y=px+4.65。p是斜率。首先,我们绘制一些具有不同 值的线p。一个好的区间是-1.2,-1.1,...,-0.8

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{calc,intersections}
\begin{document}
\begin{tikzpicture}
    % u_1
    \node (h) at (1.5,4.2) {};
    \node (b) at (2.4,2.3) {};
    \node (j) at (4.4,1.6) {};
    \draw[very thin,name path=curve 1] plot[smooth,tension=0.9] 
          coordinates {(h) (b) (j)} node[right]{$u_{1}$};
    \coordinate[label=left:{\scriptsize$I_{0}$}] (i0) at (0,4.65);
 \foreach \p in {-1.2,-1.1,...,-0.8}
 {\draw[blue,very thin] (0,4.65) -- (5,5*\p+4.65);}    
\end{tikzpicture}
\end{document}

在此处输入图片描述

现在我们知道斜率是 -1.0,...,-0.96

第2步 通过交集我们可以找到更好的值:

我命名了curve 2由y=-0.96*x+4.65'curve 3定义的直线y=-0.9828*x+4.65' and,并尝试找到曲线与曲线 1 的交点。我-0.9828通过试验找到了答案。例如,-0.99我得到了一个错误,因为曲线没有交点。

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{calc,intersections}

\begin{document}

\begin{tikzpicture}
    % u_1
    \node (h) at (1.5,4.2) {};
    \node (b) at (2.4,2.3) {};
    \node (j) at (4.4,1.6) {};
    \draw[very thin,name path=curve 1] plot[smooth,tension=0.9] coordinates {(h) (b) (j)} node[right]{$u_{1}$};
    \coordinate[label=left:{\scriptsize$I_{0}$}] (i0) at (0,4.65);

 \draw[green,very thin,name path=curve 3] (0,4.65) -- (5,-5*0.96+4.65);
 \fill [name intersections={of=curve 1 and curve 3, name=i, total=\t}]
         [orange, opacity=0.5, every node/.style={above left, black, opacity=1}]
        \foreach \s in {1,...,\t}{(i-\s) circle (2pt) node {}};  

 \draw[blue,very thin,name path=curve 2] (0,4.65) -- (5,-5*0.9828+4.65);
 \fill [name intersections={of=curve 1 and curve 2, name=i, total=\t}]
         [red, opacity=0.5, every node/.style={above left, black, opacity=1}]
        \foreach \s in {1,...,\t}{(i-\s) circle (2pt) node {}};            
\end{tikzpicture}
\end{document} 

在此处输入图片描述

p=-0.9828看起来价值不错。

步骤3

现在我们知道 的切线斜率u_0-0.9828。切线的方程为 y=-0.9828 *x +m

你可以用不同的 值绘制多条线m。一个好的区间似乎是 m 在 {5,5.5,...,7} 中

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{calc,intersections}
\begin{document}
\begin{tikzpicture}
    % u_0
    \node (e) at (2.2,4.9) {};
    \node (f) at (3,3) {};
    \node (g) at (5,2.3) {};
    \draw[thick,name path=curve 4] plot[smooth,tension=0.9] coordinates {(e) (f) (g)} node[right]{$u_{0}$};     
\foreach \m in {5,5.5,...,7}
 {\draw[red,very thin] (0,\m) -- (5,-0.9828*5+\m);} 
\end{tikzpicture}
\end{document}

在此处输入图片描述

步骤4 现在我们知道 m 非常接近 6.0。我添加了 \def\m{5.9}以轻松修改此值。我将曲线命名为 ,u_0并将curve 4线命名为curve 5。我查看交叉点。

我在这里只使用两个值m=6.2和“m=5.9410”

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{calc,intersections}

\begin{document}

\begin{tikzpicture}
    % u_0
    \node (e) at (2.2,4.9) {};
    \node (f) at (3,3) {};
    \node (g) at (5,2.3) {};
    \draw[thick,name path=curve 4] plot[smooth,tension=0.9] coordinates {(e) (f) (g)} node[right]{$u_{0}$};     

   \def\m{6.2}
  \draw[green,very thin,name path=curve 5] (0,\m) -- (5,-0.9828*5+\m);
 \fill [name intersections={of=curve 4 and curve 5, name=i, total=\t}]
         [orange, opacity=0.5, every node/.style={above left, black, opacity=1}]
        \foreach \s in {1,...,\t}{(i-\s) circle (2pt) node {}};   

  \def\m{5.9410}
  \draw[red,very thin,name path=curve 5] (0,\m) -- (5,-0.9828*5+\m);
 \fill [name intersections={of=curve 4 and curve 5, name=i, total=\t}]
         [red, opacity=0.5, every node/.style={above left, black, opacity=1}]
        \foreach \s in {1,...,\t}{(i-\s) circle (2pt) node {}};    

\end{tikzpicture}
\end{document}   

在此处输入图片描述

最后一步

最后我取 p=-0.992 和 m=5.941,结果是:

(我用夹子将线条限制到你想要的范围)

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{calc}

\begin{document}

\begin{tikzpicture}
    % axes
    \coordinate (origin) at (0,0);
    \draw (origin) -- (0,6.5) node[left]{$x_{2}$};
    \draw (origin) -- (9.5,0) node[below]{$x_{1}$};
    % u_0
    \node (e) at (2.2,4.9) {};
    \node (f) at (3,3) {};
    \node (g) at (5,2.3) {};
    \draw[thick] plot[smooth,tension=0.9] coordinates {(e) (f) (g)} node[right]{$u_{0}$};
    % u_1
    \node (h) at (1.5,4.2) {};
    \node (b) at (2.4,2.3) {};
    \node (j) at (4.4,1.6) {};
    \draw[thick] plot[smooth,tension=0.9] coordinates {(h) (b) (j)} node[right]{$u_{1}$};
    % first budget line (I_0 to p_0)
    \coordinate[label=left:{\scriptsize$I_{0}$}] (i0) at (0,4.65);
    \clip (0,0) rectangle (8,7) ;
    \draw[blue,very thin] (0,4.65) -- (7,-7*0.9828+4.65); 
    % second budget line (I_0 to p_1)
    \draw[red,very thin] (0,5.9410) -- (7,-0.9828*7+5.9410);   
\end{tikzpicture}
\end{document}  

在此处输入图片描述

答案3

(重大更新)

这是绘制所需图表的解决方案。它应该用于演示目的(例如在课堂上展示这些想法)。结果是

在此处输入图片描述

我的代码有点繁重和不优雅。一些宏可以让它变得更好,但我专注于完成任务。代码是:

\documentclass[border=5pt]{standalone}

\usepackage{tikz}
\usetikzlibrary{calc}

\begin{document}
\begin{tikzpicture}[point/.style={fill=blue,circle,minimum size=2pt,inner sep=0pt}]

\pgfmathsetmacro{\k}{0.2}

\draw[->] (0,0) -- (5,0) node[right] {$x_1$};
\draw[->] (0,0) -- (0,5) node[left] {$x_2$};

\coordinate[point] (a) at (1,2);
\coordinate[point] (c) at (1.25,1.3);
\coordinate[point] (b) at (2,1);
\coordinate[point] (i) at (0,2.5);

\coordinate (v) at ($(c) - (i)$);

%This is a (quadratic) Bézier curve. You can use Tikz' Bézier curves and adjust the control points  to do the same.
\draw[thick,blue] let
            \p{a} = (a),
            \p{c} = (c),
            \p{b} = (b),
            \p{v1} = ($(c) - \k*(v)$),
            \p{v2} = ($(c) + \k*(v)$)
            in
            plot[domain=0:1,variable=\t,samples =20,smooth] ({(1-\t)^2*\x{a} + 2*(1-\t)*\t*\x{v1} + (\t)^2*\x{c}}, {(1-\t)^2*\y{a} + 2*(1-\t)*\t*\y{v1} + (\t)^2*\y{c}})
            plot[domain=0:1,variable=\t,samples =20,smooth] ({(1-\t)^2*\x{c} + 2*(1-\t)*\t*\x{v2} + (\t)^2*\x{b}}, {(1-\t)^2*\y{c} + 2*(1-\t)*\t*\y{v2} + (\t)^2*\y{b}});

\draw[red] let
            \p{i} = (i),
            \p{v} = (v)
            in
            (i) -- ++(${-\y{i}/\y{v}}*(v)$) coordinate[point];

\coordinate[point] (d) at (1.2,2.3);
\coordinate[point] (f) at (1.55,1.6);
\coordinate[point] (e) at (2.3,1.3);

\draw[thick,blue,dashed] let
            \p{d} = (d),
            \p{f} = (f),
            \p{e} = (e),
            \p{v1} = ($(f) - \k*(v)$),
            \p{v2} = ($(f) + \k*(v)$)
            in
            plot[domain=0:1,variable=\t,samples =20,smooth] ({(1-\t)^2*\x{d} + 2*(1-\t)*\t*\x{v1} + (\t)^2*\x{f}}, {(1-\t)^2*\y{d} + 2*(1-\t)*\t*\y{v1} + (\t)^2*\y{f}})
            plot[domain=0:1,variable=\t,samples =20,smooth] ({(1-\t)^2*\x{f} + 2*(1-\t)*\t*\x{v2} + (\t)^2*\x{e}}, {(1-\t)^2*\y{f} + 2*(1-\t)*\t*\y{v2} + (\t)^2*\y{e}});

\draw[dashed,red] let
            \p{f} = (f),
            \p{v} = (v)
            in
            ($(f) + {-\x{f}/\x{v}}*(v)$) coordinate[point]  -- ($(f) + {-\y{f}/\y{v}}*(v)$) coordinate[point];

\draw[green] let
            \p{i} = (i),
            \p{f} = (f),
            \p{w} = ($(f) - (i)$)
            in
            (i) -- ++(${-\y{i}/\y{w}}*(\p{w})$) coordinate[point];


\end{tikzpicture}
\end{document}

(初始答案)

正如我在评论中提到的,tkz-fct 包可以计算切线,您可以查看文档,那里有很好的例子。它使用 Gnuplot。由于我没有 Gnuplot,所以我无法为您构建示例。

接下来是另一种方法,使用intersections库。

\documentclass[border=5pt]{standalone}

\usepackage{tikz,pgfplots}
\usetikzlibrary{calc}
\usetikzlibrary{intersections}

\begin{document}

\begin{tikzpicture}

\coordinate (a) at (0,0);
\coordinate (b) at (2,2);
\coordinate (c) at (4,1);

\draw[name path=curve,smooth] plot coordinates {(a) (b) (c)};
\path[name path= circle] (b) circle[radius=1pt];

\draw[red, name intersections={of=curve and circle},shorten <=-2cm,shorten >=-2cm]
    (intersection-1) -- (intersection-2);

\end{tikzpicture}

\end{document}

结果是

在此处输入图片描述

设置较小的圆半径将得到更精确的结果。

如您所见,我的解决方案使用了shorten。解决此问题的一种可能方法是再次使用交点来查找点在轴线上的位置。

答案4

我尝试使用tzplot包裹:

在此处输入图片描述

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

\begin{document}

\begin{tikzpicture}
\tzhelplines[step=.5](10,7)
\tzaxes*(9.5,7){$x_1$}[b]{$x_2$}[l]
% budget lines
\tzcoors(0,4.65)(A)(4.67,0)(B)(8.1,0)(C);
\tzline[black]"bgtA"(A)(B)
\tzline[blue]"bgtB"(A)(C)
% getting ready for curves
\tzanglemark[draw=none](A)(B)(C)
\def\sA{\tzangleresult}
\tzanglemark[draw=none](A)(C)($(C)+(1,0)$)
\def\sB{\tzangleresult}
% u_0
\tzvXpointat*{bgtA}{2.5}(X)
\tzcoors($(X)!2cm!-20:(A)$)(XL)
        ($(X)!2cm! 20:(B)$)(XR);
\tzplotcurve[thick](XL)(X)(XR){$u_0$}[-90];
% u_1
\tzvXpointat*{bgtB}{3.5}(Y)
\tzcoors($(Y)!2cm!-20:(A)$)(YL)
        ($(Y)!2cm! 15:(C)$)(YR);
\tztos[thick]"U1"(YL)[out=-70,in=\sB](Y)[out=\sB-180,in=175](YR){$u_1$}[r];
% dashed line
\tzvXpointat*{U1}{2.8}(K)
\clip (0,0) rectangle (7,6);
\tzLFn[red,densely dashed](K){-4.65/4.67}[0:6]
\end{tikzpicture}

\end{document}

相关内容