1 | // Copyright 2019 the Kurbo Authors
|
2 | // SPDX-License-Identifier: Apache-2.0 OR MIT
|
3 |
|
4 | //! A description of the distances between the edges of two rectangles.
|
5 |
|
6 | use core::ops::{Add, Neg, Sub};
|
7 |
|
8 | use crate::{Rect, Size};
|
9 |
|
10 | /// Insets from the edges of a rectangle.
|
11 | ///
|
12 | ///
|
13 | /// The inset value for each edge can be thought of as a delta computed from
|
14 | /// the center of the rect to that edge. For instance, with an inset of `2.0` on
|
15 | /// the x-axis, a rectangle with the origin `(0.0, 0.0)` with that inset added
|
16 | /// will have the new origin at `(-2.0, 0.0)`.
|
17 | ///
|
18 | /// Put alternatively, a positive inset represents increased distance from center,
|
19 | /// and a negative inset represents decreased distance from center.
|
20 | ///
|
21 | /// # Examples
|
22 | ///
|
23 | /// Positive insets added to a [`Rect`] produce a larger [`Rect`]:
|
24 | /// ```
|
25 | /// # use kurbo::{Insets, Rect};
|
26 | /// let rect = Rect::from_origin_size((0., 0.,), (10., 10.,));
|
27 | /// let insets = Insets::uniform_xy(3., 0.,);
|
28 | ///
|
29 | /// let inset_rect = rect + insets;
|
30 | /// assert_eq!(inset_rect.width(), 16.0, "10.0 + 3.0 × 2" );
|
31 | /// assert_eq!(inset_rect.x0, -3.0);
|
32 | /// ```
|
33 | ///
|
34 | /// Negative insets added to a [`Rect`] produce a smaller [`Rect`]:
|
35 | ///
|
36 | /// ```
|
37 | /// # use kurbo::{Insets, Rect};
|
38 | /// let rect = Rect::from_origin_size((0., 0.,), (10., 10.,));
|
39 | /// let insets = Insets::uniform_xy(-3., 0.,);
|
40 | ///
|
41 | /// let inset_rect = rect + insets;
|
42 | /// assert_eq!(inset_rect.width(), 4.0, "10.0 - 3.0 × 2" );
|
43 | /// assert_eq!(inset_rect.x0, 3.0);
|
44 | /// ```
|
45 | ///
|
46 | /// [`Insets`] operate on the absolute rectangle [`Rect::abs`], and so ignore
|
47 | /// existing negative widths and heights.
|
48 | ///
|
49 | /// ```
|
50 | /// # use kurbo::{Insets, Rect};
|
51 | /// let rect = Rect::new(7., 11., 0., 0.,);
|
52 | /// let insets = Insets::uniform_xy(0., 1.,);
|
53 | ///
|
54 | /// assert_eq!(rect.width(), -7.0);
|
55 | ///
|
56 | /// let inset_rect = rect + insets;
|
57 | /// assert_eq!(inset_rect.width(), 7.0);
|
58 | /// assert_eq!(inset_rect.x0, 0.0);
|
59 | /// assert_eq!(inset_rect.height(), 13.0);
|
60 | /// ```
|
61 | ///
|
62 | /// The width and height of an inset operation can still be negative if the
|
63 | /// [`Insets`]' dimensions are greater than the dimensions of the original [`Rect`].
|
64 | ///
|
65 | /// ```
|
66 | /// # use kurbo::{Insets, Rect};
|
67 | /// let rect = Rect::new(0., 0., 3., 5.);
|
68 | /// let insets = Insets::uniform_xy(0., 7.,);
|
69 | ///
|
70 | /// let inset_rect = rect - insets;
|
71 | /// assert_eq!(inset_rect.height(), -9., "5 - 7 × 2" )
|
72 | /// ```
|
73 | ///
|
74 | /// `Rect - Rect = Insets`:
|
75 | ///
|
76 | ///
|
77 | /// ```
|
78 | /// # use kurbo::{Insets, Rect};
|
79 | /// let rect = Rect::new(0., 0., 5., 11.);
|
80 | /// let insets = Insets::uniform_xy(1., 7.,);
|
81 | ///
|
82 | /// let inset_rect = rect + insets;
|
83 | /// let insets2 = inset_rect - rect;
|
84 | ///
|
85 | /// assert_eq!(insets2.x0, insets.x0);
|
86 | /// assert_eq!(insets2.y1, insets.y1);
|
87 | /// assert_eq!(insets2.x_value(), insets.x_value());
|
88 | /// assert_eq!(insets2.y_value(), insets.y_value());
|
89 | /// ```
|
90 | #[derive (Clone, Copy, Default, Debug, PartialEq)]
|
91 | #[cfg_attr (feature = "schemars" , derive(schemars::JsonSchema))]
|
92 | #[cfg_attr (feature = "serde" , derive(serde::Serialize, serde::Deserialize))]
|
93 | pub struct Insets {
|
94 | /// The minimum x coordinate (left edge).
|
95 | pub x0: f64,
|
96 | /// The minimum y coordinate (top edge in y-down spaces).
|
97 | pub y0: f64,
|
98 | /// The maximum x coordinate (right edge).
|
99 | pub x1: f64,
|
100 | /// The maximum y coordinate (bottom edge in y-down spaces).
|
101 | pub y1: f64,
|
102 | }
|
103 |
|
104 | impl Insets {
|
105 | /// Zeroed insets.
|
106 | pub const ZERO: Insets = Insets::uniform(0.);
|
107 |
|
108 | /// New uniform insets.
|
109 | #[inline ]
|
110 | pub const fn uniform(d: f64) -> Insets {
|
111 | Insets {
|
112 | x0: d,
|
113 | y0: d,
|
114 | x1: d,
|
115 | y1: d,
|
116 | }
|
117 | }
|
118 |
|
119 | /// New insets with uniform values along each axis.
|
120 | #[inline ]
|
121 | pub const fn uniform_xy(x: f64, y: f64) -> Insets {
|
122 | Insets {
|
123 | x0: x,
|
124 | y0: y,
|
125 | x1: x,
|
126 | y1: y,
|
127 | }
|
128 | }
|
129 |
|
130 | /// New insets. The ordering of the arguments is "left, top, right, bottom",
|
131 | /// assuming a y-down coordinate space.
|
132 | #[inline ]
|
133 | pub const fn new(x0: f64, y0: f64, x1: f64, y1: f64) -> Insets {
|
134 | Insets { x0, y0, x1, y1 }
|
135 | }
|
136 |
|
137 | /// The total delta on the x-axis represented by these insets.
|
138 | ///
|
139 | /// # Examples
|
140 | ///
|
141 | /// ```
|
142 | /// use kurbo::Insets;
|
143 | ///
|
144 | /// let insets = Insets::uniform_xy(3., 8.);
|
145 | /// assert_eq!(insets.x_value(), 6.);
|
146 | ///
|
147 | /// let insets = Insets::new(5., 0., -12., 0.,);
|
148 | /// assert_eq!(insets.x_value(), -7.);
|
149 | /// ```
|
150 | #[inline ]
|
151 | pub fn x_value(self) -> f64 {
|
152 | self.x0 + self.x1
|
153 | }
|
154 |
|
155 | /// The total delta on the y-axis represented by these insets.
|
156 | ///
|
157 | /// # Examples
|
158 | ///
|
159 | /// ```
|
160 | /// use kurbo::Insets;
|
161 | ///
|
162 | /// let insets = Insets::uniform_xy(3., 7.);
|
163 | /// assert_eq!(insets.y_value(), 14.);
|
164 | ///
|
165 | /// let insets = Insets::new(5., 10., -12., 4.,);
|
166 | /// assert_eq!(insets.y_value(), 14.);
|
167 | /// ```
|
168 | #[inline ]
|
169 | pub fn y_value(self) -> f64 {
|
170 | self.y0 + self.y1
|
171 | }
|
172 |
|
173 | /// Returns the total delta represented by these insets as a [`Size`].
|
174 | ///
|
175 | /// This is equivalent to creating a [`Size`] from the values returned by
|
176 | /// [`x_value`] and [`y_value`].
|
177 | ///
|
178 | /// This function may return a size with negative values.
|
179 | ///
|
180 | /// # Examples
|
181 | ///
|
182 | /// ```
|
183 | /// use kurbo::{Insets, Size};
|
184 | ///
|
185 | /// let insets = Insets::new(11.1, -43.3, 3.333, -0.0);
|
186 | /// assert_eq!(insets.size(), Size::new(insets.x_value(), insets.y_value()));
|
187 | /// ```
|
188 | ///
|
189 | /// [`x_value`]: Insets::x_value
|
190 | /// [`y_value`]: Insets::y_value
|
191 | pub fn size(self) -> Size {
|
192 | Size::new(self.x_value(), self.y_value())
|
193 | }
|
194 |
|
195 | /// Return `true` iff all values are nonnegative.
|
196 | pub fn are_nonnegative(self) -> bool {
|
197 | let Insets { x0, y0, x1, y1 } = self;
|
198 | x0 >= 0.0 && y0 >= 0.0 && x1 >= 0.0 && y1 >= 0.0
|
199 | }
|
200 |
|
201 | /// Return new `Insets` with all negative values replaced with `0.0`.
|
202 | ///
|
203 | /// This is provided as a convenience for applications where negative insets
|
204 | /// are not meaningful.
|
205 | ///
|
206 | /// # Examples
|
207 | ///
|
208 | /// ```
|
209 | /// use kurbo::Insets;
|
210 | ///
|
211 | /// let insets = Insets::new(-10., 3., -0.2, 4.);
|
212 | /// let nonnegative = insets.nonnegative();
|
213 | /// assert_eq!(nonnegative.x_value(), 0.0);
|
214 | /// assert_eq!(nonnegative.y_value(), 7.0);
|
215 | /// ```
|
216 | pub fn nonnegative(self) -> Insets {
|
217 | let Insets { x0, y0, x1, y1 } = self;
|
218 | Insets {
|
219 | x0: x0.max(0.0),
|
220 | y0: y0.max(0.0),
|
221 | x1: x1.max(0.0),
|
222 | y1: y1.max(0.0),
|
223 | }
|
224 | }
|
225 |
|
226 | /// Are these insets finite?
|
227 | #[inline ]
|
228 | pub fn is_finite(&self) -> bool {
|
229 | self.x0.is_finite() && self.y0.is_finite() && self.x1.is_finite() && self.y1.is_finite()
|
230 | }
|
231 |
|
232 | /// Are these insets NaN?
|
233 | #[inline ]
|
234 | pub fn is_nan(&self) -> bool {
|
235 | self.x0.is_nan() || self.y0.is_nan() || self.x1.is_nan() || self.y1.is_nan()
|
236 | }
|
237 | }
|
238 |
|
239 | impl Neg for Insets {
|
240 | type Output = Insets;
|
241 |
|
242 | #[inline ]
|
243 | fn neg(self) -> Insets {
|
244 | Insets::new(-self.x0, -self.y0, -self.x1, -self.y1)
|
245 | }
|
246 | }
|
247 |
|
248 | impl Add<Rect> for Insets {
|
249 | type Output = Rect;
|
250 |
|
251 | #[inline ]
|
252 | #[allow (clippy::suspicious_arithmetic_impl)]
|
253 | fn add(self, other: Rect) -> Rect {
|
254 | let other: Rect = other.abs();
|
255 | Rect::new(
|
256 | x0:other.x0 - self.x0,
|
257 | y0:other.y0 - self.y0,
|
258 | x1:other.x1 + self.x1,
|
259 | y1:other.y1 + self.y1,
|
260 | )
|
261 | }
|
262 | }
|
263 |
|
264 | impl Add<Insets> for Rect {
|
265 | type Output = Rect;
|
266 |
|
267 | #[inline ]
|
268 | fn add(self, other: Insets) -> Rect {
|
269 | other + self
|
270 | }
|
271 | }
|
272 |
|
273 | impl Sub<Rect> for Insets {
|
274 | type Output = Rect;
|
275 |
|
276 | #[inline ]
|
277 | fn sub(self, other: Rect) -> Rect {
|
278 | other + -self
|
279 | }
|
280 | }
|
281 |
|
282 | impl Sub<Insets> for Rect {
|
283 | type Output = Rect;
|
284 |
|
285 | #[inline ]
|
286 | fn sub(self, other: Insets) -> Rect {
|
287 | other - self
|
288 | }
|
289 | }
|
290 |
|
291 | impl From<f64> for Insets {
|
292 | fn from(src: f64) -> Insets {
|
293 | Insets::uniform(src)
|
294 | }
|
295 | }
|
296 |
|
297 | impl From<(f64, f64)> for Insets {
|
298 | fn from(src: (f64, f64)) -> Insets {
|
299 | Insets::uniform_xy(x:src.0, y:src.1)
|
300 | }
|
301 | }
|
302 |
|
303 | impl From<(f64, f64, f64, f64)> for Insets {
|
304 | fn from(src: (f64, f64, f64, f64)) -> Insets {
|
305 | Insets::new(x0:src.0, y0:src.1, x1:src.2, y1:src.3)
|
306 | }
|
307 | }
|
308 | |