use core::{mem, ops};
use serde::{Deserialize, Serialize};
use super::aspect_ratio_mode::AspectRatioMode;
use super::margins::{Margins, MarginsF};
use crate::util::{fuzzy_compare, fuzzy_is_zero};
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct Size {
width: i32,
height: i32,
}
impl Size {
#[must_use]
pub const fn new() -> Self {
Self {
width: 0,
height: 0,
}
}
#[must_use]
pub const fn from(width: i32, height: i32) -> Self {
Self { width, height }
}
#[must_use]
pub const fn shrunk_by(&self, margins: &Margins) -> Self {
Self {
width: self.width - margins.left() - margins.right(),
height: self.height - margins.top() - margins.bottom(),
}
}
#[must_use]
pub const fn grown_by(&self, margins: &Margins) -> Self {
Self {
width: self.width + margins.left() + margins.right(),
height: self.height + margins.top() + margins.bottom(),
}
}
#[must_use]
pub fn bounded_to(&self, other: &Self) -> Self {
Self {
width: self.width.min(other.width),
height: self.height.min(other.height),
}
}
#[must_use]
pub fn expanded_to(&self, other: &Self) -> Self {
Self {
width: self.width.max(other.width),
height: self.height.max(other.height),
}
}
#[must_use]
pub const fn height(&self) -> i32 {
self.height
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.width == 0 || self.height == 0
}
#[must_use]
pub const fn is_null(&self) -> bool {
self.width == 0 && self.height == 0
}
#[must_use]
pub const fn is_valid(&self) -> bool {
self.width >= 0 || self.height >= 0
}
pub fn height_mut(&mut self) -> &mut i32 {
&mut self.height
}
pub fn width_mut(&mut self) -> &mut i32 {
&mut self.width
}
pub fn scale(&mut self, width: i32, height: i32, mode: AspectRatioMode) {
self.scale_by(Self::from(width, height), mode);
}
pub fn scale_by(&mut self, size: Self, mode: AspectRatioMode) {
let new_size = self.scaled_by(size, mode);
*self = new_size;
}
#[must_use]
pub fn scaled(&self, width: i32, height: i32, mode: AspectRatioMode) -> Self {
self.scaled_by(Self::from(width, height), mode)
}
#[must_use]
pub fn scaled_by(&self, size: Self, mode: AspectRatioMode) -> Self {
if mode == AspectRatioMode::IgnoreAspectRatio || self.width == 0 || self.height == 0 {
return size;
}
let rw = (size.height * self.width / self.height) as i32;
let use_height = if mode == AspectRatioMode::KeepAspectRatio {
rw <= size.width
} else {
debug_assert!(mode == AspectRatioMode::KeepAspectRatioByExpanding);
rw >= size.width
};
if use_height {
Self::from(rw, size.height)
} else {
#[allow(clippy::cast_possible_truncation)]
let height =
(i64::from(size.width) * i64::from(self.height) / i64::from(self.width)) as i32;
Self::from(size.width, height)
}
}
pub fn set_height(&mut self, height: i32) {
self.height = height;
}
pub fn set_width(&mut self, width: i32) {
self.width = width;
}
pub fn transpose(&mut self) {
mem::swap(&mut self.width, &mut self.height);
}
#[must_use]
pub const fn transposed(&self) -> Self {
Self {
width: self.height,
height: self.width,
}
}
#[must_use]
pub const fn width(&self) -> i32 {
self.width
}
}
impl ops::AddAssign<Self> for Size {
fn add_assign(&mut self, other: Self) {
self.width += other.width;
self.height += other.height;
}
}
impl ops::AddAssign<&Self> for Size {
fn add_assign(&mut self, other: &Self) {
self.width += other.width;
self.height += other.height;
}
}
impl ops::Add<Self> for Size {
type Output = Self;
fn add(self, other: Self) -> Self {
Self {
width: self.width + other.width,
height: self.height + other.height,
}
}
}
impl ops::Add<&Size> for &Size {
type Output = Size;
fn add(self, other: &Size) -> Size {
Size {
width: self.width + other.width,
height: self.height + other.height,
}
}
}
impl ops::Mul<f64> for &Size {
type Output = Size;
fn mul(self, factor: f64) -> Self::Output {
#[allow(clippy::cast_possible_truncation)]
Self::Output {
width: (f64::from(self.width) * factor).round() as i32,
height: (f64::from(self.height) * factor).round() as i32,
}
}
}
impl ops::MulAssign<f64> for Size {
#[allow(clippy::cast_possible_truncation)]
fn mul_assign(&mut self, factor: f64) {
self.width = (f64::from(self.width) * factor).round() as i32;
self.height = (f64::from(self.height) * factor).round() as i32;
}
}
impl ops::Div<f64> for &Size {
type Output = Size;
fn div(self, divisor: f64) -> Self::Output {
assert!(divisor != 0.0);
#[allow(clippy::cast_possible_truncation)]
Self::Output {
width: (f64::from(self.width) / divisor).round() as i32,
height: (f64::from(self.height) / divisor).round() as i32,
}
}
}
impl ops::DivAssign<f64> for Size {
#[allow(clippy::cast_possible_truncation)]
fn div_assign(&mut self, divisor: f64) {
assert!(divisor != 0.0);
self.width = (f64::from(self.width) / divisor).round() as i32;
self.height = (f64::from(self.height) / divisor).round() as i32;
}
}
#[allow(clippy::module_name_repetitions)]
#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize)]
pub struct SizeF {
width: f64,
height: f64,
}
impl PartialEq for SizeF {
fn eq(&self, other: &Self) -> bool {
fuzzy_compare(self.width, other.width) && fuzzy_compare(self.height, other.height)
}
}
impl SizeF {
#[must_use]
pub const fn new() -> Self {
Self {
width: 0.0,
height: 0.0,
}
}
#[must_use]
pub const fn from(width: f64, height: f64) -> Self {
Self { width, height }
}
#[must_use]
pub fn shrunk_by(&self, margins: &MarginsF) -> Self {
Self {
width: self.width - margins.left() - margins.right(),
height: self.height - margins.top() - margins.bottom(),
}
}
#[must_use]
pub fn grown_by(&self, margins: &MarginsF) -> Self {
Self {
width: self.width + margins.left() + margins.right(),
height: self.height + margins.top() + margins.bottom(),
}
}
#[must_use]
pub fn bounded_to(&self, other: &Self) -> Self {
Self {
width: self.width.min(other.width),
height: self.height.min(other.height),
}
}
#[must_use]
pub fn expanded_to(&self, other: &Self) -> Self {
Self {
width: self.width.max(other.width),
height: self.height.max(other.height),
}
}
#[must_use]
pub const fn height(&self) -> f64 {
self.height
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.width == 0.0 || self.height == 0.0
}
#[must_use]
pub fn is_null(&self) -> bool {
self.width == 0.0 && self.height == 0.0
}
#[must_use]
pub fn is_valid(&self) -> bool {
self.width >= 0.0 || self.height >= 0.0
}
pub fn height_mut(&mut self) -> &mut f64 {
&mut self.height
}
pub fn width_mut(&mut self) -> &mut f64 {
&mut self.width
}
pub fn scale(&mut self, width: f64, height: f64, mode: AspectRatioMode) {
self.scale_by(Self::from(width, height), mode);
}
pub fn scale_by(&mut self, size: Self, mode: AspectRatioMode) {
let new_size = self.scaled_by(size, mode);
*self = new_size;
}
#[must_use]
pub fn scaled(&self, width: f64, height: f64, mode: AspectRatioMode) -> Self {
self.scaled_by(Self::from(width, height), mode)
}
#[must_use]
pub fn scaled_by(&self, size: Self, mode: AspectRatioMode) -> Self {
if mode == AspectRatioMode::IgnoreAspectRatio || self.width == 0.0 || self.height == 0.0 {
return size;
}
let rw = size.height * self.width / self.height;
let use_height = if mode == AspectRatioMode::KeepAspectRatio {
rw <= size.width
} else {
debug_assert!(mode == AspectRatioMode::KeepAspectRatioByExpanding);
rw >= size.width
};
if use_height {
Self::from(rw, size.height)
} else {
let height = size.width * self.height / self.width;
Self::from(size.width, height)
}
}
pub fn set_height(&mut self, height: f64) {
self.height = height;
}
pub fn set_width(&mut self, width: f64) {
self.width = width;
}
#[must_use]
pub fn to_size(&self) -> Size {
#[allow(clippy::cast_possible_truncation)]
Size::from(self.width.round() as i32, self.height.round() as i32)
}
pub fn transpose(&mut self) {
mem::swap(&mut self.width, &mut self.height);
}
#[must_use]
pub const fn transposed(&self) -> Self {
Self {
width: self.height,
height: self.width,
}
}
#[must_use]
pub const fn width(&self) -> f64 {
self.width
}
}
impl ops::AddAssign<Self> for SizeF {
fn add_assign(&mut self, other: Self) {
self.width += other.width;
self.height += other.height;
}
}
impl ops::AddAssign<&Self> for SizeF {
fn add_assign(&mut self, other: &Self) {
self.width += other.width;
self.height += other.height;
}
}
impl ops::Add<Self> for SizeF {
type Output = Self;
fn add(self, other: Self) -> Self {
Self {
width: self.width + other.width,
height: self.height + other.height,
}
}
}
impl ops::Add<&SizeF> for &SizeF {
type Output = SizeF;
fn add(self, other: &SizeF) -> SizeF {
SizeF {
width: self.width + other.width,
height: self.height + other.height,
}
}
}
impl ops::Mul<f64> for &SizeF {
type Output = SizeF;
fn mul(self, factor: f64) -> Self::Output {
Self::Output {
width: self.width * factor,
height: self.height * factor,
}
}
}
impl ops::MulAssign<f64> for SizeF {
fn mul_assign(&mut self, factor: f64) {
self.width *= factor;
self.height *= factor;
}
}
impl ops::Div<f64> for &SizeF {
type Output = SizeF;
fn div(self, divisor: f64) -> Self::Output {
assert!(!fuzzy_is_zero(divisor));
Self::Output {
width: self.width / divisor,
height: self.height / divisor,
}
}
}
impl ops::DivAssign<f64> for SizeF {
fn div_assign(&mut self, divisor: f64) {
assert!(!fuzzy_is_zero(divisor));
self.width /= divisor;
self.height /= divisor;
}
}