有没有一种简单的方法使用 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}
如果可能的话,解决方案应该是一个产生以下行为的单个命令:
- 画一个圆 C(由其圆心和半径指定)
- 画一条包含三部分的线:
- 第一部分是从任意点 P 到 C 周长上的点 Q 的线段。
- 第二部分是沿 C 的周长的 90 度角的圆弧。
- 第三部分是远离圆弧的线段。
在伪代码:
\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
可关闭角的圆化。
不同x
和y
单位
- 如果单位和方向不同,则选项
rounded corners
将不起作用,因为舍入保持对称。x
y
- 此外,在上面的例子中必须明确指定单位。
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
:
cw wrap around
顺时针方向绕圆一周。acw wrap around
以逆时针方向绕圆一周。wrap around
在顺时针和逆时针之间选择更好的方向。
这三个选项的参数相同:{
中心by
半径}
。当不存在解决方案时,会发生错误。
使用示例(a
和b
分别是起点和终点):
\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;
在这个例子中,改变a
并b
获取您想要的角度。
a=0.4
并b=2.6
产生这个:
a=0
并b=2
产生这个:
a=1
和b=7
这个:
等等。
如果您喜欢环绕椭圆,请尝试类似如下的方法C = fullcircle xscaled 20 yscaled 10
。