Rerun Graphs 图可视化实战:基于 Fjädra 力导向布局引擎绘制节点连线图与气泡图
【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun
导读
本文围绕 Rerun 仓库中的 graphs 示例 展开,讲解如何借助 Rerun 内置的力导向布局引擎Fjädra,仅通过GraphNodes/GraphEdges两类原语记录图数据,即可在 viewer 中自动完成节点排布与布局渲染。你将掌握 Rerun 力导向物理模拟的五大力(Center、Collision、Many-Body、Link、Position)的含义与调参方式,学会用rr.GraphNodes、rr.GraphEdges记录树、格子、马尔可夫链等常见图结构,并通过 Blueprint 的GraphView与ForceLink、ForceManyBody、ForceCollisionRadius等力参数配置不同形态的图可视化(节点连线图、气泡图)。
一、示例概览:Rerun 能画出哪些图
Rerun 的re_view_graph视图(对应 crate crates/views/re_view_graph)专门用于渲染节点连线图(node-link diagram)。graphs 示例演示了四种典型图结构:
- 随机树(node_link / bubble_chart):以
root为根,逐步随机挂载新节点的有向树,同时以节点连线图和气泡图两种形态展示; - 格子(lattice):10×10 的二维网格有向图,节点颜色按坐标渐变;
- 马尔可夫链(markov_chain):三状态(sunny / rainy / cloudy)随机游走的时间序列动画,节点位置固定;
- 描述面板(description):以 Markdown 文本展示示例说明。
示例的核心卖点在于:记录数据时完全不需要提供节点坐标(除马尔可夫链外),节点位置由 Rerun 内部的力导向布局引擎实时计算,用户既可以在选择面板(selection panel)里动态调整力参数,也可以通过 Blueprint 在代码中预设。
二、Rerun 的力导向布局引擎 Fjädra
Rerun 内置了一个与d3-force非常相似的物理模拟引擎,名为Fjädra(瑞典语"弹簧")。它被集成在re_view_graph视图中,负责把图数据(节点 + 边)转换为布局坐标。
从源码看,布局的核心实现在 crates/views/re_view_graph/src/layout/provider.rs 的ForceLayoutProvider中:
fjadra::Simulation承载所有节点与力的模拟状态;- 每个布局请求会把节点转换为
fj::Node,把边转换为节点索引对(usize, usize),节点的半径取自其包围盒尺寸的一半(v.size.max_elem() as f64 / 2.0); - 每次视图刷新调用
tick(1)推进模拟一步,直到is_finished()判定收敛; - 若所有节点都带固定位置(如马尔可夫链),则直接跳过模拟,仅按固定坐标摆放(见 provider.rs 中
all_nodes_fixed()分支)。
力导向布局的优点在于灵活:同一条图数据,只需调整力的组合与参数,就能产出节点连线图、气泡图等差异巨大的可视化形态。
三、五大物理力详解
Fjädra 实现了五种与d3-force对应力。它们在 Rerun 中均有对应的 Blueprint archetype,定义于 crates/store/re_sdk_types/src/blueprint/archetypes,并在 provider.rs 的update_simulation中按开关逐一生效:
| 力 | Blueprint 类型 | 参数 | 作用 |
|---|---|---|---|
| 居中力(Center) | force_center.rs | enabled、strength | 将整个图的质心拉向原点(注释原文:tries to move the center of mass of the graph to the origin) |
| 碰撞半径力(Collision radius) | force_collision_radius.rs | enabled、strength、iterations | 依据节点半径解析节点间碰撞,避免节点互相重叠 |
| 多体力(Many-Body) | force_many_body.rs | enabled、strength | 在任意两节点间施加类似电荷的作用力,strength为正吸引、为负排斥 |
| 链接力(Link) | force_link.rs | enabled、distance、iterations | 让存在边相连的节点趋向目标距离,行为类似弹簧 |
| 位置力(Position) | force_position.rs | enabled、strength、position | 类似重力,把所有节点拉向指定位置 |
在 provider.rs 中可以看到每支力的具体装配方式:fj::Link::new(edges).distance(d).iterations(n)、fj::ManyBody::new().strength(s)、fj::Collide::new().radius(...).iterations(n).strength(s)、fj::Center::new().strength(s),而 Position 力被拆分为fj::PositionX与fj::PositionY两个方向分别施力。所有参数的默认值均来自ForceLayoutParams的查询回退逻辑(见 crates/views/re_view_graph/src/layout/params.rs),它通过ViewProperty::component_or_fallback从 Blueprint 中读取,未配置时自动使用各组件类型的默认值——这就是"不开蓝图也能自动出布局"的原因。
说明:这些 Force archetype 在源码注释中均标注为unstable(可能在不兼容的情况下变更),使用时建议跟随 Rerun SDK 版本固定行为。
四、运行 graphs 示例
在仓库根目录下安装并运行:
pip install -e examples/python/graphs python -m graphs示例项目配置见 examples/python/graphs/pyproject.toml:依赖rerun-sdk,并注册了graphs命令入口(graphs = "graphs:main"),因此也可以直接用graphs命令启动。运行后 viewer 中会同时出现四个面板:Node-link diagram、Bubble chart、Lattice 与 Markov Chain(外加 Description 文本面板)。
五、核心代码:用 GraphNodes / GraphEdges 记录图数据
Rerun 记录图数据只依赖两个 archetype:
GraphNodes:节点列表,支持node_ids、positions(可选坐标)、colors、labels、show_labels、radii等字段,定义见 crates/store/re_sdk_types/src/archetypes/graph_nodes.rs;GraphEdges:边列表,支持graph_type="directed"/"undirected"以及GraphType.Directed等枚举值。
5.1 记录格子图(静态)
rr.log( "lattice", rr.GraphNodes( nodes, colors=colors, # 按坐标渐变的颜色 labels=[f"({x}, {y})" for x, y in itertools.product(...)], ), static=True, # 静态数据,不随时间变化 ) rr.log("lattice", rr.GraphEdges(edges, graph_type="directed"), static=True)要点:使用static=True表示该数据不随时间演化;节点 ID 使用字符串(如"0"、"1"),边的两端引用这些 ID。由于没有提供positions,节点坐标完全交由 Fjädra 计算。
5.2 记录随机树(随时间演化)
rr.set_time("frame", sequence=i) # 以 frame 为时间轴 rr.log( "node_link", # 节点连线图 rr.GraphNodes(nodes, labels=nodes, radii=radii, colors=colors), rr.GraphEdges(edges, graph_type=rr.GraphType.Directed), ) rr.log( "bubble_chart", # 气泡图:只记节点,不记边 rr.GraphNodes(nodes, labels=nodes, radii=radii, colors=colors), )要点:同一批节点被记录到两个不同 entity 路径(node_link与bubble_chart),其中气泡图不记录边,仅靠节点的radii与碰撞力形成圆形打包(circle packing)效果;rr.set_time让每帧节点数递增,形成动态生长动画。完整实现见 examples/python/graphs/graphs.py 的log_trees()。
5.3 记录马尔可夫链(固定坐标 + 状态动画)
rr.log( "markov_chain", rr.GraphNodes(state_names, labels=state_names, colors=colors, positions=positions), rr.GraphEdges(edges, graph_type="directed"), )要点:positions为硬编码坐标[[0, 0], [150, 150], [300, 0]],因此无需任何力即可布局(viewer 检测到所有节点位置固定时直接跳过物理模拟)。每帧通过np.random.choice按转移矩阵采样下一个状态,并高亮当前状态节点,形成马尔可夫链动画(见log_markov_chain())。
六、用 Blueprint 预设布局:GraphView 与力参数
除了在运行时手动调节选择面板,还可以在代码中通过rr.send_blueprint精确配置每个图面板的布局方式。示例的log_blueprint()展示了四种典型配置:
rrb.GraphView( origin="node_link", name="Node-link diagram", force_link=ForceLink(distance=60), # 弹簧目标距离 60 force_many_body=ForceManyBody(strength=-60), # 节点间互相排斥 ), rrb.GraphView( origin="bubble_chart", name="Bubble chart", force_link=ForceLink(enabled=False), # 关闭链接力 force_many_body=ForceManyBody(enabled=False), # 关闭多体力 force_collision_radius=ForceCollisionRadius(enabled=True), # 仅靠碰撞力打包 defaults=[rr.GraphNodes.from_fields(show_labels=False)], # 全局关闭标签 ), rrb.GraphView( origin="lattice", name="Lattice", force_link=ForceLink(distance=60), force_many_body=ForceManyBody(strength=-60), defaults=[rr.GraphNodes.from_fields(show_labels=False, radii=10)], ), rrb.GraphView( origin="markov_chain", name="Markov Chain", # 节点坐标固定,无需任何力 ),从这段代码可以提炼出可复用的调参思路:
- 节点连线图:保留
ForceLink(控制边长度)与ForceManyBody(控制节点疏密)。ForceLink(distance=60)使相邻节点趋向 60 的间距;ForceManyBody(strength=-60)让不相连节点互相排斥,防止过度拥挤; - 气泡图:关闭 Link 与 Many-Body 力,只保留
ForceCollisionRadius。此时节点在碰撞力作用下自动挤压成紧密的圆形堆叠,节点的radii直接决定气泡大小——这就是"圆打包"布局的实现原理; - 固定布局图:节点自带
positions时无需配置任何力; defaults参数:可对 entity 内的节点统一附加属性(如show_labels=False、radii=10),免去在数据端逐节点设置。
这些 Force archetype 的 Python 导入路径为rerun.blueprint.archetypes.force_*(对应 Rust 侧 crates/store/re_sdk_types/src/blueprint/archetypes),全部字段均为可选(0 required / 0 recommended / 若干 optional),意味着最小化配置只需设置关心的参数即可。
七、布局运行机制与注意事项
结合源码与示例,有几点值得注意:
- 边方向:
GraphEdges的graph_type控制边是否带箭头;多条平行边在布局收敛后会以贝塞尔曲线扇出绘制(见 provider.rs 的SlotKind::Regular分支),自环边单独以顶部锚点的曲线渲染; - 节点半径来源:布局中碰撞力使用的半径来自节点包围盒(
size.max_elem() / 2),因此节点越大,碰撞力作用范围越大; - 性能取舍:
iterations参数决定每步模拟内单支力的应用次数,源码注释明确"提高该参数可获得更好结果,但会延长计算时间"(见 force_link.rs); - 数据体积控制:图数据量较大时,应使用
static=True记录不变的部分(如格子图结构),随时间演化的部分(如树、马尔可夫链)再用rr.set_time逐帧记录,避免冗余存储。
八、延伸阅读
- 示例源码与元数据:examples/python/graphs/graphs.py、examples/python/graphs/README.md、examples/python/graphs/pyproject.toml
- 图视图实现:crates/views/re_view_graph/src/view.rs、布局模块 crates/views/re_view_graph/src/layout/mod.rs
- 数据模型定义:crates/store/re_sdk_types/src/archetypes/graph_nodes.rs、crates/store/re_sdk_types/src/archetypes/graph_edges.rs
- 力参数查询与默认值:crates/views/re_view_graph/src/layout/params.rs
【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考