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}