| 1 | // Copyright 2020 Yevhenii Reizner |
| 2 | // |
| 3 | // Use of this source code is governed by a BSD-style license that can be |
| 4 | // found in the LICENSE file. |
| 5 | |
| 6 | use strict_num::NonZeroPositiveF32; |
| 7 | |
| 8 | use crate::{IntRect, LengthU32, NonZeroRect, Rect}; |
| 9 | |
| 10 | #[cfg (all(not(feature = "std" ), feature = "no-std-float" ))] |
| 11 | use crate::NoStdFloat; |
| 12 | |
| 13 | /// An integer size. |
| 14 | /// |
| 15 | /// # Guarantees |
| 16 | /// |
| 17 | /// - Width and height are positive and non-zero. |
| 18 | #[derive (Copy, Clone, PartialEq, Debug)] |
| 19 | pub struct IntSize { |
| 20 | width: LengthU32, |
| 21 | height: LengthU32, |
| 22 | } |
| 23 | |
| 24 | impl IntSize { |
| 25 | /// Creates a new `IntSize` from width and height. |
| 26 | pub fn from_wh(width: u32, height: u32) -> Option<Self> { |
| 27 | Some(IntSize { |
| 28 | width: LengthU32::new(width)?, |
| 29 | height: LengthU32::new(height)?, |
| 30 | }) |
| 31 | } |
| 32 | |
| 33 | pub(crate) fn from_wh_safe(width: LengthU32, height: LengthU32) -> Self { |
| 34 | IntSize { width, height } |
| 35 | } |
| 36 | |
| 37 | /// Returns width. |
| 38 | pub fn width(&self) -> u32 { |
| 39 | self.width.get() |
| 40 | } |
| 41 | |
| 42 | /// Returns height. |
| 43 | pub fn height(&self) -> u32 { |
| 44 | self.height.get() |
| 45 | } |
| 46 | |
| 47 | /// Returns width and height as a tuple. |
| 48 | pub fn dimensions(&self) -> (u32, u32) { |
| 49 | (self.width(), self.height()) |
| 50 | } |
| 51 | |
| 52 | /// Scales current size by the specified factor. |
| 53 | #[inline ] |
| 54 | pub fn scale_by(&self, factor: f32) -> Option<Self> { |
| 55 | Self::from_wh( |
| 56 | (self.width() as f32 * factor).round() as u32, |
| 57 | (self.height() as f32 * factor).round() as u32, |
| 58 | ) |
| 59 | } |
| 60 | |
| 61 | /// Scales current size to the specified size. |
| 62 | #[inline ] |
| 63 | pub fn scale_to(&self, to: Self) -> Self { |
| 64 | size_scale(*self, to, false) |
| 65 | } |
| 66 | |
| 67 | /// Scales current size to the specified width. |
| 68 | #[inline ] |
| 69 | pub fn scale_to_width(&self, new_width: u32) -> Option<Self> { |
| 70 | let new_height = (new_width as f32 * self.height() as f32 / self.width() as f32).ceil(); |
| 71 | Self::from_wh(new_width, new_height as u32) |
| 72 | } |
| 73 | |
| 74 | /// Scales current size to the specified height. |
| 75 | #[inline ] |
| 76 | pub fn scale_to_height(&self, new_height: u32) -> Option<Self> { |
| 77 | let new_width = (new_height as f32 * self.width() as f32 / self.height() as f32).ceil(); |
| 78 | Self::from_wh(new_width as u32, new_height) |
| 79 | } |
| 80 | |
| 81 | /// Converts into [`Size`]. |
| 82 | pub fn to_size(&self) -> Size { |
| 83 | Size::from_wh(self.width() as f32, self.height() as f32).unwrap() |
| 84 | } |
| 85 | |
| 86 | /// Converts into [`IntRect`] at the provided position. |
| 87 | pub fn to_int_rect(&self, x: i32, y: i32) -> IntRect { |
| 88 | IntRect::from_xywh(x, y, self.width(), self.height()).unwrap() |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | fn size_scale(s1: IntSize, s2: IntSize, expand: bool) -> IntSize { |
| 93 | let rw: u32 = (s2.height() as f32 * s1.width() as f32 / s1.height() as f32).ceil() as u32; |
| 94 | let with_h: bool = if expand { |
| 95 | rw <= s2.width() |
| 96 | } else { |
| 97 | rw >= s2.width() |
| 98 | }; |
| 99 | |
| 100 | if !with_h { |
| 101 | IntSize::from_wh(width:rw, s2.height()).unwrap() |
| 102 | } else { |
| 103 | let h: u32 = (s2.width() as f32 * s1.height() as f32 / s1.width() as f32).ceil() as u32; |
| 104 | IntSize::from_wh(s2.width(), height:h).unwrap() |
| 105 | } |
| 106 | } |
| 107 | |
| 108 | #[cfg (test)] |
| 109 | mod tests { |
| 110 | use super::*; |
| 111 | |
| 112 | #[test ] |
| 113 | fn int_size_tests() { |
| 114 | assert_eq!(IntSize::from_wh(0, 0), None); |
| 115 | assert_eq!(IntSize::from_wh(1, 0), None); |
| 116 | assert_eq!(IntSize::from_wh(0, 1), None); |
| 117 | |
| 118 | let size = IntSize::from_wh(3, 4).unwrap(); |
| 119 | assert_eq!( |
| 120 | size.to_int_rect(1, 2), |
| 121 | IntRect::from_xywh(1, 2, 3, 4).unwrap() |
| 122 | ); |
| 123 | } |
| 124 | } |
| 125 | |
| 126 | /// A size. |
| 127 | /// |
| 128 | /// # Guarantees |
| 129 | /// |
| 130 | /// - Width and height are positive, non-zero and finite. |
| 131 | #[derive (Copy, Clone, PartialEq, Debug)] |
| 132 | pub struct Size { |
| 133 | width: NonZeroPositiveF32, |
| 134 | height: NonZeroPositiveF32, |
| 135 | } |
| 136 | |
| 137 | impl Size { |
| 138 | /// Creates a new `Size` from width and height. |
| 139 | pub fn from_wh(width: f32, height: f32) -> Option<Self> { |
| 140 | Some(Size { |
| 141 | width: NonZeroPositiveF32::new(width)?, |
| 142 | height: NonZeroPositiveF32::new(height)?, |
| 143 | }) |
| 144 | } |
| 145 | |
| 146 | /// Returns width. |
| 147 | pub fn width(&self) -> f32 { |
| 148 | self.width.get() |
| 149 | } |
| 150 | |
| 151 | /// Returns height. |
| 152 | pub fn height(&self) -> f32 { |
| 153 | self.height.get() |
| 154 | } |
| 155 | |
| 156 | /// Scales current size to specified size. |
| 157 | pub fn scale_to(&self, to: Self) -> Self { |
| 158 | size_scale_f64(*self, to, false) |
| 159 | } |
| 160 | |
| 161 | /// Expands current size to specified size. |
| 162 | pub fn expand_to(&self, to: Self) -> Self { |
| 163 | size_scale_f64(*self, to, true) |
| 164 | } |
| 165 | |
| 166 | /// Converts into [`IntSize`]. |
| 167 | pub fn to_int_size(&self) -> IntSize { |
| 168 | IntSize::from_wh( |
| 169 | core::cmp::max(1, self.width().round() as u32), |
| 170 | core::cmp::max(1, self.height().round() as u32), |
| 171 | ) |
| 172 | .unwrap() |
| 173 | } |
| 174 | |
| 175 | /// Converts the current size to `Rect` at provided position. |
| 176 | pub fn to_rect(&self, x: f32, y: f32) -> Option<Rect> { |
| 177 | Rect::from_xywh(x, y, self.width.get(), self.height.get()) |
| 178 | } |
| 179 | |
| 180 | /// Converts the current size to `NonZeroRect` at provided position. |
| 181 | pub fn to_non_zero_rect(&self, x: f32, y: f32) -> NonZeroRect { |
| 182 | NonZeroRect::from_xywh(x, y, self.width.get(), self.height.get()).unwrap() |
| 183 | } |
| 184 | } |
| 185 | |
| 186 | fn size_scale_f64(s1: Size, s2: Size, expand: bool) -> Size { |
| 187 | let rw: f32 = s2.height.get() * s1.width.get() / s1.height.get(); |
| 188 | let with_h: bool = if expand { |
| 189 | rw <= s2.width.get() |
| 190 | } else { |
| 191 | rw >= s2.width.get() |
| 192 | }; |
| 193 | if !with_h { |
| 194 | Size::from_wh(width:rw, height:s2.height.get()).unwrap() |
| 195 | } else { |
| 196 | let h: f32 = s2.width.get() * s1.height.get() / s1.width.get(); |
| 197 | Size::from_wh(width:s2.width.get(), height:h).unwrap() |
| 198 | } |
| 199 | } |
| 200 | |