#directed-graph #graph #heuristics #graph-display

rust-sugiyama

实现了Sugiyama算法来计算有向图的坐标

3个版本 (破坏性)

0.3.0 2024年6月17日
0.2.0 2024年4月4日
0.1.0 2024年3月14日

293算法

每月下载量40

MIT许可证

155KB
4K SLoC

Rust Sugiyama

example worklfow

描述

用于显示层状图的Sugiyama算法的实现。

此crate底层大量使用petgraph crate。

通过petgraph的greedy_feedback_arc_set函数实现循环移除,然后反转集合中的边。

排名分配算法根据Gansner等人撰写的论文A Technique for Drawing Directed Graphs实现,该论文可在此处找到。它首先为节点分配一层,并创建一个排名分配的最优可行树。

交叉减少遵循上述论文中描述的加权中位数启发式方法,也可以通过配置使用重心启发式方法进行交叉减少。为了计算交叉,使用论文Simple and Efficient Bilayer Cross Counting中描述的Bilayer Cross Count算法,该论文由Wilhelm Barth、Petra Mutzel和Michael Juenger撰写,可在网上找到。

最后,坐标分配的实现遵循Brandes和Köpf提供的算法,该算法可在此论文中找到。

错误或功能请求可以通过github问题提交,或者通过联系[email protected]

使用方法

目前,有三种方法来创建布局

  1. from_edges,它接受一个&[(u32, u32)]
  2. from_vertices_and_edges,它接受一个&[u32]和一个&[(u32, u32)]
  3. from_graph,它接受一个petgraph::StableDiGraph<V, E>

它们会将图分解为其连通分量,并为每个分量分别计算坐标。API通过构建器模式实现,用户可以指定诸如顶点之间最小间距等值。

从边构建布局

该函数接收一个&[u32, u32]切片,并计算图的x和y坐标、高度和宽度。

use rust_sugiyama::from_edges;

let edges = [
    (0, 1), 
    (1, 2), 
    (1, 3), (1, 4), (1, 5), (1, 6), 
    (3, 7), (3, 8), (4, 7), (4, 8), 
    (5, 7), (5, 8), (6, 7), (6, 8), 
    (7, 9), (8, 9)
];
let 
let layouts = from_edges(&edges)
    .vertex_spacing(20)
    .build();

for (layout, width, height) in layouts {
    println!("Coordinates: {:?}", layouts);
    println!("width: {width}, height: {height}");
}

从图构建布局

接收输入一个&StableDiGraph<V, E>,并计算图的x和y坐标、高度和宽度。《code>NodeIndices在布局间保留,并直接映射到输入图中。

use rust_sugiuama::from_graph;
let mut g: StableDiGraph<String, usize> = StableDiGraph::new();

let rick = g.add_node("Rick".to_string());
let morty = g.add_node("Morty".to_string());
let beth = g.add_node("Beth".to_string());
let jerry = g.add_node("Jerry".to_string());
let summer = g.add_node("Summer".to_string());

g.add_edge(rick, beth, 1);
g.add_edge(rick, jerry, 1);
g.add_edge(beth, summer, 1);
g.add_edge(jerry, summer, 1);
g.add_edge(beth, morty, 1);
g.add_edge(jerry, morty, 1);

let layouts = from_graph(&g).build();
    .into_iter()
    .map(|(layout, width, height)| {
        let mut new_layout = HashMap::new();
        for (id, coords) in layout {
            new_layout.insert(g[NodeIndex::from(id)], coords);
        }
        (new_layout, width, height)
    })
    .collect::<Vec<_>>(); 

for (layout, width, height) in layouts {
    println!("Coordinates: {:?}", layout);
    println!("width: {width}, height: {height}");
}

通过环境变量进行配置

还可以通过环境变量使用configure_from_env()方法配置算法。

可以设置的环境变量有

ENV 默认值 描述
RUST_GRAPH_MIN_LEN 整数,> 0 1 层之间最小边长
RUST_GRAPH_V_SPACING 整数,> 0 10 同一层顶点之间最小间距
RUST_GRAPH_DUMMIES (y|n) y 如果将虚拟顶点包含在最终布局中
RUST_GRAPH_R_TYPE (original|minimize|up|down) minimize 定义如何垂直放置顶点
RUST_GRAPH_CROSS_MIN (barycenter|median) barycenter 用于交叉减少的启发式算法
RUST_GRAPH_TRANSPOSE (y|n) y 如果使用转置函数进一步尝试减少交叉(对于大型图可能会显著增加运行时间)
RUST_GRAPH_DUMMY_SIZE 浮点数,> 0,<= 1 1.0 最终布局中虚拟顶点的尺寸,如果包含虚拟顶点。这将水平压缩图

依赖项

~2MB
~31K SLoC