Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add common aspect ratio constants and improve documentation #15091

Merged
merged 14 commits into from
Sep 9, 2024
4 changes: 3 additions & 1 deletion crates/bevy_core_pipeline/src/bloom/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,9 @@ impl ExtractComponent for BloomSettings {
viewport: UVec4::new(origin.x, origin.y, size.x, size.y).as_vec4()
/ UVec4::new(target_size.x, target_size.y, target_size.x, target_size.y)
.as_vec4(),
aspect: AspectRatio::from_pixels(size.x, size.y).into(),
aspect: AspectRatio::try_from_pixels(size.x, size.y)
.expect("Valid screen size values for Bloom settings")
.ratio(),
uv_offset: settings.uv_offset,
};

Expand Down
84 changes: 75 additions & 9 deletions crates/bevy_math/src/aspect_ratio.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Provides a simple aspect ratio struct to help with calculations.

use crate::Vec2;
use thiserror::Error;

#[cfg(feature = "bevy_reflect")]
use bevy_reflect::Reflect;
Expand All @@ -11,23 +12,74 @@ use bevy_reflect::Reflect;
pub struct AspectRatio(f32);

impl AspectRatio {
/// Create a new `AspectRatio` from a given `width` and `height`.
/// Standard 16:9 aspect ratio
pub const SIXTEEN_NINE: Self = Self(16.0 / 9.0);
/// Standard 4:3 aspect ratio
pub const FOUR_THREE: Self = Self(4.0 / 3.0);
/// Standard 21:9 ultrawide aspect ratio
pub const ULTRAWIDE: Self = Self(21.0 / 9.0);

/// Attempts to create a new [`AspectRatio`] from a given width and height.
///
/// # Errors
///
/// Returns an `Err` with `AspectRatioError` if:
/// - Either width or height is zero (`AspectRatioError::Zero`)
/// - Either width or height is infinite (`AspectRatioError::Infinite`)
/// - Either width or height is NaN (`AspectRatioError::NaN`)
#[inline]
pub fn try_new(width: f32, height: f32) -> Result<Self, AspectRatioError> {
match (width, height) {
(w, h) if w == 0.0 || h == 0.0 => Err(AspectRatioError::Zero),
(w, h) if w.is_infinite() || h.is_infinite() => Err(AspectRatioError::Infinite),
(w, h) if w.is_nan() || h.is_nan() => Err(AspectRatioError::NaN),
_ => Ok(Self(width / height)),
}
}

/// Attempts to create a new [`AspectRatio`] from a given amount of x pixels and y pixels.
#[inline]
pub fn try_from_pixels(x: u32, y: u32) -> Result<Self, AspectRatioError> {
Self::try_new(x as f32, y as f32)
}

/// Returns the aspect ratio as a f32 value.
#[inline]
pub fn ratio(&self) -> f32 {
self.0
}

/// Returns the inverse of this aspect ratio (height/width).
#[inline]
pub fn new(width: f32, height: f32) -> Self {
Self(width / height)
pub fn inverse(&self) -> Self {
Self(1.0 / self.0)
}

/// Create a new `AspectRatio` from a given amount of `x` pixels and `y` pixels.
/// Returns true if the aspect ratio represents a landscape orientation.
#[inline]
pub fn from_pixels(x: u32, y: u32) -> Self {
Self::new(x as f32, y as f32)
pub fn is_landscape(&self) -> bool {
self.0 > 1.0
}

/// Returns true if the aspect ratio represents a portrait orientation.
#[inline]
pub fn is_portrait(&self) -> bool {
self.0 < 1.0
}

/// Returns true if the aspect ratio is exactly square.
#[inline]
pub fn is_square(&self) -> bool {
self.0 == 1.0
}
}

impl From<Vec2> for AspectRatio {
impl TryFrom<Vec2> for AspectRatio {
type Error = AspectRatioError;

#[inline]
fn from(value: Vec2) -> Self {
Self::new(value.x, value.y)
fn try_from(value: Vec2) -> Result<Self, Self::Error> {
Self::try_new(value.x, value.y)
}
}

Expand All @@ -37,3 +89,17 @@ impl From<AspectRatio> for f32 {
aspect_ratio.0
}
}

/// An Error type for when [`super::AspectRatio`] is provided invalid width or height values
#[derive(Error, Debug, PartialEq, Eq, Clone, Copy)]
pub enum AspectRatioError {
/// Error due to width or height having zero as a value.
#[error("AspectRatio error: width or height is zero")]
Zero,
/// Error due towidth or height being infinite.
#[error("AspectRatio error: width or height is infinite")]
Infinite,
/// Error due to width or height being Not a Number (NaN).
#[error("AspectRatio error: width or height is NaN")]
NaN,
}
5 changes: 3 additions & 2 deletions crates/bevy_pbr/src/cluster/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,8 +257,9 @@ impl ClusterConfig {
ClusterConfig::FixedZ {
total, z_slices, ..
} => {
let aspect_ratio: f32 =
AspectRatio::from_pixels(screen_size.x, screen_size.y).into();
let aspect_ratio: f32 = AspectRatio::try_from_pixels(screen_size.x, screen_size.y)
.expect("Failed to calculate aspect ratio for Cluster: screen dimensions must be positive, non-zero values")
.ratio();
let mut z_slices = *z_slices;
if *total < z_slices {
warn!("ClusterConfig has more z-slices than total clusters!");
Expand Down
4 changes: 3 additions & 1 deletion crates/bevy_render/src/camera/projection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,9 @@ impl CameraProjection for PerspectiveProjection {
}

fn update(&mut self, width: f32, height: f32) {
self.aspect_ratio = AspectRatio::new(width, height).into();
self.aspect_ratio = AspectRatio::try_new(width, height)
.expect("Failed to update PerspectiveProjection: width and height must be positive, non-zero values")
.ratio();
}

fn far(&self) -> f32 {
Expand Down
4 changes: 3 additions & 1 deletion crates/bevy_render/src/texture/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -666,7 +666,9 @@ impl Image {
/// Returns the aspect ratio (width / height) of a 2D image.
#[inline]
pub fn aspect_ratio(&self) -> AspectRatio {
AspectRatio::from_pixels(self.width(), self.height())
AspectRatio::try_from_pixels(self.width(), self.height()).expect(
"Failed to calculate aspect ratio: Image dimensions must be positive, non-zero values",
)
}

/// Returns the size of a 2D image as f32.
Expand Down