更新 1

更新 1

如果有人以演示 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.luanoCrossing.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}

在此处输入图片描述

相关内容