cadmus_core/frontlight/
mod.rs1pub mod auto;
3mod natural;
4mod premixed;
5mod standard;
6
7pub use self::natural::NaturalFrontlight;
8pub use self::premixed::PremixedFrontlight;
9pub use self::standard::StandardFrontlight;
10use crate::geom::lerp;
11use libc::c_int;
12use serde::{Deserialize, Serialize};
13use std::cmp::Ordering;
14use std::fmt::{Display, Formatter};
15
16#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq, PartialOrd)]
18pub struct LightLevel(f32);
19
20impl Default for LightLevel {
23 fn default() -> Self {
24 Self(1f32)
25 }
26}
27
28impl From<LightLevel> for c_int {
29 fn from(value: LightLevel) -> Self {
30 value.0.round() as c_int
31 }
32}
33
34impl From<LightLevel> for i16 {
35 fn from(value: LightLevel) -> Self {
36 value.0.round() as i16
37 }
38}
39
40impl From<LightLevel> for String {
41 fn from(value: LightLevel) -> Self {
42 format!("{:.0}", value.0)
43 }
44}
45
46impl Display for LightLevel {
47 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
48 write!(f, "{:.0}%", self.value())
49 }
50}
51
52impl std::cmp::PartialEq<f32> for LightLevel {
53 fn eq(&self, other: &f32) -> bool {
54 self.value().eq(other)
55 }
56}
57
58impl std::cmp::PartialOrd<f32> for LightLevel {
59 fn partial_cmp(&self, other: &f32) -> Option<Ordering> {
60 self.value().partial_cmp(other)
61 }
62}
63
64impl std::ops::Sub<f32> for LightLevel {
65 type Output = LightLevel;
66
67 fn sub(self, rhs: f32) -> Self::Output {
68 Self(self.value() - rhs)
69 }
70}
71impl From<f32> for LightLevel {
72 fn from(value: f32) -> Self {
73 Self(value.clamp(0.0, 100.0))
74 }
75}
76
77impl From<LightLevel> for f32 {
78 fn from(value: LightLevel) -> Self {
79 value.0
80 }
81}
82
83impl LightLevel {
84 const ZERO: f32 = 0.0;
85
86 pub fn abs(self) -> Self {
88 self.value().abs().into()
89 }
90
91 pub fn off() -> Self {
93 Self(Self::ZERO)
94 }
95
96 fn value(&self) -> f32 {
97 self.0
98 }
99
100 pub fn from_fraction(fraction: f32) -> Self {
102 Self::from(fraction * 100f32)
103 }
104
105 pub fn as_fraction(&self) -> f32 {
107 self.value() / 100.0
108 }
109
110 pub fn as_10_base(&self) -> i16 {
114 (self.value() / 10.0).round() as i16
115 }
116
117 pub fn as_10_base_inverted(&self) -> i16 {
119 10 - self.as_10_base()
120 }
121}
122
123#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
125pub struct LightLevels {
126 pub intensity: LightLevel,
128 pub warmth: LightLevel,
130}
131
132impl Default for LightLevels {
133 fn default() -> Self {
134 LightLevels {
135 intensity: LightLevel::default(),
136 warmth: LightLevel::default(),
137 }
138 }
139}
140
141impl LightLevels {
142 pub fn interpolate(self, other: Self, t: f32) -> Self {
146 LightLevels {
147 intensity: LightLevel(lerp(self.intensity.value(), other.intensity.value(), t)),
148 warmth: LightLevel(lerp(self.warmth.value(), other.warmth.value(), t)),
149 }
150 }
151}
152
153pub trait Frontlight {
154 fn set_intensity(&mut self, value: LightLevel) -> anyhow::Result<()>;
156 fn set_warmth(&mut self, value: LightLevel) -> anyhow::Result<()>;
157 fn levels(&self) -> LightLevels;
158 fn turn_off(&mut self) -> anyhow::Result<()> {
160 self.set_intensity(LightLevel::off())?;
161 self.set_warmth(LightLevel::off())?;
162 Ok(())
163 }
164}
165
166impl Frontlight for LightLevels {
167 fn set_intensity(&mut self, value: LightLevel) -> anyhow::Result<()> {
168 self.intensity = value;
169 Ok(())
170 }
171
172 fn set_warmth(&mut self, value: LightLevel) -> anyhow::Result<()> {
173 self.warmth = value;
174 Ok(())
175 }
176
177 fn levels(&self) -> LightLevels {
178 *self
179 }
180}