1 | // Copyright 2021 the Kurbo Authors
|
2 | // SPDX-License-Identifier: Apache-2.0 OR MIT
|
3 |
|
4 | //! A description of the radii for each corner of a rounded rectangle.
|
5 |
|
6 | use core::convert::From;
|
7 |
|
8 | #[cfg (not(feature = "std" ))]
|
9 | use crate::common::FloatFuncs;
|
10 |
|
11 | /// Radii for each corner of a rounded rectangle.
|
12 | ///
|
13 | /// The use of `top` as in `top_left` assumes a y-down coordinate space. Piet
|
14 | /// (and Druid by extension) uses a y-down coordinate space, but Kurbo also
|
15 | /// supports a y-up coordinate space, in which case `top_left` would actually
|
16 | /// refer to the bottom-left corner, and vice versa. Top may not always
|
17 | /// actually be the top, but `top` corners will always have a smaller y-value
|
18 | /// than `bottom` corners.
|
19 | #[derive (Clone, Copy, Default, Debug, PartialEq)]
|
20 | #[cfg_attr (feature = "schemars" , derive(schemars::JsonSchema))]
|
21 | #[cfg_attr (feature = "serde" , derive(serde::Serialize, serde::Deserialize))]
|
22 | pub struct RoundedRectRadii {
|
23 | /// The radius of the top-left corner.
|
24 | pub top_left: f64,
|
25 | /// The radius of the top-right corner.
|
26 | pub top_right: f64,
|
27 | /// The radius of the bottom-right corner.
|
28 | pub bottom_right: f64,
|
29 | /// The radius of the bottom-left corner.
|
30 | pub bottom_left: f64,
|
31 | }
|
32 |
|
33 | impl RoundedRectRadii {
|
34 | /// Create a new RoundedRectRadii. This function takes radius values for
|
35 | /// the four corners. The argument order is "top_left, top_right,
|
36 | /// bottom_right, bottom_left", or clockwise starting from top_left.
|
37 | pub const fn new(top_left: f64, top_right: f64, bottom_right: f64, bottom_left: f64) -> Self {
|
38 | RoundedRectRadii {
|
39 | top_left,
|
40 | top_right,
|
41 | bottom_right,
|
42 | bottom_left,
|
43 | }
|
44 | }
|
45 |
|
46 | /// Create a new RoundedRectRadii from a single radius. The `radius`
|
47 | /// argument will be set as the radius for all four corners.
|
48 | pub const fn from_single_radius(radius: f64) -> Self {
|
49 | RoundedRectRadii {
|
50 | top_left: radius,
|
51 | top_right: radius,
|
52 | bottom_right: radius,
|
53 | bottom_left: radius,
|
54 | }
|
55 | }
|
56 |
|
57 | /// Takes the absolute value of all corner radii.
|
58 | pub fn abs(&self) -> Self {
|
59 | RoundedRectRadii::new(
|
60 | self.top_left.abs(),
|
61 | self.top_right.abs(),
|
62 | self.bottom_right.abs(),
|
63 | self.bottom_left.abs(),
|
64 | )
|
65 | }
|
66 |
|
67 | /// For each corner, takes the min of that corner's radius and `max`.
|
68 | pub fn clamp(&self, max: f64) -> Self {
|
69 | RoundedRectRadii::new(
|
70 | self.top_left.min(max),
|
71 | self.top_right.min(max),
|
72 | self.bottom_right.min(max),
|
73 | self.bottom_left.min(max),
|
74 | )
|
75 | }
|
76 |
|
77 | /// Returns true if all radius values are finite.
|
78 | pub fn is_finite(&self) -> bool {
|
79 | self.top_left.is_finite()
|
80 | && self.top_right.is_finite()
|
81 | && self.bottom_right.is_finite()
|
82 | && self.bottom_left.is_finite()
|
83 | }
|
84 |
|
85 | /// Returns true if any corner radius value is NaN.
|
86 | pub fn is_nan(&self) -> bool {
|
87 | self.top_left.is_nan()
|
88 | || self.top_right.is_nan()
|
89 | || self.bottom_right.is_nan()
|
90 | || self.bottom_left.is_nan()
|
91 | }
|
92 |
|
93 | /// If all radii are equal, returns the value of the radii. Otherwise,
|
94 | /// returns `None`.
|
95 | pub fn as_single_radius(&self) -> Option<f64> {
|
96 | let epsilon = 1e-9;
|
97 |
|
98 | if (self.top_left - self.top_right).abs() < epsilon
|
99 | && (self.top_right - self.bottom_right).abs() < epsilon
|
100 | && (self.bottom_right - self.bottom_left).abs() < epsilon
|
101 | {
|
102 | Some(self.top_left)
|
103 | } else {
|
104 | None
|
105 | }
|
106 | }
|
107 | }
|
108 |
|
109 | impl From<f64> for RoundedRectRadii {
|
110 | fn from(radius: f64) -> Self {
|
111 | RoundedRectRadii::from_single_radius(radius)
|
112 | }
|
113 | }
|
114 |
|
115 | impl From<(f64, f64, f64, f64)> for RoundedRectRadii {
|
116 | fn from(radii: (f64, f64, f64, f64)) -> Self {
|
117 | RoundedRectRadii::new(top_left:radii.0, top_right:radii.1, bottom_right:radii.2, bottom_left:radii.3)
|
118 | }
|
119 | }
|
120 | |