Rerun GraphEdge 组件详解:图数据边定义的 Arrow 编码与跨语言使用
2026/9/16 19:16:29 网站建设 项目流程

Rerun GraphEdge 组件详解:图数据边定义的 Arrow 编码与跨语言使用

【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun

本文以 Rerun 数据模型中的GraphEdge组件为核心,讲解图数据中"边"的表示方式:它如何通过Utf8Pair编码用一对 UTF-8 字符串连接两个节点,如何以 ArrowStruct数据类型落地存储,以及如何在GraphEdges原型(archetype)中与GraphNodesGraphType配合,在 Python、Rust、C++ 三端 SDK 中完成有向图与无向图的记录与可视化。读完本文,你将掌握GraphEdge的完整定义链路——从类型定义文件到生成代码、再到真实示例的运行方式。

GraphEdge:用两个节点标识符定义一条边

在 Rerun 的图(Graph)数据模型中,节点由GraphNodes原型管理,而节点之间的连接关系则由GraphEdge组件描述。官方文档对其的定义只有一句话:

An edge in a graph connecting two nodes.

即"连接两个节点的图中一条边"。GraphEdge本身不携带坐标、权重或样式信息,它只负责回答一个最基础的问题:这条边连接了哪两个节点

从类型定义看,GraphEdge是一个极简的透明包装结构。在crates/build/re_type_definitions/rerun/components/graph_edge.def.rs中:

/// An edge in a graph connecting two nodes. #[rerun::rerun_type] #[rust(derive(Default, PartialEq, Eq, PartialOrd, Ord))] #[rust(repr = "transparent")] #[rerun(state = "stable")] pub struct GraphEdge { pub edge: rerun::encodings::Utf8Pair, }

几个值得注意的细节:

  • #[rerun(state = "stable")]表明该组件已进入稳定状态,属于 SDK 对外承诺的稳定类型;
  • #[rust(repr = "transparent")]#[rust(derive(...))]说明生成的 Rust 绑定是一个透明表示的新类型(newtype),并自动派生了PartialEqEqPartialOrdOrdDefault等 trait;
  • 组件唯一的字段edgeUtf8Pair编码类型,即一对 UTF-8 字符串。

这种"一个组件 = 一个编码类型"的设计是 Rerun 类型系统的典型模式:编码(encoding)类型负责底层的 Arrow 数据布局,组件(component)类型负责语义,原型(archetype)负责把它们组装成可记录的日志对象。

Rerun 编码:底层是 Utf8Pair

GraphEdge在 Rerun 中的序列化编码是Utf8Pair(见 encoding 文档)。Utf8Pair的定义是"存储一个 UTF-8 字符串元组",包含两个非空字段:

  • first:类型为non-null Utf8,即第一个字符串;
  • second:类型为non-null Utf8,即第二个字符串。

需要特别说明的是:字段名为first/second只是Utf8Pair这一编码层的通用命名。在GraphEdge的语义层,这两个字符串分别对应起始节点(from)与目标节点(to)的标识符。节点的标识符可以任意 UTF-8 字符串,但必须与GraphNodes中记录的node_ids严格一致,否则图中会出现无法对应到节点的"悬空边"。

docs/content/reference/types/components/graph_edge.md中,文档明确给出了GraphEdge的 Arrow 数据类型:

Struct( "first": non-null Utf8 "second": non-null Utf8 )

Arrow 数据类型:一个双字段 Struct

GraphEdge对应的 Arrow 数据类型是一个双字段 Struct,两个字段均非空(non-null):

字段Arrow 类型语义(在图中)
firstUtf8(非空)边的起始节点标识符
secondUtf8(非空)边的目标节点标识符

这一布局是 Rerun 类型系统由定义文件(.def.rs)经过re_types_builder代码生成器自动翻译而来的。在crates/store/re_sdk_types/src/components/graph_edge.rs(自动生成文件)中可以看到,GraphEdge被生成为:

#[repr(transparent)] pub struct GraphEdge(pub crate::encodings::Utf8Pair);

并实现了WrapperComponenttrait,其Encoding关联类型被固定为Utf8Pair,组件名称为"rerun.components.GraphEdge"。同时,生成代码还为GraphEdge提供了:

  • From<T: Into<Utf8Pair>>转换,因此任何能转换为Utf8Pair的类型(如二元组)都能直接构造GraphEdge
  • Borrow<Utf8Pair>DerefDerefMut实现,使其在使用体验上几乎等同于直接操作一个Utf8Pair

这就是为什么在 SDK 调用层面,你可以直接写GraphEdges::new([("a", "b"), ("b", "c")])——字符串二元组会被自动转换为Utf8Pair,再包装成GraphEdge,最终按 Struct 布局序列化进 Arrow 数组。

所在原型:GraphEdges 中的角色

GraphEdge并不是独立记录的,它作为必填组件隶属于GraphEdges原型。GraphEdges的定义(见crates/build/re_type_definitions/rerun/archetypes/graph_edges.def.rs):

pub struct GraphEdges { /// A list of node tuples. #[rerun(no_ui_edit)] #[rerun(required)] pub edges: Vec<rerun::components::GraphEdge>, /// Specifies if the graph is directed or undirected. #[rerun(recommended)] pub graph_type: Option<rerun::components::GraphType>, }

GraphEdges共包含两个字段,这也是生成代码中NUM_COMPONENTS = 2(1 个 required + 1 个 recommended + 0 个 optional)的来源:

  • edges必填):Vec<GraphEdge>,一条边的列表。文档注释将其描述为"A list of node tuples"(节点二元组列表),即每条边就是(节点A, 节点B)这样一个节点对。注意#[rerun(no_ui_edit)]表明该字段在 Viewer 界面中不支持直接编辑;
  • graph_type推荐):Option<GraphType>,指定图是有向还是无向。如果不提供,图默认按无向图处理(这一点在graph_edges.md文档与graph_edges.def.rs注释中均有明确说明)。

GraphType 与有向/无向语义

GraphType是一个u8表示的枚举(见crates/build/re_type_definitions/rerun/components/graph_type.def.rs):

  • Undirected = 1,且是默认值(#[default])——无向边;
  • Directed = 2——有向边。

在 Rust SDK 中,GraphEdges额外提供了两个便捷方法(见crates/store/re_sdk_types/src/archetypes/graph_edges_ext.rs):

impl GraphEdges { pub fn with_undirected_edges(self) -> Self { self.with_graph_type(crate::components::GraphType::Undirected) } pub fn with_directed_edges(self) -> Self { self.with_graph_type(crate::components::GraphType::Directed) } }

因此"边"的方向性语义完全由GraphType决定,GraphEdge本身只负责提供(first, second)节点对:在无向图中("a", "b")("b", "a")等价,在有向图中则代表方向相反的两种连接。

跨语言使用:Python / Rust / C++ 三端示例

GraphEdge最常见的用法是配合GraphNodes一起记录:先用GraphNodes声明节点及其位置,再用GraphEdges声明边。仓库在docs/snippets/all/archetypes/下提供了完整的官方示例,以下展示无向图与有向图两种写法。

Python:无向图

docs/snippets/all/archetypes/graph_undirected.py

"""Log a simple undirected graph.""" import rerun as rr rr.init("rerun_example_graph_undirected", spawn=True) rr.log( "simple", rr.GraphNodes( node_ids=["a", "b", "c"], positions=[(0.0, 100.0), (-100.0, 0.0), (100.0, 0.0)], labels=["A", "B", "C"], ), rr.GraphEdges( edges=[("a", "b"), ("b", "c"), ("c", "a")], # Optional: graphs are undirected by default. graph_type="undirected", ), )

要点:

  • GraphEdgesedges参数接收节点标识符二元组列表,Python 端会将其转换为Utf8Pair编码的GraphEdge序列;
  • graph_type="undirected"是可选项——注释明确提示"graphs are undirected by default"(图默认是无向的),即使省略此行,三个节点a、b、c之间的三条边也会按无向渲染;
  • 节点标识符"a""b""c"必须与GraphNodesnode_ids一一对应。

Rust:有向图

docs/snippets/all/archetypes/graph_directed.rs

//! Log a simple directed graph. fn main() -> Result<(), Box<dyn std::error::Error>> { let rec = rerun::RecordingStreamBuilder::new("rerun_example_graph_directed") .spawn()?; rec.log( "simple", &[ &rerun::GraphNodes::new(["a", "b", "c"]) .with_positions([(0.0, 100.0), (-100.0, 0.0), (100.0, 0.0)]) .with_labels(["A", "B", "C"]) as &dyn rerun::AsComponents, &rerun::GraphEdges::new([("a", "b"), ("b", "c"), ("c", "a")]) .with_directed_edges(), ], )?; Ok(()) }

要点:

  • GraphEdges::new([("a", "b"), ...])直接接受字符串二元组数组,得益于GraphEdge: From<T: Into<Utf8Pair>>的自动转换;
  • .with_directed_edges()等价于.with_graph_type(GraphType::Directed),将三条边标记为有向边,在 Viewer 的 GraphView 中会渲染出方向箭头;
  • rec.log可以一次记录多个实现了AsComponents的对象,因此GraphNodesGraphEdges被放在同一个数组中按同一 entity 路径"simple"记录。

C++ 版本

仓库同样提供 C++ 版本:docs/snippets/all/archetypes/graph_undirected.cppdocs/snippets/all/archetypes/graph_directed.cpp。三者(Python/Rust/C++)在语义上完全一致,均遵循"GraphNodes定义节点 +GraphEdges定义边"的模式,这也是 round-trip 测试能够跨语言对齐的基础。

渲染与查看:GraphView 与 DataframeView

GraphEdge所在的原型GraphEdges在官方文档中声明可显示于两种视图(见graph_edges.md):

  • GraphView:图拓扑渲染视图,直接展示节点与边的连接关系,是图数据的首选查看方式;
  • DataframeView:数据表视图,将每条GraphEdge(first, second)两列的形式展开,便于检查与导出原始边数据。

在类型定义中,GraphEdges通过#[docs(category = "Graph")]#[docs(view_types = "GraphView")]以及#[rerun(visualizer = "GraphEdges")](见graph_edges.def.rs)与 GraphView 的GraphEdges可视化器(visualizer)绑定,这也是数据链路中"组件 → 原型 → 可视化器"三层解耦的体现。

内部原理:从定义文件到跨语言代码

GraphEdge文档头部有一行注释:DO NOT EDIT! This file was auto-generated by crates/build/re_types_builder/src/codegen/docs/website.rs。这揭示了整个类型系统的构建流程:

  1. 人类维护类型定义文件.def.rs),如 graph_edge.def.rs;
  2. re_types_builder(位于 crates/build/re_types_builder)解析定义,一次性生成 Rust、Python、C++ 三端绑定,以及本文所见的 Markdown 文档;
  3. 生成的 Rust 绑定落在 crates/store/re_sdk_types/src/components/graph_edge.rs,原型代码落在 crates/store/re_sdk_types/src/archetypes/graph_edges.rs;
  4. 文档页面 graph_edge.md 与 utf8pair.md 同样是这一流水线的产物。

因此,GraphEdge文档中列出的三项 API 参考(C++ 的GraphEdge、Python 的rerun.components.GraphEdge、Rust 的rerun::components::GraphEdge)在语义上完全等价,只是分别面向三种语言生态的消费者。Utf8Pair编码还被KeyValuePairs组件复用,可见其作为通用"字符串键值对"底层编码的通用性。

总结

  • GraphEdge是 Rerun 中定义"图中一条边"的稳定组件,核心信息只有两个节点标识符;
  • 它的 Rerun 编码是Utf8Pair,Arrow 数据类型为双字段Struct(first: Utf8, second: Utf8)
  • 它作为必填组件隶属于GraphEdges原型,配合GraphType(默认Undirected)决定图的有向/无向语义;
  • 使用上,GraphEdges::new([("a", "b"), ...])的二元组写法在 Python / Rust / C++ 三端保持一致,节点的node_ids必须与GraphNodes对齐;
  • 完整的定义 → 生成 → 序列化 → 可视化链路,可以从 graph_edge.def.rs 出发,沿re_types_builder与生成代码逐层追踪,再通过graph_undirected.py/graph_directed.rs示例直接运行验证。

【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询