我正在尝试在 TikZ 中围绕一组凸圆形节点绘制边界。目标是将其填充 1cm,并在各段之间的角处设置一个圆弧,如下所示:
我目前有以下代码:
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc}
\begin{document}
\begin{tikzpicture}[every node/.style={circle,draw=blue}]
\node at (0,0) (a) {};
\node at (1,3) (b) {};
\node at (2,2) (c) {};
\draw[red]
($(a)!1cm!-90:(c)$)
let \p1 = ($(a)!1cm!-90:(c) - (a)$),
\n1 = {atan2(\x1,\y1)},
\p2 = ($(a)!1cm!90:(b) - (a)$),
\n2 = {atan2(\x2,\y2)},
\n{delta} = {-Mod(\n1-\n2,360)}
in
arc [start angle=\n1, delta angle=\n{delta}, radius=1cm]
--
($(b)!1cm!-90:(a)$)
let \p1 = ($(b)!1cm!-90:(a) - (b)$),
\n1 = {atan2(\x1,\y1)},
\p2 = ($(b)!1cm!90:(c) - (b)$),
\n2 = {atan2(\x2,\y2)},
\n{delta} = {-Mod(\n1-\n2,360)}
in
arc [start angle=\n1, delta angle=\n{delta}, radius=1cm]
--
($(c)!1cm!-90:(b)$)
let \p1 = ($(c)!1cm!-90:(b) - (c)$),
\n1 = {atan2(\x1,\y1)},
\p2 = ($(c)!1cm!90:(a) - (c)$),
\n2 = {atan2(\x2,\y2)},
\n{delta} = {-Mod(\n1-\n2,360)}
in
arc [start angle=\n1, delta angle=\n{delta}, radius=1cm]
-- cycle;
\end{tikzpicture}
\end{document}
我将要绘制很多这样的图,因此如果我可以用\draw
类似以下内容替换该语句,将会很有用:
\draw[red] \convexpath{(a) (b) (c)}{1cm};
其中凸角上的节点按顺时针方向排列(a) (b) (c)
。对于一个节点,我希望它围绕相关节点绘制一个指定半径的圆。
有人对如何编写这样的宏有什么建议吗?
答案1
如果有人感兴趣的话,这是对杰克的答案的一个小修改:它简化了一些计算,现在适用于单个节点(在这种情况下它绘制一个圆圈):
\newcommand{\convexpath}[2]{
[
create hullcoords/.code={
\global\edef\namelist{#1}
\foreach [count=\counter] \nodename in \namelist {
\global\edef\numberofnodes{\counter}
\coordinate (hullcoord\counter) at (\nodename);
}
\coordinate (hullcoord0) at (hullcoord\numberofnodes);
\pgfmathtruncatemacro\lastnumber{\numberofnodes+1}
\coordinate (hullcoord\lastnumber) at (hullcoord1);
},
create hullcoords
]
($(hullcoord1)!#2!-90:(hullcoord0)$)
\foreach [
evaluate=\currentnode as \previousnode using \currentnode-1,
evaluate=\currentnode as \nextnode using \currentnode+1
] \currentnode in {1,...,\numberofnodes} {
let \p1 = ($(hullcoord\currentnode) - (hullcoord\previousnode)$),
\n1 = {atan2(\x1,\y1) + 90},
\p2 = ($(hullcoord\nextnode) - (hullcoord\currentnode)$),
\n2 = {atan2(\x2,\y2) + 90},
\n{delta} = {Mod(\n2-\n1,360) - 360}
in
{arc [start angle=\n1, delta angle=\n{delta}, radius=#2]}
-- ($(hullcoord\nextnode)!#2!-90:(hullcoord\currentnode)$)
}
}
注意如果使用 pgf/tikz >=3.0,你需要切换 atan2 的参数(因此atan2(\x1,\y1)
变成)atan2(\y1,\x1)
。如果你需要支持多个版本(用于与合作者合作、上传到 arxiv 等),你可以使用\@ifpackagelater
例如
\@ifpackagelater{tikz}{2013/12/01}{...}{...}
答案2
这里有一种方法:我曾经[<new pgf key>/.code={...}, <new pgf key>]
将一些代码偷偷放入 TikZ 路径中,这些代码可以在不中断路径构建的情况下执行。该代码在所有指定节点的位置创建新的不可见节点并按顺序命名它们(hullnode[1...n]
),然后hullnode0
在hullnode[n]
和hullnode[n+1]
处创建两个附加节点hullnode1
。这样就可以循环hullnode[1...n]
并绘制弧线。
该命令有两个参数:以逗号分隔的节点名称列表(不带括号)和缓冲区长度。如果有人成功将缓冲区大小参数设为可选,请告诉我怎么做!
代码
\node at (0,0) (a) {};
\node at (2,3) (b) {};
\node at (3,-1) (c) {};
\node at (1,-2) (d) {};
\draw[red] \convexpath{a,b,c,d}{1cm};
\draw[thick,blue] \convexpath{a,c,d}{1.2cm};
将产生
请注意,对于 pgf<3.0.0,您必须切换下面的参数atan2
,即它出现的两次都atan2(\y2,\x2)
应该是atan2(\x2,\y2)
。如果代码似乎无法正常工作,请尝试进行此更改。
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{calc}
\newcommand{\convexpath}[2]{
[
create hullnodes/.code={
\global\edef\namelist{#1}
\foreach [count=\counter] \nodename in \namelist {
\global\edef\numberofnodes{\counter}
\node at (\nodename) [draw=none,name=hullnode\counter] {};
}
\node at (hullnode\numberofnodes) [name=hullnode0,draw=none] {};
\pgfmathtruncatemacro\lastnumber{\numberofnodes+1}
\node at (hullnode1) [name=hullnode\lastnumber,draw=none] {};
},
create hullnodes
]
($(hullnode1)!#2!-90:(hullnode0)$)
\foreach [
evaluate=\currentnode as \previousnode using \currentnode-1,
evaluate=\currentnode as \nextnode using \currentnode+1
] \currentnode in {1,...,\numberofnodes} {
-- ($(hullnode\currentnode)!#2!-90:(hullnode\previousnode)$)
let \p1 = ($(hullnode\currentnode)!#2!-90:(hullnode\previousnode) - (hullnode\currentnode)$),
\n1 = {atan2(\y1,\x1)},
\p2 = ($(hullnode\currentnode)!#2!90:(hullnode\nextnode) - (hullnode\currentnode)$),
\n2 = {atan2(\y2,\x2)},
\n{delta} = {-Mod(\n1-\n2,360)}
in
{arc [start angle=\n1, delta angle=\n{delta}, radius=#2]}
}
-- cycle
}
\begin{document}
\begin{tikzpicture}[every node/.style={circle,draw=blue}]
\node at (0,0) (a) {};
\node at (2,3) (b) {};
\node at (3,-1) (c) {};
\node at (1,-2) (d) {};
\draw[red] \convexpath{a,b,c,d}{1cm};
\draw[thick,blue] \convexpath{a,c,d}{1.2cm};
\end{tikzpicture}
\end{document}
答案3
这是另一种解决方案。虽然不如其他两个答案那么花哨,但我认为使用起来更简单,而且您可以更轻松地自定义边框。此外,顺序是顺时针还是逆时针并不重要。
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{backgrounds}
\usetikzlibrary{calc}
\usepackage[active,tightpage]{preview}
\PreviewEnvironment{tikzpicture}
\setlength\PreviewBorder{5mm}
\begin{document}
\begin{tikzpicture}
\tikzstyle{n}=[draw,circle,minimum size=10mm];
\node[n] at (0,0) (A) {A};
\node[n] at (2,0.5) (B) {B};
\node[n] at (2,2.5) (C) {C};
\node[n] at (1,3) (D) {D};
\begin{pgfonlayer}{background}
% Create coordinate place holders
\foreach \nodename in {A,B,C,D} {
\coordinate (\nodename') at (\nodename);
}
\path[fill=blue!50,draw=blue!50,line width=1.4cm, line cap=round, line join=round]
(A') to[bend left=20] (B')
to[bend left=50] (C')
to (D')
to[bend left] (A') -- cycle;
\end{pgfonlayer}
\end{tikzpicture}
\end{document}
如果您只想要线条,则可以使用与double
线条样式相同的方法。我们只需再次绘制相同的路径,但路径要小一点,并且使用背景色(本例中为白色)。
\path[fill=blue!50,draw=blue!50,line width=1.4cm, line cap=round, line join=round]
(A') to[bend left=20] (B')
to[bend left=50] (C')
to (D')
to[bend left] (A') -- cycle;
\path[fill=white,draw=white,line width=1.3cm, line cap=round, line join=round]
(A') to[bend left=20] (B')
to[bend left=50] (C')
to (D')
to[bend left] (A') -- cycle;
这是填充和线条后的样子:
答案4
以下代码(我希望更简洁一些)来自 jackal 的回答,没有使用一些手动计算选项,例如 [bend left=20]。
\documentclass[tikz,border=5mm]{standalone}
\begin{document}
\begin{tikzpicture}
\tikzstyle{n}=[draw,circle,minimum size=10mm];
\path
(0,0) coordinate (A)
(5,0) coordinate (B)
(3,3) coordinate (C)
(1,4) coordinate (D);
\path[draw=blue!50,fill=blue!50,line width=1.4cm,line cap=round,line join=round]
(A)--(B)--(C)--(D)--cycle
(A)--(C) (B)--(D);
\path
(A) node[n]{A}
(B) node[n]{B}
(C) node[n]{C}
(D) node[n]{D};
\end{tikzpicture}
\begin{tikzpicture}
\tikzstyle{n}=[draw,circle,minimum size=10mm];
\path
(0,0) coordinate (A)
(5,0) coordinate (B)
(3,3) coordinate (C)
(1,4) coordinate (D);
\path[fill=blue!50,draw=blue!50,line width=1.4cm,line cap=round,line join=round]
(A)--(B)--(C)--(D)--cycle
(A)--(C) (B)--(D);
\path[fill=white,draw=white,line width=1.3cm,line cap=round,line join=round]
(A)--(B)--(C)--(D)--cycle
(A)--(C) (B)--(D);
\path
(A) node[n]{A}
(B) node[n]{B}
(C) node[n]{C}
(D) node[n]{D};
\end{tikzpicture}
\end{document}