可选功能

可选功能

有没有一种简单的方法使用 TikZ 将一条线与一个圆对齐,而无需手动指定一个圆弧来连接两条单独的线,如下例所示?

\documentclass{article}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}
    \filldraw[black] (0.5,0.5) circle (0.5);
    \draw[-,thick,red] (-1,1) -- (0.5,1);
    \draw[-,thick,red] (1,-1) -- (1,0.5);
    \draw[-,thick,red] (1,0.5) arc (0:90:0.5);
\end{tikzpicture}
\end{document}

示例图像

如果可能的话,解决方案应该是一个产生以下行为的单个命令:

  1. 画一个圆 C(由其圆心和半径指定)
  2. 画一条包含三部分的线:
    1. 第一部分是从任意点 P 到 C 周长上的点 Q 的线段。
    2. 第二部分是沿 C 的周长的 90 度角的圆弧。
    3. 第三部分是远离圆弧的线段。

伪代码

\documentclass{article}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}
    \filldraw[black] (0.5,0.5) coordinate (C) circle (0.5);
    \draw[-,thick,red] (-1,1) -- (0.5,1) arc (C,90) -- (1,0.5);
    % The line above would draw a line to (0.5,1), 
    % an arc of 90 degrees around (C) and continue 
    % from there towards (1,0.5)
\end{tikzpicture}
\end{document}

上述伪代码应产生与上述相同的图像。

可选功能

更方便的是允许任意角度,并在弧后以出口点的切线方向继续画线:

伪代码

\documentclass{article}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}
    \filldraw[black] (0.5,0.5) coordinate (C) circle (0.5);
    \draw[-,thick,red] (-1,1) -- (0.5,1) arc (C,45) -- (1cm);
    % The line above would draw a line to (0.5,1), 
    % an arc of 45 degrees around (C) and continue a 
    % line of 1cm length from there
\end{tikzpicture}
\end{document}

答案1

可以使用选项rounded corners来绘制弧线,而无需指定更复杂的语法arc

\documentclass{article}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}[x=1cm, y=1cm]
    \filldraw[black] (0.5,0.5) circle (0.5);
    \draw[-,thick,red]
      (-1,1) [rounded corners=0.5cm] -- (1,1)
      [sharp corners] -- (1,-1) -- (-1,-1)
    ;
\end{tikzpicture}
\end{document}

结果

  • 选项sharp corners可关闭角的圆化。

不同xy单位

  • 如果单位和方向不同,则选项rounded corners将不起作用,因为舍入保持对称。xy
  • 此外,在上面的例子中必须明确指定单位。

Optionrounded corners内部使用\pgfpoint,通过使用\pgfpointxy两个问题得到解决:

\documentclass{article}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}[x=2cm,y=1cm]
    \filldraw[black] (0.5,0.5) circle (0.5);
    \draw[-,thick,red]
      (-1,1) \pgfextra{\pgfsetcornersarced{\pgfpointxy{.5}{.5}}} -- (1,1)
      [sharp corners] -- (1,-1) -- (-1,-1)
    ;
\end{tikzpicture}
\end{document}

或者通过定义一个新选项来实现更好的可读性:

\documentclass{article}
\usepackage{tikz}
\tikzoption{circle corner}{\pgfsetcornersarced{\pgfpointxy{#1}{#1}}}
\begin{document}
\begin{tikzpicture}[x=2cm,y=1cm]
    \filldraw[black] (0.5,0.5) circle (0.5);
    \draw[-,thick,red]
      (-1,1) [circle corner=0.5] -- (1,1)
      [sharp corners] -- (1,-1) -- (-1,-1)
    ;
\end{tikzpicture}
\end{document}

结果

答案2

推荐使用 PSTricks 的解决方案。

静态版本

pst-eucl

\documentclass[pstricks,border=12pt]{standalone}
\usepackage{pst-eucl}
\psset{PointName=none,PointSymbol=none,linewidth=3\pslinewidth}
\begin{document}
\begin{pspicture}[showgrid](9,7)
    \pstGeonode(4,4){O}(1,2){A}(8,1){B}
    \pstCircleOA[Radius=\pstDistVal{2}]{O}{}
    \pstMiddleAB{A}{O}{A'}
    \pstInterCC[RadiusA=\pstDistVal{2},RadiusB=\pstDistAB{A'}{O}]{O}{}{A'}{}{M1}{M2}
    \pstMiddleAB{B}{O}{B'}
    \pstInterCC[RadiusA=\pstDistVal{2},RadiusB=\pstDistAB{B'}{O}]{O}{}{B'}{}{N1}{N2}    
    \psset{linecolor=red}
    \psline(A)(M2)
    \psline(B)(N1)
    \psarc[origin={O}](O){2}{(N1)}{(M2)}
\end{pspicture}
\end{document}

使用pstricks-add(较短):

\documentclass[pstricks,border=12pt]{standalone}
\usepackage{pstricks-add}
\psset{dimen=medusa,linewidth=3\pslinewidth}
\begin{document}
\begin{pspicture}[showgrid](9,7)
    \pnodes(4,4){O}(1,2){A}(8,1){B}
    \pscircle(O){2}
    \psCircleTangents(A)(O){2}
    \pnodes(CircleT2){M}
    \psCircleTangents(B)(O){2}
    \pnodes(CircleT1){N}
    \psset{linecolor=red}
    \psline(A)(M)
    \psline(B)(N)
    \psarc[origin={O}](O){2}{(N)}{(M)}
\end{pspicture}
\end{document}

在此处输入图片描述

动画版

pst-eucl

\documentclass[pstricks,border=12pt]{standalone}
\usepackage{pst-eucl}
\psset{PointName=none,PointSymbol=none,linewidth=3\pslinewidth}
\begin{document}
\multido{\r=0.0+.5}{18}{%
\begin{pspicture}[showgrid](9,7)
    \pstGeonode(4,4){O}(1,2){A}(\r,1){B}
    \pstCircleOA[Radius=\pstDistVal{2}]{O}{}
    \pstMiddleAB{A}{O}{A'}
    \pstInterCC[RadiusA=\pstDistVal{2},RadiusB=\pstDistAB{A'}{O}]{O}{}{A'}{}{M1}{M2}
    \pstMiddleAB{B}{O}{B'}
    \pstInterCC[RadiusA=\pstDistVal{2},RadiusB=\pstDistAB{B'}{O}]{O}{}{B'}{}{N1}{N2}    
    \psset{linecolor=red}
    \psline(A)(M2)
    \psline(B)(N1)
    \psarc[origin={O}](O){2}{(N1)}{(M2)}
\end{pspicture}}
\end{document}

使用pstricks-add(较短):

\documentclass[pstricks,border=12pt]{standalone}
\usepackage{pstricks-add}
\psset{dimen=medusa,linewidth=3\pslinewidth}
\begin{document}
\multido{\r=0.0+.5}{18}{%
\begin{pspicture}[showgrid](9,7)
    \pnodes(4,4){O}(1,2){A}(\r,1){B}
    \pscircle(O){2}
    \psCircleTangents(A)(O){2}
    \pnodes(CircleT2){M}
    \psCircleTangents(B)(O){2}
    \pnodes(CircleT1){N}
    \psset{linecolor=red}
    \psline(A)(M)
    \psline(B)(N)
    \psarc[origin={O}](O){2}{(N)}{(M)}
\end{pspicture}}
\end{document}

在此处输入图片描述

笔记:

对于的多次调用\psCircleTangents,在下一次调用之前,必须先将输出节点CircleT1和/或CircleT2保存或缓冲或备份到临时节点中,\psCircleTangents因为\psCircleTangents输出具有相同名称的切点。

答案3

以下是通过三个新方法实现的 TikZ 解决方案to path

  1. cw wrap around顺时针方向绕圆一周。
  2. acw wrap around以逆时针方向绕圆一周。
  3. wrap around在顺时针和逆时针之间选择更好的方向。

这三个选项的参数相同:{中心by半径}。当不存在解决方案时,会发生错误。

使用示例(ab分别是起点和终点):

\draw (a) to[wrap around=c by 1cm] (b);
\draw (a) to[cw wrap around=c by 1cm] (b);
\draw (a) to[acw wrap around=c by 1cm] (b);

一些结果(橙色:顺时针,黄绿色:逆时针,黑色:最佳):

在此处输入图片描述

代码:

\documentclass[tikz]{standalone}
\usetikzlibrary{calc}

\makeatletter
\tikzset{
  cw wrap around/.style args={#1 by #2}{
    to path={
      let
      \p1=(\tikztostart), \p2=(\tikztotarget), \p3=(#1),
      \n1={veclen(\x3-\x1,\y3-\y1)}, \n2={veclen(\x3-\x2,\y3-\y2)}, \n3={#2},
      \n{test}={((\n1 < \n3)||(\n2 < \n3))?1:0}
      in
      \pgfextra{
        \pgfmathtruncatemacro\val{\n{test}}
        \ifnum\val=1 %
        \@latex@error{radius too long to wrap!}{}
        \fi
        \pgfinterruptpath
        \node[line width=0,circle,minimum size=2*\n3,inner sep=0] (cir) at (#1) {};
        \coordinate (i1) at (tangent cs:node=cir,point={(\p1)},solution=1);
        \coordinate (i2) at (tangent cs:node=cir,point={(\p2)},solution=2);
        \endpgfinterruptpath
      }
      let \p{i1}=(i1), \p{i2}=(i2) in
      \pgfextra{
        \pgfmathsetmacro\angstart{atan2(\y{i1}-\y1,\x{i1}-\x1)-90}
        \pgfmathsetmacro\angend{atan2(\y{i2}-\y2,\x{i2}-\x2)+90}
        \pgfmathsetmacro\angend{\angend<\angstart?\angend+360:\angend}
        \pgfmathsetmacro\angend{\angend>\angstart+360?\angend-360:\angend}
      }
      -- (i1) arc[start angle=\angstart,end angle=\angend,radius=\n3] --(\p2)
    },
  },
  acw wrap around/.style args={#1 by #2}{
    to path={
      let
      \p1=(\tikztostart), \p2=(\tikztotarget), \p3=(#1),
      \n1={veclen(\x3-\x1,\y3-\y1)}, \n2={veclen(\x3-\x2,\y3-\y2)}, \n3={#2},
      \n{test}={((\n1 < \n3)||(\n2 < \n3))?1:0}
      in
      \pgfextra{
        \pgfmathtruncatemacro\val{\n{test}}
        \ifnum\val=1 %
        \@latex@error{radius too long to wrap!}{}
        \fi
        \pgfinterruptpath
        \node[line width=0,circle,minimum size=2*\n3,inner sep=0] (cir) at (#1) {};
        \coordinate (i1) at (tangent cs:node=cir,point={(\p1)},solution=2);
        \coordinate (i2) at (tangent cs:node=cir,point={(\p2)},solution=1);
        \endpgfinterruptpath
      }
      let \p{i1}=(i1), \p{i2}=(i2) in
      \pgfextra{
        \pgfmathsetmacro\angstart{atan2(\y{i1}-\y1,\x{i1}-\x1)+90}
        \pgfmathsetmacro\angend{atan2(\y{i2}-\y2,\x{i2}-\x2)-90}
        \pgfmathsetmacro\angend{\angend>\angstart?\angend-360:\angend}
        \pgfmathsetmacro\angend{\angend<\angstart-360?\angend+360:\angend}
      }
      -- (i1) arc[start angle=\angstart,end angle=\angend,radius=\n3] --(\p2)
    },
  },
  wrap around/.style args={#1 by #2}{
    to path={
      let
      \p1=(\tikztostart), \p2=(\tikztotarget), \p3=(#1),
      \n1={veclen(\x3-\x1,\y3-\y1)}, \n2={veclen(\x3-\x2,\y3-\y2)}, \n3={#2},
      \n{test}={((\n1 < \n3)||(\n2 < \n3))?1:0}
      in
      \pgfextra{
        \pgfmathtruncatemacro\val{\n{test}}
        \ifnum\val=1 %
        \@latex@error{radius too long to wrap!}{}
        \fi
        \pgfinterruptpath
        \node[line width=0,circle,minimum size=2*\n3,inner sep=0] (cir) at (#1) {};
        \coordinate (i1cw) at (tangent cs:node=cir,point={(\p1)},solution=1);
        \coordinate (i1acw) at (tangent cs:node=cir,point={(\p1)},solution=2);
        \coordinate (i2cw) at (tangent cs:node=cir,point={(\p2)},solution=2);
        \coordinate (i2acw) at (tangent cs:node=cir,point={(\p2)},solution=1);
        \endpgfinterruptpath
      }
      let
      \p{i1cw}=(i1cw), \p{i2cw}=(i2cw),
      \p{i1acw}=(i1acw), \p{i2acw}=(i2acw)
      in
      \pgfextra{
        % acw
        \pgfmathsetmacro\angstartacw{atan2(\y{i1acw}-\y1,\x{i1acw}-\x1)+90}
        \pgfmathsetmacro\angendacw{atan2(\y{i2acw}-\y2,\x{i2acw}-\x2)-90}
        \pgfmathsetmacro\angendacw{\angendacw>\angstartacw?\angendacw-360:\angendacw}
        \pgfmathsetmacro\angendacw{\angendacw<\angstartacw-360?\angendacw+360:\angendacw}
        % cw
        \pgfmathsetmacro\angstartcw{atan2(\y{i1cw}-\y1,\x{i1cw}-\x1)-90}
        \pgfmathsetmacro\angendcw{atan2(\y{i2cw}-\y2,\x{i2cw}-\x2)+90}
        \pgfmathsetmacro\angendcw{\angendcw<\angstartcw?\angendcw+360:\angendcw}
        \pgfmathsetmacro\angendcw{\angendcw>\angstartcw+360?\angendcw-360:\angendcw}
        % test
        \pgfmathtruncatemacro\difftest{(\angendcw-\angstartcw<\angstartacw-\angendacw)?1:0}
        \ifnum\difftest=1 %
        % choice: cw
        \pgfinterruptpath
        \coordinate (i1) at (i1cw);
        \coordinate (i2) at (i2cw);
        \endpgfinterruptpath
        \pgfmathsetmacro\angstart{\angstartcw}
        \pgfmathsetmacro\angend{\angendcw}
        \else
        % choice: acw
        \pgfinterruptpath
        \coordinate (i1) at (i1acw);
        \coordinate (i2) at (i2acw);
        \endpgfinterruptpath
        \pgfmathsetmacro\angstart{\angstartacw}
        \pgfmathsetmacro\angend{\angendacw}
        \fi
      }
      -- (i1) arc[start angle=\angstart,end angle=\angend,radius=\n3] --(\p2)
    },
  },
}
\makeatother

\begin{document}
\foreach \i in {1,...,36}{
  \begin{tikzpicture}
    \pgfmathsetmacro\rnda{rnd*360}
    \pgfmathsetmacro\rndb{rnd*360}
    \pgfmathsetmacro\rndc{rnd*4mm}
    % \pgfmathsetmacro\rnda{\i}
    % \pgfmathsetmacro\rndb{0}
    % \pgfmathsetmacro\rndc{1cm}
    \coordinate (a) at (\rnda:2cm);
    \coordinate (b) at (\rndb:2cm);
    \coordinate (c) at (0,0);
    \pgfmathsetmacro\radius{\rndc};

    \draw[gray] (c) circle(\radius pt);
    \draw[orange,line width=.8pt] (a) to[cw wrap around={c by \radius pt}] (b);
    \draw[lime,line width=.8pt] (a) to[acw wrap around={c by \radius pt}] (b);
    \draw[black] (a) to[wrap around={c by \radius pt}] (b);

    \path circle(2cm);
  \end{tikzpicture}
}
\end{document}

答案4

我们也可以在 Metapost 中巧妙地做到这一点。fullcircle普通 Metapost 定义的路径周围有 8 个(或多或少)均匀分布的点。点 0 对应于 3 点,点 2 对应于 12 点,依此类推。宏direction为您提供切线向量。

beginfig(1);
path C; C = fullcircle scaled 20;
a = 0.4; b = 2.6;
z0 = point a of C - 20*(unitvector direction a of C);
z1 = point b of C + 20*(unitvector direction b of C);
fill C withcolor .7white;
drawarrow z0 -- subpath (a,b) of C -- z1 withcolor .637 red;
endfig;

在这个例子中,改变ab获取您想要的角度。

a=0.4b=2.6产生这个:在此处输入图片描述

a=0b=2产生这个:在此处输入图片描述

a=1b=7这个:在此处输入图片描述

等等。

如果您喜欢环绕椭圆,请尝试类似如下的方法C = fullcircle xscaled 20 yscaled 10

相关内容