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

Adding feature: Image rotation #2334

Merged
merged 7 commits into from
May 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 74 additions & 1 deletion core/src/angle.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
use crate::{Point, Rectangle, Vector};

use std::f32::consts::{FRAC_PI_2, PI};
use std::ops::{Add, AddAssign, Div, Mul, RangeInclusive, Sub, SubAssign};
use std::ops::{Add, AddAssign, Div, Mul, RangeInclusive, Rem, Sub, SubAssign};

/// Degrees
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
pub struct Degrees(pub f32);

impl Degrees {
/// The range of degrees of a circle.
pub const RANGE: RangeInclusive<Self> = Self(0.0)..=Self(360.0);
}

impl PartialEq<f32> for Degrees {
fn eq(&self, other: &f32) -> bool {
self.0.eq(other)
Expand All @@ -19,6 +24,52 @@ impl PartialOrd<f32> for Degrees {
}
}

impl From<f32> for Degrees {
fn from(degrees: f32) -> Self {
Self(degrees)
}
}

impl From<u8> for Degrees {
fn from(degrees: u8) -> Self {
Self(f32::from(degrees))
}
}

impl From<Degrees> for f32 {
fn from(degrees: Degrees) -> Self {
degrees.0
}
}

impl From<Degrees> for f64 {
fn from(degrees: Degrees) -> Self {
Self::from(degrees.0)
}
}

impl Mul<f32> for Degrees {
type Output = Degrees;

fn mul(self, rhs: f32) -> Self::Output {
Self(self.0 * rhs)
}
}

impl num_traits::FromPrimitive for Degrees {
fn from_i64(n: i64) -> Option<Self> {
Some(Self(n as f32))
}

fn from_u64(n: u64) -> Option<Self> {
Some(Self(n as f32))
}

fn from_f64(n: f64) -> Option<Self> {
Some(Self(n as f32))
}
}

/// Radians
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
pub struct Radians(pub f32);
Expand Down Expand Up @@ -65,6 +116,12 @@ impl From<u8> for Radians {
}
}

impl From<Radians> for f32 {
fn from(radians: Radians) -> Self {
radians.0
}
}

impl From<Radians> for f64 {
fn from(radians: Radians) -> Self {
Self::from(radians.0)
Expand Down Expand Up @@ -107,6 +164,14 @@ impl Add for Radians {
}
}

impl Add<Degrees> for Radians {
type Output = Self;

fn add(self, rhs: Degrees) -> Self::Output {
Self(self.0 + rhs.0.to_radians())
}
}

impl AddAssign for Radians {
fn add_assign(&mut self, rhs: Radians) {
self.0 = self.0 + rhs.0;
Expand Down Expand Up @@ -153,6 +218,14 @@ impl Div for Radians {
}
}

impl Rem for Radians {
type Output = Self;

fn rem(self, rhs: Self) -> Self::Output {
Self(self.0 % rhs.0)
}
}

impl PartialEq<f32> for Radians {
fn eq(&self, other: &f32) -> bool {
self.0.eq(other)
Expand Down
17 changes: 16 additions & 1 deletion core/src/content_fit.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
//! Control the fit of some content (like an image) within a space.
use crate::Size;

use std::fmt;

/// The strategy used to fit the contents of a widget to its bounding box.
///
/// Each variant of this enum is a strategy that can be applied for resolving
Expand All @@ -11,7 +13,7 @@ use crate::Size;
/// in CSS, see [Mozilla's docs][1], or run the `tour` example
///
/// [1]: https://developer.mozilla.org/en-US/docs/Web/CSS/object-fit
#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq)]
#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq, Default)]
pub enum ContentFit {
/// Scale as big as it can be without needing to crop or hide parts.
///
Expand All @@ -23,6 +25,7 @@ pub enum ContentFit {
/// This is a great fit for when you need to display an image without losing
/// any part of it, particularly when the image itself is the focus of the
/// screen.
#[default]
Contain,

/// Scale the image to cover all of the bounding box, cropping if needed.
Expand Down Expand Up @@ -117,3 +120,15 @@ impl ContentFit {
}
}
}

impl fmt::Display for ContentFit {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
ContentFit::Contain => "Contain",
ContentFit::Cover => "Cover",
ContentFit::Fill => "Fill",
ContentFit::None => "None",
ContentFit::ScaleDown => "Scale Down",
})
}
}
3 changes: 2 additions & 1 deletion core/src/image.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Load and draw raster graphics.
pub use bytes::Bytes;

use crate::{Rectangle, Size};
use crate::{Radians, Rectangle, Size};

use rustc_hash::FxHasher;
use std::hash::{Hash, Hasher};
Expand Down Expand Up @@ -173,5 +173,6 @@ pub trait Renderer: crate::Renderer {
handle: Self::Handle,
filter_method: FilterMethod,
bounds: Rectangle,
rotation: Radians,
);
}
2 changes: 2 additions & 0 deletions core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ mod padding;
mod pixels;
mod point;
mod rectangle;
mod rotation;
mod shadow;
mod shell;
mod size;
Expand All @@ -64,6 +65,7 @@ pub use pixels::Pixels;
pub use point::Point;
pub use rectangle::Rectangle;
pub use renderer::Renderer;
pub use rotation::Rotation;
pub use shadow::Shadow;
pub use shell::Shell;
pub use size::Size;
Expand Down
32 changes: 30 additions & 2 deletions core/src/rectangle.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{Point, Size, Vector};
use crate::{Point, Radians, Size, Vector};

/// A rectangle.
/// An axis-aligned rectangle.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Rectangle<T = f32> {
/// X coordinate of the top-left corner.
Expand Down Expand Up @@ -172,6 +172,18 @@ impl Rectangle<f32> {
height: self.height + amount * 2.0,
}
}

/// Rotates the [`Rectangle`] and returns the smallest [`Rectangle`]
/// containing it.
pub fn rotate(self, rotation: Radians) -> Self {
let size = self.size().rotate(rotation);
let position = Point::new(
self.center_x() - size.width / 2.0,
self.center_y() - size.height / 2.0,
);

Self::new(position, size)
}
}

impl std::ops::Mul<f32> for Rectangle<f32> {
Expand Down Expand Up @@ -227,3 +239,19 @@ where
}
}
}

impl<T> std::ops::Mul<Vector<T>> for Rectangle<T>
where
T: std::ops::Mul<Output = T> + Copy,
{
type Output = Rectangle<T>;

fn mul(self, scale: Vector<T>) -> Self {
Rectangle {
x: self.x * scale.x,
y: self.y * scale.y,
width: self.width * scale.x,
height: self.height * scale.y,
}
}
}
5 changes: 4 additions & 1 deletion core/src/renderer/null.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ use crate::renderer::{self, Renderer};
use crate::svg;
use crate::text::{self, Text};
use crate::{
Background, Color, Font, Pixels, Point, Rectangle, Size, Transformation,
Background, Color, Font, Pixels, Point, Radians, Rectangle, Size,
Transformation,
};

impl Renderer for () {
Expand Down Expand Up @@ -171,6 +172,7 @@ impl image::Renderer for () {
_handle: Self::Handle,
_filter_method: image::FilterMethod,
_bounds: Rectangle,
_rotation: Radians,
) {
}
}
Expand All @@ -185,6 +187,7 @@ impl svg::Renderer for () {
_handle: svg::Handle,
_color: Option<Color>,
_bounds: Rectangle,
_rotation: Radians,
) {
}
}
72 changes: 72 additions & 0 deletions core/src/rotation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//! Control the rotation of some content (like an image) within a space.
use crate::{Degrees, Radians, Size};

/// The strategy used to rotate the content.
///
/// This is used to control the behavior of the layout when the content is rotated
/// by a certain angle.
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Rotation {
/// The element will float while rotating. The layout will be kept exactly as it was
/// before the rotation.
///
/// This is especially useful when used for animations, as it will avoid the
/// layout being shifted or resized when smoothly i.e. an icon.
///
/// This is the default.
Floating(Radians),
/// The element will be solid while rotating. The layout will be adjusted to fit
/// the rotated content.
///
/// This allows you to rotate an image and have the layout adjust to fit the new
/// size of the image.
Solid(Radians),
}

impl Rotation {
/// Returns the angle of the [`Rotation`] in [`Radians`].
pub fn radians(self) -> Radians {
match self {
Rotation::Floating(radians) | Rotation::Solid(radians) => radians,
}
}

/// Returns a mutable reference to the angle of the [`Rotation`] in [`Radians`].
pub fn radians_mut(&mut self) -> &mut Radians {
match self {
Rotation::Floating(radians) | Rotation::Solid(radians) => radians,
}
}

/// Returns the angle of the [`Rotation`] in [`Degrees`].
pub fn degrees(self) -> Degrees {
Degrees(self.radians().0.to_degrees())
}

/// Applies the [`Rotation`] to the given [`Size`], returning
/// the minimum [`Size`] containing the rotated one.
pub fn apply(self, size: Size) -> Size {
match self {
Self::Floating(_) => size,
Self::Solid(rotation) => size.rotate(rotation),
}
}
}

impl Default for Rotation {
fn default() -> Self {
Self::Floating(Radians(0.0))
}
}

impl From<Radians> for Rotation {
fn from(radians: Radians) -> Self {
Self::Floating(radians)
}
}

impl From<f32> for Rotation {
fn from(radians: f32) -> Self {
Self::Floating(Radians(radians))
}
}
29 changes: 28 additions & 1 deletion core/src/size.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::Vector;
use crate::{Radians, Vector};

/// An amount of space in 2 dimensions.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
Expand Down Expand Up @@ -51,6 +51,19 @@ impl Size {
height: self.height + other.height,
}
}

/// Rotates the given [`Size`] and returns the minimum [`Size`]
/// containing it.
pub fn rotate(self, rotation: Radians) -> Size {
let radians = f32::from(rotation);

Size {
width: (self.width * radians.cos()).abs()
+ (self.height * radians.sin()).abs(),
height: (self.width * radians.sin()).abs()
+ (self.height * radians.cos()).abs(),
}
}
}

impl<T> From<[T; 2]> for Size<T> {
Expand Down Expand Up @@ -113,3 +126,17 @@ where
}
}
}

impl<T> std::ops::Mul<Vector<T>> for Size<T>
where
T: std::ops::Mul<Output = T> + Copy,
{
type Output = Size<T>;

fn mul(self, scale: Vector<T>) -> Self::Output {
Size {
width: self.width * scale.x,
height: self.height * scale.y,
}
}
}
3 changes: 2 additions & 1 deletion core/src/svg.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//! Load and draw vector graphics.
use crate::{Color, Rectangle, Size};
use crate::{Color, Radians, Rectangle, Size};

use rustc_hash::FxHasher;
use std::borrow::Cow;
Expand Down Expand Up @@ -100,5 +100,6 @@ pub trait Renderer: crate::Renderer {
handle: Handle,
color: Option<Color>,
bounds: Rectangle,
rotation: Radians,
);
}
Loading
Loading