TikZ 中自由群的凯莱图

TikZ 中自由群的凯莱图

在不使用绝对位置并“手动”完成所有操作的情况下创建类似于下图(自由群的凯莱图)的 TikZ 图片的最佳方法是什么?

该图片具有非常递归的结构,因此我认为使用自动生成可能是一种更有效的方法。我说得对吗?

二元素生成的自由群的凯莱图

答案1

下面是使用该库的示例。它需要该库的lindenmayersystems最新版本。PGFarrows.meta

\documentclass[tikz,border=5]{standalone}
\usetikzlibrary{lindenmayersystems,arrows.meta}
\newcount\quadrant
\pgfdeclarelindenmayersystem{cayley}{
  \rule{A -> B [ R [A] [+A] [-A] ]}
  \symbol{R}{ \pgflsystemstep=0.5\pgflsystemstep } 
  \symbol{-}{
    \pgfmathsetcount\quadrant{Mod(\quadrant+1,4)}
    \tikzset{rotate=90}
  }
  \symbol{+}{
    \pgfmathsetcount\quadrant{Mod(\quadrant-1,4)}
    \tikzset{rotate=-90}
  }
  \symbol{B}{
    \draw [dot-cayley] (0,0) -- (\pgflsystemstep,0) 
       node [font=\footnotesize, midway, 
         anchor={270-mod(\the\quadrant,2)*90}, inner sep=.5ex] 
           {\ifcase\quadrant$a$\or$b$\or$a^{-1}$\or$b^{-1}$\fi};
    \tikzset{xshift=\pgflsystemstep}
  }
}
\tikzset{
  dot/.tip={Circle[sep=-1.5pt,length=3pt]}, cayley/.tip={Stealth[]dot[]}
}
\begin{document}
\begin{tikzpicture}
\draw l-system [l-system={cayley, axiom=[A] [+A] [-A] [++A], step=5cm, order=4}];
\end{tikzpicture}
\end{document}

在此处输入图片描述

如果不需要标签(或箭头),那么它可以更简单:

\documentclass[tikz,border=5]{standalone}
\usetikzlibrary{lindenmayersystems}
\pgfdeclarelindenmayersystem{cayley}{
  \rule{F -> F [ R [F] [+F] [-F] ]}
  \symbol{R}{
    \pgflsystemstep=0.5\pgflsystemstep
  } 
}
\begin{document}
\begin{tikzpicture}
\draw l-system [l-system={cayley, axiom=[F] [+F] [-F] [++F], step=5cm, order=6}];
\end{tikzpicture}
\end{document}

在此处输入图片描述

并使用:

\draw l-system [l-system={cayley, axiom=[F] [+F] [-F] [++F] [--F],
  angle=72, step=5cm, order=6}];

结果是

在此处输入图片描述

\draw l-system [l-system={cayley, axiom=[F] [+F] [-F], angle=120,step=5cm, order=6}];

给出

在此处输入图片描述

最后,根据评论中的要求,这里有一个版本,它以度数开始90,在第一次迭代后切换到自定义角度。在这种情况下,标签的逻辑不清楚,所以线条用它们的角度标记(为此需要一点混乱):

\documentclass[tikz,border=5]{standalone}
\usetikzlibrary{lindenmayersystems,arrows.meta,calc}
\def\tikzpoint{\csname tikz@scan@one@point\endcsname\pgfpointtransformed}
\pgfdeclarelindenmayersystem{cayley}{
  \rule{A -> B [ R [A] [+A] [-A] ]}
  \symbol{R}{ \pgflsystemstep=0.5\pgflsystemstep } 
  \symbol{>}{\tikzset{rotate=90}}
  \symbol{B}{
    \pgfmathanglebetweenpoints{\tikzpoint(0,0)}{\tikzpoint(\pgflsystemstep,0)}
    \pgfmathparse{int(round(\pgfmathresult))}%
    \let\lineangle=\pgfmathresult
    \draw [dot-cayley] (0,0) -- (\pgflsystemstep,0) 
       node [pos=2/3, transform shape, font=\tiny, above] {$\lineangle$};    
    \tikzset{xshift=\pgflsystemstep}
  }
}
\tikzset{
  dot/.tip={Circle[sep=-1.5pt,length=3pt]}, cayley/.tip={Stealth[]dot[]}
}
\begin{document}
\begin{tikzpicture}
\draw l-system [l-system={cayley, axiom=[A] [>A] [>>A] [>>>A], step=5cm, angle=60, order=4}];
\end{tikzpicture} 
\end{document}

在此处输入图片描述

答案2

这是一种使用的可能性tikzmath,但可能不是“最佳方式”。

\documentclass[border=7mm]{standalone}
\usepackage{tikz}
\usetikzlibrary{calc,math}
\tikzmath{
  % function that draws the edge (\x1,\y1) -- (\x2,\y2)
  %   with label depending on: \l=0 => "a", \l=1 => "b"
  %   \c is a count downd counter, we stop at \c=0
  function drawgen(\x1,\y1,\x2,\y2,\c,\l)
    coordinate \p;
    \p1 = (\x1 pt,\y1 pt);\p2 = (\x2 pt,\y2 pt);
    if (\l < .5) then {let \g=a;let \a=above;} else {let \g=b;let \a=right;};
    if (\x1 > \x2 || \y1 > \y2 ) then {let \e=^{-1};} else {let \e={};};
    {
      \draw[draw=red,-latex] (\px1,\py1)  -- node[\a,pos=.55]{$\g\e$} (\px2,\py2);
    };
    if (\c > 0) then {
      \c = \c-1;
      \p3 = 1.5*(\px2,\py2)-.5*(\px1,\py1);
      drawgen(\px2,\py2,\px3,\py3,\c,\l);
      \p3 = (\px2,\py2)+.5*(\py2,\px2)-.5*(\py1,\px1);
      drawgen(\px2,\py2,\px3,\py3,\c,1-\l);
      \p3 = (\px2,\py2)-.5*(\py2,\px2)+.5*(\py1,\px1);
      drawgen(\px2,\py2,\px3,\py3,\c,1-\l);
    };
  };
}
\begin{document}
\begin{tikzpicture}
  \tikzmath{
    \d=5cm; \n=3;
    drawgen(0,0,\d,0,\n,0);
    drawgen(0,0,-\d,0,\n,0);
    drawgen(0,0,0,\d,\n,1);
    drawgen(0,0,0,-\d,\n,1);
  }
\end{tikzpicture}
\end{document}

在此处输入图片描述

更新 1:在我的第一个版本中,我忘记了$^{-1}$。所以我添加了以下行:

if (\x1 > \x2 || \y1 > \y2 ) then {let \e=^{-1};} else {let \e={};};

-1将边缘向下和向右的功率设置为。当出现一些重叠时,我调整了边缘标签的位置。

更新2:由于每个人都采用自己的未标记方法,因此这里采用mathtikz 库的方法。

\documentclass[border=7mm]{standalone}
\usepackage{tikz}
\usetikzlibrary{calc,math}
\begin{document}
\begin{tikzpicture}[scale=.02pt]
\tikzmath{
  \power=2; \deviation=90; \numsteps=7; let \startcolor=green; let \endcolor=red;
  function branch(\x,\y,\rotate,\step){ \step=\step-1;
    if (\step >= 0) then { \mix = int(100*\step/(\numsteps-1));
      {\draw[shift={(\x pt,\y pt)},scale=\power^\step, rotate=\rotate, color=\startcolor!\mix!\endcolor]
        (0,0)--(1,0) coordinate(newbase);};
      coordinate \b; \b1 = (newbase);
      for \a in {-\deviation,0,\deviation}{
        branch(\bx1,\by1,mod(\rotate+\a,360),\step);
      };
    };
  };
  for \angle in {0,90,180,-90}{
    branch(0,0,\angle,\numsteps);
  };
}
\end{tikzpicture}
\end{document}

在此处输入图片描述

使用以下参数:

\power=2.3; \deviation=70; \numsteps=7; let \startcolor=magenta; let \endcolor=black;

在此处输入图片描述

使用以下参数:

\power=2.5; \deviation=120; \numsteps=7; let \startcolor=black; let \endcolor=orange;

在此处输入图片描述

答案3

第二个提议

如果不需要箭头和标签,这里有一个非常简单的递归解决方案,使用单路径,在订单 8 处(如果您尝试订单 9,pdflatex则会停止并出现错误TeX capacity exceeded...:)。

\documentclass[tikz]{standalone}
\usetikzlibrary{calc}
\newcommand\caley[3]{% level, length, angle
  \ifnum0<#1 \pgfextra{
    \pgfmathtruncatemacro\newlev{#1-1}
    \pgfmathsetmacro\len{#2}
    \pgfmathtruncatemacro\angle{#3}
  } -- ++(\angle:\len pt)
  \foreach \a in {-90,0,90}{\caley{\newlev}{\len/2}{\angle+\a}} ++(\angle:-1*\len pt)
  \fi%
}
\begin{document}
\tikz \draw[red] (0,0) \foreach \a in {-90,0,90,180}{\caley{8}{4cm}{\a}};
\end{document}

在此处输入图片描述

第一个命题

这是一个使用递归的解决方案旋转宏:

在此处输入图片描述

\documentclass[tikz]{standalone}
\usepackage{ifthen}
\usetikzlibrary{calc}
\tikzset{
  my label/.style={font=\scriptsize,inner sep=2pt},
  a/.style={my label,above,node contents={$a$}},
  b/.style={my label,right,node contents={$b$}},
  a-1/.style={my label,above,node contents={$a^{-1}$}},
  b-1/.style={my label,right,node contents={$b^{-1}$}},
}
\newcommand\caley[6]{% level, length, l1, l2, l3, l4
  \ifthenelse{0<#1}{
    \pgfmathtruncatemacro\newlev{#1-1}
    \pgfmathtruncatemacro\len{#2}
    \draw[draw=red,-latex] (0,0) -- (\len pt,0) node[pos=.6,#3] coordinate (O);
    \begin{scope}[shift={(O)}]
      \begin{scope}[rotate=90] \caley{\newlev}{\len/2}{#4}{#5}{#6}{#3} \end{scope}
      \begin{scope}[rotate=0]  \caley{\newlev}{\len/2}{#3}{#4}{#5}{#6} \end{scope}
      \begin{scope}[rotate=-90]\caley{\newlev}{\len/2}{#6}{#3}{#4}{#5} \end{scope}
    \end{scope}
  }{\fill[red] circle(1pt);}
}
\begin{document}
\begin{tikzpicture}
  \begin{scope}[rotate=-90] \caley{4}{4cm}{b-1}{a}{b}{a-1} \end{scope}
  \begin{scope}[rotate=0]   \caley{4}{4cm}{a}{b}{a-1}{b-1} \end{scope}
  \begin{scope}[rotate=90]  \caley{4}{4cm}{b}{a-1}{b-1}{a} \end{scope}
  \begin{scope}[rotate=180] \caley{4}{4cm}{a-1}{b-1}{a}{b} \end{scope}
\end{tikzpicture}
\end{document}

答案4

这只是 MetaPost 的另一次递归尝试,与 Thruston 的不同,也不如他的好(我的标签有些粗糙),但我设法产生了一些在我看来可以接受的东西。要使用 MetaPost 进行编译并将 numbersystem 选项设置为“double”:

input latexmp; setupLaTeXMP(mode = rerun, textextlabel = enable);
u := 1cm ; % Scaling
m := 3; % Number of recursions 
d := m;% Depth labelling
labeloffset := 2bp;

% the recursive macro
vardef free_group(expr loc, v, n) = 
  save w ; pair w; w = v rotated 90;
  if (n>0): 
    drawdblarrow loc - v -- loc + v withcolor red;
    drawarrow loc -- loc + w withcolor red;
    % labels (the hard part)
    if (m-n)<d:
      if (xpart v) > epsilon :
        label.top("$a$", loc + 0.5v);
        label.rt("$b$", loc + 0.5w);
        label.top("$a^{-1}$", loc - 0.5v);
      elseif (xpart v) < -epsilon:
        label.top("$a^{-1}$", loc + 0.5v);
        label.rt("$b^{-1}$", loc + 0.5w);
        label.top("$a$", loc - 0.5v);
      elseif (xpart w) > epsilon:
        label.rt("$b^{-1}$", loc + 0.5v);
        label.top("$a$", loc + 0.5w);
        label.rt("$b$", loc - 0.5v);
      else:
        label.top("$a^{-1}$", loc + 0.5w);
        label.rt("$b$", loc + 0.5v);
        label.rt("$b^{-1}$", loc - 0.5v);
      fi;
    fi;
    free_group(loc + v, 0.5v rotated -90, n-1) ;
    free_group(loc + w, 0.5v, n-1); 
    free_group(loc - v, 0.5v rotated 90, n-1);
  fi;
enddef;

beginfig(1);
  pair v, w, loc; v = (6u, 0) ; w = v rotated 90; loc = origin; 
  % First "star"
  draw origin withpen pencircle scaled 3bp withcolor red;
  drawdblarrow -v -- v withcolor red; drawdblarrow -w -- w withcolor red;
  label.top("$a$", 0.5v); label.top("$a^{-1}$", -0.5v); 
  label.rt("$b$", 0.5w); label.rt("$b^{-1}$", -0.5w); 
  % Recursion calls on each apex
  free_group(v, -0.5w, m);
  free_group(w, 0.5v, m);
  free_group(-v, 0.5w, m);
  free_group(-w, -0.5v, m);
endfig; 

end.

递归次数 m 和标记深度 d 均设置为 3,结果如下:

在此处输入图片描述

当 m = 8 且 d = 2 时,它会产生此图片。当 m=8 以上时,MetaPost 可能仍能工作,但 MPtoPDF 会失败。

在此处输入图片描述

编辑奇怪的是,将前面的代码包含在 LuaLaTeX 文件中比使用 MPtoPDF 得到的结果更好。使用 LuaLaTeX,我成功地产生了高达 11 级递归的结果。生成的 PDF 文件非常大:11.2 Mo,所以我不敢在这里上传它的位图版本!该程序甚至可以超过 11 级递归,但这会花费不合理的时间,而且图形的外观无论如何都不会改变。

相关内容