使用 tikz-cd 将多个方向交替的箭头堆叠在一起

使用 tikz-cd 将多个方向交替的箭头堆叠在一起

在数学中绘制单纯形图时,结果通常如下所示:

\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 pathline toedgeto path

我们不需要直接使用edge路径,而是需要扩展\tikztostart/,\tikztotarget因为否则我们会陷入类似的问题,因为它会尝试执行类似的操作\def\tikztostart{\tikztostart},而这是没有用的。

最后,我们将应用stack/every evenstack/every odd

在 中stack,键的默认值shift left(通常为.56ex)将设置为 的值stack distance。该值将与上述表达式的结果相乘\pgfinteval,以获得最终要移动的距离。(您也可以指定一个绝对尺寸,这将忽略默认值。)


不过,我不会直接使用,而是建议stack使用适当的样式stack >stack <设置。只要它只是(可以即时重新定义),就可以相当轻松地绘制反向箭头。stack/every oddstack/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

可用于反转箭头尖端规范,但这是否真的有必要取决于用例的复杂性。


笔记:

  1. shift left尽管基础计算相同,但对每支箭进行整体评估。可以手动将其分为两部分以加快流程。

  2. 所有样式都将应用于 TikZ 命名空间/tikz,这使得使用来自 TikZ-CD 的键(在命名空间中)变得有点困难,/tikz/commutative diagrams但我不确定哪些 TikZ-CD 键实际上可以在这里使用。

  3. 堆叠箭头沿线的节点将被吞噬。

  4. 我看到了一种stack以某种方式接受三个参数的风格:

    1. 行数(必填)
    2. to path应该使用的内部(默认为line to
    3. 箭头之间的默认距离(默认为其他默认距离,类似于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}

在此处输入图片描述

相关内容