cadmus_core/settings/
preset.rs

1use crate::frontlight::LightLevels;
2use crate::geom::circular_distances;
3use chrono::{Local, Timelike};
4use serde::{Deserialize, Serialize};
5
6const MINUTES_PER_DAY: u16 = 24 * 60;
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
9#[serde(default, rename_all = "camelCase")]
10pub struct LightPreset {
11    pub timestamp: u16,
12    #[serde(skip_serializing_if = "Option::is_none")]
13    pub lightsensor_level: Option<u16>,
14    pub frontlight_levels: LightLevels,
15}
16
17impl Default for LightPreset {
18    fn default() -> Self {
19        let now = Local::now();
20        LightPreset {
21            timestamp: (60 * now.hour() + now.minute()) as u16,
22            frontlight_levels: LightLevels::default(),
23            lightsensor_level: None,
24        }
25    }
26}
27
28impl LightPreset {
29    pub fn name(&self) -> String {
30        let hours = self.timestamp / 60;
31        let minutes = self.timestamp - hours * 60;
32        format!("{:02}:{:02}", hours, minutes)
33    }
34}
35
36pub fn guess_frontlight(
37    lightsensor_level: Option<u16>,
38    light_presets: &[LightPreset],
39) -> Option<LightLevels> {
40    if light_presets.len() < 2 {
41        return None;
42    }
43    let cur = LightPreset {
44        lightsensor_level,
45        ..Default::default()
46    };
47
48    let mut dmin = [u16::MAX; 2];
49    let mut index = [usize::MAX; 2];
50
51    if light_presets[0].lightsensor_level.is_some() {
52        let s = cur.lightsensor_level.unwrap_or_default();
53
54        for (i, lp) in light_presets.iter().enumerate() {
55            let p = lp.lightsensor_level.unwrap_or_default();
56            let d = if s >= p { s - p } else { p - s };
57
58            if p >= s && d < dmin[0] {
59                dmin[0] = d;
60                index[0] = i;
61            }
62
63            if p <= s && d < dmin[1] {
64                dmin[1] = d;
65                index[1] = i;
66            }
67        }
68    } else {
69        for (i, lp) in light_presets.iter().enumerate() {
70            let (d0, d1) = circular_distances(cur.timestamp, lp.timestamp, MINUTES_PER_DAY);
71
72            if d0 < dmin[0] {
73                dmin[0] = d0;
74                index[0] = i;
75            }
76
77            if d1 < dmin[1] {
78                dmin[1] = d1;
79                index[1] = i;
80            }
81        }
82    }
83
84    if dmin[0] == 0 || dmin[1] == u16::MAX {
85        return Some(light_presets[index[0]].frontlight_levels);
86    }
87
88    if dmin[1] == 0 || dmin[0] == u16::MAX {
89        return Some(light_presets[index[1]].frontlight_levels);
90    }
91
92    let fl0 = light_presets[index[0]].frontlight_levels;
93    let fl1 = light_presets[index[1]].frontlight_levels;
94    let t = dmin[0] as f32 / (dmin[0] + dmin[1]) as f32;
95
96    Some(fl0.interpolate(fl1, t))
97}