
我正在尝试制作一些东西相似的给定图像。但是,关于同心圆之间的“之字形”箭头,我希望这些之字形本身勾勒出一个圆弧,其半径位于两个边界同心圆的中间。
到目前为止,我有以下 MWE(请注意,节点位置现在并不重要)
\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{arrows}
\begin{document}
\begin{tikzpicture}[scale=0.7,rotate=90]
% outer circle
\def \radius {5}
\draw (0,0) circle (5);
\foreach \angle / \label in {0/0, 30/11, 60/10, 90/9, 120/8, 150/7, 180/6, 210/5, 240/4, 270/3, 300/2, 330/1}
{
\node[draw,fill=gray!30,circle] at (\angle:\radius) {\label};
}
% middle circle
\def \n {7}
\def \radius {3}
\draw (0,0) circle (\radius);
\foreach \s in {1,...,\n}
{
\node[draw,fill=black,circle] at ({360/\n * (\s - 1)}:\radius) {};
}
% inner circle
\def \n {3}
\def \radius {1}
\draw (0,0) circle (\radius);
\foreach \s in {1,...,\n}
{
\node[draw,fill=white,circle] at ({360/\n * (\s - 1)}:\radius) {};
}
% arrow
\draw[->] (0,0) -- (6,0);
\draw[->] (0:0) -- (240:6);
\end{tikzpicture}
\end{document}
这将产生以下内容:
这里的“12 点钟”箭头没有问题;问题在于 4 点钟箭头,它应该实际上在 3 点时退出外圈。
请求:理想情况下,这支箭将:
- 退出白圈节点;
- 在到达中间圆(有黑色节点的圆)的一半位置(MWE 中的半径 2),箭头将逆时针旋转,直到与中间圆中最近的黑色圆节点对齐。但这个转弯应该是弧线,而不是直线;
- 通过这个黑色圆圈节点前进;
- 重复步骤 2,这次与“3”节点相关。
编辑:下面是一个示例图像,显示了我正在寻找的箭头类型:
答案1
锯齿形圆弧线可以用以下calc
库来完成:
\tikzset{
zigzagarc/.default={0,0},
zigzagarc/.style={to path={
let \p0=($(\tikztostart)-(#1)$),
\p1=($(\tikztotarget)-(#1)$),
\n0={atanXY(\p0)}, \n1={atanXY(\p1)},
% \n0={atan2(\y0,\x0)}, \n1={atan2(\y1,\x1)},
\n d={.5*veclen(\p1)-.5*veclen(\p0)},
\n r={.5*veclen(\p1)+.5*veclen(\p0)} in
-- ++ (\n0:\n d) arc [radius=\n r, start angle=\n0, end angle=\n1] \tikztonodes
-- (\tikztotarget)}}}
我正在使用一个修改过的atan2
函数,atanXY
定义为
\pgfmathdeclarefunction{atanXY}{2}{\pgfmathatantwo@{#2}{#1}}
因此仍然可以这样做,atanXY(\p?)
因为calc
的\p
宏只是扩展为\x?, \y?
(这使得您可以在路径上使用它而无需使用\x
和\y
。(参见A244619)
atanXY
同时还给出了不带的替代方案。
然后您就可以使用(<p1>) to[zigzagarc] (<p2>)
它来绘制一条直线(0,0)
、一条圆弧和线的其余部分。
参数zigzagarc
允许它使用另一个圆心作为圆弧,但是(0,0)
。
代码
\documentclass[tikz]{standalone}
\usetikzlibrary{calc,arrows.meta}
\tikzset{
zigzagarc/.default={0,0},
zigzagarc/.style={to path={
let \p0=($(\tikztostart)-(#1)$),
\p1=($(\tikztotarget)-(#1)$),
\n0={atanXY(\p0)}, \n1={atanXY(\p1)},
% \n0={atan2(\y0,\x0)}, \n1={atan2(\y1,\x1)},
\n d={.5*veclen(\p1)-.5*veclen(\p0)},
\n r={.5*veclen(\p1)+.5*veclen(\p0)} in
-- ++ (\n0:\n d) arc [radius=\n r, start angle=\n0, end angle=\n1] \tikztonodes
-- (\tikztotarget)}}}
\makeatletter
% for easy usage of calc's atanXY(\p?):
\pgfmathdeclarefunction{atanXY}{2}{\pgfmathatantwo@{#2}{#1}}
\tikzset{if/.code n args=3{\pgfmathparse{#1}\ifnum\pgfmathresult=0\relax
\expandafter\pgfutil@firstoftwo\else\expandafter\pgfutil@secondoftwo\fi
{\pgfkeysalso{#3}}{\pgfkeysalso{#2}}}}
\makeatother
\newcommand*\tvo[1]{\pgfkeysvalueof{/tikz/#1}}% tikzvalueof
\begin{document}
\begin{tikzpicture}[scale=.7,
declare function={posonarc(\x,\n)=360/\n*\x;},
n-0/.initial=3, n-1/.initial=7, n-2/.initial=12,
d-0/.initial=80, d-1/.initial=15, d-2/.initial=90,
every snode/.style={shape=circle, draw, inner sep=+0pt, minimum size=+5pt},
s-0/.style={every snode, node contents=,fill=white},
s-1/.style={every snode, node contents=,
if={or(#1==0,or(#1==1,or(#1==3,#1==5)))}{fill=black}{fill=white}},
s-2/.style={every snode, shape=circle, draw, inner sep=+1pt,
fill=white, node contents={#1}, fill=gray!50, minimum size=1.2em,
if={or(#1==4,#1==7)}{fill=white}
{if={#1==10}{node contents=$t$}
{if={#1==11}{fill=white, node contents=$e$}{}}}},
]
\foreach \r in {1,...,3} \draw circle[radius=\r];
\foreach \ring in {0,...,2}
\foreach \x[count=\xx from 0] in {1,...,\tvo{n-\ring}}
\node (n-\ring-\x) at ({\tvo{d-\ring}-posonarc(\xx,\tvo{n-\ring})}:
\ring+1) [s-\ring=\xx];
\foreach \a/\b/\c in {1/6/12, 2/2/5, 3/4/8}% down here!
\draw[zigzagarc] (n-0-\a) to (n-1-\b) to (n-2-\c)
-- ++ ({\tvo{d-2}-posonarc(\c-1,\tvo{n-2})}:1) [-Latex];
\end{tikzpicture}
\end{document}
答案2
最初,我非常重视逆时针旋转的要求。从你的模型来看,我不再确定这是你想要的,所以这里有一个替代方案。原始版本如下。
根据模型更新参数化答案
如果你真的想逆时针走,有时就意味着要画一条 359 度的弧。即使这条线总是经过一个白色节点,情况也是如此。例如,如果从 240 处的白色节点离开,那么到最近的黑色节点的最近路线是顺时针到 3,而不是逆时针到 4。我不确定这是否是你真正想要的,因为它看起来很奇怪。下面,我选择了最短的路线,但我不知道这是否正确。
当然,这就是试图画出代表我不知道是什么的东西的麻烦。它应该如何进行对你来说可能很明显,但对我来说却完全晦涩难懂!
\documentclass[tikz,border=10pt,multi]{standalone}
\usetikzlibrary{arrows.meta}
\begin{document}
% ateb i gwestiwn Richard: http://tex.stackexchange.com/q/315559/
\begin{tikzpicture}
[
scale=0.7,
rotate=90,
>=Triangle,
outer radius/.store in=\oradius,
middle radius/.store in=\mradius,
inner radius/.store in=\iradius,
outer no/.store in=\ono,
middle no/.store in=\mno,
inner no/.store in=\ino,
outer number/.store in=\onumber,
middle number/.store in=\mnumber,
inner number/.store in=\inumber,
outer n/.code={%
\pgfmathsetmacro\tno{int(#1-1)}
\tikzset{%
outer no/.expanded=\tno,
outer number=#1,
}
},
middle n/.code={%
\pgfmathsetmacro\tno{int(#1-1)}
\tikzset{%
middle no/.expanded=\tno,
middle number=#1,
}
},
inner n/.code={%
\pgfmathsetmacro\tno{int(#1-1)}
\tikzset{%
inner no/.expanded=\tno,
inner number=#1,
}
},
outer radius=5,
middle radius=3,
inner radius=1,
outer n=12,
middle n=7,
inner n=3,
]
% outer circle
\draw (0,0) circle (\oradius);
\foreach \i in {0,...,\ono}
{
\node (o\i) [draw, fill=gray!30, circle] at (-{\i*360/\onumber}:\oradius) {\i};
}
% middle circle
\draw (0,0) circle (\mradius);
\foreach \s in {0,...,\mno}
{
\node (m\s) [draw, fill=black, circle] at ({360*\s/\mnumber}:\mradius) {};
}
% inner circle
\draw (0,0) circle (\iradius);
\foreach \s in {0,...,\ino}
{
\node (i\s) [draw, fill=white, circle] at ({360*\s/\inumber}:\iradius) {};
}
% \foreach \i in {0,...,\ono} \node [font=\tiny, red] at (o\i) {o\i};
% \foreach \i in {0,...,\mno} \node [font=\tiny, red] at (m\i) {m\i};
% \foreach \i in {0,...,\ino} \node [font=\tiny, red] at (i\i) {i\i};
% arrow
\draw[->] (0,0) -- ({\oradius+1},0);
\foreach \i in {0,120,240}
{
\pgfmathsetmacro\n{int(round(\i*\mnumber/360))}
\pgfmathsetmacro\m{int(round((\n*360/\mnumber)*\onumber/360))}
\draw [->, magenta] (0,0) -- (\i:{(\iradius+\mradius)/2}) arc (\i:{\n*360/\mnumber}:{(\iradius+\mradius)/2}) -- ({\n*360/\mnumber}:{(\oradius+\mradius)/2}) arc ({\n*360/\mnumber}:{\m*360/\onumber}:{(\oradius+\mradius)/2}) -- ({\m*360/\onumber}:{\oradius+1});
}
\end{tikzpicture}
\end{document}
原始答案
你想要这样的东西吗?在某些情况下,应用你的算法显然会导致不同的出口节点,因为 3 比 4 更接近最近的黑色节点,即使 4 显然更接近原始方向。
以下是一次性版本的代码:
\documentclass[tikz,border=10pt,multi]{standalone}
\usetikzlibrary{arrows.meta}
\begin{document}
\begin{tikzpicture}[scale=0.7, rotate=90, >=Triangle]
% outer circle
\def \radius {5}
\draw (0,0) circle (5);
\foreach \angle / \label in {0/0, 30/11, 60/10, 90/9, 120/8, 150/7, 180/6, 210/5, 240/4, 270/3, 300/2, 330/1}
{
\node (o\label) [draw,fill=gray!30,circle] at (\angle:\radius) {\label};
}
% middle circle
\def \n {6}
\def \radius {3}
\draw (0,0) circle (\radius);
\foreach \s in {0,...,\n}
{
\node (m\s) [draw,fill=black,circle] at ({360*\s/(\n+1)}:\radius) {};
}
% inner circle
\def \n {2}
\def \radius {1}
\draw (0,0) circle (\radius);
\foreach \s in {0,...,\n}
{
\node (i\s) [draw,fill=white,circle] at ({360*\s/(\n+1)}:\radius) {};
}
% \foreach \i in {0,...,11} \node [font=\tiny, red] at (o\i) {o\i};
% \foreach \i in {0,...,6} \node [font=\tiny, red] at (m\i) {m\i};
% \foreach \i in {0,...,2} \node [font=\tiny, red] at (i\i) {i\i};
% arrow
\draw[->] (0,0) -- (6,0);
\pgfmathsetmacro\n{int(round(240*7/360))}
\pgfmathsetmacro\m{int(round((\n*360/7)*12/360))}
\draw [->] (0,0) -- (240:2) [out=180+240,in=180+\n*360/7] to ({\n*360/7}:2) -- ({\n*360/7}:4) [out=180+\n*360/7,in=180+\m*360/12] to ({\m*360/12}:4) -- ({\m*360/12}:6);
\end{tikzpicture}
\end{document}
参数化版本
这是一个参数化版本:
\documentclass[tikz,border=10pt,multi]{standalone}
\usetikzlibrary{arrows.meta}
\begin{document}
% ateb i gwestiwn Richard: http://tex.stackexchange.com/q/315559/
\begin{tikzpicture}
[
scale=0.7,
rotate=90,
>=Triangle,
outer radius/.store in=\oradius,
middle radius/.store in=\mradius,
inner radius/.store in=\iradius,
outer no/.store in=\ono,
middle no/.store in=\mno,
inner no/.store in=\ino,
outer number/.store in=\onumber,
middle number/.store in=\mnumber,
inner number/.store in=\inumber,
outer n/.code={%
\pgfmathsetmacro\tno{int(#1-1)}
\tikzset{%
outer no/.expanded=\tno,
outer number=#1,
}
},
middle n/.code={%
\pgfmathsetmacro\tno{int(#1-1)}
\tikzset{%
middle no/.expanded=\tno,
middle number=#1,
}
},
inner n/.code={%
\pgfmathsetmacro\tno{int(#1-1)}
\tikzset{%
inner no/.expanded=\tno,
inner number=#1,
}
},
outer radius=5,
middle radius=3,
inner radius=1,
outer n=12,
middle n=7,
inner n=3,
]
% outer circle
\draw (0,0) circle (\oradius);
\foreach \i in {0,...,\ono}
{
\node (o\i) [draw, fill=gray!30, circle] at (-{\i*360/\onumber}:\oradius) {\i};
}
% middle circle
\draw (0,0) circle (\mradius);
\foreach \s in {0,...,\mno}
{
\node (m\s) [draw, fill=black, circle] at ({360*\s/\mnumber}:\mradius) {};
}
% inner circle
\draw (0,0) circle (\iradius);
\foreach \s in {0,...,\ino}
{
\node (i\s) [draw, fill=white, circle] at ({360*\s/\inumber}:\iradius) {};
}
% \foreach \i in {0,...,\ono} \node [font=\tiny, red] at (o\i) {o\i};
% \foreach \i in {0,...,\mno} \node [font=\tiny, red] at (m\i) {m\i};
% \foreach \i in {0,...,\ino} \node [font=\tiny, red] at (i\i) {i\i};
% arrow
\draw[->] (0,0) -- ({\oradius+1},0);
\foreach \j [evaluate=\j as \i using (\j*360/\mnumber)+20] in {0,...,\mno}
{
\pgfmathsetmacro\n{int(round(\i*\mnumber/360))}
\pgfmathsetmacro\m{int(round((\n*360/\mnumber)*\onumber/360))}
\draw [->, blue] (0,0) -- (\i:{(\iradius+\mradius)/2}) [out=180+\i,in=180+\n*360/\mnumber] to ({\n*360/\mnumber}:{(\iradius+\mradius)/2}) -- ({\n*360/\mnumber}:{(\oradius+\mradius)/2}) [out=180+\n*360/\mnumber,in=180+\m*360/\onumber] to ({\m*360/\onumber}:{(\oradius+\mradius)/2}) -- ({\m*360/\onumber}:{\oradius+1});
}
\end{tikzpicture}
\end{document}