1use std::f64;
3use std::f64::consts::PI;
4use std::str::FromStr;
5
6use crate::error;
7
8pub fn inches_to_pixels(value: f64, dpi: i32) -> i32 {
10 (value * (dpi as f64)).round() as i32
11}
12
13pub fn cm_to_pixels(value: f64, dpi: i32) -> i32 {
15 inches_to_pixels(cm_to_inches(value), dpi)
16}
17
18pub fn mm_to_pixels(value: f64, dpi: i32) -> i32 {
20 inches_to_pixels(mm_to_inches(value), dpi)
21}
22
23pub fn cm_to_inches(value: f64) -> f64 {
25 (value * 10.0) / 25.4
26}
27
28pub fn mm_to_inches(value: f64) -> f64 {
30 (value) / 25.4
31}
32
33pub fn inches_to_mm(value: f64) -> f64 {
35 value * 25.4
36}
37
38pub fn inches_to_cm(value: f64) -> f64 {
40 (value * 25.4) / 10.0
41}
42
43pub fn pixels_to_mm(value: i32, dpi: i32) -> f64 {
45 inches_to_mm(value as f64 / dpi as f64)
46}
47
48pub fn pixels_to_cm(value: i32, dpi: i32) -> f64 {
50 inches_to_cm(value as f64 / dpi as f64)
51}
52
53pub fn pixels_to_inches(value: i32, dpi: i32) -> f64 {
55 value as f64 / dpi as f64
56}
57
58pub fn parse_length(value: &str, dpi: i32) -> Result<i32, error::Error> {
65 let l = value.len();
66
67 match extract_unit(value)? {
68 "mm" => {
69 let mm = f64::from_str(&value[..l - 2])?;
70 Ok(mm_to_pixels(mm, dpi))
71 }
72 "cm" => {
73 let cm = f64::from_str(&value[..l - 2])?;
74 Ok(cm_to_pixels(cm, dpi))
75 }
76 "in" => {
77 let inches = f64::from_str(&value[..l - 2])?;
78 Ok(inches_to_pixels(inches, dpi))
79 }
80 "px" => {
81 let px = i32::from_str(&value[..l - 2])?;
82 Ok(px)
83 }
84 _ => {
85 let inches = f64::from_str(value)?;
86 Ok(inches_to_pixels(inches, dpi))
87 }
88 }
89}
90
91pub fn px_to_length(value: i32, unit: &str, dpi: i32) -> Result<String, error::Error> {
94 match unit {
95 "mm" => {
96 let mm = pixels_to_mm(value, dpi);
97 Ok(format!("{mm:.2}mm"))
98 }
99 "cm" => {
100 let cm = pixels_to_cm(value, dpi);
101 Ok(format!("{cm:.2}cm"))
102 }
103 "in" => {
104 let inches = pixels_to_inches(value, dpi);
105 Ok(format!("{inches:.2}in"))
106 }
107 "px" => Ok(format!("{value}px")),
108 _ => {
109 let inches = pixels_to_inches(value, dpi);
110 Ok(format!("{inches:.2}in"))
111 }
112 }
113}
114
115pub fn extract_unit(value: &str) -> Result<&str, error::Error> {
117 let l = value.len();
118 if l > 2 {
119 if value[l - 2..].chars().any(char::is_numeric) {
120 Ok("")
121 } else {
122 Ok(&value[l - 2..])
123 }
124 } else {
125 Ok("")
126 }
127}
128
129pub fn parse_angle(value: &str) -> Result<f64, error::Error> {
131 let angle = f64::from_str(value)?;
132 if !(0.0..=360.0).contains(&angle) {
133 Err(error::Error::AngleOutOfRange(angle))
134 } else {
135 Ok(angle.to_radians())
136 }
137}
138
139pub fn parse_colour(value: &str) -> Result<(f64, f64, f64, f64), error::Error> {
151 if value.len() < 6 {
152 return Err(error::Error::ColourError(value.to_string()));
153 }
154 let mut start = 0;
155 if value.starts_with('#') {
156 start = 1;
157 }
158
159 let red = i32::from_str_radix(&value[start..start + 2], 16)?;
160 let green = i32::from_str_radix(&value[start + 2..start + 4], 16)?;
161 let blue = i32::from_str_radix(&value[start + 4..start + 6], 16)?;
162 let mut alpha = 255;
163 if value.len() > start + 6 {
164 alpha = i32::from_str_radix(&value[start + 6..], 16)?;
165 }
166
167 Ok((
168 red as f64 / 255.0,
169 green as f64 / 255.0,
170 blue as f64 / 255.0,
171 alpha as f64 / 255.0,
172 ))
173}
174
175pub const DEG_30: f64 = 30.0 * (PI / 180.0);
177pub const DEG_45: f64 = 45.0 * (PI / 180.0);
179pub const DEG_60: f64 = 60.0 * (PI / 180.0);
181pub const DEG_90: f64 = 90.0 * (PI / 180.0);
183pub const DEG_120: f64 = 120.0 * (PI / 180.0);
185pub const DEG_180: f64 = 180.0 * (PI / 180.0);
187pub const DEG_270: f64 = 270.0 * (PI / 180.0);
189pub const DEG_360: f64 = 360.0 * (PI / 180.0);
191
192#[cfg(test)]
193mod tests {
194
195 use super::parse_colour;
196 #[test]
197 pub fn colour_conversion_invalid() {
198 assert!(parse_colour("invalid").is_err());
199 assert!(parse_colour("i").is_err());
200 assert!(parse_colour("#i").is_err());
201 }
202
203 #[test]
204 pub fn colour_conversion_valid() {
205 let (r, g, b, a) = parse_colour("#0c0c0c").unwrap();
206 assert_eq!(r, 0.047058823529411764);
207 assert_eq!(g, 0.047058823529411764);
208 assert_eq!(b, 0.047058823529411764);
209 assert_eq!(a, 1.0);
210 }
211}