Skip to content
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

Add UI for precisely picking an exact sequence time #7673

Merged
merged 1 commit into from
Oct 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 40 additions & 2 deletions crates/store/re_log_types/src/time_point/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,22 @@ impl TimeType {
}
}

#[inline]
pub fn format_sequence(time_int: TimeInt) -> String {
Self::Sequence.format(time_int, TimeZone::Utc)
}

pub fn parse_sequence(s: &str) -> Option<TimeInt> {
match s {
"<static>" => Some(TimeInt::STATIC),
"−∞" => Some(TimeInt::MIN),
"+∞" => Some(TimeInt::MAX),
_ => {
let s = s.strip_prefix('#').unwrap_or(s);
re_format::parse_i64(s).map(TimeInt::new_temporal)
}
}
}

pub fn format(
&self,
time_int: impl Into<TimeInt>,
Expand All @@ -136,7 +151,7 @@ impl TimeType {
let time_int = time_int.into();
match time_int {
TimeInt::STATIC => "<static>".into(),
TimeInt::MIN => "-∞".into(),
TimeInt::MIN => "∞".into(),
TimeInt::MAX => "+∞".into(),
_ => match self {
Self::Time => Time::from(time_int).format(time_zone_for_timestamps),
Expand Down Expand Up @@ -221,3 +236,26 @@ impl<T: TryInto<TimeInt>, const N: usize> From<[(Timeline, T); N]> for TimePoint
)
}
}

// ----------------------------------------------------------------------------

#[cfg(test)]
mod tests {
use super::{TimeInt, TimeType};

#[test]
fn test_format_parse() {
let cases = [
(TimeInt::STATIC, "<static>"),
(TimeInt::MIN, "−∞"),
(TimeInt::MAX, "+∞"),
(TimeInt::new_temporal(-42), "#−42"),
(TimeInt::new_temporal(12345), "#12 345"),
];

for (int, s) in cases {
assert_eq!(TimeType::format_sequence(int), s);
assert_eq!(TimeType::parse_sequence(s), Some(int));
}
}
}
16 changes: 8 additions & 8 deletions crates/store/re_log_types/src/time_point/time_int.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ impl TimeInt {
pub const ONE: Self = Self(Some(NonMinI64::ONE));

#[inline]
pub fn is_static(&self) -> bool {
*self == Self::STATIC
pub fn is_static(self) -> bool {
self == Self::STATIC
}

/// Creates a new temporal [`TimeInt`].
Expand Down Expand Up @@ -100,7 +100,7 @@ impl TimeInt {

/// Returns `i64::MIN` for [`Self::STATIC`].
#[inline]
pub const fn as_i64(&self) -> i64 {
pub const fn as_i64(self) -> i64 {
match self.0 {
Some(t) => t.get(),
None => i64::MIN,
Expand All @@ -109,7 +109,7 @@ impl TimeInt {

/// Returns `f64::MIN` for [`Self::STATIC`].
#[inline]
pub const fn as_f64(&self) -> f64 {
pub const fn as_f64(self) -> f64 {
match self.0 {
Some(t) => t.get() as _,
None => f64::MIN,
Expand All @@ -119,20 +119,20 @@ impl TimeInt {
/// Always returns [`Self::STATIC`] for [`Self::STATIC`].
#[inline]
#[must_use]
pub fn inc(&self) -> Self {
pub fn inc(self) -> Self {
match self.0 {
Some(t) => Self::new_temporal(t.get().saturating_add(1)),
None => *self,
None => self,
}
}

/// Always returns [`Self::STATIC`] for [`Self::STATIC`].
#[inline]
#[must_use]
pub fn dec(&self) -> Self {
pub fn dec(self) -> Self {
match self.0 {
Some(t) => Self::new_temporal(t.get().saturating_sub(1)),
None => *self,
None => self,
}
}
}
Expand Down
14 changes: 14 additions & 0 deletions crates/utils/re_format/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,20 @@ pub fn parse_f64(text: &str) -> Option<f64> {
text.parse().ok()
}

/// Parses a number, ignoring whitespace (e.g. thousand separators),
/// and treating the special minus character `MINUS` (−) as a minus sign.
pub fn parse_i64(text: &str) -> Option<i64> {
let text: String = text
.chars()
// Ignore whitespace (trailing, leading, and thousands separators):
.filter(|c| !c.is_whitespace())
// Replace special minus character with normal minus (hyphen):
.map(|c| if c == '−' { '-' } else { c })
.collect();

text.parse().ok()
}

/// Pretty format a large number by using SI notation (base 10), e.g.
///
/// ```
Expand Down
29 changes: 26 additions & 3 deletions crates/viewer/re_time_panel/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use re_data_ui::{item_ui::guess_instance_path_icon, sorted_component_list_for_ui
use re_entity_db::{EntityTree, InstancePath};
use re_log_types::{
external::re_types_core::ComponentName, ComponentPath, EntityPath, EntityPathPart,
ResolvedTimeRange, TimeInt, TimeReal,
ResolvedTimeRange, TimeInt, TimeReal, TimeType,
};
use re_types::blueprint::components::PanelState;
use re_ui::{list_item, ContextExt as _, DesignTokens, UiExt as _};
Expand Down Expand Up @@ -1088,10 +1088,33 @@ fn help_button(ui: &mut egui::Ui) {
);
}

fn current_time_ui(ctx: &ViewerContext<'_>, ui: &mut egui::Ui, time_ctrl: &TimeControl) {
fn current_time_ui(ctx: &ViewerContext<'_>, ui: &mut egui::Ui, time_ctrl: &mut TimeControl) {
if let Some(time_int) = time_ctrl.time_int() {
let time_type = time_ctrl.time_type();
ui.monospace(time_type.format(time_int, ctx.app_options.time_zone));
match time_type {
re_log_types::TimeType::Time => {
// TODO(#7653): parse time stamps
ui.monospace(time_type.format(time_int, ctx.app_options.time_zone));
}
re_log_types::TimeType::Sequence => {
// NOTE: egui uses `f64` for all numbers internally, so we get precision problems if the integer gets too big.
if time_int.as_f64() as i64 == time_int.as_i64() {
let mut int = time_int.as_i64();
let drag_value = egui::DragValue::new(&mut int)
.custom_formatter(|x, _range| {
TimeType::format_sequence(TimeInt::new_temporal(x as i64))
})
.custom_parser(|s| TimeType::parse_sequence(s).map(TimeInt::as_f64));
let response = ui.add(drag_value);
if response.changed() {
time_ctrl.set_time(TimeInt::new_temporal(int));
}
} else {
// Avoid the precision problems by just displaying the number without the ability to change it (here).
ui.monospace(time_type.format(time_int, ctx.app_options.time_zone));
}
}
}
}
}

Expand Down
Loading