Skip to content

Commit fbf0993

Browse files
authored
Merge pull request #426 from xemwebe/time_value_unmap_dev
Time value unmap dev
2 parents eec03a0 + 6bb34ff commit fbf0993

File tree

1 file changed

+126
-2
lines changed

1 file changed

+126
-2
lines changed

plotters/src/coord/ranged1d/types/datetime.rs

Lines changed: 126 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ use std::ops::{Add, Range, Sub};
44

55
use crate::coord::ranged1d::{
66
AsRangedCoord, DefaultFormatting, DiscreteRanged, KeyPointHint, NoDefaultFormatting, Ranged,
7-
ValueFormatter,
7+
ReversibleRanged, ValueFormatter,
88
};
99

1010
/// The trait that describe some time value. This is the uniformed abstraction that works
1111
/// for both Date, DateTime and Duration, etc.
12-
pub trait TimeValue: Eq {
12+
pub trait TimeValue: Eq + Sized {
1313
type DateType: Datelike + PartialOrd;
1414

1515
/// Returns the date that is no later than the time
@@ -20,6 +20,8 @@ pub trait TimeValue: Eq {
2020
fn earliest_after_date(date: Self::DateType) -> Self;
2121
/// Returns the duration between two time value
2222
fn subtract(&self, other: &Self) -> Duration;
23+
/// Add duration to time value
24+
fn add(&self, duration: &Duration) -> Self;
2325
/// Instantiate a date type for current time value;
2426
fn ymd(&self, year: i32, month: u32, date: u32) -> Self::DateType;
2527
/// Cast current date type into this type
@@ -46,6 +48,31 @@ pub trait TimeValue: Eq {
4648

4749
(f64::from(limit.1 - limit.0) * value_days / total_days) as i32 + limit.0
4850
}
51+
52+
/// Map pixel to coord spec
53+
fn unmap_coord(point: i32, begin: &Self, end: &Self, limit: (i32, i32)) -> Self {
54+
let total_span = end.subtract(begin);
55+
let offset = (point - limit.0) as i64;
56+
57+
// Check if nanoseconds fit in i64
58+
if let Some(total_ns) = total_span.num_nanoseconds() {
59+
let pixel_span = (limit.1 - limit.0) as i64;
60+
let factor = total_ns / pixel_span;
61+
let remainder = total_ns % pixel_span;
62+
if factor == 0
63+
|| i64::MAX / factor > offset.abs()
64+
|| (remainder == 0 && i64::MAX / factor >= offset.abs())
65+
{
66+
let nano_seconds = offset * factor + (remainder * offset) / pixel_span;
67+
return begin.add(&Duration::nanoseconds(nano_seconds));
68+
}
69+
}
70+
71+
// Otherwise, use days
72+
let total_days = total_span.num_days() as f64;
73+
let days = (((offset as f64) * total_days) / ((limit.1 - limit.0) as f64)) as i64;
74+
begin.add(&Duration::days(days))
75+
}
4976
}
5077

5178
impl TimeValue for NaiveDate {
@@ -62,6 +89,9 @@ impl TimeValue for NaiveDate {
6289
fn subtract(&self, other: &NaiveDate) -> Duration {
6390
*self - *other
6491
}
92+
fn add(&self, other: &Duration) -> NaiveDate {
93+
*self + *other
94+
}
6595

6696
fn ymd(&self, year: i32, month: u32, date: u32) -> Self::DateType {
6797
NaiveDate::from_ymd(year, month, date)
@@ -86,6 +116,9 @@ impl<Z: TimeZone> TimeValue for Date<Z> {
86116
fn subtract(&self, other: &Date<Z>) -> Duration {
87117
self.clone() - other.clone()
88118
}
119+
fn add(&self, other: &Duration) -> Date<Z> {
120+
self.clone() + *other
121+
}
89122

90123
fn ymd(&self, year: i32, month: u32, date: u32) -> Self::DateType {
91124
self.timezone().ymd(year, month, date)
@@ -115,6 +148,9 @@ impl<Z: TimeZone> TimeValue for DateTime<Z> {
115148
fn subtract(&self, other: &DateTime<Z>) -> Duration {
116149
self.clone() - other.clone()
117150
}
151+
fn add(&self, other: &Duration) -> DateTime<Z> {
152+
self.clone() + *other
153+
}
118154

119155
fn ymd(&self, year: i32, month: u32, date: u32) -> Self::DateType {
120156
self.timezone().ymd(year, month, date)
@@ -144,6 +180,9 @@ impl TimeValue for NaiveDateTime {
144180
fn subtract(&self, other: &NaiveDateTime) -> Duration {
145181
*self - *other
146182
}
183+
fn add(&self, other: &Duration) -> NaiveDateTime {
184+
*self + *other
185+
}
147186

148187
fn ymd(&self, year: i32, month: u32, date: u32) -> Self::DateType {
149188
NaiveDate::from_ymd(year, month, date)
@@ -663,6 +702,19 @@ where
663702
}
664703
}
665704

705+
impl<DT> ReversibleRanged for RangedDateTime<DT>
706+
where
707+
DT: Datelike + Timelike + TimeValue + Clone + PartialOrd,
708+
DT: Add<Duration, Output = DT>,
709+
DT: Sub<DT, Output = Duration>,
710+
RangedDate<DT::DateType>: Ranged<ValueType = DT::DateType>,
711+
{
712+
/// Perform the reverse mapping
713+
fn unmap(&self, input: i32, limit: (i32, i32)) -> Option<Self::ValueType> {
714+
Some(TimeValue::unmap_coord(input, &self.0, &self.1, limit))
715+
}
716+
}
717+
666718
/// The coordinate that for duration of time
667719
#[derive(Clone)]
668720
pub struct RangedDuration(Duration, Duration);
@@ -1168,4 +1220,76 @@ mod test {
11681220
assert_eq!(coord1.index_of(&coord1.from_index(i).unwrap()).unwrap(), i);
11691221
}
11701222
}
1223+
1224+
#[test]
1225+
fn test_datetime_with_unmap() {
1226+
let start_time = Utc.ymd(2021, 1, 1).and_hms(8, 0, 0);
1227+
let end_time = Utc.ymd(2023, 1, 1).and_hms(8, 0, 0);
1228+
let mid = Utc.ymd(2022, 1, 1).and_hms(8, 0, 0);
1229+
let coord: RangedDateTime<_> = (start_time..end_time).into();
1230+
let pos = coord.map(&mid, (1000, 2000));
1231+
assert_eq!(pos, 1500);
1232+
let value = coord.unmap(pos, (1000, 2000));
1233+
assert_eq!(value, Some(mid));
1234+
}
1235+
1236+
#[test]
1237+
fn test_naivedatetime_with_unmap() {
1238+
let start_time = NaiveDate::from_ymd(2021, 1, 1).and_hms_milli(8, 0, 0, 0);
1239+
let end_time = NaiveDate::from_ymd(2023, 1, 1).and_hms_milli(8, 0, 0, 0);
1240+
let mid = NaiveDate::from_ymd(2022, 1, 1).and_hms_milli(8, 0, 0, 0);
1241+
let coord: RangedDateTime<_> = (start_time..end_time).into();
1242+
let pos = coord.map(&mid, (1000, 2000));
1243+
assert_eq!(pos, 1500);
1244+
let value = coord.unmap(pos, (1000, 2000));
1245+
assert_eq!(value, Some(mid));
1246+
}
1247+
1248+
#[test]
1249+
fn test_date_with_unmap() {
1250+
let start_date = Utc.ymd(2021, 1, 1);
1251+
let end_date = Utc.ymd(2023, 1, 1);
1252+
let mid = Utc.ymd(2022, 1, 1);
1253+
let coord: RangedDate<Date<_>> = (start_date..end_date).into();
1254+
let pos = coord.map(&mid, (1000, 2000));
1255+
assert_eq!(pos, 1500);
1256+
let value = coord.unmap(pos, (1000, 2000));
1257+
assert_eq!(value, Some(mid));
1258+
}
1259+
1260+
#[test]
1261+
fn test_naivedate_with_unmap() {
1262+
let start_date = NaiveDate::from_ymd(2021, 1, 1);
1263+
let end_date = NaiveDate::from_ymd(2023, 1, 1);
1264+
let mid = NaiveDate::from_ymd(2022, 1, 1);
1265+
let coord: RangedDate<NaiveDate> = (start_date..end_date).into();
1266+
let pos = coord.map(&mid, (1000, 2000));
1267+
assert_eq!(pos, 1500);
1268+
let value = coord.unmap(pos, (1000, 2000));
1269+
assert_eq!(value, Some(mid));
1270+
}
1271+
1272+
#[test]
1273+
fn test_datetime_unmap_for_nanoseconds() {
1274+
let start_time = Utc.ymd(2021, 1, 1).and_hms(8, 0, 0);
1275+
let end_time = start_time + Duration::nanoseconds(1900);
1276+
let mid = start_time + Duration::nanoseconds(950);
1277+
let coord: RangedDateTime<_> = (start_time..end_time).into();
1278+
let pos = coord.map(&mid, (1000, 2000));
1279+
assert_eq!(pos, 1500);
1280+
let value = coord.unmap(pos, (1000, 2000));
1281+
assert_eq!(value, Some(mid));
1282+
}
1283+
1284+
#[test]
1285+
fn test_datetime_unmap_for_nanoseconds_small_period() {
1286+
let start_time = Utc.ymd(2021, 1, 1).and_hms(8, 0, 0);
1287+
let end_time = start_time + Duration::nanoseconds(400);
1288+
let coord: RangedDateTime<_> = (start_time..end_time).into();
1289+
let value = coord.unmap(2000, (1000, 2000));
1290+
assert_eq!(value, Some(end_time));
1291+
let mid = start_time + Duration::nanoseconds(200);
1292+
let value = coord.unmap(500, (0, 1000));
1293+
assert_eq!(value, Some(mid));
1294+
}
11711295
}

0 commit comments

Comments
 (0)