Skip to content

Commit 1202bd4

Browse files
grtlrabey79Wumpf
authored
Implement graph components and archetypes (#7500)
<!-- Open the PR up as a draft until you feel it is ready for a proper review. Do not make PR:s from your own `main` branch, as that makes it difficult for reviewers to add their own fixes. Add any improvements to the branch as new commits to make it easier for reviewers to follow the progress. All commits will be squashed to a single commit once the PR is merged into `main`. Make sure you mention any issues that this PR closes in the description, as well as any other related issues. To get an auto-generated PR description you can put "copilot:summary" or "copilot:walkthrough" anywhere. --> Tracking issue: #7897 This implements basic graph primitives in the Rerun data model. This is a first step towards visualizing node-link diagrams in Rerun (related issue: #6898). In addition to the changes to the data model, this PR adds two example crates, `node_link_graph` and `graph_view` to the Rust examples that show how these primitives can be used. ## Design Decisions - Nodes and edges are stored as `components` and can be batched. To have a single node per entity we can use Rerun’s [[clamping mechanism](https://rerun.io/docs/concepts/batches#component-clamping)](https://rerun.io/docs/concepts/batches#component-clamping). - `GraphNodeId` is modeled as ~`u32` to improve performance when using `petgraph`~ strings for better user experience. - A node is unique identified by combining its `GraphNodeId` and its `EntityPath`. - Labels of the nodes can be set via the `labels` component and toggled via `show_labels` - ~Hierarchical graphs can be modeled through entity paths. For edges that cross entity boundaries we can insert dummy nodes to properly render subparts of the hierarchy.~ - ~Nodes and edges need to be logged to the same entity, otherwise the selections will collide. We provider helper functions / conversions to link nodes that are stored in different entities.~ > [!WARNING] > This PR has changed considerably from its initial version: > * No linking of nodes between entities (an therefore hierarchy) yet. > * For now, Graphs are local only and therefore have to be logged to the same entity. ## Logging example ```rs 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"]), )?; // Note: We log to the same entity here. rec.log( "simple", &rerun::GraphEdges::new([("a", "b"), ("b", "c"), ("c", "a")]).with_directed_edges(), )?; ``` ## TODOs - [x] ~Get rid of the `Default` derive for `GraphNodeId` and `GraphEdge` in the flatbuffer definitions.~ - [x] Improve ergonomics for generating graph edges during logging. - [x] Ensure that logging works from Python and C++ too. - [x] Fix remaining lints. ### Checklist * [x] I have read and agree to [Contributor Guide](https://github.com/rerun-io/rerun/blob/main/CONTRIBUTING.md) and the [Code of Conduct](https://github.com/rerun-io/rerun/blob/main/CODE_OF_CONDUCT.md) * [x] I've included a screenshot or gif (if applicable) * [x] I have tested the web demo (if applicable): * Using examples from latest `main` build: [rerun.io/viewer](https://rerun.io/viewer/pr/7500?manifest_url=https://app.rerun.io/version/main/examples_manifest.json) * Using full set of examples from `nightly` build: [rerun.io/viewer](https://rerun.io/viewer/pr/7500?manifest_url=https://app.rerun.io/version/nightly/examples_manifest.json) * [x] The PR title and labels are set such as to maximize their usefulness for the next release's CHANGELOG * [x] If applicable, add a new check to the [release checklist](https://github.com/rerun-io/rerun/blob/main/tests/python/release_checklist)! * [x] If have noted any breaking changes to the log API in `CHANGELOG.md` and the migration guide - [PR Build Summary](https://build.rerun.io/pr/7500) - [Recent benchmark results](https://build.rerun.io/graphs/crates.html) - [Wasm size tracking](https://build.rerun.io/graphs/sizes.html) To run all checks from `main`, comment on the PR with `@rerun-bot full-check`. --------- Co-authored-by: Antoine Beyeler <antoine@rerun.io> Co-authored-by: Andreas Reich <r_andreas2@web.de>
1 parent 8343647 commit 1202bd4

File tree

146 files changed

+5844
-505
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

146 files changed

+5844
-505
lines changed

ARCHITECTURE.md

+1
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ Update instructions:
134134
| re_space_view | Types & utilities for defining Space View classes and communicating with the Viewport. |
135135
| re_space_view_bar_chart | A Space View that shows a single bar chart. |
136136
| re_space_view_dataframe | A Space View that shows the data contained in entities in a table. |
137+
| re_space_view_graph | A Space View that shows a graph (node-link diagram). |
137138
| re_space_view_map | A Space View that shows geospatial data on a map. |
138139
| re_space_view_spatial | Space Views that show entities in a 2D or 3D spatial relationship. |
139140
| re_space_view_tensor | A Space View dedicated to visualizing tensors with arbitrary dimensionality. |

Cargo.lock

+48
Original file line numberDiff line numberDiff line change
@@ -2376,6 +2376,12 @@ version = "0.4.2"
23762376
source = "registry+https://github.com/rust-lang/crates.io-index"
23772377
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
23782378

2379+
[[package]]
2380+
name = "fjadra"
2381+
version = "0.1.0"
2382+
source = "registry+https://github.com/rust-lang/crates.io-index"
2383+
checksum = "ccc0416b27f53bba6c9bd564260f27d4784c5f430926eb16a519356be7d66bbc"
2384+
23792385
[[package]]
23802386
name = "flatbuffers"
23812387
version = "23.5.26"
@@ -2754,6 +2760,25 @@ dependencies = [
27542760
"bitflags 2.6.0",
27552761
]
27562762

2763+
[[package]]
2764+
name = "graph_binary_tree"
2765+
version = "0.21.0-alpha.1+dev"
2766+
dependencies = [
2767+
"anyhow",
2768+
"clap",
2769+
"rerun",
2770+
]
2771+
2772+
[[package]]
2773+
name = "graph_lattice"
2774+
version = "0.21.0-alpha.1+dev"
2775+
dependencies = [
2776+
"anyhow",
2777+
"clap",
2778+
"itertools 0.13.0",
2779+
"rerun",
2780+
]
2781+
27572782
[[package]]
27582783
name = "h2"
27592784
version = "0.3.26"
@@ -6052,6 +6077,28 @@ dependencies = [
60526077
"thiserror",
60536078
]
60546079

6080+
[[package]]
6081+
name = "re_space_view_graph"
6082+
version = "0.21.0-alpha.1+dev"
6083+
dependencies = [
6084+
"ahash",
6085+
"bytemuck",
6086+
"egui",
6087+
"fjadra",
6088+
"nohash-hasher",
6089+
"re_chunk",
6090+
"re_format",
6091+
"re_log_types",
6092+
"re_query",
6093+
"re_renderer",
6094+
"re_space_view",
6095+
"re_tracing",
6096+
"re_types",
6097+
"re_ui",
6098+
"re_viewer_context",
6099+
"re_viewport_blueprint",
6100+
]
6101+
60556102
[[package]]
60566103
name = "re_space_view_map"
60576104
version = "0.21.0-alpha.1+dev"
@@ -6462,6 +6509,7 @@ dependencies = [
64626509
"re_smart_channel",
64636510
"re_space_view_bar_chart",
64646511
"re_space_view_dataframe",
6512+
"re_space_view_graph",
64656513
"re_space_view_map",
64666514
"re_space_view_spatial",
64676515
"re_space_view_tensor",

Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ re_space_view = { path = "crates/viewer/re_space_view", version = "=0.21.0-alpha
9999
re_space_view_bar_chart = { path = "crates/viewer/re_space_view_bar_chart", version = "=0.21.0-alpha.1", default-features = false }
100100
re_space_view_spatial = { path = "crates/viewer/re_space_view_spatial", version = "=0.21.0-alpha.1", default-features = false }
101101
re_space_view_dataframe = { path = "crates/viewer/re_space_view_dataframe", version = "=0.21.0-alpha.1", default-features = false }
102+
re_space_view_graph = { path = "crates/viewer/re_space_view_graph", version = "=0.21.0-alpha.1", default-features = false }
102103
re_space_view_map = { path = "crates/viewer/re_space_view_map", version = "=0.21.0-alpha.1", default-features = false }
103104
re_space_view_tensor = { path = "crates/viewer/re_space_view_tensor", version = "=0.21.0-alpha.1", default-features = false }
104105
re_space_view_text_document = { path = "crates/viewer/re_space_view_text_document", version = "=0.21.0-alpha.1", default-features = false }
@@ -186,6 +187,7 @@ enumset = "1.0.12"
186187
env_logger = { version = "0.10", default-features = false }
187188
ffmpeg-sidecar = { version = "2.0.2", default-features = false }
188189
fixed = { version = "1.28", default-features = false }
190+
fjadra = "0.1"
189191
flatbuffers = "23.0"
190192
futures-channel = "0.3"
191193
futures-util = { version = "0.3", default-features = false }

crates/store/re_types/definitions/rerun/archetypes.fbs

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
namespace rerun.archetypes;
2+
3+
// ---
4+
5+
// TODO(ab): Add images to snippets.
6+
7+
/// A list of edges in a graph.
8+
///
9+
/// By default, edges are undirected.
10+
///
11+
/// \example archetypes/graph_undirected !api title="Simple undirected graph" image=""
12+
/// \example archetypes/graph_directed !api title="Simple directed graph" image=""
13+
table GraphEdges (
14+
"attr.docs.category": "Graph",
15+
"attr.docs.unreleased",
16+
"attr.docs.view_types": "GraphView",
17+
"attr.rust.derive": "PartialEq, Eq",
18+
"attr.rerun.experimental"
19+
) {
20+
// --- Required ---
21+
22+
/// A list of node tuples.
23+
edges: [rerun.components.GraphEdge] ("attr.rerun.component_required", order: 1000);
24+
25+
26+
// --- Recommended ---
27+
28+
/// Specifies if the graph is directed or undirected.
29+
///
30+
/// If no `GraphType` is provided, the graph is assumed to be undirected.
31+
graph_type: rerun.components.GraphType ("attr.rerun.component_recommended", nullable, order: 2000);
32+
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
namespace rerun.archetypes;
2+
3+
// ---
4+
5+
// TODO(ab): Add images to snippets.
6+
7+
/// A list of nodes in a graph with optional labels, colors, etc.
8+
///
9+
/// \example archetypes/graph_undirected !api title="Simple undirected graph" image=""
10+
/// \example archetypes/graph_directed !api title="Simple directed graph" image=""
11+
table GraphNodes (
12+
"attr.docs.category": "Graph",
13+
"attr.docs.unreleased",
14+
"attr.docs.view_types": "GraphView",
15+
"attr.rust.derive": "PartialEq",
16+
"attr.rerun.experimental"
17+
) {
18+
// --- Required ---
19+
20+
/// A list of node IDs.
21+
node_ids: [rerun.components.GraphNode] ("attr.rerun.component_required", order: 1000);
22+
23+
// --- Optional ---
24+
25+
/// Optional center positions of the nodes.
26+
positions: [rerun.components.Position2D] ("attr.rerun.component_optional", nullable, order: 3000);
27+
28+
/// Optional colors for the boxes.
29+
colors: [rerun.components.Color] ("attr.rerun.component_optional", nullable, order: 3100);
30+
31+
/// Optional text labels for the node.
32+
labels: [rerun.components.Text] ("attr.rerun.component_optional", nullable, order: 3200);
33+
34+
/// Optional choice of whether the text labels should be shown by default.
35+
show_labels: rerun.components.ShowLabels ("attr.rerun.component_optional", nullable, order: 3250);
36+
37+
/// Optional radii for nodes.
38+
radii: [rerun.components.Radius] ("attr.rerun.component_optional", nullable, order: 3300);
39+
}

crates/store/re_types/definitions/rerun/blueprint/views.fbs

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
namespace rerun.blueprint.views;
2+
3+
/// A graph view to display time-variying, directed or undirected graph visualization.
4+
///
5+
/// \example views/graph title="Use a blueprint to create a graph view." image="https://static.rerun.io/graph_lattice/f9169da9c3f35b7260c9d74cd5be5fe710aec6a8/1200w.png"
6+
table GraphView (
7+
"attr.rerun.view_identifier": "Graph",
8+
"attr.docs.unreleased"
9+
) {
10+
/// Everything within these bounds is guaranteed to be visible.
11+
///
12+
/// Somethings outside of these bounds may also be visible due to letterboxing.
13+
visual_bounds: rerun.blueprint.archetypes.VisualBounds2D (order: 1000);
14+
}

crates/store/re_types/definitions/rerun/components.fbs

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace rerun.components;
2+
3+
// ---
4+
5+
/// An edge in a graph connecting two nodes.
6+
table GraphEdge (
7+
"attr.docs.unreleased",
8+
"attr.rust.derive": "Default, PartialEq, Eq, PartialOrd, Ord",
9+
"attr.rust.repr": "transparent"
10+
) {
11+
edge: rerun.datatypes.Utf8Pair (order: 100);
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
namespace rerun.components;
2+
3+
// ---
4+
5+
/// A string-based ID representing a node in a graph.
6+
table GraphNode (
7+
"attr.docs.unreleased",
8+
"attr.python.aliases": "str",
9+
"attr.python.array_aliases": "str, Sequence[str]",
10+
"attr.rust.derive": "Default, PartialEq, Eq, PartialOrd, Ord, Hash",
11+
"attr.rust.repr": "transparent"
12+
) {
13+
id: rerun.datatypes.Utf8 (order: 100);
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
namespace rerun.components;
2+
3+
// --
4+
5+
/// Specifies if a graph has directed or undirected edges.
6+
enum GraphType: ubyte (
7+
"attr.docs.unreleased",
8+
"attr.rust.derive": "Default, PartialEq, Eq"
9+
) {
10+
/// Invalid value. Won't show up in generated types.
11+
Invalid = 0,
12+
13+
/// The graph has undirected edges.
14+
Undirected (default),
15+
16+
/// The graph has directed edges.
17+
Directed,
18+
}

crates/store/re_types/definitions/rerun/datatypes.fbs

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
namespace rerun.datatypes;
2+
3+
/// Stores a tuple of UTF-8 strings.
4+
table Utf8Pair (
5+
"attr.docs.unreleased",
6+
"attr.python.aliases": "Tuple[datatypes.Utf8Like, datatypes.Utf8Like]",
7+
"attr.python.array_aliases": "npt.NDArray[np.str_]",
8+
"attr.rust.derive": "Default, PartialEq, Eq, PartialOrd, Ord"
9+
) {
10+
/// The first string.
11+
first: rerun.datatypes.Utf8 (order: 100);
12+
13+
/// The second string.
14+
second: rerun.datatypes.Utf8 (order: 200);
15+
}

crates/store/re_types/src/archetypes/.gitattributes

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)