在数学中绘制单纯形图时,结果通常如下所示:
\documentclass{article}
\usepackage{tikz-cd}
\tikzcdset{
diagrams={>={Straight Barb[scale=0.5]}}
}
\begin{document}
Consider the simplicial set~\( X_{\bullet} \) given by
\[\begin{tikzcd}
\cdots
\ar[r,yshift=6pt,->]
\ar[r,yshift=4.5pt,<-]
\ar[r,yshift=3pt,->]
\ar[r,yshift=1.5pt,<-]
\ar[r,yshift=0pt,->]
\ar[r,yshift=-1.5pt,<-]
\ar[r,yshift=-3pt,->]
\ar[r,yshift=-4.5pt,<-]
\ar[r,yshift=-6pt,->]
&
X_3
\ar[r,yshift=4.5pt,->]
\ar[r,yshift=3pt,<-]
\ar[r,yshift=1.5pt,->]
\ar[r,yshift=0pt,<-]
\ar[r,yshift=-1.5pt,->]
\ar[r,yshift=-3pt,<-]
\ar[r,yshift=-4.5pt,->]
&
X_2
\ar[r,yshift=3pt,->]
\ar[r,yshift=1.5pt,<-]
\ar[r,yshift=0pt,->]
\ar[r,yshift=-1.5pt,<-]
\ar[r,yshift=-3pt,->]
&
X_1
\ar[r,yshift=1.5pt,->]
\ar[r,yshift=0pt,<-]
\ar[r,yshift=-1.5pt,->]
&
X_0
\end{tikzcd}\]
\end{document}
我想知道是否有办法实现自动化?例如,我们是否可以定义一个键stack
,使箭头以交替方向stack=n
堆叠n
在一起,顶部箭头指向“向前”方向。
\documentclass{article}
\usepackage{tikz-cd}
\tikzcdset{
diagrams={>={Straight Barb[scale=0.5]}}
}
\tikzset{
stack/.style={
%insert code
}
}
\begin{document}
Consider the simplicial set~\( X_{\bullet} \) given by
\[\begin{tikzcd}
\cdots
\ar[r,stack=9]
&
X_3
\ar[r,stack=7]
&
X_2
\ar[r,stack=5]
&
X_1
\ar[r,stack=3]
&
X_0
\end{tikzcd}\]
\end{document}
答案1
更新的解决方案
考虑之后另一个问题我意识到该pathreplacing
库可用于创建可合并到 中的新箭头。这是一个更新的解决方案,允许在 内tikzcd
执行命令。任何方向都可以。链接的问题还包含一个方向上的多个箭头的代码。\arrow[r, altstackar=7]
tikzcd
\documentclass{article}
\usepackage{amsmath, tikz-cd}
\usetikzlibrary{decorations.pathreplacing, calc}
\tikzcdset{
diagrams={>={Straight Barb[scale=0.5]}}
}
\tikzset{
altstackar/.style={decorate, decoration={show path construction,
lineto code={
\path (\tikzinputsegmentfirst); \pgfgetlastxy{\xstart}{\ystart}
\path (\tikzinputsegmentlast); \pgfgetlastxy{\xend}{\yend}
\path ($(0,0)!1.5pt!(\ystart-\yend,\xend-\xstart)$); \pgfgetlastxy{\xperp}{\yperp}
\foreach \n[evaluate=\n as \k using .5*#1-\n+.5] in {1,...,#1}{
\ifodd\n{\draw[->, shorten <=2pt, shift={($\k*(\xperp,\yperp)$)}](\xstart,\ystart)--(\xend,\yend);}
\else{\draw[<-, shorten >=2pt, shift={($\k*(\xperp,\yperp)$)}](\xstart,\ystart)--(\xend,\yend);}\fi
}
}
}}, altstackar/.default={1}
}
\begin{document}
\[\begin{tikzcd}
\dotsb\arrow[r, altstackar=7] & X_2\arrow[r, altstackar=5] & X_1\arrow[r, altstackar=3] & X_0
\end{tikzcd}\]
\end{document}
旧解决方案
这是一个自动化的tikz
解决方案。箭头处理tikz-cd
更加复杂。
上图的调用是
\cdots\stack{9}X_3\stack{7}X_2\stack{5}X_1\stack{3}X_0
箭头之间的间距可通过设置 进行全局调整\stackspace
。单位为pt
。箭头的长度是 中的可选参数,\stack
默认值为1cm
。因此\stack[.5]{3}
会给您 1/2 厘米的箭头。您可以通过将命令替换为您喜欢的水平空间来调整箭头周围的空间\;
,例如\hspace{2mm}
。
以下是代码:
\documentclass{article}
\usepackage{tikz}
\newcommand{\stackspace}{1.5}
\newcommand{\stack}[2][1cm]{\;\tikz[baseline, yshift=.65ex]%
{\foreach \k [evaluate=\k as \r using (.5*#2+.5-\k)*\stackspace] in {1,...,#2}{%
\ifodd\k{\draw[->](0,\r pt)--(#1,\r pt);}%
\else{\draw[<-](0,\r pt)--(#1,\r pt);}\fi
}}\;}
\begin{document}
\[
\cdots\stack{9}X_3\stack{7}X_2\stack{5}X_1\stack{3}X_0
\]
\end{document}
答案2
TikZ shift left
-CD 已经具有内置机制,可以将箭头垂直于其方向移动。
样式stack
会循环遍历每个小箭头,并shift left
为每个箭头应用样式。我在这里使用\pgfinteval
(即\numexpr
)(这是 PGF 自己对 L3 的实现\inteval
),因为它会将除法四舍五入为最接近的整数,这意味着(#1)/2
将始终为 (#1 + 1)/2。
机制shift left
挂钩,execute at begin to
这就是为什么必须指定它的原因前。并且由于它使用了需要在之前设置的edge
当前值(否则它将使用我们刚刚定义的值,这将导致无限循环/堆栈)。to path
line to
edge
to path
我们不需要直接使用edge
路径,而是需要扩展\tikztostart
/,\tikztotarget
因为否则我们会陷入类似的问题,因为它会尝试执行类似的操作\def\tikztostart{\tikztostart}
,而这是没有用的。
最后,我们将应用stack/every even
或stack/every odd
。
在 中stack
,键的默认值shift left
(通常为.56ex
)将设置为 的值stack distance
。该值将与上述表达式的结果相乘\pgfinteval
,以获得最终要移动的距离。(您也可以指定一个绝对尺寸,这将忽略默认值。)
不过,我不会直接使用,而是建议stack
使用适当的样式stack >
和stack <
设置。只要它只是(可以即时重新定义),就可以相当轻松地绘制反向箭头。stack/every odd
stack/every even
>
我添加了一个stack alt
键,它只交换偶数箭头的起点和终点,而不是交换->
/ <-
,这使得使用任何箭头尖变得更容易一些,但缩短需要手动完成。
swap arrow tips
最后,像这样定义的键
\makeatletter
\pgfset{
swap arrow tips/.code=%
\let\pgf@arrow@tip@sequence\pgf@end@tip@sequence
\let\pgf@end@tip@sequence\pgf@start@tip@sequence
\let\pgf@start@tip@sequence\pgf@arrow@tip@sequence}
\makeatother
可用于反转箭头尖端规范,但这是否真的有必要取决于用例的复杂性。
笔记:
shift left
尽管基础计算相同,但对每支箭进行整体评估。可以手动将其分为两部分以加快流程。所有样式都将应用于 TikZ 命名空间
/tikz
,这使得使用来自 TikZ-CD 的键(在命名空间中)变得有点困难,/tikz/commutative diagrams
但我不确定哪些 TikZ-CD 键实际上可以在这里使用。堆叠箭头沿线的节点将被吞噬。
我看到了一种
stack
以某种方式接受三个参数的风格:- 行数(必填)
to path
应该使用的内部(默认为line to
)- 箭头之间的默认距离(默认为其他默认距离,类似于
every stack
)。
代码
\documentclass[varwidth]{standalone}
\usepackage{tikz-cd}
\tikzset{edges/.style={every edge/.append style={#1}}}
\tikzcdset{
diagrams={>={Straight Barb[scale=0.5]}},
stack distance/.initial=+.38ex,
every stack/.style=,
stacks/.style={/tikz/commutative diagrams/every stack/.append style={#1}},
stack/do path/.style={
/tikz/insert path/.expanded={(\tikztostart)edge(\tikztotarget)}},
stack >/.style={
/tikz/commutative diagrams/stack with setting=
{->, shorten <=5\pgflinewidth}{<-, shorten >=5\pgflinewidth}{#1}},
stack </.style={
/tikz/commutative diagrams/stack with setting=
{<-, shorten >=5\pgflinewidth}{->, shorten <=5\pgflinewidth}{#1}},
stack/.style={
/tikz/to path={foreach \stackArrow in {1,...,#1}{[
commutative diagrams/shift left/.default=%
\pgfkeysvalueof{/tikz/commutative diagrams/stack distance},
commutative diagrams/shift left={\pgfinteval{\stackArrow-(#1)/2}},
line to,
commutative diagrams/every stack/.try,
commutative diagrams/stack/every \ifodd\stackArrow odd\else even\fi/.try,
commutative diagrams/stack/do path]}}},
stack with setting/.style n args={3}{
/tikz/commutative diagrams/stack/every odd/.append style={#1},
/tikz/commutative diagrams/stack/every even/.append style={#2},
/tikz/commutative diagrams/stack={#3}},
stack alt/.style={
/tikz/commutative diagrams/stack with setting={}
{/tikz/commutative diagrams/stack/do path/.style={
/tikz/insert path/.expanded={(\tikztotarget)edge(\tikztostart)}}}
{#1}}}
\begin{document}
Consider the simplicial set~\( X_{\bullet} \) given by
\[\begin{tikzcd}
\cdots \ar[stack >=9, r]
& X_3 \ar[stack <=7, r]
& X_2 \ar[stack <=5, r]
& X_1 \ar[stack >=3, r]
& X_0
\end{tikzcd}\]
\[\begin{tikzcd}[
stack/every odd/.append style={edges=red},
stack/every even/.append style={edges=green}]
\cdots \ar[stack alt=9, r, ->.>>, shorten <=15\pgflinewidth]
& X_3 \ar[stack <=7, r]
& X_2 \ar[stack <=5, d, stacks=bend right, stack distance=+.5ex]
\ar[stack >=3, d, stacks=bend left, stack distance=+.5ex,
stack/every odd/.append style={edges=blue},
stack/every even/.append style={edges=orange}]\\
&
& X_1 \ar[stack >=3, r]
& X_0
\end{tikzcd}\]
\end{document}
输出
答案3
目前,我的想法如下。它并没有真正优化,但它可以让你了解如何实现自动化。实际上,改进新命令\arrowstack
以使其只提供所需的箭头数量并不是什么大问题。
\documentclass{article}
\usepackage{tikz}
\usepackage{ifthen}
\usepackage{intcalc}
\usetikzlibrary{positioning,calc}
\newcommand{\arrowstack}[1]{\ifthenelse{\equal{\intcalcMod{\i}{2}}{0}}%
{\draw[-stealth] ($(#1)+(-.5,\s pt)$) --++ (1,0);}%
{\draw[stealth-] ($(#1)+(-.5,\s pt)$) --++ (1,0);}
}
\begin{document}
\begin{tikzpicture}[node distance=.75cm]
\node (X) {$\dots$};
\coordinate[right= of X] (X');
\foreach \s [count=\i] in {12,9,6,3,0,-3,-6,-9,-12}
{\arrowstack{X'}}
\node[right= of X'] (X3) {$X_3$};
\coordinate[right= of X3] (X3');
\foreach \s [count=\i] in {9,6,3,0,-3,-6,-9}
{\arrowstack{X3'}}
\node[right= of X3'] (X2) {$X_2$};
\coordinate[right= of X2] (X2');
\foreach \s [count=\i] in {6,3,0,-3,-6}
{\arrowstack{X2'}}
\node[right= of X2'] (X1) {$X_1$};
\coordinate[right= of X1] (X1');
\foreach \s [count=\i] in {3,0,-3}
{\arrowstack{X1'}}
\node[right= of X1'] (X0) {$X_0$};
\end{tikzpicture}
\end{document}
答案4
我最终使用了以下非常简单的解决方案。我意识到使用普通的 LaTeX 命令比解析 TikZ 键更容易。浮点运算显然可以简化,但会失去对正在发生的事情的清晰理解。
请随意提出改进建议(例如,维度表达式可能比浮点表达式更好?)。
\documentclass{article}
\usepackage{tikz-cd}
\tikzcdset{
diagrams={>={Straight Barb[scale=0.5]}}
}
\ExplSyntaxOn
\NewDocumentCommand\stackar{ m O{} }
{
\int_step_inline:nn { #1 }
{
\int_if_odd:nTF { ##1 }
{
\ar[->,yshift=(#1-1)/2*1.5pt - (##1-1)*1.5pt,#2]
}
{
\ar[<-,yshift=(#1-1)/2*1.5pt - (##1-1)*1.5pt,#2]
}
}
}
\ExplSyntaxOff
\begin{document}
\[\begin{tikzcd}
\cdots
\stackar{9}[r]
&
X_3
\stackar{7}[r]
&
X_2
\stackar{5}[r]
&
X_1
\stackar{3}[r]
&
X_0
\end{tikzcd}\]
\end{document}