Skip to main content

inset_embedder/geometry/
geometry.rs

1//! Flutter counterpart: `engine/src/flutter/lib/ui/geometry.dart`.
2
3use std::fmt::{self, Debug};
4use std::ops::{Add, BitAnd, Div, Mul, Neg, Rem, Sub};
5
6use super::lerp::lerp_double_non_null;
7use super::math::clamp_double;
8
9/// An immutable 2D floating-point offset.
10///
11/// Generally speaking, Offsets can be interpreted in two ways:
12///
13/// 1. As representing a point in Cartesian space a specified distance from a
14///    separately-maintained origin. For example, the top-left position of
15///    children in the `RenderBox` protocol is typically represented as an
16///    [`Offset`] from the top left of the parent box.
17///
18/// 2. As a vector that can be applied to coordinates. For example, when painting
19///    a `RenderObject`, the parent is passed an [`Offset`] from the screen's
20///    origin which it can add to the offsets of its children to find the
21///    [`Offset`] from the screen's origin to each of the children.
22///
23/// Because a particular [`Offset`] can be interpreted as one sense at one time
24/// then as the other sense at a later time, the same class is used for both
25/// senses.
26///
27/// See also:
28///
29///  * [`Size`], which represents a vector describing the size of a rectangle.
30#[derive(Clone, Copy, PartialEq)]
31pub struct Offset {
32    dx: f64,
33    dy: f64,
34}
35
36impl Offset {
37    /// Creates an offset. The first argument sets [`dx`](Offset::dx), the
38    /// horizontal component, and the second sets [`dy`](Offset::dy), the
39    /// vertical component.
40    pub const fn new(dx: f64, dy: f64) -> Offset {
41        Offset { dx, dy }
42    }
43
44    /// Creates an offset from its [`direction`](Offset::direction) and
45    /// [`distance`](Offset::distance).
46    ///
47    /// The direction is in radians clockwise from the positive x-axis.
48    ///
49    /// Pass `1.0` for `distance` to create a unit vector.
50    pub fn from_direction(direction: f64, distance: f64) -> Offset {
51        Offset::new(distance * direction.cos(), distance * direction.sin())
52    }
53
54    /// The x component of the offset.
55    ///
56    /// The y component is given by [`dy`](Offset::dy).
57    pub const fn dx(&self) -> f64 {
58        self.dx
59    }
60
61    /// The y component of the offset.
62    ///
63    /// The x component is given by [`dx`](Offset::dx).
64    pub const fn dy(&self) -> f64 {
65        self.dy
66    }
67
68    /// Returns true if either component is [`f64::INFINITY`], and false if both
69    /// are finite (or negative infinity, or NaN).
70    ///
71    /// This is different than comparing for equality with an instance that has
72    /// _both_ components set to [`f64::INFINITY`].
73    ///
74    /// See also:
75    ///
76    ///  * [`is_finite`](Offset::is_finite), which is true if both components are
77    ///    finite (and not NaN).
78    pub fn is_infinite(&self) -> bool {
79        self.dx >= f64::INFINITY || self.dy >= f64::INFINITY
80    }
81
82    /// Whether both components are finite (neither infinite nor NaN).
83    ///
84    /// See also:
85    ///
86    ///  * [`is_infinite`](Offset::is_infinite), which returns true if either
87    ///    component is equal to positive infinity.
88    pub fn is_finite(&self) -> bool {
89        self.dx.is_finite() && self.dy.is_finite()
90    }
91
92    /// Less-than operator. Compares an [`Offset`] or [`Size`] to another
93    /// [`Offset`] or [`Size`], and returns true if both the horizontal and
94    /// vertical values of the left-hand-side operand are smaller than the
95    /// horizontal and vertical values of the right-hand-side operand
96    /// respectively. Returns false otherwise.
97    ///
98    /// This is a partial ordering. It is possible for two values to be neither
99    /// less, nor greater than, nor equal to, another.
100    pub fn lt(&self, other: &Offset) -> bool {
101        self.dx < other.dx && self.dy < other.dy
102    }
103
104    /// Less-than-or-equal-to operator. Compares an [`Offset`] or [`Size`] to
105    /// another [`Offset`] or [`Size`], and returns true if both the horizontal
106    /// and vertical values of the left-hand-side operand are smaller than or
107    /// equal to the horizontal and vertical values of the right-hand-side
108    /// operand respectively. Returns false otherwise.
109    ///
110    /// This is a partial ordering. It is possible for two values to be neither
111    /// less, nor greater than, nor equal to, another.
112    pub fn le(&self, other: &Offset) -> bool {
113        self.dx <= other.dx && self.dy <= other.dy
114    }
115
116    /// Greater-than operator. Compares an [`Offset`] or [`Size`] to another
117    /// [`Offset`] or [`Size`], and returns true if both the horizontal and
118    /// vertical values of the left-hand-side operand are bigger than the
119    /// horizontal and vertical values of the right-hand-side operand
120    /// respectively. Returns false otherwise.
121    ///
122    /// This is a partial ordering. It is possible for two values to be neither
123    /// less, nor greater than, nor equal to, another.
124    pub fn gt(&self, other: &Offset) -> bool {
125        self.dx > other.dx && self.dy > other.dy
126    }
127
128    /// Greater-than-or-equal-to operator. Compares an [`Offset`] or [`Size`] to
129    /// another [`Offset`] or [`Size`], and returns true if both the horizontal
130    /// and vertical values of the left-hand-side operand are bigger than or
131    /// equal to the horizontal and vertical values of the right-hand-side
132    /// operand respectively. Returns false otherwise.
133    ///
134    /// This is a partial ordering. It is possible for two values to be neither
135    /// less, nor greater than, nor equal to, another.
136    pub fn ge(&self, other: &Offset) -> bool {
137        self.dx >= other.dx && self.dy >= other.dy
138    }
139
140    /// The magnitude of the offset.
141    ///
142    /// If you need this value to compare it to another [`Offset`]'s distance,
143    /// consider using [`distance_squared`](Offset::distance_squared) instead,
144    /// since it is cheaper to compute.
145    pub fn distance(&self) -> f64 {
146        (self.dx * self.dx + self.dy * self.dy).sqrt()
147    }
148
149    /// The square of the magnitude of the offset.
150    ///
151    /// This is cheaper than computing the [`distance`](Offset::distance) itself.
152    pub fn distance_squared(&self) -> f64 {
153        self.dx * self.dx + self.dy * self.dy
154    }
155
156    /// The angle of this offset as radians clockwise from the positive x-axis,
157    /// in the range -pi to pi, assuming positive values of the x-axis go to the
158    /// right and positive values of the y-axis go down.
159    ///
160    /// Zero means that [`dy`](Offset::dy) is zero and [`dx`](Offset::dx) is zero
161    /// or positive.
162    ///
163    /// Values from zero to pi/2 indicate positive values of [`dx`](Offset::dx)
164    /// and [`dy`](Offset::dy), the bottom-right quadrant.
165    ///
166    /// Values from pi/2 to pi indicate negative values of [`dx`](Offset::dx) and
167    /// positive values of [`dy`](Offset::dy), the bottom-left quadrant.
168    ///
169    /// Values from zero to -pi/2 indicate positive values of [`dx`](Offset::dx)
170    /// and negative values of [`dy`](Offset::dy), the top-right quadrant.
171    ///
172    /// Values from -pi/2 to -pi indicate negative values of [`dx`](Offset::dx)
173    /// and [`dy`](Offset::dy), the top-left quadrant.
174    ///
175    /// When [`dy`](Offset::dy) is zero and [`dx`](Offset::dx) is negative, the
176    /// direction is pi.
177    ///
178    /// When [`dx`](Offset::dx) is zero, direction is pi/2 if [`dy`](Offset::dy)
179    /// is positive and -pi/2 if [`dy`](Offset::dy) is negative.
180    ///
181    /// See also:
182    ///
183    ///  * [`distance`](Offset::distance), to compute the magnitude of the
184    ///    vector.
185    pub fn direction(&self) -> f64 {
186        self.dy.atan2(self.dx)
187    }
188
189    /// An offset with zero magnitude.
190    ///
191    /// This can be used to represent the origin of a coordinate space.
192    pub const ZERO: Offset = Offset::new(0.0, 0.0);
193
194    /// An offset with infinite x and y components.
195    ///
196    /// See also:
197    ///
198    ///  * [`is_infinite`](Offset::is_infinite), which checks whether either
199    ///    component is infinite.
200    ///  * [`is_finite`](Offset::is_finite), which checks whether both
201    ///    components are finite.
202    // This is included for completeness, because [Size.infinite] exists.
203    pub const INFINITE: Offset = Offset::new(f64::INFINITY, f64::INFINITY);
204
205    /// Returns a new offset with the x component scaled by `scale_x` and the y
206    /// component scaled by `scale_y`.
207    ///
208    /// If the two scale arguments are the same, consider using the `*` operator
209    /// instead:
210    ///
211    /// ```
212    /// # use inset_embedder::Offset;
213    /// let a = Offset::new(10.0, 10.0);
214    /// let b = a * 2.0; // same as: a.scale(2.0, 2.0)
215    /// # assert_eq!(b, a.scale(2.0, 2.0));
216    /// ```
217    ///
218    /// If the two arguments are -1, consider using the unary `-` operator
219    /// instead:
220    ///
221    /// ```
222    /// # use inset_embedder::Offset;
223    /// let a = Offset::new(10.0, 10.0);
224    /// let b = -a; // same as: a.scale(-1.0, -1.0)
225    /// # assert_eq!(b, a.scale(-1.0, -1.0));
226    /// ```
227    pub fn scale(&self, scale_x: f64, scale_y: f64) -> Offset {
228        Offset::new(self.dx * scale_x, self.dy * scale_y)
229    }
230
231    /// Returns a new offset with `translate_x` added to the x component and
232    /// `translate_y` added to the y component.
233    ///
234    /// If the arguments come from another [`Offset`], consider using the `+` or
235    /// `-` operators instead:
236    ///
237    /// ```
238    /// # use inset_embedder::Offset;
239    /// let a = Offset::new(10.0, 10.0);
240    /// let b = Offset::new(10.0, 10.0);
241    /// let c = a + b; // same as: a.translate(b.dx(), b.dy())
242    /// let d = a - b; // same as: a.translate(-b.dx(), -b.dy())
243    /// # assert_eq!(c, a.translate(b.dx(), b.dy()));
244    /// # assert_eq!(d, a.translate(-b.dx(), -b.dy()));
245    /// ```
246    pub fn translate(&self, translate_x: f64, translate_y: f64) -> Offset {
247        Offset::new(self.dx + translate_x, self.dy + translate_y)
248    }
249
250    /// Integer (truncating) division operator.
251    ///
252    /// Returns an offset whose coordinates are the coordinates of this offset
253    /// divided by the scalar `operand`, rounded towards zero.
254    pub fn truncating_div(&self, operand: f64) -> Offset {
255        Offset::new((self.dx / operand).trunc(), (self.dy / operand).trunc())
256    }
257
258    /// Linearly interpolate between two offsets.
259    ///
260    /// If either offset is null, this function interpolates from
261    /// [`Offset::ZERO`].
262    ///
263    /// The `t` argument represents position on the timeline, with 0.0 meaning
264    /// that the interpolation has not started, returning `a` (or something
265    /// equivalent to `a`), 1.0 meaning that the interpolation has finished,
266    /// returning `b` (or something equivalent to `b`), and values in between
267    /// meaning that the interpolation is at the relevant point on the timeline
268    /// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
269    /// 1.0, so negative values and values greater than 1.0 are valid (and can
270    /// easily be generated by curves such as `Curves.elasticInOut`).
271    ///
272    /// Values for `t` are usually obtained from an `Animation<double>`, such as
273    /// an `AnimationController`.
274    pub fn lerp(a: Option<Offset>, b: Option<Offset>, t: f64) -> Option<Offset> {
275        match (a, b) {
276            (None, None) => None,
277            (Some(a), None) => Some(a * (1.0 - t)),
278            (None, Some(b)) => Some(b * t),
279            (Some(a), Some(b)) => Some(Offset::new(
280                lerp_double_non_null(a.dx, b.dx, t),
281                lerp_double_non_null(a.dy, b.dy, t),
282            )),
283        }
284    }
285}
286
287/// Unary negation operator.
288///
289/// Returns an offset with the coordinates negated.
290///
291/// If the [`Offset`] represents an arrow on a plane, this operator returns the
292/// same arrow but pointing in the reverse direction.
293impl Neg for Offset {
294    type Output = Offset;
295
296    fn neg(self) -> Offset {
297        Offset::new(-self.dx, -self.dy)
298    }
299}
300
301/// Binary subtraction operator.
302///
303/// Returns an offset whose [`dx`](Offset::dx) value is the left-hand-side
304/// operand's [`dx`](Offset::dx) minus the right-hand-side operand's
305/// [`dx`](Offset::dx) and whose [`dy`](Offset::dy) value is the left-hand-side
306/// operand's [`dy`](Offset::dy) minus the right-hand-side operand's
307/// [`dy`](Offset::dy).
308///
309/// See also [`translate`](Offset::translate).
310impl Sub for Offset {
311    type Output = Offset;
312
313    fn sub(self, other: Offset) -> Offset {
314        Offset::new(self.dx - other.dx, self.dy - other.dy)
315    }
316}
317
318/// Binary addition operator.
319///
320/// Returns an offset whose [`dx`](Offset::dx) value is the sum of the
321/// [`dx`](Offset::dx) values of the two operands, and whose [`dy`](Offset::dy)
322/// value is the sum of the [`dy`](Offset::dy) values of the two operands.
323///
324/// See also [`translate`](Offset::translate).
325impl Add for Offset {
326    type Output = Offset;
327
328    fn add(self, other: Offset) -> Offset {
329        Offset::new(self.dx + other.dx, self.dy + other.dy)
330    }
331}
332
333/// Multiplication operator.
334///
335/// Returns an offset whose coordinates are the coordinates of the left-hand-side
336/// operand (an Offset) multiplied by the scalar right-hand-side operand (an
337/// `f64`).
338///
339/// See also [`scale`](Offset::scale).
340impl Mul<f64> for Offset {
341    type Output = Offset;
342
343    fn mul(self, operand: f64) -> Offset {
344        Offset::new(self.dx * operand, self.dy * operand)
345    }
346}
347
348/// Division operator.
349///
350/// Returns an offset whose coordinates are the coordinates of the left-hand-side
351/// operand (an Offset) divided by the scalar right-hand-side operand (an `f64`).
352///
353/// See also [`scale`](Offset::scale).
354impl Div<f64> for Offset {
355    type Output = Offset;
356
357    fn div(self, operand: f64) -> Offset {
358        Offset::new(self.dx / operand, self.dy / operand)
359    }
360}
361
362/// Modulo (remainder) operator.
363///
364/// Returns an offset whose coordinates are the remainder of dividing the
365/// coordinates of the left-hand-side operand (an Offset) by the scalar
366/// right-hand-side operand (an `f64`).
367impl Rem<f64> for Offset {
368    type Output = Offset;
369
370    fn rem(self, operand: f64) -> Offset {
371        Offset::new(self.dx.rem_euclid(operand), self.dy.rem_euclid(operand))
372    }
373}
374
375impl Debug for Offset {
376    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
377        write!(f, "Offset({:.1}, {:.1})", self.dx, self.dy)
378    }
379}
380
381/// Holds a 2D floating-point size.
382///
383/// You can think of this as an [`Offset`] from the origin.
384#[derive(Clone, Copy, PartialEq)]
385pub struct Size {
386    width: f64,
387    height: f64,
388}
389
390impl Size {
391    /// Creates a [`Size`] with the given [`width`](Size::width) and
392    /// [`height`](Size::height).
393    pub const fn new(width: f64, height: f64) -> Size {
394        Size { width, height }
395    }
396
397    /// Creates a square [`Size`] whose [`width`](Size::width) and
398    /// [`height`](Size::height) are the given dimension.
399    ///
400    /// See also:
401    ///
402    ///  * [`from_radius`](Size::from_radius), which is more convenient when the
403    ///    available size is the radius of a circle.
404    pub const fn square(dimension: f64) -> Size {
405        Size::new(dimension, dimension)
406    }
407
408    /// Creates a [`Size`] with the given [`width`](Size::width) and an infinite
409    /// [`height`](Size::height).
410    pub const fn from_width(width: f64) -> Size {
411        Size::new(width, f64::INFINITY)
412    }
413
414    /// Creates a [`Size`] with the given [`height`](Size::height) and an
415    /// infinite [`width`](Size::width).
416    pub const fn from_height(height: f64) -> Size {
417        Size::new(f64::INFINITY, height)
418    }
419
420    /// Creates a square [`Size`] whose [`width`](Size::width) and
421    /// [`height`](Size::height) are twice the given dimension.
422    ///
423    /// This is a square that contains a circle with the given radius.
424    ///
425    /// See also:
426    ///
427    ///  * [`square`](Size::square), which creates a square with the given
428    ///    dimension.
429    pub const fn from_radius(radius: f64) -> Size {
430        Size::new(radius * 2.0, radius * 2.0)
431    }
432
433    /// The horizontal extent of this size.
434    pub const fn width(&self) -> f64 {
435        self.width
436    }
437
438    /// The vertical extent of this size.
439    pub const fn height(&self) -> f64 {
440        self.height
441    }
442
443    /// Returns true if either component is [`f64::INFINITY`], and false if both
444    /// are finite (or negative infinity, or NaN).
445    ///
446    /// This is different than comparing for equality with an instance that has
447    /// _both_ components set to [`f64::INFINITY`].
448    ///
449    /// See also:
450    ///
451    ///  * [`is_finite`](Size::is_finite), which is true if both components are
452    ///    finite (and not NaN).
453    pub fn is_infinite(&self) -> bool {
454        self.width >= f64::INFINITY || self.height >= f64::INFINITY
455    }
456
457    /// Whether both components are finite (neither infinite nor NaN).
458    ///
459    /// See also:
460    ///
461    ///  * [`is_infinite`](Size::is_infinite), which returns true if either
462    ///    component is equal to positive infinity.
463    pub fn is_finite(&self) -> bool {
464        self.width.is_finite() && self.height.is_finite()
465    }
466
467    /// Less-than operator. Compares an [`Offset`] or [`Size`] to another
468    /// [`Offset`] or [`Size`], and returns true if both the horizontal and
469    /// vertical values of the left-hand-side operand are smaller than the
470    /// horizontal and vertical values of the right-hand-side operand
471    /// respectively. Returns false otherwise.
472    ///
473    /// This is a partial ordering. It is possible for two values to be neither
474    /// less, nor greater than, nor equal to, another.
475    pub fn lt(&self, other: &Size) -> bool {
476        self.width < other.width && self.height < other.height
477    }
478
479    /// Less-than-or-equal-to operator. Compares an [`Offset`] or [`Size`] to
480    /// another [`Offset`] or [`Size`], and returns true if both the horizontal
481    /// and vertical values of the left-hand-side operand are smaller than or
482    /// equal to the horizontal and vertical values of the right-hand-side
483    /// operand respectively. Returns false otherwise.
484    ///
485    /// This is a partial ordering. It is possible for two values to be neither
486    /// less, nor greater than, nor equal to, another.
487    pub fn le(&self, other: &Size) -> bool {
488        self.width <= other.width && self.height <= other.height
489    }
490
491    /// Greater-than operator. Compares an [`Offset`] or [`Size`] to another
492    /// [`Offset`] or [`Size`], and returns true if both the horizontal and
493    /// vertical values of the left-hand-side operand are bigger than the
494    /// horizontal and vertical values of the right-hand-side operand
495    /// respectively. Returns false otherwise.
496    ///
497    /// This is a partial ordering. It is possible for two values to be neither
498    /// less, nor greater than, nor equal to, another.
499    pub fn gt(&self, other: &Size) -> bool {
500        self.width > other.width && self.height > other.height
501    }
502
503    /// Greater-than-or-equal-to operator. Compares an [`Offset`] or [`Size`] to
504    /// another [`Offset`] or [`Size`], and returns true if both the horizontal
505    /// and vertical values of the left-hand-side operand are bigger than or
506    /// equal to the horizontal and vertical values of the right-hand-side
507    /// operand respectively. Returns false otherwise.
508    ///
509    /// This is a partial ordering. It is possible for two values to be neither
510    /// less, nor greater than, nor equal to, another.
511    pub fn ge(&self, other: &Size) -> bool {
512        self.width >= other.width && self.height >= other.height
513    }
514
515    /// The aspect ratio of this size.
516    ///
517    /// This returns the [`width`](Size::width) divided by the
518    /// [`height`](Size::height).
519    ///
520    /// If the [`width`](Size::width) is zero, the result will be zero. If the
521    /// [`height`](Size::height) is zero (and the [`width`](Size::width) is not),
522    /// the result will be [`f64::INFINITY`] or [`f64::NEG_INFINITY`] as
523    /// determined by the sign of [`width`](Size::width).
524    ///
525    /// See also:
526    ///
527    ///  * `AspectRatio`, a widget for giving a child widget a specific aspect
528    ///    ratio.
529    ///  * `FittedBox`, a widget that (in most modes) attempts to maintain a
530    ///    child widget's aspect ratio while changing its size.
531    pub fn aspect_ratio(&self) -> f64 {
532        if self.height != 0.0 {
533            return self.width / self.height;
534        }
535        if self.width > 0.0 {
536            return f64::INFINITY;
537        }
538        if self.width < 0.0 {
539            return f64::NEG_INFINITY;
540        }
541        0.0
542    }
543
544    /// An empty size, one with a zero width and a zero height.
545    pub const ZERO: Size = Size::new(0.0, 0.0);
546
547    /// A size whose [`width`](Size::width) and [`height`](Size::height) are
548    /// infinite.
549    ///
550    /// See also:
551    ///
552    ///  * [`is_infinite`](Size::is_infinite), which checks whether either
553    ///    dimension is infinite.
554    ///  * [`is_finite`](Size::is_finite), which checks whether both dimensions
555    ///    are finite.
556    pub const INFINITE: Size = Size::new(f64::INFINITY, f64::INFINITY);
557
558    /// Whether this size encloses a zero area.
559    ///
560    /// Negative areas are considered empty.
561    pub fn is_empty(&self) -> bool {
562        self.width <= 0.0 || self.height <= 0.0
563    }
564
565    /// Integer (truncating) division operator.
566    ///
567    /// Returns a [`Size`] whose dimensions are the dimensions of this size
568    /// divided by the scalar `operand`, rounded towards zero.
569    pub fn truncating_div(&self, operand: f64) -> Size {
570        Size::new(
571            (self.width / operand).trunc(),
572            (self.height / operand).trunc(),
573        )
574    }
575
576    /// The lesser of the magnitudes of the [`width`](Size::width) and the
577    /// [`height`](Size::height).
578    pub fn shortest_side(&self) -> f64 {
579        self.width.abs().min(self.height.abs())
580    }
581
582    /// The greater of the magnitudes of the [`width`](Size::width) and the
583    /// [`height`](Size::height).
584    pub fn longest_side(&self) -> f64 {
585        self.width.abs().max(self.height.abs())
586    }
587
588    /// The offset to the intersection of the top and left edges of the
589    /// rectangle described by the given [`Offset`] (which is interpreted as the
590    /// top-left corner) and this [`Size`].
591    ///
592    /// See also [`Rect::top_left`].
593    pub fn top_left(&self, origin: Offset) -> Offset {
594        origin
595    }
596
597    /// The offset to the center of the top edge of the rectangle described by
598    /// the given offset (which is interpreted as the top-left corner) and this
599    /// size.
600    ///
601    /// See also [`Rect::top_center`].
602    pub fn top_center(&self, origin: Offset) -> Offset {
603        Offset::new(origin.dx() + self.width / 2.0, origin.dy())
604    }
605
606    /// The offset to the intersection of the top and right edges of the
607    /// rectangle described by the given offset (which is interpreted as the
608    /// top-left corner) and this size.
609    ///
610    /// See also [`Rect::top_right`].
611    pub fn top_right(&self, origin: Offset) -> Offset {
612        Offset::new(origin.dx() + self.width, origin.dy())
613    }
614
615    /// The offset to the center of the left edge of the rectangle described by
616    /// the given offset (which is interpreted as the top-left corner) and this
617    /// size.
618    ///
619    /// See also [`Rect::center_left`].
620    pub fn center_left(&self, origin: Offset) -> Offset {
621        Offset::new(origin.dx(), origin.dy() + self.height / 2.0)
622    }
623
624    /// The offset to the point halfway between the left and right and the top
625    /// and bottom edges of the rectangle described by the given offset (which
626    /// is interpreted as the top-left corner) and this size.
627    ///
628    /// See also [`Rect::center`].
629    pub fn center(&self, origin: Offset) -> Offset {
630        Offset::new(
631            origin.dx() + self.width / 2.0,
632            origin.dy() + self.height / 2.0,
633        )
634    }
635
636    /// The offset to the center of the right edge of the rectangle described by
637    /// the given offset (which is interpreted as the top-left corner) and this
638    /// size.
639    ///
640    /// See also [`Rect::center_right`].
641    pub fn center_right(&self, origin: Offset) -> Offset {
642        Offset::new(origin.dx() + self.width, origin.dy() + self.height / 2.0)
643    }
644
645    /// The offset to the intersection of the bottom and left edges of the
646    /// rectangle described by the given offset (which is interpreted as the
647    /// top-left corner) and this size.
648    ///
649    /// See also [`Rect::bottom_left`].
650    pub fn bottom_left(&self, origin: Offset) -> Offset {
651        Offset::new(origin.dx(), origin.dy() + self.height)
652    }
653
654    /// The offset to the center of the bottom edge of the rectangle described
655    /// by the given offset (which is interpreted as the top-left corner) and
656    /// this size.
657    ///
658    /// See also [`Rect::bottom_center`].
659    pub fn bottom_center(&self, origin: Offset) -> Offset {
660        Offset::new(origin.dx() + self.width / 2.0, origin.dy() + self.height)
661    }
662
663    /// The offset to the intersection of the bottom and right edges of the
664    /// rectangle described by the given offset (which is interpreted as the
665    /// top-left corner) and this size.
666    ///
667    /// See also [`Rect::bottom_right`].
668    pub fn bottom_right(&self, origin: Offset) -> Offset {
669        Offset::new(origin.dx() + self.width, origin.dy() + self.height)
670    }
671
672    /// Whether the point specified by the given offset (which is assumed to be
673    /// relative to the top left of the size) lies between the left and right
674    /// and the top and bottom edges of a rectangle of this size.
675    ///
676    /// Rectangles include their top and left edges but exclude their bottom and
677    /// right edges.
678    pub fn contains(&self, offset: Offset) -> bool {
679        offset.dx() >= 0.0
680            && offset.dx() < self.width
681            && offset.dy() >= 0.0
682            && offset.dy() < self.height
683    }
684
685    /// A [`Size`] with the [`width`](Size::width) and [`height`](Size::height)
686    /// swapped.
687    pub fn flipped(&self) -> Size {
688        Size::new(self.height, self.width)
689    }
690
691    /// Linearly interpolate between two sizes.
692    ///
693    /// If either size is null, this function interpolates from [`Size::ZERO`].
694    ///
695    /// The `t` argument represents position on the timeline, with 0.0 meaning
696    /// that the interpolation has not started, returning `a` (or something
697    /// equivalent to `a`), 1.0 meaning that the interpolation has finished,
698    /// returning `b` (or something equivalent to `b`), and values in between
699    /// meaning that the interpolation is at the relevant point on the timeline
700    /// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
701    /// 1.0, so negative values and values greater than 1.0 are valid (and can
702    /// easily be generated by curves such as `Curves.elasticInOut`).
703    ///
704    /// Values for `t` are usually obtained from an `Animation<double>`, such as
705    /// an `AnimationController`.
706    pub fn lerp(a: Option<Size>, b: Option<Size>, t: f64) -> Option<Size> {
707        match (a, b) {
708            (None, None) => None,
709            (Some(a), None) => Some(a * (1.0 - t)),
710            (None, Some(b)) => Some(b * t),
711            (Some(a), Some(b)) => Some(Size::new(
712                lerp_double_non_null(a.width, b.width, t),
713                lerp_double_non_null(a.height, b.height, t),
714            )),
715        }
716    }
717}
718
719/// Binary subtraction operator for [`Size`].
720///
721/// Subtracting a [`Size`] from a [`Size`] returns the [`Offset`] that describes
722/// how much bigger the left-hand-side operand is than the right-hand-side
723/// operand. Adding that resulting [`Offset`] to the [`Size`] that was the
724/// right-hand-side operand would return a [`Size`] equal to the [`Size`] that
725/// was the left-hand-side operand. (i.e. if `size_a - size_b -> offset_a`, then
726/// `offset_a + size_b -> size_a`)
727impl Sub<Size> for Size {
728    type Output = Offset;
729
730    fn sub(self, other: Size) -> Offset {
731        Offset::new(self.width - other.width, self.height - other.height)
732    }
733}
734
735/// Subtracting an [`Offset`] from a [`Size`] returns the [`Size`] that is
736/// smaller than the [`Size`] operand by the difference given by the [`Offset`]
737/// operand. In other words, the returned [`Size`] has a [`width`](Size::width)
738/// consisting of the [`width`](Size::width) of the left-hand-side operand minus
739/// the [`Offset::dx`] dimension of the right-hand-side operand, and a
740/// [`height`](Size::height) consisting of the [`height`](Size::height) of the
741/// left-hand-side operand minus the [`Offset::dy`] dimension of the
742/// right-hand-side operand.
743impl Sub<Offset> for Size {
744    type Output = Size;
745
746    fn sub(self, other: Offset) -> Size {
747        Size::new(self.width - other.dx(), self.height - other.dy())
748    }
749}
750
751/// Binary addition operator for adding an [`Offset`] to a [`Size`].
752///
753/// Returns a [`Size`] whose [`width`](Size::width) is the sum of the
754/// [`width`](Size::width) of the left-hand-side operand, a [`Size`], and the
755/// [`Offset::dx`] dimension of the right-hand-side operand, an [`Offset`], and
756/// whose [`height`](Size::height) is the sum of the [`height`](Size::height) of
757/// the left-hand-side operand and the [`Offset::dy`] dimension of the
758/// right-hand-side operand.
759impl Add<Offset> for Size {
760    type Output = Size;
761
762    fn add(self, other: Offset) -> Size {
763        Size::new(self.width + other.dx(), self.height + other.dy())
764    }
765}
766
767/// Multiplication operator.
768///
769/// Returns a [`Size`] whose dimensions are the dimensions of the left-hand-side
770/// operand (a [`Size`]) multiplied by the scalar right-hand-side operand (an
771/// `f64`).
772impl Mul<f64> for Size {
773    type Output = Size;
774
775    fn mul(self, operand: f64) -> Size {
776        Size::new(self.width * operand, self.height * operand)
777    }
778}
779
780/// Division operator.
781///
782/// Returns a [`Size`] whose dimensions are the dimensions of the left-hand-side
783/// operand (a [`Size`]) divided by the scalar right-hand-side operand (an
784/// `f64`).
785impl Div<f64> for Size {
786    type Output = Size;
787
788    fn div(self, operand: f64) -> Size {
789        Size::new(self.width / operand, self.height / operand)
790    }
791}
792
793/// Modulo (remainder) operator.
794///
795/// Returns a [`Size`] whose dimensions are the remainder of dividing the
796/// left-hand-side operand (a [`Size`]) by the scalar right-hand-side operand
797/// (an `f64`).
798impl Rem<f64> for Size {
799    type Output = Size;
800
801    fn rem(self, operand: f64) -> Size {
802        Size::new(
803            self.width.rem_euclid(operand),
804            self.height.rem_euclid(operand),
805        )
806    }
807}
808
809/// Rectangle constructor operator.
810///
811/// Combines an [`Offset`] and a [`Size`] to form a [`Rect`] whose top-left
812/// coordinate is the point given by adding this offset, the left-hand-side
813/// operand, to the origin, and whose size is the right-hand-side operand.
814///
815/// ```
816/// # use inset_embedder::{Offset, Rect, Size};
817/// let my_rect = Offset::ZERO & Size::new(100.0, 100.0);
818/// // same as: Rect::from_ltwh(0.0, 0.0, 100.0, 100.0)
819/// # assert_eq!(my_rect, Rect::from_ltwh(0.0, 0.0, 100.0, 100.0));
820/// ```
821impl BitAnd<Size> for Offset {
822    type Output = Rect;
823
824    fn bitand(self, other: Size) -> Rect {
825        Rect::from_ltwh(self.dx(), self.dy(), other.width, other.height)
826    }
827}
828
829impl Debug for Size {
830    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
831        write!(f, "Size({:.1}, {:.1})", self.width, self.height)
832    }
833}
834
835/// An immutable, 2D, axis-aligned, floating-point rectangle whose coordinates
836/// are relative to a given origin.
837///
838/// A Rect can be created with one of its constructors or from an [`Offset`] and
839/// a [`Size`] using the `&` operator:
840///
841/// ```
842/// # use inset_embedder::{Offset, Rect, Size};
843/// let my_rect = Offset::new(1.0, 2.0) & Size::new(3.0, 4.0);
844/// # assert_eq!(my_rect, Rect::from_ltwh(1.0, 2.0, 3.0, 4.0));
845/// ```
846#[derive(Clone, Copy, PartialEq)]
847pub struct Rect {
848    /// The offset of the left edge of this rectangle from the x axis.
849    pub left: f64,
850
851    /// The offset of the top edge of this rectangle from the y axis.
852    pub top: f64,
853
854    /// The offset of the right edge of this rectangle from the x axis.
855    pub right: f64,
856
857    /// The offset of the bottom edge of this rectangle from the y axis.
858    pub bottom: f64,
859}
860
861impl Rect {
862    /// Construct a rectangle from its left, top, right, and bottom edges.
863    pub const fn from_ltrb(left: f64, top: f64, right: f64, bottom: f64) -> Rect {
864        Rect {
865            left,
866            top,
867            right,
868            bottom,
869        }
870    }
871
872    /// Construct a rectangle from its left and top edges, its width, and its
873    /// height.
874    ///
875    /// To construct a [`Rect`] from an [`Offset`] and a [`Size`], you can use
876    /// the rectangle constructor operator `&`. See [`BitAnd`] for [`Offset`].
877    pub const fn from_ltwh(left: f64, top: f64, width: f64, height: f64) -> Rect {
878        Rect::from_ltrb(left, top, left + width, top + height)
879    }
880
881    /// Construct a rectangle that bounds the given circle.
882    ///
883    /// The `center` argument is assumed to be an offset from the origin.
884    pub fn from_circle(center: Offset, radius: f64) -> Rect {
885        Rect::from_center(center, radius * 2.0, radius * 2.0)
886    }
887
888    /// Constructs a rectangle from its center point, width, and height.
889    ///
890    /// The `center` argument is assumed to be an offset from the origin.
891    pub fn from_center(center: Offset, width: f64, height: f64) -> Rect {
892        Rect::from_ltrb(
893            center.dx() - width / 2.0,
894            center.dy() - height / 2.0,
895            center.dx() + width / 2.0,
896            center.dy() + height / 2.0,
897        )
898    }
899
900    /// Construct the smallest rectangle that encloses the given offsets,
901    /// treating them as vectors from the origin.
902    pub fn from_points(a: Offset, b: Offset) -> Rect {
903        Rect::from_ltrb(
904            a.dx().min(b.dx()),
905            a.dy().min(b.dy()),
906            a.dx().max(b.dx()),
907            a.dy().max(b.dy()),
908        )
909    }
910
911    /// The distance between the left and right edges of this rectangle.
912    pub fn width(&self) -> f64 {
913        self.right - self.left
914    }
915
916    /// The distance between the top and bottom edges of this rectangle.
917    pub fn height(&self) -> f64 {
918        self.bottom - self.top
919    }
920
921    /// The distance between the upper-left corner and the lower-right corner of
922    /// this rectangle.
923    pub fn size(&self) -> Size {
924        Size::new(self.width(), self.height())
925    }
926
927    /// Whether any of the dimensions are `NaN`.
928    pub fn has_nan(&self) -> bool {
929        self.left.is_nan() || self.top.is_nan() || self.right.is_nan() || self.bottom.is_nan()
930    }
931
932    /// A rectangle with left, top, right, and bottom edges all at zero.
933    pub const ZERO: Rect = Rect::from_ltrb(0.0, 0.0, 0.0, 0.0);
934
935    const GIANT_SCALAR: f64 = 1.0E+9; // matches kGiantRect from layer.h
936
937    /// A rectangle that covers the entire coordinate space.
938    ///
939    /// This covers the space from -1e9,-1e9 to 1e9,1e9. This is the space over
940    /// which graphics operations are valid.
941    pub const LARGEST: Rect = Rect::from_ltrb(
942        -Rect::GIANT_SCALAR,
943        -Rect::GIANT_SCALAR,
944        Rect::GIANT_SCALAR,
945        Rect::GIANT_SCALAR,
946    );
947
948    /// Whether any of the coordinates of this rectangle are equal to positive
949    /// infinity.
950    // included for consistency with Offset and Size
951    pub fn is_infinite(&self) -> bool {
952        self.left >= f64::INFINITY
953            || self.top >= f64::INFINITY
954            || self.right >= f64::INFINITY
955            || self.bottom >= f64::INFINITY
956    }
957
958    /// Whether all coordinates of this rectangle are finite.
959    pub fn is_finite(&self) -> bool {
960        self.left.is_finite()
961            && self.top.is_finite()
962            && self.right.is_finite()
963            && self.bottom.is_finite()
964    }
965
966    /// Whether this rectangle encloses a non-zero area. Negative areas are
967    /// considered empty.
968    pub fn is_empty(&self) -> bool {
969        self.left >= self.right || self.top >= self.bottom
970    }
971
972    /// Returns a new rectangle translated by the given offset.
973    ///
974    /// To translate a rectangle by separate x and y components rather than by
975    /// an [`Offset`], consider [`translate`](Rect::translate).
976    pub fn shift(&self, offset: Offset) -> Rect {
977        Rect::from_ltrb(
978            self.left + offset.dx(),
979            self.top + offset.dy(),
980            self.right + offset.dx(),
981            self.bottom + offset.dy(),
982        )
983    }
984
985    /// Returns a new rectangle with translateX added to the x components and
986    /// translateY added to the y components.
987    ///
988    /// To translate a rectangle by an [`Offset`] rather than by separate x and
989    /// y components, consider [`shift`](Rect::shift).
990    pub fn translate(&self, translate_x: f64, translate_y: f64) -> Rect {
991        Rect::from_ltrb(
992            self.left + translate_x,
993            self.top + translate_y,
994            self.right + translate_x,
995            self.bottom + translate_y,
996        )
997    }
998
999    /// Returns a new rectangle with edges moved outwards by the given delta.
1000    pub fn inflate(&self, delta: f64) -> Rect {
1001        Rect::from_ltrb(
1002            self.left - delta,
1003            self.top - delta,
1004            self.right + delta,
1005            self.bottom + delta,
1006        )
1007    }
1008
1009    /// Returns a new rectangle with edges moved inwards by the given delta.
1010    pub fn deflate(&self, delta: f64) -> Rect {
1011        self.inflate(-delta)
1012    }
1013
1014    /// Returns a new rectangle that is the intersection of the given rectangle
1015    /// and this rectangle. The two rectangles must overlap for this to be
1016    /// meaningful. If the two rectangles do not overlap, then the resulting
1017    /// Rect will have a negative width or height.
1018    pub fn intersect(&self, other: Rect) -> Rect {
1019        Rect::from_ltrb(
1020            self.left.max(other.left),
1021            self.top.max(other.top),
1022            self.right.min(other.right),
1023            self.bottom.min(other.bottom),
1024        )
1025    }
1026
1027    /// Returns a new rectangle which is the bounding box containing this
1028    /// rectangle and the given rectangle.
1029    pub fn expand_to_include(&self, other: Rect) -> Rect {
1030        Rect::from_ltrb(
1031            self.left.min(other.left),
1032            self.top.min(other.top),
1033            self.right.max(other.right),
1034            self.bottom.max(other.bottom),
1035        )
1036    }
1037
1038    /// Whether `other` has a nonzero area of overlap with this rectangle.
1039    pub fn overlaps(&self, other: Rect) -> bool {
1040        if self.right <= other.left || other.right <= self.left {
1041            return false;
1042        }
1043        if self.bottom <= other.top || other.bottom <= self.top {
1044            return false;
1045        }
1046        true
1047    }
1048
1049    /// The lesser of the magnitudes of the [`width`](Rect::width) and the
1050    /// [`height`](Rect::height) of this rectangle.
1051    pub fn shortest_side(&self) -> f64 {
1052        self.width().abs().min(self.height().abs())
1053    }
1054
1055    /// The greater of the magnitudes of the [`width`](Rect::width) and the
1056    /// [`height`](Rect::height) of this rectangle.
1057    pub fn longest_side(&self) -> f64 {
1058        self.width().abs().max(self.height().abs())
1059    }
1060
1061    /// The offset to the intersection of the top and left edges of this
1062    /// rectangle.
1063    ///
1064    /// See also [`Size::top_left`].
1065    pub fn top_left(&self) -> Offset {
1066        Offset::new(self.left, self.top)
1067    }
1068
1069    /// The offset to the center of the top edge of this rectangle.
1070    ///
1071    /// See also [`Size::top_center`].
1072    pub fn top_center(&self) -> Offset {
1073        Offset::new(self.left + self.width() / 2.0, self.top)
1074    }
1075
1076    /// The offset to the intersection of the top and right edges of this
1077    /// rectangle.
1078    ///
1079    /// See also [`Size::top_right`].
1080    pub fn top_right(&self) -> Offset {
1081        Offset::new(self.right, self.top)
1082    }
1083
1084    /// The offset to the center of the left edge of this rectangle.
1085    ///
1086    /// See also [`Size::center_left`].
1087    pub fn center_left(&self) -> Offset {
1088        Offset::new(self.left, self.top + self.height() / 2.0)
1089    }
1090
1091    /// The offset to the point halfway between the left and right and the top
1092    /// and bottom edges of this rectangle.
1093    ///
1094    /// See also [`Size::center`].
1095    pub fn center(&self) -> Offset {
1096        Offset::new(
1097            self.left + self.width() / 2.0,
1098            self.top + self.height() / 2.0,
1099        )
1100    }
1101
1102    /// The offset to the center of the right edge of this rectangle.
1103    ///
1104    /// See also [`Size::center_right`].
1105    pub fn center_right(&self) -> Offset {
1106        Offset::new(self.right, self.top + self.height() / 2.0)
1107    }
1108
1109    /// The offset to the intersection of the bottom and left edges of this
1110    /// rectangle.
1111    ///
1112    /// See also [`Size::bottom_left`].
1113    pub fn bottom_left(&self) -> Offset {
1114        Offset::new(self.left, self.bottom)
1115    }
1116
1117    /// The offset to the center of the bottom edge of this rectangle.
1118    ///
1119    /// See also [`Size::bottom_center`].
1120    pub fn bottom_center(&self) -> Offset {
1121        Offset::new(self.left + self.width() / 2.0, self.bottom)
1122    }
1123
1124    /// The offset to the intersection of the bottom and right edges of this
1125    /// rectangle.
1126    ///
1127    /// See also [`Size::bottom_right`].
1128    pub fn bottom_right(&self) -> Offset {
1129        Offset::new(self.right, self.bottom)
1130    }
1131
1132    /// Whether the point specified by the given offset (which is assumed to be
1133    /// relative to the origin) lies between the left and right and the top and
1134    /// bottom edges of this rectangle.
1135    ///
1136    /// Rectangles include their top and left edges but exclude their bottom and
1137    /// right edges.
1138    pub fn contains(&self, offset: Offset) -> bool {
1139        offset.dx() >= self.left
1140            && offset.dx() < self.right
1141            && offset.dy() >= self.top
1142            && offset.dy() < self.bottom
1143    }
1144
1145    /// Linearly interpolate between two rectangles.
1146    ///
1147    /// If either rect is null, [`Rect::ZERO`] is used as a substitute.
1148    ///
1149    /// The `t` argument represents position on the timeline, with 0.0 meaning
1150    /// that the interpolation has not started, returning `a` (or something
1151    /// equivalent to `a`), 1.0 meaning that the interpolation has finished,
1152    /// returning `b` (or something equivalent to `b`), and values in between
1153    /// meaning that the interpolation is at the relevant point on the timeline
1154    /// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
1155    /// 1.0, so negative values and values greater than 1.0 are valid (and can
1156    /// easily be generated by curves such as `Curves.elasticInOut`).
1157    ///
1158    /// Values for `t` are usually obtained from an `Animation<double>`, such as
1159    /// an `AnimationController`.
1160    pub fn lerp(a: Option<Rect>, b: Option<Rect>, t: f64) -> Option<Rect> {
1161        match (a, b) {
1162            (None, None) => None,
1163            (Some(a), None) => {
1164                let k = 1.0 - t;
1165                Some(Rect::from_ltrb(
1166                    a.left * k,
1167                    a.top * k,
1168                    a.right * k,
1169                    a.bottom * k,
1170                ))
1171            }
1172            (None, Some(b)) => Some(Rect::from_ltrb(
1173                b.left * t,
1174                b.top * t,
1175                b.right * t,
1176                b.bottom * t,
1177            )),
1178            (Some(a), Some(b)) => Some(Rect::from_ltrb(
1179                lerp_double_non_null(a.left, b.left, t),
1180                lerp_double_non_null(a.top, b.top, t),
1181                lerp_double_non_null(a.right, b.right, t),
1182                lerp_double_non_null(a.bottom, b.bottom, t),
1183            )),
1184        }
1185    }
1186}
1187
1188impl Debug for Rect {
1189    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1190        write!(
1191            f,
1192            "Rect.fromLTRB({:.1}, {:.1}, {:.1}, {:.1})",
1193            self.left, self.top, self.right, self.bottom
1194        )
1195    }
1196}
1197
1198/// A radius for either circular or elliptical shapes.
1199#[derive(Clone, Copy, PartialEq)]
1200pub struct Radius {
1201    /// The radius value on the horizontal axis.
1202    pub x: f64,
1203
1204    /// The radius value on the vertical axis.
1205    pub y: f64,
1206}
1207
1208impl Radius {
1209    /// Constructs a circular radius. [`x`](Radius::x) and [`y`](Radius::y) will
1210    /// have the same radius value.
1211    pub const fn circular(radius: f64) -> Radius {
1212        Radius::elliptical(radius, radius)
1213    }
1214
1215    /// Constructs an elliptical radius with the given radii.
1216    pub const fn elliptical(x: f64, y: f64) -> Radius {
1217        Radius { x, y }
1218    }
1219
1220    /// A radius with [`x`](Radius::x) and [`y`](Radius::y) values set to zero.
1221    ///
1222    /// You can use [`Radius::ZERO`] with `RRect` or `RSuperellipse` to have
1223    /// right-angle corners.
1224    pub const ZERO: Radius = Radius::circular(0.0);
1225
1226    /// Returns this [`Radius`], with values clamped to the given min and max
1227    /// [`Radius`] values.
1228    ///
1229    /// The `minimum` value defaults to `Radius::circular(f64::NEG_INFINITY)`,
1230    /// and the `maximum` value defaults to `Radius::circular(f64::INFINITY)`.
1231    pub fn clamp(&self, minimum: Option<Radius>, maximum: Option<Radius>) -> Radius {
1232        let minimum = minimum.unwrap_or(Radius::circular(f64::NEG_INFINITY));
1233        let maximum = maximum.unwrap_or(Radius::circular(f64::INFINITY));
1234        Radius::elliptical(
1235            clamp_double(self.x, minimum.x, maximum.x),
1236            clamp_double(self.y, minimum.y, maximum.y),
1237        )
1238    }
1239
1240    /// Returns this [`Radius`], with values clamped to the given min and max
1241    /// values in each dimension.
1242    ///
1243    /// The `minimum_x` and `minimum_y` values default to [`f64::NEG_INFINITY`],
1244    /// and the `maximum_x` and `maximum_y` values default to [`f64::INFINITY`].
1245    pub fn clamp_values(
1246        &self,
1247        minimum_x: Option<f64>,
1248        minimum_y: Option<f64>,
1249        maximum_x: Option<f64>,
1250        maximum_y: Option<f64>,
1251    ) -> Radius {
1252        Radius::elliptical(
1253            clamp_double(
1254                self.x,
1255                minimum_x.unwrap_or(f64::NEG_INFINITY),
1256                maximum_x.unwrap_or(f64::INFINITY),
1257            ),
1258            clamp_double(
1259                self.y,
1260                minimum_y.unwrap_or(f64::NEG_INFINITY),
1261                maximum_y.unwrap_or(f64::INFINITY),
1262            ),
1263        )
1264    }
1265
1266    /// Integer (truncating) division operator.
1267    ///
1268    /// Returns a radius whose coordinates are the coordinates of this radius
1269    /// divided by the scalar `operand`, rounded towards zero.
1270    pub fn truncating_div(&self, operand: f64) -> Radius {
1271        Radius::elliptical((self.x / operand).trunc(), (self.y / operand).trunc())
1272    }
1273
1274    /// Linearly interpolate between two radii.
1275    ///
1276    /// If either is null, this function substitutes [`Radius::ZERO`] instead.
1277    ///
1278    /// The `t` argument represents position on the timeline, with 0.0 meaning
1279    /// that the interpolation has not started, returning `a` (or something
1280    /// equivalent to `a`), 1.0 meaning that the interpolation has finished,
1281    /// returning `b` (or something equivalent to `b`), and values in between
1282    /// meaning that the interpolation is at the relevant point on the timeline
1283    /// between `a` and `b`. The interpolation can be extrapolated beyond 0.0 and
1284    /// 1.0, so negative values and values greater than 1.0 are valid (and can
1285    /// easily be generated by curves such as `Curves.elasticInOut`).
1286    ///
1287    /// Values for `t` are usually obtained from an `Animation<double>`, such as
1288    /// an `AnimationController`.
1289    pub fn lerp(a: Option<Radius>, b: Option<Radius>, t: f64) -> Option<Radius> {
1290        match (a, b) {
1291            (None, None) => None,
1292            (Some(a), None) => {
1293                let k = 1.0 - t;
1294                Some(Radius::elliptical(a.x * k, a.y * k))
1295            }
1296            (None, Some(b)) => Some(Radius::elliptical(b.x * t, b.y * t)),
1297            (Some(a), Some(b)) => Some(Radius::elliptical(
1298                lerp_double_non_null(a.x, b.x, t),
1299                lerp_double_non_null(a.y, b.y, t),
1300            )),
1301        }
1302    }
1303}
1304
1305/// Unary negation operator.
1306///
1307/// Returns a Radius with the distances negated.
1308///
1309/// Radiuses with negative values aren't geometrically meaningful, but could
1310/// occur as part of expressions. For example, negating a radius of one pixel
1311/// and then adding the result to another radius is equivalent to subtracting
1312/// a radius of one pixel from the other.
1313impl Neg for Radius {
1314    type Output = Radius;
1315
1316    fn neg(self) -> Radius {
1317        Radius::elliptical(-self.x, -self.y)
1318    }
1319}
1320
1321/// Binary subtraction operator.
1322///
1323/// Returns a radius whose [`x`](Radius::x) value is the left-hand-side
1324/// operand's [`x`](Radius::x) minus the right-hand-side operand's
1325/// [`x`](Radius::x) and whose [`y`](Radius::y) value is the left-hand-side
1326/// operand's [`y`](Radius::y) minus the right-hand-side operand's
1327/// [`y`](Radius::y).
1328impl Sub for Radius {
1329    type Output = Radius;
1330
1331    fn sub(self, other: Radius) -> Radius {
1332        Radius::elliptical(self.x - other.x, self.y - other.y)
1333    }
1334}
1335
1336/// Binary addition operator.
1337///
1338/// Returns a radius whose [`x`](Radius::x) value is the sum of the
1339/// [`x`](Radius::x) values of the two operands, and whose [`y`](Radius::y)
1340/// value is the sum of the [`y`](Radius::y) values of the two operands.
1341impl Add for Radius {
1342    type Output = Radius;
1343
1344    fn add(self, other: Radius) -> Radius {
1345        Radius::elliptical(self.x + other.x, self.y + other.y)
1346    }
1347}
1348
1349/// Multiplication operator.
1350///
1351/// Returns a radius whose coordinates are the coordinates of the left-hand-side
1352/// operand (a radius) multiplied by the scalar right-hand-side operand (an
1353/// `f64`).
1354impl Mul<f64> for Radius {
1355    type Output = Radius;
1356
1357    fn mul(self, operand: f64) -> Radius {
1358        Radius::elliptical(self.x * operand, self.y * operand)
1359    }
1360}
1361
1362/// Division operator.
1363///
1364/// Returns a radius whose coordinates are the coordinates of the left-hand-side
1365/// operand (a radius) divided by the scalar right-hand-side operand (an `f64`).
1366impl Div<f64> for Radius {
1367    type Output = Radius;
1368
1369    fn div(self, operand: f64) -> Radius {
1370        Radius::elliptical(self.x / operand, self.y / operand)
1371    }
1372}
1373
1374/// Modulo (remainder) operator.
1375///
1376/// Returns a radius whose coordinates are the remainder of dividing the
1377/// coordinates of the left-hand-side operand (a radius) by the scalar
1378/// right-hand-side operand (an `f64`).
1379impl Rem<f64> for Radius {
1380    type Output = Radius;
1381
1382    fn rem(self, operand: f64) -> Radius {
1383        Radius::elliptical(self.x.rem_euclid(operand), self.y.rem_euclid(operand))
1384    }
1385}
1386
1387impl Debug for Radius {
1388    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1389        if self.x == self.y {
1390            write!(f, "Radius.circular({:.1})", self.x)
1391        } else {
1392            write!(f, "Radius.elliptical({:.1}, {:.1})", self.x, self.y)
1393        }
1394    }
1395}
1396
1397#[cfg(test)]
1398mod tests {
1399    use super::*;
1400
1401    #[test]
1402    fn an_offset_is_equal_to_another_with_the_same_components() {
1403        assert_eq!(Offset::new(1.0, 2.0), Offset::new(1.0, 2.0));
1404        assert_ne!(Offset::new(1.0, 2.0), Offset::new(2.0, 1.0));
1405    }
1406
1407    #[test]
1408    fn arithmetic_operators_work_component_wise() {
1409        let a = Offset::new(10.0, 20.0);
1410        let b = Offset::new(1.0, 2.0);
1411
1412        assert_eq!(a + b, Offset::new(11.0, 22.0));
1413        assert_eq!(a - b, Offset::new(9.0, 18.0));
1414        assert_eq!(a * 2.0, Offset::new(20.0, 40.0));
1415        assert_eq!(a / 2.0, Offset::new(5.0, 10.0));
1416        assert_eq!(-a, Offset::new(-10.0, -20.0));
1417        assert_eq!(a.scale(2.0, 3.0), Offset::new(20.0, 60.0));
1418        assert_eq!(a.translate(1.0, -1.0), Offset::new(11.0, 19.0));
1419    }
1420
1421    #[test]
1422    fn remainder_follows_dart_and_stays_non_negative() {
1423        assert_eq!(Offset::new(-5.0, 5.0) % 3.0, Offset::new(1.0, 2.0));
1424        assert_eq!(-5.0_f64 % 3.0, -2.0);
1425    }
1426
1427    #[test]
1428    fn truncating_division_rounds_towards_zero() {
1429        assert_eq!(
1430            Offset::new(-5.0, 5.0).truncating_div(3.0),
1431            Offset::new(-1.0, 1.0)
1432        );
1433    }
1434
1435    #[test]
1436    fn distance_and_direction_describe_the_same_vector_as_the_components() {
1437        let offset = Offset::new(3.0, 4.0);
1438        assert_eq!(offset.distance(), 5.0);
1439        assert_eq!(offset.distance_squared(), 25.0);
1440
1441        let rebuilt = Offset::from_direction(offset.direction(), offset.distance());
1442        assert!((rebuilt.dx() - 3.0).abs() < 1e-12);
1443        assert!((rebuilt.dy() - 4.0).abs() < 1e-12);
1444    }
1445
1446    #[test]
1447    fn comparison_is_component_wise_and_only_partial() {
1448        let a = Offset::new(1.0, 2.0);
1449        let b = Offset::new(2.0, 3.0);
1450        assert!(a.lt(&b));
1451        assert!(a.le(&b));
1452        assert!(b.gt(&a));
1453        assert!(b.ge(&a));
1454
1455        let c = Offset::new(1.0, 3.0);
1456        let d = Offset::new(2.0, 2.0);
1457        assert!(!c.lt(&d) && !c.le(&d) && !c.gt(&d) && !c.ge(&d));
1458
1459        let e = Offset::new(1.0, 2.0);
1460        let f = Offset::new(1.0, 3.0);
1461        assert!(e.le(&f));
1462        assert!(!e.lt(&f));
1463        assert_ne!(e, f);
1464    }
1465
1466    #[test]
1467    fn finiteness_asks_about_either_component_or_both() {
1468        assert!(Offset::INFINITE.is_infinite());
1469        assert!(Offset::new(0.0, f64::INFINITY).is_infinite());
1470        assert!(!Offset::new(0.0, f64::NEG_INFINITY).is_infinite());
1471        assert!(!Offset::new(0.0, f64::NAN).is_infinite());
1472
1473        assert!(Offset::ZERO.is_finite());
1474        assert!(!Offset::new(0.0, f64::NAN).is_finite());
1475    }
1476
1477    #[test]
1478    fn lerp_defaults_a_missing_end_to_zero() {
1479        let a = Offset::new(0.0, 10.0);
1480        let b = Offset::new(10.0, 0.0);
1481
1482        assert_eq!(
1483            Offset::lerp(Some(a), Some(b), 0.5),
1484            Some(Offset::new(5.0, 5.0))
1485        );
1486        assert_eq!(
1487            Offset::lerp(Some(a), None, 0.5),
1488            Some(Offset::new(0.0, 5.0))
1489        );
1490        assert_eq!(
1491            Offset::lerp(None, Some(b), 0.5),
1492            Some(Offset::new(5.0, 0.0))
1493        );
1494        assert_eq!(Offset::lerp(None, None, 0.5), None);
1495    }
1496
1497    #[test]
1498    fn debug_matches_flutter_to_string() {
1499        assert_eq!(format!("{:?}", Offset::new(1.0, 2.5)), "Offset(1.0, 2.5)");
1500    }
1501
1502    // engine geometry_test.dart 'Size.aspectRatio'.
1503    #[test]
1504    fn size_aspect_ratio_covers_the_zero_and_infinite_arms() {
1505        assert_eq!(Size::new(0.0, 0.0).aspect_ratio(), 0.0);
1506        assert_eq!(Size::new(-0.0, 0.0).aspect_ratio(), 0.0);
1507        assert_eq!(Size::new(1.0, 0.0).aspect_ratio(), f64::INFINITY);
1508        assert_eq!(Size::new(-1.0, 0.0).aspect_ratio(), f64::NEG_INFINITY);
1509        assert_eq!(Size::new(3.0, 2.0).aspect_ratio(), 1.5);
1510        assert_eq!(Size::new(3.0, -2.0).aspect_ratio(), -1.5);
1511        assert_eq!(Size::new(-3.0, 2.0).aspect_ratio(), -1.5);
1512        assert_eq!(Size::new(0.0, -1.0).aspect_ratio(), -0.0);
1513    }
1514
1515    #[test]
1516    fn size_shortest_and_longest_side_use_magnitudes() {
1517        let size = Size::new(-9.0, 4.0);
1518        assert_eq!(size.shortest_side(), 4.0);
1519        assert_eq!(size.longest_side(), 9.0);
1520    }
1521
1522    #[test]
1523    fn subtracting_sizes_gives_the_offset_between_their_corners() {
1524        let offset: Offset = Size::new(5.0, 7.0) - Size::new(3.0, 2.0);
1525        assert_eq!(offset, Offset::new(2.0, 5.0));
1526
1527        let size: Size = Size::new(5.0, 7.0) - Offset::new(3.0, 2.0);
1528        assert_eq!(size, Size::new(2.0, 5.0));
1529
1530        assert_eq!(
1531            Size::new(5.0, 7.0) + Offset::new(3.0, 2.0),
1532            Size::new(8.0, 9.0)
1533        );
1534    }
1535
1536    #[test]
1537    fn size_comparison_operators_are_component_wise() {
1538        let small = Size::new(1.0, 2.0);
1539        let large = Size::new(3.0, 4.0);
1540        let mixed = Size::new(5.0, 1.0);
1541
1542        assert!(small.lt(&large));
1543        assert!(small.le(&small));
1544        assert!(large.gt(&small));
1545        assert!(!small.lt(&mixed), "only one component is smaller");
1546        assert!(!mixed.gt(&small));
1547    }
1548
1549    #[test]
1550    fn size_lerp_matches_darts_null_arms() {
1551        assert_eq!(Size::lerp(None, None, 0.5), None);
1552        assert_eq!(
1553            Size::lerp(Some(Size::new(4.0, 8.0)), None, 0.25),
1554            Some(Size::new(3.0, 6.0))
1555        );
1556        assert_eq!(
1557            Size::lerp(None, Some(Size::new(4.0, 8.0)), 0.25),
1558            Some(Size::new(1.0, 2.0))
1559        );
1560        assert_eq!(
1561            Size::lerp(Some(Size::ZERO), Some(Size::new(4.0, 8.0)), 0.5),
1562            Some(Size::new(2.0, 4.0))
1563        );
1564    }
1565
1566    #[test]
1567    fn size_contains_includes_top_left_and_excludes_bottom_right() {
1568        let size = Size::new(2.0, 2.0);
1569        assert!(size.contains(Offset::ZERO));
1570        assert!(size.contains(Offset::new(1.9, 1.9)));
1571        assert!(!size.contains(Offset::new(2.0, 1.0)));
1572        assert!(!size.contains(Offset::new(-0.1, 1.0)));
1573    }
1574
1575    // engine geometry_test.dart 'Rect accessors': fromLTRB(1,3,5,7).
1576    #[test]
1577    fn rect_accessors() {
1578        let rect = Rect::from_ltrb(1.0, 3.0, 5.0, 7.0);
1579        assert_eq!(rect.left, 1.0);
1580        assert_eq!(rect.top, 3.0);
1581        assert_eq!(rect.right, 5.0);
1582        assert_eq!(rect.bottom, 7.0);
1583        assert_eq!(rect.width(), 4.0);
1584        assert_eq!(rect.height(), 4.0);
1585        assert_eq!(rect.size(), Size::new(4.0, 4.0));
1586        assert_eq!(rect.center(), Offset::new(3.0, 5.0));
1587    }
1588
1589    // engine geometry_test.dart 'Rect created by width and height'.
1590    #[test]
1591    fn rect_from_ltwh_matches_ltrb() {
1592        assert_eq!(
1593            Rect::from_ltwh(1.0, 3.0, 4.0, 4.0),
1594            Rect::from_ltrb(1.0, 3.0, 5.0, 7.0)
1595        );
1596    }
1597
1598    // engine geometry_test.dart 'Rect created from center': 5x7 about (1, 3),
1599    // plus the NaN case.
1600    #[test]
1601    fn rect_from_center() {
1602        let rect = Rect::from_center(Offset::new(1.0, 3.0), 5.0, 7.0);
1603        assert_eq!(rect, Rect::from_ltrb(-1.5, -0.5, 3.5, 6.5));
1604
1605        let nan = Rect::from_center(Offset::new(f64::NAN, 3.0), 5.0, 7.0);
1606        assert!(nan.has_nan());
1607
1608        assert_eq!(
1609            Rect::from_points(Offset::new(5.0, 7.0), Offset::new(1.0, 3.0)),
1610            Rect::from_ltrb(1.0, 3.0, 5.0, 7.0)
1611        );
1612    }
1613
1614    // engine geometry_test.dart 'Rect.toString'.
1615    #[test]
1616    fn rect_debug_matches_darts_to_string() {
1617        assert_eq!(
1618            format!("{:?}", Rect::from_ltrb(1.0, 3.0, 5.0, 7.0)),
1619            "Rect.fromLTRB(1.0, 3.0, 5.0, 7.0)"
1620        );
1621        assert_eq!(format!("{:?}", Size::new(2.0, 3.0)), "Size(2.0, 3.0)");
1622    }
1623
1624    #[test]
1625    fn an_offset_and_a_size_make_a_rect() {
1626        let from_origin = Offset::ZERO & Size::new(100.0, 100.0);
1627        assert_eq!(from_origin, Rect::from_ltwh(0.0, 0.0, 100.0, 100.0));
1628
1629        let rect = Offset::new(1.0, 2.0) & Size::new(3.0, 4.0);
1630        assert_eq!(rect, Rect::from_ltwh(1.0, 2.0, 3.0, 4.0));
1631    }
1632
1633    #[test]
1634    fn rect_intersect_and_overlaps_agree_on_the_shared_edge() {
1635        let a = Rect::from_ltrb(0.0, 0.0, 10.0, 10.0);
1636        let b = Rect::from_ltrb(5.0, 5.0, 15.0, 15.0);
1637        assert_eq!(a.intersect(b), Rect::from_ltrb(5.0, 5.0, 10.0, 10.0));
1638        assert!(a.overlaps(b));
1639
1640        let touching = Rect::from_ltrb(10.0, 0.0, 20.0, 10.0);
1641        assert!(!a.overlaps(touching), "a shared edge is not an overlap");
1642        assert!(a.intersect(touching).is_empty());
1643
1644        assert_eq!(
1645            a.expand_to_include(touching),
1646            Rect::from_ltrb(0.0, 0.0, 20.0, 10.0)
1647        );
1648
1649        let zero_area = Rect::from_ltrb(-5.0, 2.0, -5.0, 2.0);
1650        assert_eq!(
1651            a.expand_to_include(zero_area),
1652            Rect::from_ltrb(-5.0, 0.0, 10.0, 10.0)
1653        );
1654    }
1655
1656    #[test]
1657    fn rect_contains_includes_top_left_and_excludes_bottom_right() {
1658        let rect = Rect::from_ltrb(1.0, 1.0, 2.0, 2.0);
1659        assert!(rect.contains(Offset::new(1.0, 1.0)));
1660        assert!(!rect.contains(Offset::new(2.0, 2.0)));
1661        assert!(!rect.contains(Offset::new(2.0, 1.5)));
1662    }
1663
1664    #[test]
1665    fn rect_lerp_matches_darts_null_arms() {
1666        let rect = Rect::from_ltrb(4.0, 8.0, 12.0, 16.0);
1667        assert_eq!(Rect::lerp(None, None, 0.5), None);
1668        assert_eq!(
1669            Rect::lerp(Some(rect), None, 0.5),
1670            Some(Rect::from_ltrb(2.0, 4.0, 6.0, 8.0))
1671        );
1672        assert_eq!(
1673            Rect::lerp(None, Some(rect), 0.5),
1674            Some(Rect::from_ltrb(2.0, 4.0, 6.0, 8.0))
1675        );
1676        assert_eq!(
1677            Rect::lerp(Some(Rect::ZERO), Some(rect), 0.5),
1678            Some(Rect::from_ltrb(2.0, 4.0, 6.0, 8.0))
1679        );
1680    }
1681
1682    #[test]
1683    fn rect_shift_translate_inflate_deflate() {
1684        let rect = Rect::from_ltrb(0.0, 0.0, 4.0, 4.0);
1685        assert_eq!(
1686            rect.shift(Offset::new(1.0, 2.0)),
1687            Rect::from_ltrb(1.0, 2.0, 5.0, 6.0)
1688        );
1689        assert_eq!(rect.translate(1.0, 2.0), rect.shift(Offset::new(1.0, 2.0)));
1690        assert_eq!(rect.inflate(1.0), Rect::from_ltrb(-1.0, -1.0, 5.0, 5.0));
1691        assert_eq!(rect.inflate(1.0).deflate(1.0), rect);
1692    }
1693
1694    // engine geometry_test.dart 'Radius.clamp() operates as expected'.
1695    #[test]
1696    fn radius_clamp_operates_as_expected() {
1697        assert_eq!(
1698            Radius::circular(-100.0).clamp(Some(Radius::ZERO), None),
1699            Radius::circular(0.0)
1700        );
1701        assert_eq!(
1702            Radius::circular(100.0).clamp(None, Some(Radius::circular(10.0))),
1703            Radius::circular(10.0)
1704        );
1705        assert_eq!(
1706            Radius::elliptical(-100.0, 100.0)
1707                .clamp(Some(Radius::ZERO), Some(Radius::circular(10.0))),
1708            Radius::elliptical(0.0, 10.0)
1709        );
1710        assert_eq!(
1711            Radius::elliptical(100.0, -100.0)
1712                .clamp(Some(Radius::ZERO), Some(Radius::circular(10.0))),
1713            Radius::elliptical(10.0, 0.0)
1714        );
1715    }
1716
1717    // engine geometry_test.dart 'Radius.clampValues() operates as expected'.
1718    #[test]
1719    fn radius_clamp_values_operates_as_expected() {
1720        assert_eq!(
1721            Radius::circular(-100.0).clamp_values(Some(0.0), Some(0.0), None, None),
1722            Radius::circular(0.0)
1723        );
1724        assert_eq!(
1725            Radius::circular(100.0).clamp_values(None, None, Some(10.0), Some(20.0)),
1726            Radius::elliptical(10.0, 20.0)
1727        );
1728        assert_eq!(
1729            Radius::elliptical(-100.0, 100.0).clamp_values(
1730                Some(5.0),
1731                Some(6.0),
1732                Some(10.0),
1733                Some(20.0)
1734            ),
1735            Radius::elliptical(5.0, 20.0)
1736        );
1737        assert_eq!(
1738            Radius::elliptical(100.0, -100.0).clamp_values(
1739                Some(5.0),
1740                Some(6.0),
1741                Some(10.0),
1742                Some(20.0)
1743            ),
1744            Radius::elliptical(10.0, 6.0)
1745        );
1746    }
1747
1748    #[test]
1749    fn radius_arithmetic_is_component_wise() {
1750        let a = Radius::elliptical(10.0, 20.0);
1751        let b = Radius::elliptical(1.0, 2.0);
1752
1753        assert_eq!(a + b, Radius::elliptical(11.0, 22.0));
1754        assert_eq!(a - b, Radius::elliptical(9.0, 18.0));
1755        assert_eq!(a * 2.0, Radius::elliptical(20.0, 40.0));
1756        assert_eq!(a / 2.0, Radius::elliptical(5.0, 10.0));
1757        assert_eq!(-a, Radius::elliptical(-10.0, -20.0));
1758        assert_eq!(a.truncating_div(3.0), Radius::elliptical(3.0, 6.0));
1759        assert_eq!(
1760            Radius::elliptical(-5.0, 5.0) % 3.0,
1761            Radius::elliptical(1.0, 2.0)
1762        );
1763        assert_eq!(
1764            Radius::elliptical(-5.0, 5.0).truncating_div(3.0),
1765            Radius::elliptical(-1.0, 1.0)
1766        );
1767    }
1768
1769    #[test]
1770    fn radius_circular_equals_elliptical_when_axes_match() {
1771        assert_eq!(Radius::circular(5.0), Radius::elliptical(5.0, 5.0));
1772        assert_ne!(Radius::circular(5.0), Radius::elliptical(5.0, 6.0));
1773        assert_eq!(Radius::ZERO, Radius::circular(0.0));
1774    }
1775
1776    #[test]
1777    fn radius_lerp_defaults_a_missing_end_to_zero() {
1778        let a = Radius::elliptical(0.0, 10.0);
1779        let b = Radius::elliptical(10.0, 0.0);
1780
1781        assert_eq!(
1782            Radius::lerp(Some(a), Some(b), 0.5),
1783            Some(Radius::elliptical(5.0, 5.0))
1784        );
1785        assert_eq!(
1786            Radius::lerp(Some(a), None, 0.5),
1787            Some(Radius::elliptical(0.0, 5.0))
1788        );
1789        assert_eq!(
1790            Radius::lerp(None, Some(b), 0.5),
1791            Some(Radius::elliptical(5.0, 0.0))
1792        );
1793        assert_eq!(Radius::lerp(None, None, 0.5), None);
1794    }
1795
1796    #[test]
1797    fn radius_debug_matches_darts_to_string() {
1798        assert_eq!(
1799            format!("{:?}", Radius::circular(1.5)),
1800            "Radius.circular(1.5)"
1801        );
1802        assert_eq!(
1803            format!("{:?}", Radius::elliptical(1.0, 2.5)),
1804            "Radius.elliptical(1.0, 2.5)"
1805        );
1806    }
1807}