TikZ 中的结图

TikZ 中的结图

这个问题导致了一个新的方案的出现:
spath3

(从技术上讲,这个问题引出了子包spath3包;该包提供了构建spath3TikZ 库的一些基础,并且 TikZ 库与该库捆绑在一起。)knotsspath3

另请参阅博客文章:我怎样在 TeX 中画一个结?

我需要使用 TikZ 绘制一些结/辫状图。这些图将由曲线族构成,这些曲线族似乎在某些地方相互交叉。在每个交叉点处,需要明显看出哪条线与哪条线交叉,因此我使用 TikZ 的双重功能,在表示曲线的细黑线下方绘制一条粗白线。这样,如果我在“底部”线段之后绘制“顶部”线段,我就会得到正确的效果。

但绘制这些图表仍然很麻烦。特别是,我可能在一张图中画出两条连续的曲线,第一条曲线在某些地方位于第二条曲线之上,而在其他地方则位于第二条曲线之下。所以我不能简单地先画出“下方”曲线:我必须分别画出曲线的不同部分。

为了便于编程,我非常希望将每条曲线的所有代码放在一起。实现这一点的一种方法是,我可以独立更改多点路径命令的每个段的层。这个想法可能是这样的:

\draw (1) to [layer=middlelayer] (2) to [layer=toplayer] (3) to [layer=bottomlayer] (4);

这可能吗?或者有更好的方法可以达到我想要的效果?

不幸的是,我的图表需要比Andrew Stacey 的辫子包。它们不是辫子群生成器整齐地堆叠在一起的形式。这是我需要的东西的一个例子:

在此处输入图片描述

答案1

更新:现在可以在 CTAN 上使用(见上文)。错误修复和改进通常可在github在上传到 CTAN 之前。请注意,该存储库相当混乱,相关.dtx文件名为spath3.dtx。这将生成全部必要的文件。


更新:在与 Jamie 在聊天室进行广泛讨论后,代码进行了相当大的修改从答案到包裹。现在可以从TeX-SX Launchpad 项目。所需文件为knots.dtxspath.dtx。该文件knots_test.tex包含一些示例代码。.sty要从 创建文件.dtx,请运行tex <file>.dtx。或者,在knots_test.tex启用 shell escapes 的情况下运行 latex(或 pdflatex)将自动生成.sty文件。


好的,这是我第一次尝试实现我们在聊天中讨论的内容。它确实有些粗糙,而且我绝对不能保证它不会用 TeX 替换你的猫。

代码:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{intersections}

\makeatletter
\newcounter{knot@strings}
\newif\ifknot@draftmode
\tikzoption{save knot path}{\tikz@addmode{\pgfsyssoftpath@getcurrentpath\knot@tmppath\expandafter\global\expandafter\let#1=\knot@tmppath}}
\tikzoption{use knot path}{\tikz@addmode{\expandafter\pgfsyssoftpath@setcurrentpath#1}}
\tikzset{
  knot/draft mode/.is if=knot@draftmode
}
\newenvironment{knot}[1][]{%
  \tikzset{#1}%
  \setcounter{knot@strings}{0}}{%
  \foreach \knot@str in {1,...,\the\value{knot@strings}} {
    \expandafter\expandafter\expandafter\draw\expandafter\expandafter\expandafter[\csname knot@string@opts@\knot@str\endcsname,use knot path=\csname knot@string@\knot@str\endcsname] (0,0);
    \ifknot@draftmode
    \begingroup
    \let\pgfsyssoftpath@movetotoken=\pgfqpoint
    \let\pgfsyssoftpath@linetotoken=\pgfqpoint
    \let\pgfsyssoftpath@curvetotoken=\pgfqpoint
    \let\pgfsyssoftpath@curvetosupportatoken=\pgfqpoint
    \let\pgfsyssoftpath@curvetosupportbtoken=\pgfqpoint
    \csname knot@string@\knot@str\endcsname
    \global\pgf@xa=\pgf@x
    \global\pgf@ya=\pgf@y
    \endgroup
    \node[circle,fill=white,fill opacity=.5] at (\pgf@xa,\pgf@ya) {\knot@str};
\fi
  }
  \pgfmathtruncatemacro{\knot@stam}{\the\value{knot@strings}-1}
  \foreach \knot@sta in {1,...,\knot@stam} {
    \pgfmathtruncatemacro{\knot@stap}{\knot@sta + 1}
    \foreach \knot@stb in {\knot@stap,...,\the\value{knot@strings}} {
      \pgfintersectionofpaths{\expandafter\pgfsetpath\csname knot@string@\knot@sta\endcsname}{\expandafter\pgfsetpath\csname knot@string@\knot@stb\endcsname}
    \foreach \intsect in {1,...,\pgfintersectionsolutions} {
      \pgfpointintersectionsolution{\intsect}
      \pgfgetlastxy{\intsectx}{\intsecty}
        \ifknot@draftmode
        \node[circle,fill=white,fill opacity=.5] at (\intsectx,\intsecty)         {\knot@sta-\knot@stb-\intsect};
\else
\@ifundefined{knot@crossing@\knot@sta-\knot@stb-\intsect}{
%\message{\knot@sta-\knot@stb-\intsect not defined}
}{
\pgfmathtruncatemacro{\knot@under}{\csname knot@crossing@\knot@sta-\knot@stb-\intsect\endcsname == \knot@sta ? \knot@stb : \knot@sta}
\expandafter\let\expandafter\knot@over\csname knot@crossing@\knot@sta-\knot@stb-\intsect\endcsname
\pgfscope
\clip (\intsectx,\intsecty) circle[radius=10pt];
    \expandafter\expandafter\expandafter\draw\expandafter\expandafter\expandafter[\csname knot@string@opts@\knot@under\endcsname,use knot path=\csname knot@string@\knot@under\endcsname] (0,0);
    \expandafter\expandafter\expandafter\draw\expandafter\expandafter\expandafter[\csname knot@string@opts@\knot@over\endcsname,use knot path=\csname knot@string@\knot@over\endcsname,white,line width=3\pgflinewidth] (0,0);
    \expandafter\expandafter\expandafter\draw\expandafter\expandafter\expandafter[\csname knot@string@opts@\knot@over\endcsname,use knot path=\csname knot@string@\knot@over\endcsname] (0,0);
\endpgfscope
}
\fi
      }
    }
  }
}
\newcommand{\strand}[1][]{%
  \stepcounter{knot@strings}%
  \expandafter\def\csname knot@string@opts@\the\value{knot@strings}\endcsname{#1}%
\path[save knot path=\csname knot@string@\the\value{knot@strings}\endcsname]}
\newcommand{\crossing}[2]{%
\expandafter\def\csname knot@crossing@#1\endcsname{#2}}

\makeatother

\begin{document}
\begin{tikzpicture}
\begin{knot}[knot/draft mode]
\strand[red,ultra thick]  (0,0) .. controls +(1,0) and +(-1,0) .. ++(2,-1) .. controls +(1,0) and +(-1,0) .. ++(2,1) .. controls +(1,0) and +(-1,0) .. ++(2,-1) .. controls +(1,0) and +(-1,0) .. ++(2,1);
\strand[blue,ultra thick] (0,-1) .. controls +(1,0) and +(-1,0) .. ++(2,1) .. controls +(1,0) and +(-1,0) .. ++(2,-1) .. controls +(1,0) and +(-1,0) .. ++(2,1) .. controls +(1,0) and +(-1,0) .. ++(2,-1);
\crossing{1-2-1}{1}
\crossing{1-2-2}{2}
\crossing{1-2-3}{1}
\crossing{1-2-4}{2}
\end{knot}
\end{tikzpicture}

\begin{tikzpicture}
\begin{knot}
\strand[red,ultra thick]  (0,0) .. controls +(1,0) and +(-1,0) .. ++(2,-1) .. controls +(1,0) and +(-1,0) .. ++(2,1) .. controls +(1,0) and +(-1,0) .. ++(2,-1) .. controls +(1,0) and +(-1,0) .. ++(2,1);
\strand[blue,ultra thick] (0,-1) .. controls +(1,0) and +(-1,0) .. ++(2,1) .. controls +(1,0) and +(-1,0) .. ++(2,-1) .. controls +(1,0) and +(-1,0) .. ++(2,1) .. controls +(1,0) and +(-1,0) .. ++(2,-1);
\crossing{1-2-1}{1}
\crossing{1-2-2}{2}
\crossing{1-2-3}{1}
\crossing{1-2-4}{2}
\end{knot}
\end{tikzpicture}
\end{document}

结果(顶部为草稿版本):

交叉结

希望从示例中可以看出语法足够明显。(原始 PDF 看起来好多了;转换为 PNG 太糟糕了!)。还有很多地方需要改进,但我想在完善之前先看看这是否正确。

相关内容