diff --git a/examples/manifest.toml b/examples/manifest.toml index 915fc0509dcfe..5be17e03604d8 100644 --- a/examples/manifest.toml +++ b/examples/manifest.toml @@ -142,6 +142,7 @@ examples = [ "notebook", "clock", "dna", + "graphs", "log_file", "openstreetmap_data", "minimal", @@ -150,8 +151,6 @@ examples = [ "plots", "live_scrolling_plot", "raw_mesh", - "graph_lattice", - "graph_binary_tree", "air_traffic_data", ] @@ -169,6 +168,7 @@ examples = [ "drone_lidar", "extend_viewer_ui", "external_data_loader", + "graph_binary_tree", "incremental_logging", "minimal_serve", "shared_recording", diff --git a/examples/python/graph_lattice/graph_lattice.py b/examples/python/graph_lattice/graph_lattice.py deleted file mode 100644 index 21eb327df1caf..0000000000000 --- a/examples/python/graph_lattice/graph_lattice.py +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env python3 -"""Examples of logging graph data to Rerun and performing a force-based layout.""" - -from __future__ import annotations - -import argparse -import itertools - -import rerun as rr - -NUM_NODES = 10 - -DESCRIPTION = """ -# Graph Lattice -This is a minimal example that logs a graph (node-link diagram) that represents a lattice. - -In this example, the node positions—and therefore the graph layout—are computed by Rerun internally. - -The full source code for this example is available -[on GitHub](https://github.com/rerun-io/rerun/blob/latest/examples/python/graph_lattice?speculative-link). -""".strip() - - -def log_data() -> None: - rr.log("description", rr.TextDocument(DESCRIPTION, media_type=rr.MediaType.MARKDOWN), static=True) - - coordinates = itertools.product(range(NUM_NODES), range(NUM_NODES)) - - nodes, colors = zip(*[ - ( - str(i), - rr.components.Color([round((x / (NUM_NODES - 1)) * 255), round((y / (NUM_NODES - 1)) * 255), 0]), - ) - for i, (x, y) in enumerate(coordinates) - ]) - - rr.log( - "/lattice", - rr.GraphNodes( - nodes, - colors=colors, - labels=[f"({x}, {y})" for x, y in itertools.product(range(NUM_NODES), range(NUM_NODES))], - ), - static=True, - ) - - edges = [] - for x, y in itertools.product(range(NUM_NODES), range(NUM_NODES)): - if y > 0: - source = (y - 1) * NUM_NODES + x - target = y * NUM_NODES + x - edges.append((str(source), str(target))) - if x > 0: - source = y * NUM_NODES + (x - 1) - target = y * NUM_NODES + x - edges.append((str(source), str(target))) - - rr.log("/lattice", rr.GraphEdges(edges, graph_type="directed"), static=True) - - -def main() -> None: - parser = argparse.ArgumentParser(description="Logs a graph lattice using the Rerun SDK.") - rr.script_add_args(parser) - args = parser.parse_args() - - rr.script_setup(args, "rerun_example_graph_lattice") - log_data() - rr.script_teardown(args) - - -if __name__ == "__main__": - main() diff --git a/examples/python/graph_lattice/README.md b/examples/python/graphs/README.md similarity index 55% rename from examples/python/graph_lattice/README.md rename to examples/python/graphs/README.md index ed764409c37c1..245143a18cc0c 100644 --- a/examples/python/graph_lattice/README.md +++ b/examples/python/graphs/README.md @@ -1,8 +1,8 @@ @@ -10,11 +10,11 @@ This example shows different attributes that you can associate with nodes in a g Since no explicit positions are passed for the nodes, Rerun will layout the graph automatically. - - - - - + + + + + ## Used Rerun types @@ -24,6 +24,6 @@ Since no explicit positions are passed for the nodes, Rerun will layout the grap ## Run the code ```bash -pip install -e examples/python/graph_lattice -python -m graph_lattice +pip install -e examples/python/graphs +python -m graphs ``` diff --git a/examples/python/graphs/graphs.py b/examples/python/graphs/graphs.py new file mode 100644 index 0000000000000..42c9c22ea5d1e --- /dev/null +++ b/examples/python/graphs/graphs.py @@ -0,0 +1,207 @@ +#!/usr/bin/env python3 +"""Examples of logging graph data to Rerun and performing force-based layouts.""" + +from __future__ import annotations + +import argparse +import random +import itertools +import numpy as np + +import rerun as rr +import rerun.blueprint as rrb + +from rerun.blueprint.archetypes.force_collision_radius import ForceCollisionRadius +from rerun.blueprint.archetypes.force_link import ForceLink +from rerun.blueprint.archetypes.force_many_body import ForceManyBody +from rerun.components.color import Color +from rerun.components.radius import Radius +from rerun.components.show_labels import ShowLabels + + +color_scheme = [ + Color([228, 26, 28]), # Red + Color([55, 126, 184]), # Blue + Color([77, 175, 74]), # Green + Color([152, 78, 163]), # Purple + Color([255, 127, 0]), # Orange + Color([255, 255, 51]), # Yellow + Color([166, 86, 40]), # Brown + Color([247, 129, 191]), # Pink + Color([153, 153, 153]), # Gray +] + +DESCRIPTION = """ +# Graphs +This example shows various graph visualizations that you can create using Rerun. +In this example, the node positions—and therefore the graph layout—are computed by Rerun internally using a force-based layout algorithm. + +You can modify how these graphs look by changing the parameters of the force-based layout algorithm in the selection panel. + +The full source code for this example is available +[on GitHub](https://github.com/rerun-io/rerun/blob/latest/examples/python/graphs?speculative-link). +""".strip() + + +# We want reproducible results +random.seed(42) + + +def log_lattice(num_nodes) -> None: + coordinates = itertools.product(range(num_nodes), range(num_nodes)) + + nodes, colors = zip(*[ + ( + str(i), + rr.components.Color([round((x / (num_nodes - 1)) * 255), round((y / (num_nodes - 1)) * 255), 0]), + ) + for i, (x, y) in enumerate(coordinates) + ]) + + rr.log( + "lattice", + rr.GraphNodes( + nodes, + colors=colors, + labels=[f"({x}, {y})" for x, y in itertools.product(range(num_nodes), range(num_nodes))], + ), + static=True, + ) + + edges = [] + for x, y in itertools.product(range(num_nodes), range(num_nodes)): + if y > 0: + source = (y - 1) * num_nodes + x + target = y * num_nodes + x + edges.append((str(source), str(target))) + if x > 0: + source = y * num_nodes + (x - 1) + target = y * num_nodes + x + edges.append((str(source), str(target))) + + rr.log("/lattice", rr.GraphEdges(edges, graph_type="directed"), static=True) + + +def log_trees() -> None: + nodes = ["root"] + radii = [42] + colors = [Color([81, 81, 81])] + edges = [] + + # Randomly add nodes and edges to the graph + for i in range(50): + existing = random.choice(nodes) + new_node = str(i) + nodes.append(new_node) + radii.append(random.randint(10, 50)) + colors.append(random.choice(color_scheme)) + edges.append((existing, new_node)) + + rr.set_time_sequence("frame", i) + 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), + ) + + +def log_markov_chain() -> None: + transition_matrix = np.array([ + [0.8, 0.1, 0.1], # Transitions from sunny + [0.3, 0.4, 0.3], # Transitions from rainy + [0.2, 0.3, 0.5], # Transitions from cloudy + ]) + state_names = ["sunny", "rainy", "cloudy"] + # For this example, we use hardcoded positions. + positions = [[0, 0], [150, 150], [300, 0]] + inactive_color = Color([153, 153, 153]) # Gray + active_colors = [ + Color([255, 127, 0]), # Orange + Color([55, 126, 184]), # Blue + Color([152, 78, 163]), # Purple + ] + + edges = [(state_names[i], state_names[j]) for i in range(len(state_names)) for j in range(len(state_names)) if transition_matrix[i][j] > 0] + + # We start in state "sunny" + state = "sunny" + + for i in range(50): + current_state_index = state_names.index(state) + next_state_index = np.random.choice( + range(len(state_names)), p=transition_matrix[current_state_index] + ) + state = state_names[next_state_index] + colors = [inactive_color] * len(state_names) + colors[next_state_index] = active_colors[next_state_index] + + print(colors) + + rr.set_time_sequence("frame", i) + rr.log( + "markov_chain", + rr.GraphNodes(state_names, labels=state_names, colors=colors, positions=positions), + rr.GraphEdges(edges, graph_type="directed") + ) + + + +def log_blueprint() -> None: + rr.send_blueprint( + rrb.Blueprint( + rrb.Grid( + rrb.GraphView( + origin="node_link", + name="Node-link diagram", + force_link=ForceLink(distance=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=[ShowLabels(False)], + ), + rrb.GraphView( + origin="lattice", + name="Lattice", + force_link=ForceLink(distance=60), + force_many_body=ForceManyBody(strength=-60), + defaults=[ShowLabels(False), Radius(10)], + ), + rrb.Horizontal( + rrb.GraphView( + origin="markov_chain", + name="Markov Chain", + force_link=ForceLink(distance=120), + force_many_body=ForceManyBody(strength=-120), + ), + rrb.TextDocumentView(origin="description", name="Description"), + ) + ) + ) + ) + + +def main() -> None: + parser = argparse.ArgumentParser(description="Logs various graphs using the Rerun SDK.") + rr.script_add_args(parser) + args = parser.parse_args() + + rr.script_setup(args, "rerun_example_graphs") + rr.log("description", rr.TextDocument(DESCRIPTION, media_type=rr.MediaType.MARKDOWN), static=True) + log_trees() + log_lattice(10) + log_markov_chain() + log_blueprint() + rr.script_teardown(args) + + +if __name__ == "__main__": + main() diff --git a/examples/python/graph_lattice/pyproject.toml b/examples/python/graphs/pyproject.toml similarity index 74% rename from examples/python/graph_lattice/pyproject.toml rename to examples/python/graphs/pyproject.toml index 128b7879d28e2..107c5d05bba68 100644 --- a/examples/python/graph_lattice/pyproject.toml +++ b/examples/python/graphs/pyproject.toml @@ -1,11 +1,11 @@ [project] -name = "graph_lattice" +name = "graphs" version = "0.1.0" readme = "README.md" dependencies = ["rerun-sdk"] [project.scripts] -graph_lattice = "graph_lattice:main" +graphs = "graphs:main" [build-system] requires = ["hatchling"] diff --git a/pixi.lock b/pixi.lock index cec6b0f84bfb3..6dba1674cc76b 100644 --- a/pixi.lock +++ b/pixi.lock @@ -2550,7 +2550,7 @@ environments: - pypi: examples/python/face_tracking - pypi: examples/python/gesture_detection - pypi: examples/python/graph_binary_tree - - pypi: examples/python/graph_lattice + - pypi: examples/python/graphs - pypi: examples/python/human_pose_tracking - pypi: examples/python/incremental_logging - pypi: examples/python/lidar @@ -2954,7 +2954,7 @@ environments: - pypi: examples/python/dna - pypi: examples/python/drone_lidar - pypi: examples/python/graph_binary_tree - - pypi: examples/python/graph_lattice + - pypi: examples/python/graphs - pypi: examples/python/incremental_logging - pypi: examples/python/lidar - pypi: examples/python/live_camera_edge_detection @@ -3339,7 +3339,7 @@ environments: - pypi: examples/python/face_tracking - pypi: examples/python/gesture_detection - pypi: examples/python/graph_binary_tree - - pypi: examples/python/graph_lattice + - pypi: examples/python/graphs - pypi: examples/python/human_pose_tracking - pypi: examples/python/incremental_logging - pypi: examples/python/lidar @@ -3726,7 +3726,7 @@ environments: - pypi: examples/python/face_tracking - pypi: examples/python/gesture_detection - pypi: examples/python/graph_binary_tree - - pypi: examples/python/graph_lattice + - pypi: examples/python/graphs - pypi: examples/python/human_pose_tracking - pypi: examples/python/incremental_logging - pypi: examples/python/lidar @@ -4112,7 +4112,7 @@ environments: - pypi: examples/python/face_tracking - pypi: examples/python/gesture_detection - pypi: examples/python/graph_binary_tree - - pypi: examples/python/graph_lattice + - pypi: examples/python/graphs - pypi: examples/python/human_pose_tracking - pypi: examples/python/incremental_logging - pypi: examples/python/lidar @@ -4542,7 +4542,7 @@ environments: - pypi: examples/python/face_tracking - pypi: examples/python/gesture_detection - pypi: examples/python/graph_binary_tree - - pypi: examples/python/graph_lattice + - pypi: examples/python/graphs - pypi: examples/python/human_pose_tracking - pypi: examples/python/incremental_logging - pypi: examples/python/lidar @@ -4898,7 +4898,7 @@ environments: - pypi: examples/python/dna - pypi: examples/python/drone_lidar - pypi: examples/python/graph_binary_tree - - pypi: examples/python/graph_lattice + - pypi: examples/python/graphs - pypi: examples/python/incremental_logging - pypi: examples/python/lidar - pypi: examples/python/live_camera_edge_detection @@ -5236,7 +5236,7 @@ environments: - pypi: examples/python/face_tracking - pypi: examples/python/gesture_detection - pypi: examples/python/graph_binary_tree - - pypi: examples/python/graph_lattice + - pypi: examples/python/graphs - pypi: examples/python/human_pose_tracking - pypi: examples/python/incremental_logging - pypi: examples/python/lidar @@ -5576,7 +5576,7 @@ environments: - pypi: examples/python/face_tracking - pypi: examples/python/gesture_detection - pypi: examples/python/graph_binary_tree - - pypi: examples/python/graph_lattice + - pypi: examples/python/graphs - pypi: examples/python/human_pose_tracking - pypi: examples/python/incremental_logging - pypi: examples/python/lidar @@ -5916,7 +5916,7 @@ environments: - pypi: examples/python/face_tracking - pypi: examples/python/gesture_detection - pypi: examples/python/graph_binary_tree - - pypi: examples/python/graph_lattice + - pypi: examples/python/graphs - pypi: examples/python/human_pose_tracking - pypi: examples/python/incremental_logging - pypi: examples/python/lidar @@ -9159,7 +9159,7 @@ environments: - pypi: examples/python/face_tracking - pypi: examples/python/gesture_detection - pypi: examples/python/graph_binary_tree - - pypi: examples/python/graph_lattice + - pypi: examples/python/graphs - pypi: examples/python/human_pose_tracking - pypi: examples/python/incremental_logging - pypi: examples/python/lidar @@ -9657,7 +9657,7 @@ environments: - pypi: examples/python/dna - pypi: examples/python/drone_lidar - pypi: examples/python/graph_binary_tree - - pypi: examples/python/graph_lattice + - pypi: examples/python/graphs - pypi: examples/python/incremental_logging - pypi: examples/python/lidar - pypi: examples/python/live_camera_edge_detection @@ -10135,7 +10135,7 @@ environments: - pypi: examples/python/face_tracking - pypi: examples/python/gesture_detection - pypi: examples/python/graph_binary_tree - - pypi: examples/python/graph_lattice + - pypi: examples/python/graphs - pypi: examples/python/human_pose_tracking - pypi: examples/python/incremental_logging - pypi: examples/python/lidar @@ -10615,7 +10615,7 @@ environments: - pypi: examples/python/face_tracking - pypi: examples/python/gesture_detection - pypi: examples/python/graph_binary_tree - - pypi: examples/python/graph_lattice + - pypi: examples/python/graphs - pypi: examples/python/human_pose_tracking - pypi: examples/python/incremental_logging - pypi: examples/python/lidar @@ -11074,7 +11074,7 @@ environments: - pypi: examples/python/face_tracking - pypi: examples/python/gesture_detection - pypi: examples/python/graph_binary_tree - - pypi: examples/python/graph_lattice + - pypi: examples/python/graphs - pypi: examples/python/human_pose_tracking - pypi: examples/python/incremental_logging - pypi: examples/python/lidar @@ -18052,13 +18052,6 @@ packages: requires_dist: - rerun-sdk editable: true -- pypi: examples/python/graph_lattice - name: graph-lattice - version: 0.1.0 - sha256: f92a889e55062d414fbf9847d0b2b216b8e4bcaf8ee2965476de877102ee52f8 - requires_dist: - - rerun-sdk - editable: true - conda: https://conda.anaconda.org/conda-forge/linux-64/graphite2-1.3.13-h59595ed_1003.conda sha256: 0595b009f20f8f60f13a6398e7cdcbd2acea5f986633adcf85f5a2283c992add md5: f87c7b7c2cb45f323ffbce941c78ab7c @@ -18113,6 +18106,13 @@ packages: purls: [] size: 95406 timestamp: 1711634622644 +- pypi: examples/python/graphs + name: graphs + version: 0.1.0 + sha256: 4e848c8bfec82cd0d79a4080a37ea03ee24c33d3c64019af981b76ba0bd8d10c + requires_dist: + - rerun-sdk + editable: true - pypi: https://files.pythonhosted.org/packages/eb/fc/570a1e503e19be24c5642ea8b93f23e3eef1dfa930e761cab72dedc2c2db/griffe-1.4.1-py3-none-any.whl name: griffe version: 1.4.1 diff --git a/pixi.toml b/pixi.toml index 4448ca0b884ad..cdef11bbfb38d 100644 --- a/pixi.toml +++ b/pixi.toml @@ -639,7 +639,7 @@ minimal_options = { path = "examples/python/minimal_options", editable = true } multiprocess_logging = { path = "examples/python/multiprocess_logging", editable = true } multithreading = { path = "examples/python/multithreading", editable = true } graph_binary_tree = { path = "examples/python/graph_binary_tree", editable = true } -graph_lattice = { path = "examples/python/graph_lattice", editable = true } +graphs = { path = "examples/python/graphs", editable = true } nuscenes_dataset = { path = "examples/python/nuscenes_dataset", editable = true } nv12 = { path = "examples/python/nv12", editable = true } objectron = { path = "examples/python/objectron", editable = true } diff --git a/tests/python/release_checklist/check_graph_time_layout.py b/tests/python/release_checklist/check_graph_time_layout.py deleted file mode 100644 index e10bde1053a59..0000000000000 --- a/tests/python/release_checklist/check_graph_time_layout.py +++ /dev/null @@ -1,111 +0,0 @@ -from __future__ import annotations - -# TODO(grtlr): Promote to example -import os -import random -from argparse import Namespace -from uuid import uuid4 - -import rerun as rr -import rerun.blueprint as rrb - -# TODO(grtlr): Clean up the exports -from rerun.blueprint.archetypes.force_collision_radius import ForceCollisionRadius -from rerun.blueprint.archetypes.force_link import ForceLink -from rerun.blueprint.archetypes.force_many_body import ForceManyBody -from rerun.components.color import Color -from rerun.components.show_labels import ShowLabels - -README = """\ -# Time-varying graph view - -Please watch out for any twitching, jumping, or other wise unexpected changes to -the layout when navigating the timeline. - -Please check the following: -* Scrub the timeline to see how the graph layout changes over time. -""" - -color_scheme = [ - Color([228, 26, 28]), # Red - Color([55, 126, 184]), # Blue - Color([77, 175, 74]), # Green - Color([152, 78, 163]), # Purple - Color([255, 127, 0]), # Orange - Color([255, 255, 51]), # Yellow - Color([166, 86, 40]), # Brown - Color([247, 129, 191]), # Pink - Color([153, 153, 153]), # Gray -] - - -def log_readme() -> None: - rr.log("readme", rr.TextDocument(README, media_type=rr.MediaType.MARKDOWN), static=True) - - -def log_graphs() -> None: - nodes = ["root"] - radii = [42] - colors = [Color([81, 81, 81])] - edges = [] - - # We want reproducible results - random.seed(42) - - # Randomly add nodes and edges to the graph - for i in range(50): - existing = random.choice(nodes) - new_node = str(i) - nodes.append(new_node) - radii.append(random.randint(10, 50)) - colors.append(random.choice(color_scheme)) - edges.append((existing, new_node)) - - rr.set_time_sequence("frame", i) - 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), - ) - - rr.send_blueprint( - rrb.Blueprint( - rrb.Grid( - rrb.GraphView( - origin="node_link", - name="Node-link diagram", - force_link=ForceLink(distance=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=[ShowLabels(False)], - ), - rrb.TextDocumentView(origin="readme", name="Instructions"), - ) - ) - ) - - -def run(args: Namespace) -> None: - rr.script_setup(args, f"{os.path.basename(__file__)}", recording_id=uuid4()) - - log_readme() - log_graphs() - - -if __name__ == "__main__": - import argparse - - parser = argparse.ArgumentParser(description="Interactive release checklist") - rr.script_add_args(parser) - args = parser.parse_args() - run(args)