-
Notifications
You must be signed in to change notification settings - Fork 377
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Sync time axis for multiple TimeSeriesView #7931
Comments
I think this boils down to being able to connect blueprint properties of views with each other, so this might quickly turn into a more general infrastructure task. A more short-term solution that's worth experimenting with would be to add an optional view id to that property archetype specifically. I'm surprised we didn't have an issue for this already (couldn't fine any at least) |
Is there anything in particular in your use case that is preventing you from displaying the two timeseries in the same view? |
The y-scale of the data is very different. This makes it hard to show the data in the same TimerSeriesView. For this task, it would be possible to have to Y-axis plotted in one TimeSeriesView. However, this works only for two y-axis and doesn't scale for more Y-axis or separating out the data into multiple TimeSeriesViews (e.g. one TimeSeriesView for forces, one for centroidal position, one for torques of a robot etc). |
I wonder if we can have one global time-series view x-axis property that the individual time series can use or not. This would make the implementation easier as there is only one global property to sync and this doesn't need to be implemented generic enough for all properties. As this is a toggle-on / toggle-off property on the TimeSeriesView, this becomes a boolean flag, which might be easy to add to the TimeSeriesView. This leaves me with two questions about the rerun infra and implementation (for now):
|
no, not yet. One part of our vision in this area is that containers can have arbitrary view properties set that then propagate down the tree and are used whenever a view doesn't set anything. I.e. the view property accessors/queries would be aware of an inheritance tree.
Generally, all views operate in isolation and are very free in how interactions are implemented. In the case of the time series view this is for the most part just egui plot. There's already properties that the interaction writes out to the blueprint store e.g. here
|
Thanks for your comments @Wumpf . Over the weekend I looked at the code a bit. It seems like the egui_plot has a build in way to link axes of different plots together. See here the example from egui_plot: https://github.com/emilk/egui_plot/blob/main/demo/src/plot_demo.rs#L669 You can play with a demo of the linked axes here: https://emilk.github.io/egui_plot/ (click on the "Linked Axes" tab on the top). If we use this linked axes feature, the TimeSeriesViews could have a new property like "SharedAxesNames" (which is a string). The TimerSeriesViews with the same SharedAxesNames will then be synced. Do you think this is worth exploring further? |
it could be that we also need to use something like this to avoid frame delays, but generally I don't think that's a good direction to take as that would completely sidestep the state we have in the blueprint and thus what is controlled from api, shown in the ui and stored on disk |
Makes sense to want to keep things stored in the blueprint. If we would store a hash-map with |
yeah I guess something like that could work out for starters :) |
Another thing I didn't think of before is that you'll need some "new special place" to store and write that map though. Right now the structure of the blueprint store is fairly rigid and very very undocumented. But in a nutshell there's a definition of a container hierarchy with the leaf level, the views, having a bit or leeway to store arbitrary properties in sub-entities. |
I managed to implement a shared-x-axis. At the moment all TimeSeriesViews use the same shared x-axis. The shared x-axis is written to the blueprint. The code is quite hacked and I am not sure it will work well when the data is streaming in and the x-axis gets updated to the last x seconds. @Wumpf : Would you mind taking a link on the current changes and tell me what you think? I was wondering if there should be a My current code is here: https://github.com/jviereck/rerun/tree/issue-7931-sync-x-axis Video of the changes: Screen.Recording.2024-11-05.at.22.32.18-1080-h264.mov |
Hum, well yeah sure it's a hack as you say 😉.
Yeah I think the linking direction could make a lot of sense and aligns well with the vague plane of entity links we wanted to do in the future! That also sidesteps the issue we talked about previously here on where and how to store the shared axis.
... well as I said, this won't be easy since so much infrastructure is missing to do this kind of thing 😅. |
Hmm actually I guess for what you want you want to specifically link the |
This could look something like this: table TimeAxis (
"attr.rerun.scope": "blueprint",
"attr.rust.derive": "Default"
) {
/// The range of the axis.
///
/// If unset, the range well be automatically determined by the visible time range of the view.
range: rerun.components.Range1D ("attr.rerun.component_optional", nullable, order: 2100);
/// If enabled, the time axis range will remain locked to the specified range when zooming.
zoom_lock: rerun.blueprint.components.LockRangeDuringZoom ("attr.rerun.component_optional", nullable, order: 2200);
// ⬅️ Add a hack for linking to another view here.
} |
Thanks for your comments @Wumpf .
What I did in the implementation so far is creating a UUID from a string like this: let shared_id = SpaceViewId::hashed_from_str("TimeSeriesShared"); I was thinking about the Would that work? |
it could work with some more hacks and custom ui. But really I'd like that to be a configurable uuid of an existing view and not a made-up one. I'm not entirely sure about the repercussions of breaking the blueprint entity hierarchy in such a way, so please understand that I'd be very hesitant to land it like that. |
Thanks @Wumpf for your last reply. That makese sense. I will explore adding a linkage in the |
So my current idea is to add a new Looking at the generated code in here: https://github.com/rerun-io/rerun/blob/main/rerun_py/rerun_sdk/rerun/blueprint/views/time_series_view.py#L143 It looks like the fields on the Assuming the field Does it therefore make senes to implement a new |
Other idea might be to add a string |
I pondered this a lil bit more and synced with @jleibs to coordinate what solution we'd like to have medium term. Leading to the writeup of this related issue documenting the issues with the time axis in the absence of syncing With that context established, back to this issue and let's say we want to skip #8050 and jump ahead as quickly as possible :). Jumping a bit for didactic reasons:
Sort of! I think what we want is a new
That's not quite how the structure on the views works: The idea is is that each view has a series of property archetypes, each containing a bunch of components defining the property.
Yes. Let me clarify a bit further: (almost) everything that is defined in those fbs files is available from all SDK languages (it's just that some important scaffolding is missing so far to have blueprint be accessible from C++ and Rust). And vice versa every built-in type that Rerun stores in blueprint or the store comes from these. Hope that makes sense! |
Thanks for the write up. I am a bit confused what to do.
What do you mean by "global"? Can you maybe draw an outline where in the blueprint debug pannel these entries would live?
When constructing the Also, would the |
Global was a bit of a misnomer: The thing you did in your prototype I would have called global because it isn't tied to the entity of a specific view (whose path is derived from the view's id). But it would be ofc better to put it in a subpath of a view, so it's not really global but also doesn't quite follow the current system since there wouldn't be a formal definition (via fbs) where that data is (unless all of #8050 is implemented :)).
For decent ergonomics this requires some work on the Python API, but for a less fluid api this should be already possible I believe: the ids are created on the python side in
yeah my idea would be to treat |
Thanks for the explanations @Wumpf.
Would this data still be saved in a blueprint? If so, how can data be loaded and stored without a fbs? I am worried side stepping the fbs and other infrastructure will make solving this issue quite a hack. I am therefore leaning towards implementing the fbs from #8050 for this feature. What do you think? |
yes. Those fbs files are strictly just for the codegen that generates types (and their serialization!) for all sdk languages. But that doesn't prevent just making up stuff that isn't defined in those. Otherwise custom component/data types wouldn't work either. doing #8050 first (and separately) would definitely be preferable, yes :). I was just looking for ways to cutting corners to speed this up 🤷 |
Let's assume #8050 is implemented, what would be the way to link multiple TimeSeriesViews together (where linking together means to share the view range and query data range)? |
I am not planning to work on this. @Wumpf , do you have a timeline by when this will be implemented? |
I was really hoping to get to that sooner, but other things keep cropping up, so no timeline unfortunately |
FYI, I started working on an alternative approach: Instead of syncing multiple TimeSeriesViews, my version of the TimeSeriesView is capable to display multiple timeseries above each other. By adding a scrolling container, this makes it possible to scroll through a list of timeseries plots easily. For this to work, the interaction with the plot is very different compared to the current one. There are also some other features I am adding that makes determining what to plot easier when you have data with a lot of dimensions. Would having such a TimeSeriesView make sense to be integrated with rerun directly? I am willing to contribute my code and would be much easier for me if it could go into the main branch. |
definitely curious about this! I believe we considered containers with larger virtual area in the past, but never designed it out |
Yes, I'm a big proponent of going the "subplots-within-a-single-view" way instead of trying to sync axes across separate, possibly arbitrarily laid out views. We've discussed this at a workshop during our last offsite and I made a proposal to that effect, but this has yet to be fleshed out into a concrete design (or even an issue). Digging into the specifics since you mention a scrolling container: I would much like to explore alternatives to that. Plots are scrollable, and, in my experience, having scrollable things inside of a scrollable thing makes for a poor UX. Off the bat, my idea would be to always fit the full subplot grid to the available space, and maybe to collapse it to a single plot with a mini-map if space becomes tight. (Surely @gavrelina will have much better ideas.) That would obviously only work for a limited number of plots (say, less than 10ish). Do you have a use case that requires many more plots? If so, could you expend on it? |
In my setup I am displaying data in two TimeSeriesView. The TimeSeriesView are positioned in a vertical viewport above each other. To compare the data from multiple axis, I have to manually position the x-axis (time in this case) to align well. It would be great if there would be a way to keep the two time-/x-axis in sync between the two views.
I was wondering if there could be a flag on the vertical viewport to sync the x-axis for contained TimeSeriesViews.
If someone could provide me with some pointers on how to implement this feature, I am more than happy to work on a pull request.
The text was updated successfully, but these errors were encountered: