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
6use strict_num::NonZeroPositiveF32;
7
8use crate::{IntRect, LengthU32, NonZeroRect, Rect};
9
10#[cfg(all(not(feature = "std"), feature = "no-std-float"))]
11use 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)]
19pub struct IntSize {
20 width: LengthU32,
21 height: LengthU32,
22}
23
24impl 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
92fn 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)]
109mod 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)]
132pub struct Size {
133 width: NonZeroPositiveF32,
134 height: NonZeroPositiveF32,
135}
136
137impl 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
186fn 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