chore: refresco desde el monorepo (pin tawasuyu.git v0.2.0)
1use std::fmt;
2use std::ops::{Add, AddAssign, Div, Mul, Sub, SubAssign};
3
4#[cfg(feature = "wayland_frontend")]
5use wayland_server::protocol::wl_output::Transform as WlTransform;
6
7/// Type-level marker for the logical coordinate space
8#[derive(Debug)]
9pub struct Logical;
10
11/// Type-level marker for the client logical coordinate space
12#[derive(Debug)]
13#[cfg(feature = "wayland_frontend")]
14pub(crate) struct Client;
15
16/// Type-level marker for the physical coordinate space
17#[derive(Debug)]
18pub struct Physical;
19
20/// Type-level marker for the buffer coordinate space
21#[derive(Debug)]
22pub struct Buffer;
23
24/// Type-level marker for raw coordinate space, provided by input devices
25#[derive(Debug)]
26pub struct Raw;
27
28/// Trait for types serving as a coordinate for other geometry utils
29pub trait Coordinate:
30 Sized + Add<Self, Output = Self> + Sub<Self, Output = Self> + PartialOrd + Default + Copy + fmt::Debug
31{
32 /// A Coordinate that is 0
33 const ZERO: Self;
34 /// Downscale the coordinate
35 fn downscale(self, scale: Self) -> Self;
36 /// Upscale the coordinate
37 fn upscale(self, scale: Self) -> Self;
38 /// Convert the coordinate to a f64
39 fn to_f64(self) -> f64;
40 /// Convert to this coordinate from a f64
41 fn from_f64(v: f64) -> Self;
42 /// Compare and return the smaller one
43 fn min(self, other: Self) -> Self {
44 if self < other {
45 self
46 } else {
47 other
48 }
49 }
50 /// Compare and return the larger one
51 fn max(self, other: Self) -> Self {
52 if self > other {
53 self
54 } else {
55 other
56 }
57 }
58 /// Test if the coordinate is not negative
59 fn non_negative(self) -> bool;
60 /// Returns the absolute value of this coordinate
61 fn abs(self) -> Self;
62
63 /// Saturating integer addition. Computes self + other, saturating at the numeric bounds instead of overflowing.
64 fn saturating_add(self, other: Self) -> Self;
65 /// Saturating integer subtraction. Computes self - other, saturating at the numeric bounds instead of overflowing.
66 fn saturating_sub(self, other: Self) -> Self;
67 /// Saturating integer multiplication. Computes self * other, saturating at the numeric bounds instead of overflowing.
68 fn saturating_mul(self, other: Self) -> Self;
69}
70
71/// Implements Coordinate for an unsigned numerical type.
72macro_rules! unsigned_coordinate_impl {
73 ($ty:ty, $ ($tys:ty),* ) => {
74 unsigned_coordinate_impl!($ty);
75 $(
76 unsigned_coordinate_impl!($tys);
77 )*
78 };
79
80 ($ty:ty) => {
81 impl Coordinate for $ty {
82 const ZERO: $ty = 0;
83
84 #[inline]
85 fn downscale(self, scale: Self) -> Self {
86 self / scale
87 }
88
89 #[inline]
90 fn upscale(self, scale: Self) -> Self {
91 self.saturating_mul(scale)
92 }
93
94 #[inline]
95 fn to_f64(self) -> f64 {
96 self as f64
97 }
98
99 #[inline]
100 fn from_f64(v: f64) -> Self {
101 v as Self
102 }
103
104 #[inline]
105 fn non_negative(self) -> bool {
106 true
107 }
108
109 #[inline]
110 fn abs(self) -> Self {
111 self
112 }
113
114 #[inline]
115 fn saturating_add(self, other: Self) -> Self {
116 self.saturating_add(other)
117 }
118 #[inline]
119 fn saturating_sub(self, other: Self) -> Self {
120 self.saturating_sub(other)
121 }
122 #[inline]
123 fn saturating_mul(self, other: Self) -> Self {
124 self.saturating_mul(other)
125 }
126 }
127 };
128}
129
130unsigned_coordinate_impl! {
131 u8,
132 u16,
133 u32,
134 u64,
135 u128
136}
137
138/// Implements Coordinate for an signed numerical type.
139macro_rules! signed_coordinate_impl {
140 ($ty:ty, $ ($tys:ty),* ) => {
141 signed_coordinate_impl!($ty);
142 $(
143 signed_coordinate_impl!($tys);
144 )*
145 };
146
147 ($ty:ty) => {
148 impl Coordinate for $ty {
149 const ZERO: $ty = 0;
150
151 #[inline]
152 fn downscale(self, scale: Self) -> Self {
153 self / scale
154 }
155
156 #[inline]
157 fn upscale(self, scale: Self) -> Self {
158 self.saturating_mul(scale)
159 }
160
161 #[inline]
162 fn to_f64(self) -> f64 {
163 self as f64
164 }
165
166 #[inline]
167 fn from_f64(v: f64) -> Self {
168 v as Self
169 }
170
171 #[inline]
172 fn non_negative(self) -> bool {
173 self >= 0
174 }
175
176 #[inline]
177 fn abs(self) -> Self {
178 self.abs()
179 }
180
181 #[inline]
182 fn saturating_add(self, other: Self) -> Self {
183 self.saturating_add(other)
184 }
185 #[inline]
186 fn saturating_sub(self, other: Self) -> Self {
187 self.saturating_sub(other)
188 }
189 #[inline]
190 fn saturating_mul(self, other: Self) -> Self {
191 self.saturating_mul(other)
192 }
193 }
194 };
195}
196
197signed_coordinate_impl! {
198 i8,
199 i16,
200 i32,
201 i64,
202 i128
203}
204
205macro_rules! floating_point_coordinate_impl {
206 ($ty:ty, $ ($tys:ty),* ) => {
207 floating_point_coordinate_impl!($ty);
208 $(
209 floating_point_coordinate_impl!($tys);
210 )*
211 };
212
213 ($ty:ty) => {
214 impl Coordinate for $ty {
215 const ZERO: $ty = 0.0;
216
217 #[inline]
218 fn downscale(self, scale: Self) -> Self {
219 self / scale
220 }
221
222 #[inline]
223 fn upscale(self, scale: Self) -> Self {
224 self * scale
225 }
226
227 #[inline]
228 fn to_f64(self) -> f64 {
229 self as f64
230 }
231
232 #[inline]
233 fn from_f64(v: f64) -> Self {
234 v as Self
235 }
236
237 #[inline]
238 fn non_negative(self) -> bool {
239 self >= 0.0
240 }
241
242 #[inline]
243 fn abs(self) -> Self {
244 self.abs()
245 }
246
247 #[inline]
248 fn saturating_add(self, other: Self) -> Self {
249 self + other
250 }
251 #[inline]
252 fn saturating_sub(self, other: Self) -> Self {
253 self - other
254 }
255 #[inline]
256 fn saturating_mul(self, other: Self) -> Self {
257 self * other
258 }
259 }
260 };
261}
262
263floating_point_coordinate_impl! {
264 f32,
265 f64
266}
267
268/*
269 * Scale
270 */
271
272/// A two-dimensional scale that can be
273/// used to scale [`Point`]s, [`Size`]s and
274/// [`Rectangle`]s
275#[derive(Debug, Clone, Copy, PartialEq, Eq)]
276pub struct Scale<N: Coordinate> {
277 /// The scale on the x axis
278 pub x: N,
279 /// The scale on the y axis
280 pub y: N,
281}
282
283impl<N: Coordinate> Scale<N> {
284 /// Convert the underlying numerical type to f64 for floating point manipulations
285 #[inline]
286 pub fn to_f64(self) -> Scale<f64> {
287 Scale {
288 x: self.x.to_f64(),
289 y: self.y.to_f64(),
290 }
291 }
292}
293
294impl<N: Coordinate> From<N> for Scale<N> {
295 #[inline]
296 fn from(scale: N) -> Self {
297 Scale { x: scale, y: scale }
298 }
299}
300
301impl<N: Coordinate> From<(N, N)> for Scale<N> {
302 #[inline]
303 fn from((scale_x, scale_y): (N, N)) -> Self {
304 Scale {
305 x: scale_x,
306 y: scale_y,
307 }
308 }
309}
310
311impl<N, T> Mul<T> for Scale<N>
312where
313 N: Coordinate,
314 T: Into<Scale<N>>,
315{
316 type Output = Scale<N>;
317
318 #[inline]
319 fn mul(self, rhs: T) -> Self::Output {
320 let rhs = rhs.into();
321 Scale {
322 x: self.x.upscale(rhs.x),
323 y: self.y.upscale(rhs.y),
324 }
325 }
326}
327
328/*
329 * Point
330 */
331
332/// A point as defined by its x and y coordinates
333///
334/// Operations on points are saturating.
335#[repr(C)]
336pub struct Point<N, Kind> {
337 /// horizontal coordinate
338 pub x: N,
339 /// vertical coordinate
340 pub y: N,
341 _kind: std::marker::PhantomData<Kind>,
342}
343
344impl<N, Kind> Point<N, Kind> {
345 /// Create a new Point
346 pub const fn new(x: N, y: N) -> Point<N, Kind> {
347 Point {
348 x,
349 y,
350 _kind: std::marker::PhantomData,
351 }
352 }
353}
354
355impl<N: Coordinate, Kind> Point<N, Kind> {
356 /// Convert this [`Point`] to a [`Size`] with the same coordinates
357 ///
358 /// Checks that the coordinates are positive with a `debug_assert!()`.
359 #[inline]
360 pub fn to_size(self) -> Size<N, Kind> {
361 debug_assert!(
362 self.x.non_negative() && self.y.non_negative(),
363 "Attempting to create a `Size` of negative size: {:?}",
364 (self.x, self.y)
365 );
366 Size {
367 w: self.x,
368 h: self.y,
369 _kind: std::marker::PhantomData,
370 }
371 }
372
373 /// Convert this [`Point`] to a [`Size`] with the same coordinates
374 ///
375 /// Ensures that the coordinates are positive by taking their absolute value
376 #[inline]
377 pub fn to_size_abs(self) -> Size<N, Kind> {
378 Size {
379 w: self.x.abs(),
380 h: self.y.abs(),
381 _kind: std::marker::PhantomData,
382 }
383 }
384
385 /// Upscale this [`Point`] by a specified [`Scale`]
386 #[inline]
387 pub fn upscale(self, scale: impl Into<Scale<N>>) -> Point<N, Kind> {
388 let scale = scale.into();
389 Point {
390 x: self.x.upscale(scale.x),
391 y: self.y.upscale(scale.y),
392 _kind: std::marker::PhantomData,
393 }
394 }
395
396 /// Downscale this [`Point`] by a specified [`Scale`]
397 #[inline]
398 pub fn downscale(self, scale: impl Into<Scale<N>>) -> Point<N, Kind> {
399 let scale = scale.into();
400 Point {
401 x: self.x.downscale(scale.x),
402 y: self.y.downscale(scale.y),
403 _kind: std::marker::PhantomData,
404 }
405 }
406}
407
408impl<N: Coordinate, Kind> Point<N, Kind> {
409 /// Constrain this [`Point`] within a [`Rectangle`] with the same coordinates
410 ///
411 /// The [`Point`] returned is guaranteed to be not smaller than the [`Rectangle`]
412 /// location and not greater than the [`Rectangle`] location plus size.
413 #[inline]
414 pub fn constrain(self, rect: impl Into<Rectangle<N, Kind>>) -> Point<N, Kind> {
415 let rect = rect.into();
416
417 Point {
418 x: self.x.max(rect.loc.x).min(rect.loc.x + rect.size.w),
419 y: self.y.max(rect.loc.y).min(rect.loc.y + rect.size.h),
420 _kind: std::marker::PhantomData,
421 }
422 }
423}
424
425impl<N: Coordinate, Kind> Point<N, Kind> {
426 /// Convert the underlying numerical type to f64 for floating point manipulations
427 #[inline]
428 pub fn to_f64(self) -> Point<f64, Kind> {
429 Point {
430 x: self.x.to_f64(),
431 y: self.y.to_f64(),
432 _kind: std::marker::PhantomData,
433 }
434 }
435}
436
437impl<Kind> Point<f64, Kind> {
438 /// Convert to i32 for integer-space manipulations by rounding float values
439 #[inline]
440 pub fn to_i32_round<N: Coordinate>(self) -> Point<N, Kind> {
441 Point {
442 x: N::from_f64(self.x.round()),
443 y: N::from_f64(self.y.round()),
444 _kind: std::marker::PhantomData,
445 }
446 }
447
448 /// Convert to i32 for integer-space manipulations by flooring float values
449 #[inline]
450 pub fn to_i32_floor<N: Coordinate>(self) -> Point<N, Kind> {
451 Point {
452 x: N::from_f64(self.x.floor()),
453 y: N::from_f64(self.y.floor()),
454 _kind: std::marker::PhantomData,
455 }
456 }
457
458 /// Convert to i32 for integer-space manipulations by ceiling float values
459 #[inline]
460 pub fn to_i32_ceil<N: Coordinate>(self) -> Point<N, Kind> {
461 Point {
462 x: N::from_f64(self.x.ceil()),
463 y: N::from_f64(self.y.ceil()),
464 _kind: std::marker::PhantomData,
465 }
466 }
467}
468
469impl<N: fmt::Debug, S> fmt::Debug for Point<N, S> {
470 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
471 f.write_fmt(format_args!("Point<{}>", std::any::type_name::<S>()))?;
472 f.debug_struct("")
473 .field("x", &self.x)
474 .field("y", &self.y)
475 .finish()
476 }
477}
478
479impl<N: Coordinate> Point<N, Logical> {
480 #[inline]
481 #[cfg(feature = "wayland_frontend")]
482 pub(crate) fn to_client(self, scale: impl Into<Scale<N>>) -> Point<N, Client> {
483 let scale: Scale<N> = scale.into();
484 Point {
485 x: self.x.upscale(scale.x),
486 y: self.y.upscale(scale.y),
487 _kind: std::marker::PhantomData,
488 }
489 }
490
491 #[inline]
492 #[cfg(feature = "wayland_frontend")]
493 pub(crate) fn to_client_precise_round<S: Coordinate, R: Coordinate>(
494 self,
495 scale: impl Into<Scale<S>>,
496 ) -> Point<R, Client> {
497 self.to_f64().to_client(scale.into().to_f64()).to_i32_round()
498 }
499
500 #[inline]
501 /// Convert this logical point to physical coordinate space according to given scale factor
502 pub fn to_physical(self, scale: impl Into<Scale<N>>) -> Point<N, Physical> {
503 let scale = scale.into();
504 Point {
505 x: self.x.upscale(scale.x),
506 y: self.y.upscale(scale.y),
507 _kind: std::marker::PhantomData,
508 }
509 }
510
511 /// Convert this logical point to physical coordinate space according to given scale factor
512 /// and round the result
513 #[inline]
514 pub fn to_physical_precise_round<S: Coordinate, R: Coordinate>(
515 self,
516 scale: impl Into<Scale<S>>,
517 ) -> Point<R, Physical> {
518 self.to_f64().to_physical(scale.into().to_f64()).to_i32_round()
519 }
520
521 /// Convert this logical point to physical coordinate space according to given scale factor
522 /// and ceil the result
523 #[inline]
524 pub fn to_physical_precise_ceil<S: Coordinate, R: Coordinate>(
525 &self,
526 scale: impl Into<Scale<S>>,
527 ) -> Point<R, Physical> {
528 self.to_f64().to_physical(scale.into().to_f64()).to_i32_ceil()
529 }
530
531 /// Convert this logical point to physical coordinate space according to given scale factor
532 /// and floor the result
533 #[inline]
534 pub fn to_physical_precise_floor<S: Coordinate, R: Coordinate>(
535 &self,
536 scale: impl Into<Scale<S>>,
537 ) -> Point<R, Physical> {
538 self.to_f64().to_physical(scale.into().to_f64()).to_i32_floor()
539 }
540
541 #[inline]
542 /// Convert this logical point to buffer coordinate space according to given scale factor
543 pub fn to_buffer(
544 self,
545 scale: impl Into<Scale<N>>,
546 transformation: Transform,
547 area: &Size<N, Logical>,
548 ) -> Point<N, Buffer> {
549 let point = transformation.transform_point_in(self, area);
550 let scale = scale.into();
551 Point {
552 x: point.x.upscale(scale.x),
553 y: point.y.upscale(scale.y),
554 _kind: std::marker::PhantomData,
555 }
556 }
557}
558
559#[cfg(feature = "wayland_frontend")]
560impl<N: Coordinate> Point<N, Client> {
561 #[inline]
562 pub(crate) fn to_logical(self, scale: impl Into<Scale<N>>) -> Point<N, Logical> {
563 let scale = scale.into();
564 Point {
565 x: self.x.downscale(scale.x),
566 y: self.y.downscale(scale.y),
567 _kind: std::marker::PhantomData,
568 }
569 }
570}
571
572impl<N: Coordinate> Point<N, Physical> {
573 #[inline]
574 /// Convert this physical point to logical coordinate space according to given scale factor
575 pub fn to_logical(self, scale: impl Into<Scale<N>>) -> Point<N, Logical> {
576 let scale = scale.into();
577 Point {
578 x: self.x.downscale(scale.x),
579 y: self.y.downscale(scale.y),
580 _kind: std::marker::PhantomData,
581 }
582 }
583}
584
585impl<N: Coordinate> Point<N, Buffer> {
586 #[inline]
587 /// Convert this physical point to logical coordinate space according to given scale factor
588 pub fn to_logical(
589 self,
590 scale: impl Into<Scale<N>>,
591 transform: Transform,
592 area: &Size<N, Buffer>,
593 ) -> Point<N, Logical> {
594 let point = transform.invert().transform_point_in(self, area);
595 let scale = scale.into();
596 Point {
597 x: point.x.downscale(scale.x),
598 y: point.y.downscale(scale.y),
599 _kind: std::marker::PhantomData,
600 }
601 }
602}
603
604impl<N, Kind> From<(N, N)> for Point<N, Kind> {
605 #[inline]
606 fn from((x, y): (N, N)) -> Point<N, Kind> {
607 Point::new(x, y)
608 }
609}
610
611impl<N, Kind> From<Point<N, Kind>> for (N, N) {
612 #[inline]
613 fn from(point: Point<N, Kind>) -> (N, N) {
614 (point.x, point.y)
615 }
616}
617
618impl<N: Coordinate, Kind> Add for Point<N, Kind> {
619 type Output = Point<N, Kind>;
620 #[inline]
621 fn add(self, other: Point<N, Kind>) -> Point<N, Kind> {
622 Point {
623 x: self.x.saturating_add(other.x),
624 y: self.y.saturating_add(other.y),
625 _kind: std::marker::PhantomData,
626 }
627 }
628}
629
630impl<N: Coordinate, Kind> AddAssign for Point<N, Kind> {
631 #[inline]
632 fn add_assign(&mut self, rhs: Self) {
633 self.x = self.x.saturating_add(rhs.x);
634 self.y = self.y.saturating_add(rhs.y);
635 }
636}
637
638impl<N: Coordinate, Kind> SubAssign for Point<N, Kind> {
639 #[inline]
640 fn sub_assign(&mut self, rhs: Self) {
641 self.x = self.x.saturating_sub(rhs.x);
642 self.y = self.y.saturating_sub(rhs.y);
643 }
644}
645
646impl<N: Coordinate, Kind> Sub for Point<N, Kind> {
647 type Output = Point<N, Kind>;
648 #[inline]
649 fn sub(self, other: Point<N, Kind>) -> Point<N, Kind> {
650 Point {
651 x: self.x.saturating_sub(other.x),
652 y: self.y.saturating_sub(other.y),
653 _kind: std::marker::PhantomData,
654 }
655 }
656}
657
658impl<N: Clone, Kind> Clone for Point<N, Kind> {
659 #[inline]
660 fn clone(&self) -> Self {
661 Point {
662 x: self.x.clone(),
663 y: self.y.clone(),
664 _kind: std::marker::PhantomData,
665 }
666 }
667}
668
669impl<N: Copy, Kind> Copy for Point<N, Kind> {}
670
671impl<N: PartialEq, Kind> PartialEq for Point<N, Kind> {
672 #[inline]
673 fn eq(&self, other: &Self) -> bool {
674 self.x == other.x && self.y == other.y
675 }
676}
677
678impl<N: Eq, Kind> Eq for Point<N, Kind> {}
679
680impl<N: Default, Kind> Default for Point<N, Kind> {
681 #[inline]
682 fn default() -> Self {
683 Point {
684 x: N::default(),
685 y: N::default(),
686 _kind: std::marker::PhantomData,
687 }
688 }
689}
690
691/*
692 * Size
693 */
694
695/// A size as defined by its width and height
696///
697/// Constructors of this type ensure that the values are always positive via
698/// `debug_assert!()`, however manually changing the values of the fields
699/// can break this invariant.
700///
701/// Operations on sizes are saturating.
702#[repr(C)]
703pub struct Size<N, Kind> {
704 /// horizontal coordinate
705 pub w: N,
706 /// vertical coordinate
707 pub h: N,
708 _kind: std::marker::PhantomData<Kind>,
709}
710
711impl<N: Coordinate, Kind> Size<N, Kind> {
712 /// Create a new Size
713 pub fn new(w: N, h: N) -> Size<N, Kind> {
714 debug_assert!(
715 w.non_negative() && h.non_negative(),
716 "Attempting to create a `Size` of negative size: {:?}",
717 (w, h)
718 );
719 Size {
720 w,
721 h,
722 _kind: std::marker::PhantomData,
723 }
724 }
725}
726
727impl<N: Coordinate, Kind> Size<N, Kind> {
728 /// Convert this [`Size`] to a [`Point`] with the same coordinates
729 #[inline]
730 pub fn to_point(self) -> Point<N, Kind> {
731 Point {
732 x: self.w,
733 y: self.h,
734 _kind: std::marker::PhantomData,
735 }
736 }
737}
738
739impl<N: Coordinate, Kind> Size<N, Kind> {
740 /// Restrict this [`Size`] to min and max [`Size`] with the same coordinates
741 pub fn clamp(self, min: impl Into<Size<N, Kind>>, max: impl Into<Size<N, Kind>>) -> Size<N, Kind> {
742 let min = min.into();
743 let max = max.into();
744
745 Size {
746 w: self.w.max(min.w).min(max.w),
747 h: self.h.max(min.h).min(max.h),
748 _kind: std::marker::PhantomData,
749 }
750 }
751}
752
753impl<N: Coordinate, Kind> Size<N, Kind> {
754 /// Convert the underlying numerical type to f64 for floating point manipulations
755 #[inline]
756 pub fn to_f64(self) -> Size<f64, Kind> {
757 Size {
758 w: self.w.to_f64(),
759 h: self.h.to_f64(),
760 _kind: std::marker::PhantomData,
761 }
762 }
763}
764
765impl<N: Coordinate, Kind> Size<N, Kind> {
766 /// Upscale this [`Size`] by a specified [`Scale`]
767 #[inline]
768 pub fn upscale(self, scale: impl Into<Scale<N>>) -> Size<N, Kind> {
769 let scale = scale.into();
770 Size {
771 w: self.w.upscale(scale.x),
772 h: self.h.upscale(scale.y),
773 _kind: std::marker::PhantomData,
774 }
775 }
776
777 /// Downscale this [`Size`] by a specified [`Scale`]
778 #[inline]
779 pub fn downscale(self, scale: impl Into<Scale<N>>) -> Size<N, Kind> {
780 let scale = scale.into();
781 Size {
782 w: self.w.downscale(scale.x),
783 h: self.h.downscale(scale.y),
784 _kind: std::marker::PhantomData,
785 }
786 }
787
788 /// Check if this [`Size`] is empty
789 ///
790 /// Returns true if either the width or the height is zero
791 #[inline]
792 pub fn is_empty(&self) -> bool {
793 self.w == N::default() || self.h == N::default()
794 }
795}
796
797impl<Kind> Size<f64, Kind> {
798 /// Convert to i32 for integer-space manipulations by rounding float values
799 #[inline]
800 pub fn to_i32_round<N: Coordinate>(self) -> Size<N, Kind> {
801 Size {
802 w: N::from_f64(self.w.round()),
803 h: N::from_f64(self.h.round()),
804 _kind: std::marker::PhantomData,
805 }
806 }
807
808 /// Convert to i32 for integer-space manipulations by flooring float values
809 #[inline]
810 pub fn to_i32_floor<N: Coordinate>(self) -> Size<N, Kind> {
811 Size {
812 w: N::from_f64(self.w.floor()),
813 h: N::from_f64(self.h.floor()),
814 _kind: std::marker::PhantomData,
815 }
816 }
817
818 /// Convert to i32 for integer-space manipulations by ceiling float values
819 #[inline]
820 pub fn to_i32_ceil<N: Coordinate>(self) -> Size<N, Kind> {
821 Size {
822 w: N::from_f64(self.w.ceil()),
823 h: N::from_f64(self.h.ceil()),
824 _kind: std::marker::PhantomData,
825 }
826 }
827}
828
829impl<N: fmt::Debug, S> fmt::Debug for Size<N, S> {
830 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
831 f.write_fmt(format_args!("Size<{}>", std::any::type_name::<S>()))?;
832 f.debug_struct("")
833 .field("w", &self.w)
834 .field("h", &self.h)
835 .finish()
836 }
837}
838
839impl<N: Coordinate> Size<N, Logical> {
840 #[inline]
841 #[cfg(feature = "wayland_frontend")]
842 pub(crate) fn to_client(self, scale: impl Into<Scale<N>>) -> Size<N, Client> {
843 let scale = scale.into();
844 Size {
845 w: self.w.upscale(scale.x),
846 h: self.h.upscale(scale.y),
847 _kind: std::marker::PhantomData,
848 }
849 }
850
851 #[inline]
852 /// Convert this logical size to physical coordinate space according to given scale factor
853 pub fn to_physical(self, scale: impl Into<Scale<N>>) -> Size<N, Physical> {
854 let scale = scale.into();
855 Size {
856 w: self.w.upscale(scale.x),
857 h: self.h.upscale(scale.y),
858 _kind: std::marker::PhantomData,
859 }
860 }
861
862 /// Convert this logical size to physical coordinate space according to given scale factor
863 /// and round the result
864 #[inline]
865 pub fn to_physical_precise_round<S: Coordinate, R: Coordinate>(
866 self,
867 scale: impl Into<Scale<S>>,
868 ) -> Size<R, Physical> {
869 self.to_f64().to_physical(scale.into().to_f64()).to_i32_round()
870 }
871
872 /// Convert this logical size to physical coordinate space according to given scale factor
873 /// and ceil the result
874 #[inline]
875 pub fn to_physical_precise_ceil<S: Coordinate, R: Coordinate>(
876 &self,
877 scale: impl Into<Scale<S>>,
878 ) -> Size<R, Physical> {
879 self.to_f64().to_physical(scale.into().to_f64()).to_i32_ceil()
880 }
881
882 /// Convert this logical size to physical coordinate space according to given scale factor
883 /// and floor the result
884 #[inline]
885 pub fn to_physical_precise_floor<S: Coordinate, R: Coordinate>(
886 &self,
887 scale: impl Into<Scale<S>>,
888 ) -> Size<R, Physical> {
889 self.to_f64().to_physical(scale.into().to_f64()).to_i32_floor()
890 }
891
892 #[inline]
893 /// Convert this logical size to buffer coordinate space according to given scale factor
894 pub fn to_buffer(self, scale: impl Into<Scale<N>>, transformation: Transform) -> Size<N, Buffer> {
895 let scale = scale.into();
896 transformation.transform_size(Size {
897 w: self.w.upscale(scale.x),
898 h: self.h.upscale(scale.y),
899 _kind: std::marker::PhantomData,
900 })
901 }
902}
903
904#[cfg(feature = "wayland_frontend")]
905impl<N: Coordinate> Size<N, Client> {
906 #[inline]
907 pub(crate) fn to_logical(self, scale: impl Into<Scale<N>>) -> Size<N, Logical> {
908 let scale = scale.into();
909 Size {
910 w: self.w.downscale(scale.x),
911 h: self.h.downscale(scale.y),
912 _kind: std::marker::PhantomData,
913 }
914 }
915}
916
917impl<N: Coordinate> Size<N, Physical> {
918 #[inline]
919 /// Convert this physical point to logical coordinate space according to given scale factor
920 pub fn to_logical(self, scale: impl Into<Scale<N>>) -> Size<N, Logical> {
921 let scale = scale.into();
922 Size {
923 w: self.w.downscale(scale.x),
924 h: self.h.downscale(scale.y),
925 _kind: std::marker::PhantomData,
926 }
927 }
928}
929
930impl<N: Coordinate> Size<N, Buffer> {
931 #[inline]
932 /// Convert this physical point to logical coordinate space according to given scale factor
933 pub fn to_logical(self, scale: impl Into<Scale<N>>, transformation: Transform) -> Size<N, Logical> {
934 let scale = scale.into();
935 transformation.invert().transform_size(Size {
936 w: self.w.downscale(scale.x),
937 h: self.h.downscale(scale.y),
938 _kind: std::marker::PhantomData,
939 })
940 }
941}
942
943impl<N: Coordinate, Kind> From<(N, N)> for Size<N, Kind> {
944 #[inline]
945 fn from((w, h): (N, N)) -> Size<N, Kind> {
946 Size::new(w, h)
947 }
948}
949
950impl<N, Kind> From<Size<N, Kind>> for (N, N) {
951 #[inline]
952 fn from(point: Size<N, Kind>) -> (N, N) {
953 (point.w, point.h)
954 }
955}
956
957impl<N: Coordinate, Kind> Add for Size<N, Kind> {
958 type Output = Size<N, Kind>;
959 #[inline]
960 fn add(self, other: Size<N, Kind>) -> Size<N, Kind> {
961 Size {
962 w: self.w.saturating_add(other.w),
963 h: self.h.saturating_add(other.h),
964 _kind: std::marker::PhantomData,
965 }
966 }
967}
968
969impl<N: Coordinate, Kind> AddAssign for Size<N, Kind> {
970 #[inline]
971 fn add_assign(&mut self, rhs: Self) {
972 self.w = self.w.saturating_add(rhs.w);
973 self.h = self.h.saturating_add(rhs.h);
974 }
975}
976
977impl<N: Coordinate, Kind> Sub for Size<N, Kind> {
978 type Output = Size<N, Kind>;
979 #[inline]
980 fn sub(self, rhs: Self) -> Self::Output {
981 Size {
982 w: self.w.saturating_sub(rhs.w).max(N::ZERO),
983 h: self.h.saturating_sub(rhs.h).max(N::ZERO),
984 _kind: std::marker::PhantomData,
985 }
986 }
987}
988
989impl<N: Coordinate, Kind> SubAssign for Size<N, Kind> {
990 #[inline]
991 fn sub_assign(&mut self, rhs: Self) {
992 self.w = self.w.saturating_sub(rhs.w).max(N::ZERO);
993 self.h = self.h.saturating_sub(rhs.h).max(N::ZERO);
994 }
995}
996
997impl<N: Coordinate + Div<Output = N>, KindLhs, KindRhs> Div<Size<N, KindRhs>> for Size<N, KindLhs> {
998 type Output = Scale<N>;
999
1000 #[inline]
1001 fn div(self, rhs: Size<N, KindRhs>) -> Self::Output {
1002 Scale {
1003 x: self.w / rhs.w,
1004 y: self.h / rhs.h,
1005 }
1006 }
1007}
1008
1009impl<N: Coordinate + Div, Kind> Div<N> for Size<N, Kind> {
1010 type Output = Size<<N as Div>::Output, Kind>;
1011
1012 #[inline]
1013 fn div(self, rhs: N) -> Self::Output {
1014 Size {
1015 w: self.w / rhs,
1016 h: self.h / rhs,
1017 _kind: std::marker::PhantomData,
1018 }
1019 }
1020}
1021
1022impl<N: Coordinate + Mul, Kind> Mul<N> for Size<N, Kind> {
1023 type Output = Size<<N as Mul>::Output, Kind>;
1024
1025 #[inline]
1026 fn mul(self, rhs: N) -> Self::Output {
1027 Size {
1028 w: self.w * rhs,
1029 h: self.h * rhs,
1030 _kind: std::marker::PhantomData,
1031 }
1032 }
1033}
1034
1035impl<N: Clone, Kind> Clone for Size<N, Kind> {
1036 #[inline]
1037 fn clone(&self) -> Self {
1038 Size {
1039 w: self.w.clone(),
1040 h: self.h.clone(),
1041 _kind: std::marker::PhantomData,
1042 }
1043 }
1044}
1045
1046impl<N: Copy, Kind> Copy for Size<N, Kind> {}
1047
1048impl<N: PartialEq, Kind> PartialEq for Size<N, Kind> {
1049 #[inline]
1050 fn eq(&self, other: &Self) -> bool {
1051 self.w == other.w && self.h == other.h
1052 }
1053}
1054
1055impl<N: Eq, Kind> Eq for Size<N, Kind> {}
1056
1057impl<N: Default, Kind> Default for Size<N, Kind> {
1058 #[inline]
1059 fn default() -> Self {
1060 Size {
1061 w: N::default(),
1062 h: N::default(),
1063 _kind: std::marker::PhantomData,
1064 }
1065 }
1066}
1067
1068impl<N: Coordinate, Kind> Add<Size<N, Kind>> for Point<N, Kind> {
1069 type Output = Point<N, Kind>;
1070 #[inline]
1071 fn add(self, other: Size<N, Kind>) -> Point<N, Kind> {
1072 Point {
1073 x: self.x.saturating_add(other.w),
1074 y: self.y.saturating_add(other.h),
1075 _kind: std::marker::PhantomData,
1076 }
1077 }
1078}
1079
1080impl<N: Coordinate, Kind> Sub<Size<N, Kind>> for Point<N, Kind> {
1081 type Output = Point<N, Kind>;
1082 #[inline]
1083 fn sub(self, other: Size<N, Kind>) -> Point<N, Kind> {
1084 Point {
1085 x: self.x.saturating_sub(other.w),
1086 y: self.y.saturating_sub(other.h),
1087 _kind: std::marker::PhantomData,
1088 }
1089 }
1090}
1091
1092/// A rectangle defined by its top-left corner and dimensions
1093///
1094/// Operations on rectangles are saturating.
1095#[repr(C)]
1096pub struct Rectangle<N, Kind> {
1097 /// Location of the top-left corner of the rectangle
1098 pub loc: Point<N, Kind>,
1099 /// Size of the rectangle, as (width, height)
1100 pub size: Size<N, Kind>,
1101}
1102
1103impl<N: Coordinate, Kind> Rectangle<N, Kind> {
1104 /// Convert the underlying numerical type to another
1105 pub fn to_f64(self) -> Rectangle<f64, Kind> {
1106 Rectangle {
1107 loc: self.loc.to_f64(),
1108 size: self.size.to_f64(),
1109 }
1110 }
1111}
1112
1113impl<N: Coordinate, Kind> Rectangle<N, Kind> {
1114 /// Upscale this [`Rectangle`] by the supplied [`Scale`]
1115 pub fn upscale(self, scale: impl Into<Scale<N>>) -> Rectangle<N, Kind> {
1116 let scale = scale.into();
1117 Rectangle {
1118 loc: self.loc.upscale(scale),
1119 size: self.size.upscale(scale),
1120 }
1121 }
1122
1123 /// Downscale this [`Rectangle`] by the supplied [`Scale`]
1124 pub fn downscale(self, scale: impl Into<Scale<N>>) -> Rectangle<N, Kind> {
1125 let scale = scale.into();
1126 Rectangle {
1127 loc: self.loc.downscale(scale),
1128 size: self.size.downscale(scale),
1129 }
1130 }
1131
1132 /// Check if this [`Rectangle`] is empty
1133 ///
1134 /// Returns true if either the width or the height
1135 /// of the [`Size`] is zero
1136 pub fn is_empty(&self) -> bool {
1137 self.size.is_empty()
1138 }
1139}
1140
1141impl<Kind> Rectangle<f64, Kind> {
1142 /// Convert to i32 for integer-space manipulations by rounding float values
1143 #[inline]
1144 pub fn to_i32_round<N: Coordinate>(self) -> Rectangle<N, Kind> {
1145 Rectangle {
1146 loc: self.loc.to_i32_round(),
1147 size: self.size.to_i32_round(),
1148 }
1149 }
1150
1151 /// Convert to i32 by returning the largest integer-space rectangle fitting into the float-based rectangle
1152 #[inline]
1153 pub fn to_i32_down<N: Coordinate>(self) -> Rectangle<N, Kind> {
1154 Rectangle::from_extremities(self.loc.to_i32_ceil(), (self.loc + self.size).to_i32_floor())
1155 }
1156
1157 /// Convert to i32 by returning the smallest integet-space rectangle encapsulating the float-based rectangle
1158 #[inline]
1159 pub fn to_i32_up<N: Coordinate>(self) -> Rectangle<N, Kind> {
1160 Rectangle::from_extremities(self.loc.to_i32_floor(), (self.loc + self.size).to_i32_ceil())
1161 }
1162}
1163
1164impl<N: Coordinate, Kind> Rectangle<N, Kind> {
1165 /// Create a new [`Rectangle`] from the coordinates of its top-left corner and its dimensions
1166 #[deprecated(note = "use new or from_size")]
1167 #[inline]
1168 pub fn from_loc_and_size(loc: impl Into<Point<N, Kind>>, size: impl Into<Size<N, Kind>>) -> Self {
1169 Rectangle {
1170 loc: loc.into(),
1171 size: size.into(),
1172 }
1173 }
1174
1175 /// Create a new [`Rectangle`] from the coordinates of its top-left corner and its dimensions
1176 #[inline]
1177 pub fn new(loc: Point<N, Kind>, size: Size<N, Kind>) -> Self {
1178 Rectangle { loc, size }
1179 }
1180
1181 /// Create a new [`Rectangle`] from its dimensions, with location zero
1182 #[inline]
1183 pub fn from_size(size: Size<N, Kind>) -> Self {
1184 Rectangle {
1185 loc: (N::ZERO, N::ZERO).into(),
1186 size,
1187 }
1188 }
1189
1190 /// Create a new [`Rectangle`] with location and size zero
1191 #[inline]
1192 pub fn zero() -> Self {
1193 Rectangle {
1194 loc: (N::ZERO, N::ZERO).into(),
1195 size: (N::ZERO, N::ZERO).into(),
1196 }
1197 }
1198
1199 /// Create a new [`Rectangle`] from the coordinates of its top-left corner and its bottom-right corner
1200 #[inline]
1201 #[deprecated = "use Rectangle::from_extremities instead"]
1202 #[doc(hidden)]
1203 pub fn from_extemities(
1204 topleft: impl Into<Point<N, Kind>>,
1205 bottomright: impl Into<Point<N, Kind>>,
1206 ) -> Self {
1207 Rectangle::from_extremities(topleft, bottomright)
1208 }
1209
1210 /// Create a new [`Rectangle`] from the coordinates of its top-left corner and its bottom-right corner
1211 #[inline]
1212 pub fn from_extremities(
1213 topleft: impl Into<Point<N, Kind>>,
1214 bottomright: impl Into<Point<N, Kind>>,
1215 ) -> Self {
1216 let topleft = topleft.into();
1217 let bottomright = bottomright.into();
1218 Rectangle {
1219 loc: topleft,
1220 size: (bottomright - topleft).to_size(),
1221 }
1222 }
1223
1224 /// Checks whether given [`Point`] is inside the rectangle
1225 #[inline]
1226 pub fn contains<P: Into<Point<N, Kind>>>(self, point: P) -> bool {
1227 let p: Point<N, Kind> = point.into();
1228 (p.x >= self.loc.x)
1229 && (p.x < self.loc.x.saturating_add(self.size.w))
1230 && (p.y >= self.loc.y)
1231 && (p.y < self.loc.y.saturating_add(self.size.h))
1232 }
1233
1234 /// Checks whether given [`Rectangle`] is inside the rectangle
1235 ///
1236 /// A rectangle is considered inside another rectangle
1237 /// if its location is inside the other rectangle and it does not
1238 /// extend outside the other rectangle.
1239 /// This includes rectangles with the same location and size
1240 #[inline]
1241 pub fn contains_rect<R: Into<Rectangle<N, Kind>>>(self, rect: R) -> bool {
1242 let r: Rectangle<N, Kind> = rect.into();
1243 r.loc.x >= self.loc.x
1244 && r.loc.y >= self.loc.y
1245 && r.loc.x.saturating_add(r.size.w) <= self.loc.x.saturating_add(self.size.w)
1246 && r.loc.y.saturating_add(r.size.h) <= self.loc.y.saturating_add(self.size.h)
1247 }
1248
1249 /// Checks whether a given [`Rectangle`] overlaps with this one
1250 ///
1251 /// Note: This operation is exclusive, touching only rectangles will return `false`.
1252 /// For inclusive overlap test see [`overlaps_or_touches`](Rectangle::overlaps_or_touches)
1253 #[inline]
1254 pub fn overlaps(self, other: impl Into<Rectangle<N, Kind>>) -> bool {
1255 let other = other.into();
1256
1257 self.loc.x < other.loc.x.saturating_add(other.size.w)
1258 && other.loc.x < self.loc.x.saturating_add(self.size.w)
1259 && self.loc.y < other.loc.y.saturating_add(other.size.h)
1260 && other.loc.y < self.loc.y.saturating_add(self.size.h)
1261 }
1262
1263 /// Checks whether a given [`Rectangle`] overlaps with this one or touches it
1264 ///
1265 /// Note: This operation is inclusive, touching only rectangles will return `true`.
1266 /// For exclusive overlap test see [`overlaps`](Rectangle::overlaps)
1267 #[inline]
1268 pub fn overlaps_or_touches(self, other: impl Into<Rectangle<N, Kind>>) -> bool {
1269 let other = other.into();
1270
1271 self.loc.x <= other.loc.x.saturating_add(other.size.w)
1272 && other.loc.x <= self.loc.x.saturating_add(self.size.w)
1273 && self.loc.y <= other.loc.y.saturating_add(other.size.h)
1274 && other.loc.y <= self.loc.y.saturating_add(self.size.h)
1275 }
1276
1277 /// Clamp rectangle to min and max corners resulting in the overlapping area of two rectangles
1278 ///
1279 /// Returns `None` if the two rectangles don't overlap
1280 #[inline]
1281 pub fn intersection(self, other: impl Into<Rectangle<N, Kind>>) -> Option<Self> {
1282 let other = other.into();
1283 if !self.overlaps(other) {
1284 return None;
1285 }
1286 Some(Rectangle::from_extremities(
1287 (self.loc.x.max(other.loc.x), self.loc.y.max(other.loc.y)),
1288 (
1289 (self.loc.x.saturating_add(self.size.w)).min(other.loc.x.saturating_add(other.size.w)),
1290 (self.loc.y.saturating_add(self.size.h)).min(other.loc.y.saturating_add(other.size.h)),
1291 ),
1292 ))
1293 }
1294
1295 /// Compute the bounding box of a given set of points
1296 pub fn bounding_box(points: impl IntoIterator<Item = Point<N, Kind>>) -> Self {
1297 let ret = points.into_iter().fold(None, |acc, point| match acc {
1298 None => Some((point, point)),
1299 Some((min_point, max_point)) => Some((
1300 (point.x.min(min_point.x), point.y.min(min_point.y)).into(),
1301 (point.x.max(max_point.x), point.y.max(max_point.y)).into(),
1302 )),
1303 });
1304
1305 match ret {
1306 None => Rectangle::default(),
1307 Some((min_point, max_point)) => Rectangle::from_extremities(min_point, max_point),
1308 }
1309 }
1310
1311 /// Merge two [`Rectangle`] by producing the smallest rectangle that contains both
1312 #[inline]
1313 pub fn merge(self, other: Self) -> Self {
1314 Self::bounding_box([self.loc, self.loc + self.size, other.loc, other.loc + other.size])
1315 }
1316
1317 /// Subtract another [`Rectangle`] from this [`Rectangle`]
1318 ///
1319 /// If the rectangles to not overlap the original rectangle will
1320 /// be returned.
1321 /// If the other rectangle contains self no rectangle will be returned,
1322 /// otherwise up to 4 rectangles will be returned.
1323 pub fn subtract_rect(self, other: Self) -> Vec<Self> {
1324 self.subtract_rects([other])
1325 }
1326
1327 /// Subtract a set of [`Rectangle`]s from this [`Rectangle`]
1328 pub fn subtract_rects(self, others: impl IntoIterator<Item = Self>) -> Vec<Self> {
1329 let mut remaining = Vec::with_capacity(4);
1330 remaining.push(self);
1331 Self::subtract_rects_many_in_place(remaining, others)
1332 }
1333
1334 /// Subtract a set of [`Rectangle`]s from a set [`Rectangle`]s
1335 pub fn subtract_rects_many(
1336 rects: impl IntoIterator<Item = Self>,
1337 others: impl IntoIterator<Item = Self>,
1338 ) -> Vec<Self> {
1339 let remaining = rects.into_iter().collect::<Vec<_>>();
1340 Self::subtract_rects_many_in_place(remaining, others)
1341 }
1342
1343 /// Subtract a set of [`Rectangle`]s from a set [`Rectangle`]s in-place
1344 pub fn subtract_rects_many_in_place(
1345 mut rects: Vec<Self>,
1346 others: impl IntoIterator<Item = Self>,
1347 ) -> Vec<Self> {
1348 for other in others {
1349 let items = rects.len();
1350 let mut checked = 0usize;
1351 let mut index = 0usize;
1352
1353 // If there is nothing left we can stop,
1354 // we won't be able to subtract any further
1355 if items == 0 {
1356 return rects;
1357 }
1358
1359 while checked != items {
1360 checked += 1;
1361
1362 // If there is no overlap there is nothing to subtract
1363 let Some(intersection) = rects[index].intersection(other) else {
1364 index += 1;
1365 continue;
1366 };
1367
1368 // We now know that we have to subtract the other rect
1369 let item = rects.remove(index);
1370
1371 // If we are completely contained then nothing is left
1372 if other.contains_rect(item) {
1373 continue;
1374 }
1375
1376 let top_rect = Rectangle::new(
1377 item.loc,
1378 (item.size.w, intersection.loc.y.saturating_sub(item.loc.y)).into(),
1379 );
1380 let left_rect: Rectangle<N, Kind> = Rectangle::new(
1381 (item.loc.x, intersection.loc.y).into(),
1382 (intersection.loc.x.saturating_sub(item.loc.x), intersection.size.h).into(),
1383 );
1384 let right_rect: Rectangle<N, Kind> = Rectangle::new(
1385 (
1386 intersection.loc.x.saturating_add(intersection.size.w),
1387 intersection.loc.y,
1388 )
1389 .into(),
1390 (
1391 (item.loc.x.saturating_add(item.size.w))
1392 .saturating_sub(intersection.loc.x.saturating_add(intersection.size.w)),
1393 intersection.size.h,
1394 )
1395 .into(),
1396 );
1397 let bottom_rect: Rectangle<N, Kind> = Rectangle::new(
1398 (item.loc.x, intersection.loc.y.saturating_add(intersection.size.h)).into(),
1399 (
1400 item.size.w,
1401 (item.loc.y.saturating_add(item.size.h))
1402 .saturating_sub(intersection.loc.y.saturating_add(intersection.size.h)),
1403 )
1404 .into(),
1405 );
1406
1407 if !top_rect.is_empty() {
1408 rects.push(top_rect);
1409 }
1410
1411 if !left_rect.is_empty() {
1412 rects.push(left_rect);
1413 }
1414
1415 if !right_rect.is_empty() {
1416 rects.push(right_rect);
1417 }
1418
1419 if !bottom_rect.is_empty() {
1420 rects.push(bottom_rect);
1421 }
1422 }
1423 }
1424
1425 rects
1426 }
1427}
1428
1429impl<N: Coordinate> Rectangle<N, Logical> {
1430 #[inline]
1431 #[cfg(feature = "xwayland")]
1432 pub(crate) fn to_client(self, scale: impl Into<Scale<N>>) -> Rectangle<N, Client> {
1433 let scale = scale.into();
1434 Rectangle {
1435 loc: self.loc.to_client(scale),
1436 size: self.size.to_client(scale),
1437 }
1438 }
1439
1440 #[inline]
1441 #[cfg(feature = "xwayland")]
1442 pub(crate) fn to_client_precise_round<S: Coordinate, R: Coordinate>(
1443 self,
1444 scale: impl Into<Scale<S>>,
1445 ) -> Rectangle<R, Client> {
1446 self.to_f64().to_client(scale.into().to_f64()).to_i32_round()
1447 }
1448
1449 /// Convert this logical rectangle to physical coordinate space according to given scale factor
1450 #[inline]
1451 pub fn to_physical(self, scale: impl Into<Scale<N>>) -> Rectangle<N, Physical> {
1452 let scale = scale.into();
1453 Rectangle {
1454 loc: self.loc.to_physical(scale),
1455 size: self.size.to_physical(scale),
1456 }
1457 }
1458
1459 /// Convert this logical rectangle to physical coordinate space according to given scale factor
1460 /// and round the result
1461 #[inline]
1462 pub fn to_physical_precise_round<S: Coordinate, R: Coordinate>(
1463 self,
1464 scale: impl Into<Scale<S>>,
1465 ) -> Rectangle<R, Physical> {
1466 self.to_f64().to_physical(scale.into().to_f64()).to_i32_round()
1467 }
1468
1469 /// Convert this logical rectangle to physical coordinate space according to given scale factor,
1470 /// returning the largest N-space rectangle fitting into the N-based rectangle
1471 ///
1472 /// This will ceil the location and floor the size after applying the scale
1473 #[inline]
1474 pub fn to_physical_precise_down<S: Coordinate, R: Coordinate>(
1475 &self,
1476 scale: impl Into<Scale<S>>,
1477 ) -> Rectangle<R, Physical> {
1478 self.to_f64().to_physical(scale.into().to_f64()).to_i32_down()
1479 }
1480
1481 /// Convert this logical rectangle to physical coordinate space according to given scale factor,
1482 /// returning the smallest N-space rectangle encapsulating the N-based rectangle
1483 ///
1484 /// This will floor the location and ceil the size after applying the scale
1485 #[inline]
1486 pub fn to_physical_precise_up<S: Coordinate, R: Coordinate>(
1487 &self,
1488 scale: impl Into<Scale<S>>,
1489 ) -> Rectangle<R, Physical> {
1490 self.to_f64().to_physical(scale.into().to_f64()).to_i32_up()
1491 }
1492
1493 /// Convert this logical rectangle to buffer coordinate space according to given scale factor
1494 #[inline]
1495 pub fn to_buffer(
1496 self,
1497 scale: impl Into<Scale<N>>,
1498 transformation: Transform,
1499 area: &Size<N, Logical>,
1500 ) -> Rectangle<N, Buffer> {
1501 let rect = transformation.transform_rect_in(self, area);
1502 let scale = scale.into();
1503 Rectangle {
1504 loc: Point {
1505 x: rect.loc.x.upscale(scale.x),
1506 y: rect.loc.y.upscale(scale.y),
1507 _kind: std::marker::PhantomData,
1508 },
1509 size: Size {
1510 w: rect.size.w.upscale(scale.x),
1511 h: rect.size.h.upscale(scale.y),
1512 _kind: std::marker::PhantomData,
1513 },
1514 }
1515 }
1516}
1517
1518#[cfg(feature = "wayland_frontend")]
1519impl<N: Coordinate> Rectangle<N, Client> {
1520 #[inline]
1521 pub(crate) fn to_logical(self, scale: impl Into<Scale<N>>) -> Rectangle<N, Logical> {
1522 let scale = scale.into();
1523 Rectangle {
1524 loc: self.loc.to_logical(scale),
1525 size: self.size.to_logical(scale),
1526 }
1527 }
1528}
1529
1530impl<N: Coordinate> Rectangle<N, Physical> {
1531 /// Convert this physical rectangle to logical coordinate space according to given scale factor
1532 #[inline]
1533 pub fn to_logical(self, scale: impl Into<Scale<N>>) -> Rectangle<N, Logical> {
1534 let scale = scale.into();
1535 Rectangle {
1536 loc: self.loc.to_logical(scale),
1537 size: self.size.to_logical(scale),
1538 }
1539 }
1540}
1541
1542impl<N: Coordinate> Rectangle<N, Buffer> {
1543 /// Convert this physical rectangle to logical coordinate space according to given scale factor
1544 #[inline]
1545 pub fn to_logical(
1546 self,
1547 scale: impl Into<Scale<N>>,
1548 transformation: Transform,
1549 area: &Size<N, Buffer>,
1550 ) -> Rectangle<N, Logical> {
1551 let rect = transformation.invert().transform_rect_in(self, area);
1552 let scale = scale.into();
1553 Rectangle {
1554 loc: Point {
1555 x: rect.loc.x.downscale(scale.x),
1556 y: rect.loc.y.downscale(scale.y),
1557 _kind: std::marker::PhantomData,
1558 },
1559 size: Size {
1560 w: rect.size.w.downscale(scale.x),
1561 h: rect.size.h.downscale(scale.y),
1562 _kind: std::marker::PhantomData,
1563 },
1564 }
1565 }
1566}
1567
1568impl<N: fmt::Debug, S> fmt::Debug for Rectangle<N, S> {
1569 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1570 f.write_fmt(format_args!("Rectangle<{}>", std::any::type_name::<S>()))?;
1571 f.debug_struct("")
1572 .field("x", &self.loc.x)
1573 .field("y", &self.loc.y)
1574 .field("width", &self.size.w)
1575 .field("height", &self.size.h)
1576 .finish()
1577 }
1578}
1579
1580impl<N: Clone, Kind> Clone for Rectangle<N, Kind> {
1581 #[inline]
1582 fn clone(&self) -> Self {
1583 Rectangle {
1584 loc: self.loc.clone(),
1585 size: self.size.clone(),
1586 }
1587 }
1588}
1589
1590impl<N: Copy, Kind> Copy for Rectangle<N, Kind> {}
1591
1592impl<N: PartialEq, Kind> PartialEq for Rectangle<N, Kind> {
1593 #[inline]
1594 fn eq(&self, other: &Self) -> bool {
1595 self.loc == other.loc && self.size == other.size
1596 }
1597}
1598
1599impl<N: Eq, Kind> Eq for Rectangle<N, Kind> {}
1600
1601impl<N: Default, Kind> Default for Rectangle<N, Kind> {
1602 #[inline]
1603 fn default() -> Self {
1604 Rectangle {
1605 loc: Default::default(),
1606 size: Default::default(),
1607 }
1608 }
1609}
1610
1611#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
1612/// Possible transformations to two-dimensional planes
1613#[derive(Default)]
1614pub enum Transform {
1615 /// Identity transformation (plane is unaltered when applied)
1616 #[default]
1617 Normal,
1618 /// Plane is rotated by 90 degrees
1619 _90,
1620 /// Plane is rotated by 180 degrees
1621 _180,
1622 /// Plane is rotated by 270 degrees
1623 _270,
1624 /// Plane is flipped vertically
1625 Flipped,
1626 /// Plane is flipped vertically and rotated by 90 degrees
1627 Flipped90,
1628 /// Plane is flipped vertically and rotated by 180 degrees
1629 Flipped180,
1630 /// Plane is flipped vertically and rotated by 270 degrees
1631 Flipped270,
1632}
1633
1634impl Transform {
1635 /// Inverts any 90-degree transformation into 270-degree transformations and vise versa.
1636 ///
1637 /// Flipping is preserved and 180/Normal transformation are uneffected.
1638 #[inline]
1639 pub fn invert(&self) -> Transform {
1640 match self {
1641 Transform::Normal => Transform::Normal,
1642 Transform::Flipped => Transform::Flipped,
1643 Transform::_90 => Transform::_270,
1644 Transform::_180 => Transform::_180,
1645 Transform::_270 => Transform::_90,
1646 Transform::Flipped90 => Transform::Flipped270,
1647 Transform::Flipped180 => Transform::Flipped180,
1648 Transform::Flipped270 => Transform::Flipped90,
1649 }
1650 }
1651
1652 /// Transforms a point inside an area of a given size by applying this transformation.
1653 pub fn transform_point_in<N: Coordinate, Kind>(
1654 &self,
1655 point: Point<N, Kind>,
1656 area: &Size<N, Kind>,
1657 ) -> Point<N, Kind> {
1658 match *self {
1659 Transform::Normal => point,
1660 Transform::_90 => (area.h - point.y, point.x).into(),
1661 Transform::_180 => (area.w - point.x, area.h - point.y).into(),
1662 Transform::_270 => (point.y, area.w - point.x).into(),
1663 Transform::Flipped => (area.w - point.x, point.y).into(),
1664 Transform::Flipped90 => (point.y, point.x).into(),
1665 Transform::Flipped180 => (point.x, area.h - point.y).into(),
1666 Transform::Flipped270 => (area.h - point.y, area.w - point.x).into(),
1667 }
1668 }
1669
1670 /// Transformed size after applying this transformation.
1671 pub fn transform_size<N: Coordinate, Kind>(&self, size: Size<N, Kind>) -> Size<N, Kind> {
1672 if *self == Transform::_90
1673 || *self == Transform::_270
1674 || *self == Transform::Flipped90
1675 || *self == Transform::Flipped270
1676 {
1677 (size.h, size.w).into()
1678 } else {
1679 size
1680 }
1681 }
1682
1683 /// Transforms a rectangle inside an area of a given size by applying this transformation.
1684 pub fn transform_rect_in<N: Coordinate, Kind>(
1685 &self,
1686 rect: Rectangle<N, Kind>,
1687 area: &Size<N, Kind>,
1688 ) -> Rectangle<N, Kind> {
1689 let size = self.transform_size(rect.size);
1690
1691 let loc = match *self {
1692 Transform::Normal => rect.loc,
1693 Transform::_90 => (area.h - rect.loc.y - rect.size.h, rect.loc.x).into(),
1694 Transform::_180 => (
1695 area.w - rect.loc.x - rect.size.w,
1696 area.h - rect.loc.y - rect.size.h,
1697 )
1698 .into(),
1699 Transform::_270 => (rect.loc.y, area.w - rect.loc.x - rect.size.w).into(),
1700 Transform::Flipped => (area.w - rect.loc.x - rect.size.w, rect.loc.y).into(),
1701 Transform::Flipped90 => (
1702 area.h - rect.loc.y - rect.size.h,
1703 area.w - rect.loc.x - rect.size.w,
1704 )
1705 .into(),
1706 Transform::Flipped180 => (rect.loc.x, area.h - rect.loc.y - rect.size.h).into(),
1707 Transform::Flipped270 => (rect.loc.y, rect.loc.x).into(),
1708 };
1709
1710 Rectangle::new(loc, size)
1711 }
1712
1713 /// Returns true if the transformation would flip contents
1714 pub fn flipped(&self) -> bool {
1715 !matches!(
1716 self,
1717 Transform::Normal | Transform::_90 | Transform::_180 | Transform::_270
1718 )
1719 }
1720
1721 /// Returns the angle (in degrees) of the transformation
1722 #[inline]
1723 pub fn degrees(&self) -> u32 {
1724 match self {
1725 Transform::Normal | Transform::Flipped => 0,
1726 Transform::_90 | Transform::Flipped90 => 90,
1727 Transform::_180 | Transform::Flipped180 => 180,
1728 Transform::_270 | Transform::Flipped270 => 270,
1729 }
1730 }
1731}
1732
1733impl std::ops::Add for Transform {
1734 type Output = Self;
1735
1736 #[inline]
1737 fn add(self, other: Self) -> Self {
1738 let flipped = matches!((self.flipped(), other.flipped()), (true, false) | (false, true));
1739 let degrees = (self.degrees() + other.degrees()) % 360;
1740 match (flipped, degrees) {
1741 (false, 0) => Transform::Normal,
1742 (false, 90) => Transform::_90,
1743 (false, 180) => Transform::_180,
1744 (false, 270) => Transform::_270,
1745 (true, 0) => Transform::Flipped,
1746 (true, 90) => Transform::Flipped90,
1747 (true, 180) => Transform::Flipped180,
1748 (true, 270) => Transform::Flipped270,
1749 _ => unreachable!(),
1750 }
1751 }
1752}
1753
1754#[cfg(feature = "wayland_frontend")]
1755impl From<Transform> for WlTransform {
1756 #[inline]
1757 fn from(transform: Transform) -> Self {
1758 match transform {
1759 Transform::Normal => WlTransform::Normal,
1760 Transform::_90 => WlTransform::_90,
1761 Transform::_180 => WlTransform::_180,
1762 Transform::_270 => WlTransform::_270,
1763 Transform::Flipped => WlTransform::Flipped,
1764 Transform::Flipped90 => WlTransform::Flipped90,
1765 Transform::Flipped180 => WlTransform::Flipped180,
1766 Transform::Flipped270 => WlTransform::Flipped270,
1767 }
1768 }
1769}
1770
1771#[cfg(test)]
1772mod tests {
1773 use super::{Logical, Rectangle, Size, Transform};
1774
1775 #[test]
1776 fn transform_rect_ident() {
1777 let rect = Rectangle::<i32, Logical>::new((10, 20).into(), (30, 40).into());
1778 let size = Size::from((70, 90));
1779 let transform = Transform::Normal;
1780
1781 assert_eq!(rect, transform.transform_rect_in(rect, &size))
1782 }
1783
1784 #[test]
1785 fn transform_rect_90() {
1786 let rect = Rectangle::<i32, Logical>::new((10, 20).into(), (30, 40).into());
1787 let size = Size::from((70, 90));
1788 let transform = Transform::_90;
1789
1790 assert_eq!(
1791 Rectangle::new((30, 10).into(), (40, 30).into()),
1792 transform.transform_rect_in(rect, &size)
1793 )
1794 }
1795
1796 #[test]
1797 fn transform_rect_180() {
1798 let rect = Rectangle::<i32, Logical>::new((10, 20).into(), (30, 40).into());
1799 let size = Size::from((70, 90));
1800 let transform = Transform::_180;
1801
1802 assert_eq!(
1803 Rectangle::new((30, 30).into(), (30, 40).into()),
1804 transform.transform_rect_in(rect, &size)
1805 )
1806 }
1807
1808 #[test]
1809 fn transform_rect_270() {
1810 let rect = Rectangle::<i32, Logical>::new((10, 20).into(), (30, 40).into());
1811 let size = Size::from((70, 90));
1812 let transform = Transform::_270;
1813
1814 assert_eq!(
1815 Rectangle::new((20, 30).into(), (40, 30).into()),
1816 transform.transform_rect_in(rect, &size)
1817 )
1818 }
1819
1820 #[test]
1821 fn transform_rect_f() {
1822 let rect = Rectangle::<i32, Logical>::new((10, 20).into(), (30, 40).into());
1823 let size = Size::from((70, 90));
1824 let transform = Transform::Flipped;
1825
1826 assert_eq!(
1827 Rectangle::new((30, 20).into(), (30, 40).into()),
1828 transform.transform_rect_in(rect, &size)
1829 )
1830 }
1831
1832 #[test]
1833 fn transform_rect_f90() {
1834 let rect = Rectangle::<i32, Logical>::new((10, 20).into(), (30, 40).into());
1835 let size = Size::from((70, 80));
1836 let transform = Transform::Flipped90;
1837
1838 assert_eq!(
1839 Rectangle::new((20, 30).into(), (40, 30).into()),
1840 transform.transform_rect_in(rect, &size)
1841 )
1842 }
1843
1844 #[test]
1845 fn transform_rect_f180() {
1846 let rect = Rectangle::<i32, Logical>::new((10, 20).into(), (30, 40).into());
1847 let size = Size::from((70, 90));
1848 let transform = Transform::Flipped180;
1849
1850 assert_eq!(
1851 Rectangle::new((10, 30).into(), (30, 40).into()),
1852 transform.transform_rect_in(rect, &size)
1853 )
1854 }
1855
1856 #[test]
1857 fn transform_rect_f270() {
1858 let rect = Rectangle::<i32, Logical>::new((10, 20).into(), (30, 40).into());
1859 let size = Size::from((70, 90));
1860 let transform = Transform::Flipped270;
1861
1862 assert_eq!(
1863 Rectangle::new((20, 10).into(), (40, 30).into()),
1864 transform.transform_rect_in(rect, &size)
1865 )
1866 }
1867
1868 #[test]
1869 fn rectangle_contains_rect_itself() {
1870 let rect = Rectangle::<i32, Logical>::new((10, 20).into(), (30, 40).into());
1871 assert!(rect.contains_rect(rect));
1872 }
1873
1874 #[test]
1875 fn rectangle_contains_rect_outside() {
1876 let first = Rectangle::<i32, Logical>::new((10, 20).into(), (30, 40).into());
1877 let second = Rectangle::<i32, Logical>::new((41, 61).into(), (30, 40).into());
1878 assert!(!first.contains_rect(second));
1879 }
1880
1881 #[test]
1882 fn rectangle_contains_rect_extends() {
1883 let first = Rectangle::<i32, Logical>::new((10, 20).into(), (30, 40).into());
1884 let second = Rectangle::<i32, Logical>::new((10, 20).into(), (30, 45).into());
1885 assert!(!first.contains_rect(second));
1886 }
1887
1888 #[test]
1889 fn rectangle_subtract_full() {
1890 let outer = Rectangle::<i32, Logical>::from_size((100, 100).into());
1891 let inner = Rectangle::<i32, Logical>::new((-10, -10).into(), (1000, 1000).into());
1892
1893 let rects = outer.subtract_rect(inner);
1894 assert_eq!(rects, vec![])
1895 }
1896
1897 #[test]
1898 fn rectangle_subtract_center_hole() {
1899 let outer = Rectangle::<i32, Logical>::from_size((100, 100).into());
1900 let inner = Rectangle::<i32, Logical>::new((10, 10).into(), (80, 80).into());
1901
1902 let rects = outer.subtract_rect(inner);
1903 assert_eq!(
1904 rects,
1905 vec![
1906 // Top rect
1907 Rectangle::<i32, Logical>::from_size((100, 10).into()),
1908 // Left rect
1909 Rectangle::<i32, Logical>::new((0, 10).into(), (10, 80).into()),
1910 // Right rect
1911 Rectangle::<i32, Logical>::new((90, 10).into(), (10, 80).into()),
1912 // Bottom rect
1913 Rectangle::<i32, Logical>::new((0, 90).into(), (100, 10).into()),
1914 ]
1915 )
1916 }
1917
1918 #[test]
1919 fn rectangle_subtract_full_top() {
1920 let outer = Rectangle::<i32, Logical>::from_size((100, 100).into());
1921 let inner = Rectangle::<i32, Logical>::new((0, -20).into(), (100, 100).into());
1922
1923 let rects = outer.subtract_rect(inner);
1924 assert_eq!(
1925 rects,
1926 vec![
1927 // Bottom rect
1928 Rectangle::<i32, Logical>::new((0, 80).into(), (100, 20).into()),
1929 ]
1930 )
1931 }
1932
1933 #[test]
1934 fn rectangle_subtract_full_bottom() {
1935 let outer = Rectangle::<i32, Logical>::from_size((100, 100).into());
1936 let inner = Rectangle::<i32, Logical>::new((0, 20).into(), (100, 100).into());
1937
1938 let rects = outer.subtract_rect(inner);
1939 assert_eq!(
1940 rects,
1941 vec![
1942 // Top rect
1943 Rectangle::<i32, Logical>::from_size((100, 20).into()),
1944 ]
1945 )
1946 }
1947
1948 #[test]
1949 fn rectangle_subtract_full_left() {
1950 let outer = Rectangle::<i32, Logical>::from_size((100, 100).into());
1951 let inner = Rectangle::<i32, Logical>::new((-20, 0).into(), (100, 100).into());
1952
1953 let rects = outer.subtract_rect(inner);
1954 assert_eq!(
1955 rects,
1956 vec![
1957 // Right rect
1958 Rectangle::<i32, Logical>::new((80, 0).into(), (20, 100).into()),
1959 ]
1960 )
1961 }
1962
1963 #[test]
1964 fn rectangle_subtract_full_right() {
1965 let outer = Rectangle::<i32, Logical>::from_size((100, 100).into());
1966 let inner = Rectangle::<i32, Logical>::new((20, 0).into(), (100, 100).into());
1967
1968 let rects = outer.subtract_rect(inner);
1969 assert_eq!(
1970 rects,
1971 vec![
1972 // Left rect
1973 Rectangle::<i32, Logical>::from_size((20, 100).into()),
1974 ]
1975 )
1976 }
1977
1978 #[test]
1979 fn rectangle_overlaps_or_touches_top() {
1980 let top = Rectangle::<i32, Logical>::new((0, -24).into(), (800, 24).into());
1981 let main = Rectangle::<i32, Logical>::from_size((800, 600).into());
1982 assert!(main.overlaps_or_touches(top));
1983 }
1984
1985 #[test]
1986 fn rectangle_overlaps_or_touches_left() {
1987 let left = Rectangle::<i32, Logical>::new((-4, -24).into(), (4, 624).into());
1988 let main = Rectangle::<i32, Logical>::from_size((800, 600).into());
1989 assert!(main.overlaps_or_touches(left));
1990 }
1991
1992 #[test]
1993 fn rectangle_overlaps_or_touches_right() {
1994 let right = Rectangle::<i32, Logical>::new((800, -24).into(), (4, 624).into());
1995 let main = Rectangle::<i32, Logical>::from_size((800, 600).into());
1996 assert!(main.overlaps_or_touches(right));
1997 }
1998
1999 #[test]
2000 fn rectangle_no_overlap_top() {
2001 let top = Rectangle::<i32, Logical>::new((0, -24).into(), (800, 24).into());
2002 let main = Rectangle::<i32, Logical>::from_size((800, 600).into());
2003 assert!(!main.overlaps(top));
2004 }
2005
2006 #[test]
2007 fn rectangle_no_overlap_left() {
2008 let left = Rectangle::<i32, Logical>::new((-4, -24).into(), (4, 624).into());
2009 let main = Rectangle::<i32, Logical>::from_size((800, 600).into());
2010 assert!(!main.overlaps(left));
2011 }
2012
2013 #[test]
2014 fn rectangle_no_overlap_right() {
2015 let right = Rectangle::<i32, Logical>::new((800, -24).into(), (4, 624).into());
2016 let main = Rectangle::<i32, Logical>::from_size((800, 600).into());
2017 assert!(!main.overlaps(right));
2018 }
2019
2020 #[test]
2021 fn size_sub_saturate_zero() {
2022 let bigger = Size::<_, Logical>::from((10, 10));
2023 let mut smaller = Size::<_, Logical>::from((5, 5));
2024
2025 assert_eq!(smaller - bigger, Size::from((0, 0)));
2026 smaller -= bigger;
2027 assert_eq!(smaller, Size::from((0, 0)));
2028 }
2029}