Skip to content

Commit

Permalink
Add utility to rr.components.Color to generate colors from any stri…
Browse files Browse the repository at this point in the history
…ng (and use it in the air traffic data example) (#8458)

### What

It's some time nice to log some color information in multiple entities
to make it easier to relate them visually. This PR adds a
`rr.components.Color.from_str()` utility that does exactly that:
generate a nice color randomly picked up based on the provided string.

This PR also updates the air traffic data example so the barometric
traces have matching colors with the map data.

---------

Co-authored-by: Clement Rey <[email protected]>
  • Loading branch information
abey79 and teh-cmc authored Dec 16, 2024
1 parent b68b4e9 commit 4ccfcef
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 9 deletions.
27 changes: 19 additions & 8 deletions examples/python/air_traffic_data/air_traffic_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ def process_measurement(self, measurement: Measurement) -> None:
)

entity_path = f"aircraft/{measurement.icao_id}"
color = rr.components.Color.from_string(entity_path)

if (
measurement.latitude is not None
Expand All @@ -247,13 +248,16 @@ def process_measurement(self, measurement: Measurement) -> None:
):
rr.log(
entity_path,
rr.Points3D([
self._proj.transform(
measurement.longitude,
measurement.latitude,
measurement.barometric_altitude,
)
]),
rr.Points3D(
[
self._proj.transform(
measurement.longitude,
measurement.latitude,
measurement.barometric_altitude,
),
],
colors=color,
),
rr.GeoPoints(lat_lon=[measurement.latitude, measurement.longitude]),
)

Expand All @@ -264,6 +268,7 @@ def process_measurement(self, measurement: Measurement) -> None:
rr.log(
entity_path + "/barometric_altitude",
rr.Scalar(measurement.barometric_altitude),
rr.SeriesLine(color=color),
)

def flush(self) -> None:
Expand Down Expand Up @@ -310,7 +315,13 @@ def log_position_and_altitude(self, df: polars.DataFrame, icao_id: str) -> None:
return

if icao_id not in self._position_indicators:
rr.log(entity_path, [rr.archetypes.Points3D.indicator(), rr.archetypes.GeoPoints.indicator()], static=True)
color = rr.components.Color.from_string(entity_path)
rr.log(
entity_path,
[rr.archetypes.Points3D.indicator(), rr.archetypes.GeoPoints.indicator(), color],
static=True,
)
rr.log(entity_path + "/barometric_altitude", [rr.archetypes.SeriesLine.indicator(), color], static=True)
self._position_indicators.add(icao_id)

timestamps = rr.TimeSecondsColumn("unix_time", df["timestamp"].to_numpy())
Expand Down
3 changes: 2 additions & 1 deletion rerun_py/rerun_sdk/rerun/components/color.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 28 additions & 0 deletions rerun_py/rerun_sdk/rerun/components/color_ext.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from __future__ import annotations

import colorsys
import math
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from . import Color

_GOLDEN_RATIO = (math.sqrt(5.0) - 1.0) / 2.0


class ColorExt:
"""Extension for [Color][rerun.components.Color]."""

@staticmethod
def from_string(s: str) -> Color:
"""
Generate a random yet deterministic color based on a string.
The color is guaranteed to be identical for the same input string.
"""

from . import Color

# adapted from egui::PlotUi
hue = (hash(s) & 0xFFFF) / 2**16 * _GOLDEN_RATIO
return Color([round(comp * 255) for comp in colorsys.hsv_to_rgb(hue, 0.85, 0.5)])

0 comments on commit 4ccfcef

Please sign in to comment.