绘制凯莱树

绘制凯莱树

我是 Tikz 新手,如果这很琐碎,请见谅。我想在 Tikz 中绘制一个凯莱树,看起来就像[这我在 Google 上找到的图像一样。

问题是我希望交替层是交替的节点形状——圆形和方形节点。其次,我希望圆形节点的度为 3,方形节点的度为 5。有没有办法生成这个,而不必手动将每个节点放置在我想要的位置?

答案1

第二个命题(使用 marsupilam 建议的两个嵌套循环)

(注意:我之前的提议没有使用正确的度数定义。感谢 cfr 和 marsupilam 注意到了这一点。)

这里有一个pdflatex解决方案。第一个节点(中心)是c-0-1。第一级节点是c-1-1c-1-2c-1-3...第四级节点是c-4-1,,c-4-2...和c-4-96

\documentclass[tikz]{standalone}
\tikzset{
  common/.style={draw,name=#1,node contents={},inner sep=0,minimum size=3},
  disc/.style={circle,common=#1},
  square/.style={rectangle,common={#1}},
}
\begin{document}
\begin{tikzpicture}
  \draw (0,0) node[disc=c-0-1];
  \xdef\radius{0cm}
  \xdef\level{0}
  \xdef\nbnodes{1}
  \xdef\degree{(3+1)} % special degree just for the root node
  \foreach \ndegree/\form in {5/square,3/disc,5/square,3/disc}{
    \pgfmathsetmacro\nlevel{int(\level+1)}
    \pgfmathsetmacro\nnbnodes{int(\nbnodes*(\degree-1))}
    \pgfmathsetmacro\nradius{\radius+1cm}
    \draw[red] (c-0-1) circle(\nradius pt);
    \foreach \div in {1,...,\nnbnodes} {
      \pgfmathtruncatemacro\src{((\div+\degree-2)/(\degree-1))}
      \path (c-0-1) ++({\div*(360/\nnbnodes)-180/\nnbnodes}:\nradius pt) node[\form=c-\nlevel-\div];
      \draw (c-\level-\src) -- (c-\nlevel-\div);
    }
    \xdef\radius{\nradius}
    \xdef\level{\nlevel}
    \xdef\nbnodes{\nnbnodes}
    \xdef\degree{\ndegree}
  }
\end{tikzpicture}
\end{document}

在此处输入图片描述

答案2

我听从了 JPi 和 cfr 的建议,选择了 lua。

(编辑:我认为这种风格给人一种盛开的樱花的感觉。)

输出

在此处输入图片描述

tikz

使用以下方式编译lualatex

\RequirePackage{luatex85}
\documentclass[12pt,tikz]{standalone}
\usepackage{luacode}
\begin{luacode*}
  print=tex.print
  makeGrow=require("makeGrow.lua")
\end{luacode*}
\begin{document}
\tikzset
{
  odd/.style = {draw=blue!50!pink,circle,ultra thick},
  even/.style = {draw=red,line width=3pt,minimum size=4mm},
}
\begin{tikzpicture}[scale=20]
  \directlua{makeGrow.make(4)}
\end{tikzpicture}
\end{document}

lua

-- makeGrow.lua
-- mode = {totalAngle = totalAngle, number = numberOfChildren, radius = lengthOrArm}
local function makeChildren(parent,mode,f)
  local f = f or function() return end
  parent.children = {}
  for k=1,mode.number do
    parent.children[k] = {
      generation = parent.generation + 1,
      angle = parent.angle + (k-.5*(mode.number+1)) * mode.totalAngle / mode.number,
      parent = parent,
      label = parent.label .. tostring(k),
    }
    f(parent.children[k]) -- we smuggled f in there to recurse.
  end
end
local function tikzNode(node)
  return {
    name = "(myNode-" .. node.label .. ")",
    style = "[" .. ({"even","odd"})[node.generation%2+1] .. "]",
  }
end
local function draw(node,mode)
  if node.parent then
    print([[\path]] .. tikzNode(node.parent).name .. " -- ++(" .. node.angle .. ":" ..  mode.radius .. ") node" .. tikzNode(node).style .. tikzNode(node).name .. "{} ;")
    print([[\draw]] .. tikzNode(node.parent).name .. " -- " .. tikzNode(node).name .. ";")
  else
    print([=[\node[odd]]=] .. tikzNode(node).name .. "at (0,0) {};")
  end
end
local function drawMakeChild(n,modes)
  local function recurse(node)
      draw(node,modes[node.generation-1])
      if node.generation<=n then
        makeChildren(node,modes[node.generation],recurse)
      end
  end
  return recurse
end
local function computeMode(generation,hash)
  local number = ({5,3})[generation%2+1] -- alternating number of children
  local radius = hash.radRatio^generation
  local totalAngle = 360 * hash.angRatio^(generation-1)
  return {totalAngle = totalAngle , number = number, radius = radius}
end
local function make(n)
  local origin = {
    generation = 1,
    angle = 0,
    label = ""
  }
  local modes = {}
  for k=1,n do modes[k] = computeMode(k,{angRatio=.98,radRatio=.35}) end
  drawMakeChild(n,modes)(origin)
end
return {make=make}

编辑:使用 Paul 的风格

输出

在此处输入图片描述

\def\N{4}

在此处输入图片描述

tikz

\RequirePackage{luatex85}
\documentclass[12pt,tikz]{standalone}
\usepackage{luacode}
\begin{luacode*}
  print=tex.print
  makeGrow=require("paulStyle.lua")
\end{luacode*}
\begin{document}
\tikzset
{
  commons/.style={fill=white},
  odd/.style = {draw=red,circle,ultra thick,commons},
  even/.style = {draw=blue,ultra thick,minimum size=3mm,commons},
}
\def\N{3}
\begin{tikzpicture}[scale=4]
  \foreach \k in {1,...,\N} \draw[help lines,purple] (0,0) circle (\k);
  \directlua{makeGrow.make(\N)}
\end{tikzpicture}
\end{document}

lua

-- paulStyle.lua
local function makeChildren(parent,multiplicity,f)
  parent.children = {}
  for k=1,multiplicity do
    parent.children[k] = {
      generation = parent.generation + 1,
      cumProd = parent.cumProd * multiplicity,
      angle = parent.angle + ((k-.5)/multiplicity-.5 ) * 360 / parent.cumProd,
      parent = parent,
      label = parent.label .. tostring(k),
    }
    f(parent.children[k]) -- we smuggled f in there to recurse.
  end
end
local function tikzNode(node)
  return {
    name = "(myNode-" .. node.label .. ")",
    style = "[" .. ({"odd","even"})[node.generation%2+1] .. "]",
  }
end
local function draw(node)
  if node.parent then
    print([[\node]].. tikzNode(node).style .. tikzNode(node).name .. " at " .. "(" .. node.angle .. ":" ..  node.generation .. ") {};")
    print([[\draw]] .. tikzNode(node.parent).name .. " -- " .. tikzNode(node).name .. ";")
  else
    print([=[\node]=] .. tikzNode(node).style .. tikzNode(node).name .. "at (0,0) {};")
  end
end
local function drawMakeChild(n,multiplicities)
  local function recurse(node)
      draw(node,multiplicities[node.generation-1])
      if node.generation<n then
        makeChildren(node,multiplicities[node.generation],recurse)
      end
  end
  return recurse
end
local function make(n)
  local origin = {
    cumProd = 1,
    generation = 0,
    angle = 0,
    label = ""
  }
  local multiplicities = {}
  for k=1,n do multiplicities[k-1] = ({5,3})[k%2+1] end
  drawMakeChild(n,multiplicities)(origin)
end
return {make=make}

答案3

这是我的。尽管我发表了评论,但事实上,这是纯钛Z 没有任何内容graphs。也就是说,你可以用 LaTeX、pdfLaTeX 或其他任何东西来编译它,它应该可以工作。(如果你愿意的话,你可以把它转换成 TeX 版本。)我计划用它graphs来绘制连接,这不需要 LuaTeX。但不知何故,我从来没有这样做过,因为一旦我放置了节点,这似乎更容易。

更改外循环的最大值以增加层数。我练习时使用了 3 和 4 的值,但这里对“显示”版本使用了 5。复杂之处在于,第一个节点有 3 个子节点,以便具有 3 度,而后面的 3 度节点只需要 2 个,因为它们有一个父节点。这需要对前 3 个层进行特殊处理,以使事物正确排列。之后,后续层只是 TeX 容量和您的耐心的问题。

用于\showtrue绘制标记版本,以用于调试目的。

\documentclass[border=10pt]{standalone}
\usepackage{tikz}
\newif\ifshow\showfalse % set true to debug
\begin{document}
\begin{tikzpicture}
  % \i: level \s: shape \a: angle \t: turn \d: degree -1 \j: number \m: rotation \g: group \k: number \c: colour
  \def\j{1}\def\dlast{1}\def\tlast{0}
  \foreach \i [remember=\i as \ilast] in {0,...,5}
  {
    \draw [darkgray] (0,0) circle (\i cm);
    \pgfmathsetmacro\a{360/\j}
    \ifodd\i\def\s{}\def\c{magenta}\def\d{4}\pgfmathsetmacro\t{\tlast-1.5*\a}\else\def\c{blue!50!cyan}\def\s{circle}\def\d{2}\pgfmathsetmacro\t{\tlast-2.5*\a}\fi
    \ifnum\i=1\pgfmathsetmacro\t{-\a}\fi
    \ifnum\i=0\def\d{3}\def\t{0}\fi
    \foreach \k [evaluate=\k as \m using {(\k*\a)+\t}, evaluate=\k as \g using {int((floor((\k-1)/\dlast)))}, count=\n from 0 ] in {1,...,\j}
    {
      \ifshow\def\tempa{n-\i-\g-\n:\k}\else\let\tempa\relax\fi
      \node (n-\i-\n) [draw, fill, \c, \s, minimum size=2.5pt, inner sep=0pt, label={[font=\tiny]{\tempa}} ] at (\m:\i cm) {};
      \ifnum\i>0 \draw [darkgray] (n-\i-\n) -- (n-\ilast-\g); \fi
    }
    \pgfmathsetmacro\j { \j*\d }
    \global\let\j\j
    \global\let\dlast\d
    \pgfmathsetmacro\tlast{(\i==1) ? 0 : (\t+\a) }
    \global\let\tlast\tlast
  }
\end{tikzpicture}
\end{document}

凯莱树

这是一个更加参数化的版本,带有(可选)特殊效果。

  • circle colour=<colour>将为圆圈提供统一的颜色;
  • circle colours=<colour>:<colour>将使圆圈呈现出两种颜色之间的不同色调;
  • square colour=<colour>将为圆圈提供统一的颜色;
  • square colours=<colour>:<colour>将使方块呈现出两种颜色之间的不同色调;
  • circle degree=<integer>设置圆形节点的度数;
  • square degree=<integer>设置方形节点的度;
  • connection colour<colour>设置圆圈和背景的颜色;
  • levels=<integer>设置级别数。

如果你不想要背景,只需删除该行

\scoped[on background layer]{\shade [inner color=lcol!5, outer color=lcol!35] circle (\l cm);}

在这种情况下,您不需要该backgrounds库,可以删除

\usetikzlibrary{backgrounds}

如果你不需要它做其他任何事情。

\documentclass[border=10pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{backgrounds}
\newif\ifshow\showfalse % set true to debug
\begin{document}
\begin{tikzpicture}
  % \i: level \s: shape \a: angle \t: turn \d: degree -1 \j: number \m: rotation \g: group \k: number ncol: colour
  \tikzset{
    circle degree/.code={\def\dr{#1}\pgfmathsetmacro\dc{int(#1-1)}\pgfmathsetmacro\tc{((\dc-1)/2)+1}},
    square degree/.code={\pgfmathsetmacro\ds{int(#1-1)}\pgfmathsetmacro\ts{((\ds-1)/2)+1}},
    levels/.code={\pgfmathsetmacro\l{int(#1-1)}},
    connection colour/.code={\colorlet{lcol}{#1}},
    circle colours/.code args={#1:#2}{\colorlet{ccol1}{#1}\colorlet{ccol2}{#2}},
    square colours/.code args={#1:#2}{\colorlet{scol1}{#1}\colorlet{scol2}{#2}},
    circle colour/.style={circle colours=#1:#1},
    square colour/.style={square colours=#1:#1},
    circle degree=3,
    square degree=5,
    levels=6,
    connection colour=darkgray,
    square colours=green:blue,
    circle colours=blue:magenta,
  }
  \def\j{1}\def\dlast{1}\def\tlast{0}
  \foreach \i [remember=\i as \ilast] in {0,...,\l}
  {
    \draw [lcol] (0,0) circle (\i cm);
    \pgfmathsetmacro\a{360/\j}
    \pgfmathsetmacro\z{6.5-\i*.8}
    \ifodd\i\def\s{}\colorlet{ncol1}{scol1}\colorlet{ncol2}{scol2}\let\d\dc\pgfmathsetmacro\t{\tlast-\ts*\a}\else\colorlet{ncol1}{ccol1}\colorlet{ncol2}{ccol2}\def\s{circle}\let\d\ds\pgfmathsetmacro\t{\tlast-\tc*\a}\fi
    \ifnum\i=1\pgfmathsetmacro\t{-\a}\fi
    \ifnum\i=0\let\d\dr\def\t{0}\fi
    \foreach \k [evaluate=\k as \m using {(\k*\a)+\t}, evaluate=\k as \g using {int((floor((\k-1)/\dlast)))}, count=\n from 0, evaluate=\k as \p using { (\k > \j/2) ? ((1-\k/\j)*200) : ((200/\j)*\k)} ] in {1,...,\j}
    {
      \ifshow\def\tempa{n-\i-\g-\n:\k}\else\let\tempa\relax\fi
      \node (n-\i-\n) [draw, fill, ncol1!\p!ncol2, \s, minimum size=\z pt, inner sep=0pt, label={[font=\tiny]{\tempa}} ] at (\m:\i cm) {};
      \ifnum\i>0 \draw [ncol1!\p!ncol2] (n-\i-\n) -- (n-\ilast-\g); \fi
    }
    \pgfmathsetmacro\j { \j*\d }
    \global\let\j\j
    \global\let\dlast\d
    \pgfmathsetmacro\tlast{(\i==1) ? 0 : (\t+\a) }
    \global\let\tlast\tlast
  }
  \scoped[on background layer]{\shade [inner color=lcol!5, outer color=lcol!35] circle (\l cm);}
\end{tikzpicture}
\end{document}

更高级的版本

相关内容