如果有人以演示 TikZ 图形算法的 lua 编程的方式回答了这个问题,我将保证向该人提供 500 点的赏金。
根据 PGF 3.0 手册第 30 章,layered layout
图形绘制程序中的关键步骤之一是重新排序每个层内的节点,以尽量减少边交叉。我发现,对于我的目的而言,我指定节点的顺序比默认算法创建的顺序更好。
我怎样才能强制 TikZ 不采用这种算法,而是做“愚蠢”的事情,只按照我给出的顺序使用节点?我知道这可能需要一定数量的 Lua 编程,但考虑到我要求的算法的简单性,作为堆栈交换问题的答案可能是可行的。
请注意,这个问题不同于为什么 PGF/TikZ 3.0 默认将我的简单分层图绘制为非平面的?主要是因为我要求一个具体的解决方案(实现替代的、简单的算法)。
一个“最小”工作示例:
% !TEX TS-program = lualatex
\documentclass[tikz,convert,margin=10pt]{standalone}
\usetikzlibrary{graphs,graphdrawing}
\usegdlibrary{layered}
\begin{document}
\tikz[rounded corners] \graph [layered layout]
{
{[same layer] \foreach \i in {1, 2, ..., 16} {
pt\i/$P^0_{\i}$,
}};
{[same layer] \foreach \i in {1, 2, ..., 17} {
l\i/$P^1_{\i}$,
}};
\foreach \i [evaluate=\i as \j using int(\i+1)] in {1,2,...,16} {
pt\i -- l\i,
pt\i -- l\j,
};
Q/$P^1 \times P^1$ [gray];
\foreach \i [evaluate=\i using int(2*\i)] in {1,2,...,7} {
l\i -- [gray, edge node = {node [fill=white] {h}}] Q,
};
pt1 -- [gray] Q;
};
\end{document}
结果:
我希望每层中的节点按顺序编号,无论 TikZ 认为什么可以最大限度地减少交叉。请注意,将前两层变成子图在这里不是一个可接受的解决方案,因为这样从第一层到第三层的边将无法避开中间的节点。
答案1
为 tikz 的图库编写新的布局算法是一项艰巨的任务,即使使用 Lua,即使像您想要的那样“简单”。原因是需要大量的“样板代码”。
相反,很容易“作弊”,使用已经提供的算法之一,但更改其中的一部分。在这种情况下,我们可以取出layered layout
并删除在每一层中执行节点重新排序的部分。
但即使这样也并不那么简单,因为我们不想修改任何 lua“系统文件”。我们希望所有更改都局限于文档文件夹。
我找到了一种方法。我并不认为这是最干净的方法(甚至也不是最简单的方法,也许可以通过 pgf 键更改算法的某些阶段)。但它确实有效(请参阅更新 1以获得更清洁、更美观的方式)。
下面显示了如何做到这一点。
结果
LaTeX 文档
\documentclass[tikz,convert,margin=10pt]{standalone}
\usetikzlibrary{graphs,graphdrawing}
%\usegdlibrary{layered} % <--- This line removed
\usegdlibrary{layered-no-reorder} % <--- This one added
\begin{document}
\tikz[rounded corners] \graph [layered layout]
{
{[same layer] \foreach \i in {1, 2, ..., 16} {
pt\i/$P^0_{\i}$,
}};
{[same layer] \foreach \i in {1, 2, ..., 17} {
l\i/$P^1_{\i}$,
}};
\foreach \i [evaluate=\i as \j using int(\i+1)] in {1,2,...,16} {
pt\i -- l\i,
pt\i -- l\j,
};
Q/$P^1 \times P^1$ [gray];
\foreach \i [evaluate=\i using int(2*\i)] in {1,2,...,7} {
l\i -- [gray, edge node = {node [fill=white] {h}}] Q,
};
pt1 -- [gray] Q;
};
\end{document}
文件夹中的剩余文件
layered-no-reorder.lua
这个基于texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/layered/library.lua
。只删除了注释,并更改了一行(标记如下):
local layered
-- Load declarations from:
require "pgf.gd.layered"
-- Load algorithms from:
require "pgf.gd.layered.Sugiyama"
require "pgf.gd.layered.cycle_removal"
require "pgf.gd.layered.node_ranking"
require "no_crossing_minimization" -- <-- This one changed
require "pgf.gd.layered.node_positioning"
require "pgf.gd.layered.edge_routing"
no_crossing_minimization.lua
这个是基于texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/layered/crossing_minimization.lua
。为简洁起见,删除了所有注释。唯一有趣的变化是标记的行,它最初是require "pgf.gd.layered.CrossingMinimizationGansnerKNV1993"
。
local declare = require("pgf.gd.interface.InterfaceToAlgorithms").declare
declare {
key = "no crossing minimization",
algorithm = require "noCrossing", -- <--- This one changed
phase = "crossing minimization",
phase_default = true,
summary = [["
Fake phase. Does nothing.
"]],
documentation = [["
For more details, please see
http://tex.stackexchange.com/questions/173540/use-layered-layout-with-user-specified-node-ordering-i-e-with
out-crossing-m
"]]
}
noCrossing.lua
这个是基于texmf-dist/tex/generic/pgf/graphdrawing/lua/pgf/gd/layered/CrossingMinimizationGansnerKNV1993.lua
,但是沉重精简。只run()
保留功能,基本上不执行任何操作。
local CrossingMinimizationNone = {}
function CrossingMinimizationNone:run()
return self.ranking
end
return CrossingMinimizationNone
更新 1
有一种更简洁的方法可以将新的“无重新排序”算法集成到通用的 中layered layout
,而无需替换库。这样,作者仍然可以控制是否希望某些图表使用标准sweep crossing minimization
(这是 Tikz 中的默认设置),而其他图表使用新的no crossing minimization
。
以下是具体方法:
- 删除上一个解决方案中使用的文件
layered-no-reorder.lua
。不再需要该文件,因为layered
将改用标准库。 - 删除文件
no_crossing_minimization.lua
。我们将以更好的方式重写它(使用不同的名称)。 - 保留与上一个解决方案相同的文件
noCrossing.lua
。这是为重新排序阶段实施新“算法”的文件。
现在,在名为的文件中写入以下内容more-crossing-algorightms.lua
:
local declare = require("pgf.gd.interface.InterfaceToAlgorithms").declare
---
declare {
key = "no crossing minimization",
algorithm = require "noCrossing",
phase = "crossing minimization",
phase_default = false,
summary = [["
Fake phase. Does nothing.
"]],
documentation = [["
For more details, please see http://tex.stackexchange.com/questions/173540/use-layered-layout-with-user-specified-node-ordering-i-e-without-crossing-m
"]]
}
注意key
属性。它是键的名称,稍后可以在 TikZ 图中使用它来选择此算法。原则上,我们可以在同一个文件中声明更多交叉算法,为每个算法赋予不同的键。这将允许使用适当的键从 tikz 中选择其中一个。还要注意phase_default
设置为 的属性false
。这意味着默认情况下不会使用此算法,而只有在使用键明确选择时才会使用no crossing algorithm
。
现在主要文件是:
\documentclass[tikz,convert,margin=10pt]{standalone}
\usetikzlibrary{graphs,graphdrawing}
\usegdlibrary{layered} % <-- Now we do not replace this one
\usegdlibrary{more-crossing-algorithms}% <-- but extend it, loading more algorithms
\begin{document}
\tikz[rounded corners] \graph [layered layout, no crossing minimization]
{
{[same layer] \foreach \i in {1, 2, ..., 16} {
pt\i/$P^0_{\i}$,
}};
{[same layer] \foreach \i in {1, 2, ..., 17} {
l\i/$P^1_{\i}$,
}};
\foreach \i [evaluate=\i as \j using int(\i+1)] in {1,2,...,16} {
pt\i -- l\i,
pt\i -- l\j,
};
Q/$P^1 \times P^1$ [gray];
\foreach \i [evaluate=\i using int(2*\i)] in {1,2,...,7} {
l\i -- [gray, edge node = {node [fill=white] {h}}] Q,
};
pt1 -- [gray] Q;
};
\end{document}
注意这一\tikz
行。它有一个选项no crossing minimization
,用于选择新算法。如果我们省略该选项,sweep crossing algorithm
则将选择默认算法(因为它在 TikZ 库中被标记为默认算法)。
总而言之,此新解决方案所需的所有文件是test.tex
(主文档)more-crossing-algorithms.lua
和noCrossing.lua
。标准 TikZ 库不会被替换,而是会得到扩展。
答案2
我认为这里的解决方案可能是使用no placement
选项并手动放置节点。这听起来很麻烦,但事实证明,考虑到使用,这项工作出奇地少\foreach
。
注意:节点的定位实际上可能不符合 OP 的要求,但使用键可以轻松x
更改y
。
\documentclass[tikz,convert,margin=10pt]{standalone}
\usetikzlibrary{graphs,graphdrawing}
\begin{document}
\tikz[rounded corners] \graph [no placement]
{
\foreach \i in {1, 2, ..., 16} {
pt\i/$P^0_{\i}$ [x=\i, y=0],
};
\foreach \i in {1, 2, ..., 17} {
l\i/$P^1_{\i}$ [x=\i-1, y=-1],
};
\foreach \i [evaluate=\i as \j using int(\i+1)] in {1,2,...,16} {
pt\i -- l\i,
pt\i -- l\j,
};
Q/$P^1 \times P^1$ [gray, x=4,y=-3];
\foreach \i [evaluate=\i using int(2*\i)] in {1,2,...,7} {
l\i -- [gray, edge node = {node [fill=white] {h}}] Q,
};
pt1 -- [gray] Q;
};
\end{document}