euv_chart/paint/impl.rs
1use super::*;
2
3/// Inherent implementation of [`Color`].
4impl Color {
5 /// Returns an opaque colour from its red, green, and blue channels.
6 ///
7 /// # Arguments
8 ///
9 /// - `u8` - A `u8` parameter.
10 pub fn rgb(red: u8, green: u8, blue: u8) -> Self {
11 Self {
12 red,
13 green,
14 blue,
15 alpha: 255,
16 }
17 }
18
19 /// Returns a colour from its red, green, blue, and alpha channels.
20 ///
21 /// # Arguments
22 ///
23 /// - `u8` - A `u8` parameter.
24 pub fn rgba(red: u8, green: u8, blue: u8, alpha: u8) -> Self {
25 Self {
26 red,
27 green,
28 blue,
29 alpha,
30 }
31 }
32
33 /// Returns a colour from a packed `0xRRGGBBAA` value.
34 ///
35 /// # Arguments
36 ///
37 /// - `u32` - A 32-bit unsigned integer (`u32`).
38 pub fn from_u32(packed: u32) -> Self {
39 Self {
40 red: ((packed >> 24) & 0xFF) as u8,
41 green: ((packed >> 16) & 0xFF) as u8,
42 blue: ((packed >> 8) & 0xFF) as u8,
43 alpha: (packed & 0xFF) as u8,
44 }
45 }
46
47 /// Returns the colour packed as `0xRRGGBBAA`.
48 ///
49 /// # Returns
50 ///
51 /// - `u32` - A 32-bit unsigned integer.
52 pub fn to_u32(self) -> u32 {
53 ((self.get_red() as u32) << 24)
54 | ((self.get_green() as u32) << 16)
55 | ((self.get_blue() as u32) << 8)
56 | (self.get_alpha() as u32)
57 }
58
59 /// Returns the colour as a CSS `rgba()` string.
60 ///
61 /// # Returns
62 ///
63 /// - `String` - A `String` value.
64 pub fn to_css(self) -> String {
65 if self.get_alpha() == 255 {
66 format!(
67 "#{:02x}{:02x}{:02x}",
68 self.get_red(),
69 self.get_green(),
70 self.get_blue()
71 )
72 } else {
73 format!(
74 "rgba({},{},{},{})",
75 self.get_red(),
76 self.get_green(),
77 self.get_blue(),
78 format!("{:.3}", self.get_alpha() as f64 / 255.0)
79 .trim_end_matches('0')
80 .trim_end_matches('.')
81 )
82 }
83 }
84
85 /// Returns the colour that lies between two colours.
86 ///
87 /// The channels are mixed one by one, which is what the ramp of a
88 /// visual map does: the middle of the ramp is the middle of its
89 /// two ends.
90 ///
91 /// # Arguments
92 ///
93 /// - `f64` - The opposite endpoint of the interpolation.
94 /// - `Self` - The opposite endpoint of the interpolation.
95 pub fn lerp(self, other: Self, amount: f64) -> Self {
96 let amount: f64 = amount.clamp(0.0, 1.0);
97 let mix = move |from: u8, to: u8| {
98 (from as f64 + (to as f64 - from as f64) * amount)
99 .round()
100 .clamp(0.0, 255.0) as u8
101 };
102 Self {
103 red: mix(self.get_red(), other.get_red()),
104 green: mix(self.get_green(), other.get_green()),
105 blue: mix(self.get_blue(), other.get_blue()),
106 alpha: mix(self.get_alpha(), other.get_alpha()),
107 }
108 }
109
110 /// Parses a CSS colour string.
111 ///
112 /// The accepted forms are the ones ECharts accepts: `#rgb`, `#rgba`,
113 /// `#rrggbb`, `#rrggbbaa`, `rgb()`, `rgba()`, `hsl()`, `hsla()`,
114 /// `transparent`, and the CSS named colours. Returns `None` when the
115 /// string is not a colour.
116 ///
117 /// # Arguments
118 ///
119 /// - `&str` - Shared reference to a `str`.
120 ///
121 /// # Returns
122 ///
123 /// - `Option<Self>` - `Some(...)` on success, `None` otherwise.
124 pub fn parse(input: &str) -> Option<Self> {
125 parse_css_color(input)
126 }
127
128 /// Returns the colour with its alpha replaced.
129 ///
130 /// # Arguments
131 ///
132 /// - `u8` - A `u8` parameter.
133 pub fn with_alpha(self, alpha: u8) -> Self {
134 Self { alpha, ..self }
135 }
136
137 /// Returns the colour mixed with another one.
138 ///
139 /// `ratio` is the weight of `other`: `0.0` returns this colour
140 /// unchanged, `1.0` returns `other`.
141 ///
142 /// # Arguments
143 ///
144 /// - `f64` - A 64-bit float (`f64`).
145 /// - `Self` - A `Self` parameter.
146 pub fn mix(self, other: Self, ratio: f64) -> Self {
147 let ratio: f64 = ratio.clamp(0.0, 1.0);
148 let blend = move |a: u8, b: u8| (a as f64 * (1.0 - ratio) + b as f64 * ratio).round() as u8;
149 Self {
150 red: blend(self.get_red(), other.get_red()),
151 green: blend(self.get_green(), other.get_green()),
152 blue: blend(self.get_blue(), other.get_blue()),
153 alpha: blend(self.get_alpha(), other.get_alpha()),
154 }
155 }
156
157 /// Returns the colour brightened towards white.
158 ///
159 /// # Arguments
160 ///
161 /// - `f64` - A 64-bit float (`f64`).
162 pub fn lighten(self, ratio: f64) -> Self {
163 self.mix(Self::rgb(255, 255, 255), ratio)
164 }
165
166 /// Returns the colour darkened towards black.
167 ///
168 /// # Arguments
169 ///
170 /// - `f64` - A 64-bit float (`f64`).
171 pub fn darken(self, ratio: f64) -> Self {
172 self.mix(Self::rgb(0, 0, 0), ratio)
173 }
174}
175
176/// Inherent implementation of [`LinearGradient`].
177impl LinearGradient {
178 /// Returns a linear gradient between the two points.
179 ///
180 /// # Arguments
181 ///
182 /// - `(f64, f64)` - A `(f64, f64)` parameter.
183 pub fn new(start: (f64, f64), end: (f64, f64)) -> Self {
184 Self {
185 start,
186 end,
187 stops: Vec::new(),
188 }
189 }
190
191 /// Adds a colour stop and returns the gradient.
192 ///
193 /// # Arguments
194 ///
195 /// - `f64` - A 64-bit float (`f64`).
196 /// - `Color` - A `Color` parameter.
197 pub fn with_stop(mut self, offset: f64, color: Color) -> Self {
198 self.get_mut_stops().push(GradientStop { offset, color });
199 self.get_mut_stops()
200 .sort_by(|a: &GradientStop, b: &GradientStop| {
201 a.offset
202 .partial_cmp(&b.offset)
203 .unwrap_or(core::cmp::Ordering::Equal)
204 });
205 self
206 }
207}
208
209/// Inherent implementation of [`RadialGradient`].
210impl RadialGradient {
211 /// Returns a radial gradient at the given centre and radius.
212 ///
213 /// # Arguments
214 ///
215 /// - `(f64, f64)` - A `(f64, f64)` parameter.
216 /// - `f64` - A 64-bit float (`f64`).
217 pub fn new(center: (f64, f64), radius: f64) -> Self {
218 Self {
219 center,
220 radius,
221 stops: Vec::new(),
222 }
223 }
224
225 /// Adds a colour stop and returns the gradient.
226 ///
227 /// # Arguments
228 ///
229 /// - `f64` - A 64-bit float (`f64`).
230 /// - `Color` - A `Color` parameter.
231 pub fn with_stop(mut self, offset: f64, color: Color) -> Self {
232 self.get_mut_stops().push(GradientStop { offset, color });
233 self.get_mut_stops()
234 .sort_by(|a: &GradientStop, b: &GradientStop| {
235 a.offset
236 .partial_cmp(&b.offset)
237 .unwrap_or(core::cmp::Ordering::Equal)
238 });
239 self
240 }
241}
242
243/// Inherent implementation of [`TextStyle`].
244impl TextStyle {
245 /// Returns the default text style.
246 pub fn new() -> Self {
247 Self {
248 family: DEFAULT_FONT_FAMILY.to_string(),
249 size: 12.0,
250 weight: DEFAULT_FONT_WEIGHT.to_string(),
251 style: DEFAULT_FONT_STYLE.to_string(),
252 align: TextAlign::default(),
253 vertical_align: TextVerticalAlign::default(),
254 line_height: 1.2,
255 wrap: false,
256 overflow: TextOverflow::default(),
257 color: Color::rgb(51, 51, 51),
258 background: None,
259 padding: (0.0, 0.0, 0.0, 0.0),
260 border_width: 0.0,
261 border_color: None,
262 border_radius: 0.0,
263 rotate: 0.0,
264 stroke_width: 0.0,
265 stroke_color: None,
266 rich: Vec::new(),
267 }
268 }
269
270 /// Returns the CSS `font` shorthand for this style.
271 ///
272 /// # Returns
273 ///
274 /// - `String` - A `String` value.
275 pub fn font(&self) -> String {
276 format!(
277 "{} {} {} {}",
278 self.get_style(),
279 self.get_weight(),
280 self.get_size(),
281 self.get_family()
282 )
283 }
284
285 /// Returns the line height in pixels.
286 ///
287 /// # Returns
288 ///
289 /// - `f64` - A 64-bit float.
290 pub fn line_height_px(&self) -> f64 {
291 self.get_size() * self.get_line_height()
292 }
293
294 /// Adds a rich-text run and returns the style.
295 ///
296 /// # Arguments
297 ///
298 /// - `&str` - Shared reference to a `str`.
299 /// - `RichTextStyle` - A `RichTextStyle` parameter.
300 pub fn with_rich(mut self, name: &str, style: RichTextStyle) -> Self {
301 self.get_mut_rich().push((name.to_string(), style));
302 self
303 }
304}
305
306/// Default construction for [`TextStyle`].
307impl Default for TextStyle {
308 /// Constructs a default [`TextStyle`] value.
309 fn default() -> Self {
310 Self::new()
311 }
312}
313
314/// Inherent implementation of [`RichTextStyle`].
315impl RichTextStyle {
316 /// Returns an empty rich-text run style.
317 pub fn new() -> Self {
318 Self {
319 family: None,
320 size: None,
321 weight: None,
322 style: None,
323 color: None,
324 background: None,
325 padding: None,
326 line_height: None,
327 }
328 }
329}
330
331/// Default construction for [`RichTextStyle`].
332impl Default for RichTextStyle {
333 /// Constructs a default [`RichTextStyle`] value.
334 fn default() -> Self {
335 Self::new()
336 }
337}
338
339/// Inherent implementation of [`LineStyle`].
340impl LineStyle {
341 /// Returns the default line style.
342 pub fn new() -> Self {
343 Self {
344 width: 1.0,
345 cap: LineCap::default(),
346 join: LineJoin::default(),
347 dash: DashPattern::solid(),
348 opacity: 1.0,
349 shadow: None,
350 color: Color::rgb(51, 51, 51),
351 }
352 }
353
354 /// Returns the geometry parameters the tessellator needs.
355 ///
356 /// # Returns
357 ///
358 /// - `StrokeStyle` - A `StrokeStyle` value.
359 pub fn geometry(&self) -> StrokeStyle {
360 StrokeStyle {
361 width: self.get_width() as f32,
362 cap: *self.get_cap(),
363 join: *self.get_join(),
364 dash: self.get_dash().clone(),
365 }
366 }
367
368 /// Returns the dash pattern for the given dash type.
369 ///
370 /// # Arguments
371 ///
372 /// - `LineDashType` - A `LineDashType` parameter.
373 /// - `f64` - A 64-bit float (`f64`).
374 ///
375 /// # Returns
376 ///
377 /// - `DashPattern` - A `DashPattern` value.
378 pub fn dash_for(dash_type: LineDashType, width: f64) -> DashPattern {
379 let width: f32 = width as f32;
380 match dash_type {
381 LineDashType::Solid => DashPattern::solid(),
382 LineDashType::Dashed => DashPattern::new(vec![width * 5.0, width * 5.0], 0.0),
383 LineDashType::Dotted => DashPattern::new(vec![width, width * 2.0], 0.0),
384 }
385 }
386}
387
388/// Default construction for [`LineStyle`].
389impl Default for LineStyle {
390 /// Constructs a default [`LineStyle`] value.
391 fn default() -> Self {
392 Self::new()
393 }
394}
395
396/// Inherent implementation of [`ShapeStyle`].
397impl ShapeStyle {
398 /// Returns the default shape style: a transparent fill and a visible
399 /// stroke.
400 pub fn new() -> Self {
401 Self {
402 fill: Fill::None,
403 stroke: Stroke::None,
404 line_style: LineStyle::new(),
405 shadow: None,
406 opacity: 1.0,
407 }
408 }
409}
410
411/// Default construction for [`ShapeStyle`].
412impl Default for ShapeStyle {
413 /// Constructs a default [`ShapeStyle`] value.
414 fn default() -> Self {
415 Self::new()
416 }
417}
418
419/// Inherent implementation of [`Fill`].
420impl Fill {
421 /// Returns the fill's solid colour, when the fill is solid.
422 ///
423 /// # Returns
424 ///
425 /// - `Option<Color>` - `Some(...)` on success, `None` otherwise.
426 pub fn solid_color(&self) -> Option<Color> {
427 match self {
428 Fill::Solid(color) => Some(*color),
429 _ => None,
430 }
431 }
432}
433
434/// Inherent implementation of [`Stroke`].
435impl Stroke {
436 /// Returns the stroke's solid colour, when the stroke is solid.
437 ///
438 /// # Returns
439 ///
440 /// - `Option<Color>` - `Some(...)` on success, `None` otherwise.
441 pub fn solid_color(&self) -> Option<Color> {
442 match self {
443 Stroke::Solid(color) => Some(*color),
444 _ => None,
445 }
446 }
447}