Skip to content

Commit 1cefe6b

Browse files
authored
Merge pull request #2334 from DKolter/image-rotation
Adding feature: Image rotation
2 parents fe240a9 + 4010e39 commit 1cefe6b

26 files changed

Lines changed: 695 additions & 109 deletions

File tree

core/src/angle.rs

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
use crate::{Point, Rectangle, Vector};
22

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

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

10+
impl Degrees {
11+
/// The range of degrees of a circle.
12+
pub const RANGE: RangeInclusive<Self> = Self(0.0)..=Self(360.0);
13+
}
14+
1015
impl PartialEq<f32> for Degrees {
1116
fn eq(&self, other: &f32) -> bool {
1217
self.0.eq(other)
@@ -19,6 +24,52 @@ impl PartialOrd<f32> for Degrees {
1924
}
2025
}
2126

27+
impl From<f32> for Degrees {
28+
fn from(degrees: f32) -> Self {
29+
Self(degrees)
30+
}
31+
}
32+
33+
impl From<u8> for Degrees {
34+
fn from(degrees: u8) -> Self {
35+
Self(f32::from(degrees))
36+
}
37+
}
38+
39+
impl From<Degrees> for f32 {
40+
fn from(degrees: Degrees) -> Self {
41+
degrees.0
42+
}
43+
}
44+
45+
impl From<Degrees> for f64 {
46+
fn from(degrees: Degrees) -> Self {
47+
Self::from(degrees.0)
48+
}
49+
}
50+
51+
impl Mul<f32> for Degrees {
52+
type Output = Degrees;
53+
54+
fn mul(self, rhs: f32) -> Self::Output {
55+
Self(self.0 * rhs)
56+
}
57+
}
58+
59+
impl num_traits::FromPrimitive for Degrees {
60+
fn from_i64(n: i64) -> Option<Self> {
61+
Some(Self(n as f32))
62+
}
63+
64+
fn from_u64(n: u64) -> Option<Self> {
65+
Some(Self(n as f32))
66+
}
67+
68+
fn from_f64(n: f64) -> Option<Self> {
69+
Some(Self(n as f32))
70+
}
71+
}
72+
2273
/// Radians
2374
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd)]
2475
pub struct Radians(pub f32);
@@ -65,6 +116,12 @@ impl From<u8> for Radians {
65116
}
66117
}
67118

119+
impl From<Radians> for f32 {
120+
fn from(radians: Radians) -> Self {
121+
radians.0
122+
}
123+
}
124+
68125
impl From<Radians> for f64 {
69126
fn from(radians: Radians) -> Self {
70127
Self::from(radians.0)
@@ -107,6 +164,14 @@ impl Add for Radians {
107164
}
108165
}
109166

167+
impl Add<Degrees> for Radians {
168+
type Output = Self;
169+
170+
fn add(self, rhs: Degrees) -> Self::Output {
171+
Self(self.0 + rhs.0.to_radians())
172+
}
173+
}
174+
110175
impl AddAssign for Radians {
111176
fn add_assign(&mut self, rhs: Radians) {
112177
self.0 = self.0 + rhs.0;
@@ -153,6 +218,14 @@ impl Div for Radians {
153218
}
154219
}
155220

221+
impl Rem for Radians {
222+
type Output = Self;
223+
224+
fn rem(self, rhs: Self) -> Self::Output {
225+
Self(self.0 % rhs.0)
226+
}
227+
}
228+
156229
impl PartialEq<f32> for Radians {
157230
fn eq(&self, other: &f32) -> bool {
158231
self.0.eq(other)

core/src/content_fit.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
//! Control the fit of some content (like an image) within a space.
22
use crate::Size;
33

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

2831
/// Scale the image to cover all of the bounding box, cropping if needed.
@@ -117,3 +120,15 @@ impl ContentFit {
117120
}
118121
}
119122
}
123+
124+
impl fmt::Display for ContentFit {
125+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126+
f.write_str(match self {
127+
ContentFit::Contain => "Contain",
128+
ContentFit::Cover => "Cover",
129+
ContentFit::Fill => "Fill",
130+
ContentFit::None => "None",
131+
ContentFit::ScaleDown => "Scale Down",
132+
})
133+
}
134+
}

core/src/image.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Load and draw raster graphics.
22
pub use bytes::Bytes;
33

4-
use crate::{Rectangle, Size};
4+
use crate::{Radians, Rectangle, Size};
55

66
use rustc_hash::FxHasher;
77
use std::hash::{Hash, Hasher};
@@ -173,5 +173,6 @@ pub trait Renderer: crate::Renderer {
173173
handle: Self::Handle,
174174
filter_method: FilterMethod,
175175
bounds: Rectangle,
176+
rotation: Radians,
176177
);
177178
}

core/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ mod padding;
3939
mod pixels;
4040
mod point;
4141
mod rectangle;
42+
mod rotation;
4243
mod shadow;
4344
mod shell;
4445
mod size;
@@ -64,6 +65,7 @@ pub use pixels::Pixels;
6465
pub use point::Point;
6566
pub use rectangle::Rectangle;
6667
pub use renderer::Renderer;
68+
pub use rotation::Rotation;
6769
pub use shadow::Shadow;
6870
pub use shell::Shell;
6971
pub use size::Size;

core/src/rectangle.rs

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
use crate::{Point, Size, Vector};
1+
use crate::{Point, Radians, Size, Vector};
22

3-
/// A rectangle.
3+
/// An axis-aligned rectangle.
44
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
55
pub struct Rectangle<T = f32> {
66
/// X coordinate of the top-left corner.
@@ -172,6 +172,18 @@ impl Rectangle<f32> {
172172
height: self.height + amount * 2.0,
173173
}
174174
}
175+
176+
/// Rotates the [`Rectangle`] and returns the smallest [`Rectangle`]
177+
/// containing it.
178+
pub fn rotate(self, rotation: Radians) -> Self {
179+
let size = self.size().rotate(rotation);
180+
let position = Point::new(
181+
self.center_x() - size.width / 2.0,
182+
self.center_y() - size.height / 2.0,
183+
);
184+
185+
Self::new(position, size)
186+
}
175187
}
176188

177189
impl std::ops::Mul<f32> for Rectangle<f32> {
@@ -227,3 +239,19 @@ where
227239
}
228240
}
229241
}
242+
243+
impl<T> std::ops::Mul<Vector<T>> for Rectangle<T>
244+
where
245+
T: std::ops::Mul<Output = T> + Copy,
246+
{
247+
type Output = Rectangle<T>;
248+
249+
fn mul(self, scale: Vector<T>) -> Self {
250+
Rectangle {
251+
x: self.x * scale.x,
252+
y: self.y * scale.y,
253+
width: self.width * scale.x,
254+
height: self.height * scale.y,
255+
}
256+
}
257+
}

core/src/renderer/null.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ use crate::renderer::{self, Renderer};
44
use crate::svg;
55
use crate::text::{self, Text};
66
use crate::{
7-
Background, Color, Font, Pixels, Point, Rectangle, Size, Transformation,
7+
Background, Color, Font, Pixels, Point, Radians, Rectangle, Size,
8+
Transformation,
89
};
910

1011
impl Renderer for () {
@@ -171,6 +172,7 @@ impl image::Renderer for () {
171172
_handle: Self::Handle,
172173
_filter_method: image::FilterMethod,
173174
_bounds: Rectangle,
175+
_rotation: Radians,
174176
) {
175177
}
176178
}
@@ -185,6 +187,7 @@ impl svg::Renderer for () {
185187
_handle: svg::Handle,
186188
_color: Option<Color>,
187189
_bounds: Rectangle,
190+
_rotation: Radians,
188191
) {
189192
}
190193
}

core/src/rotation.rs

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
//! Control the rotation of some content (like an image) within a space.
2+
use crate::{Degrees, Radians, Size};
3+
4+
/// The strategy used to rotate the content.
5+
///
6+
/// This is used to control the behavior of the layout when the content is rotated
7+
/// by a certain angle.
8+
#[derive(Debug, Clone, Copy, PartialEq)]
9+
pub enum Rotation {
10+
/// The element will float while rotating. The layout will be kept exactly as it was
11+
/// before the rotation.
12+
///
13+
/// This is especially useful when used for animations, as it will avoid the
14+
/// layout being shifted or resized when smoothly i.e. an icon.
15+
///
16+
/// This is the default.
17+
Floating(Radians),
18+
/// The element will be solid while rotating. The layout will be adjusted to fit
19+
/// the rotated content.
20+
///
21+
/// This allows you to rotate an image and have the layout adjust to fit the new
22+
/// size of the image.
23+
Solid(Radians),
24+
}
25+
26+
impl Rotation {
27+
/// Returns the angle of the [`Rotation`] in [`Radians`].
28+
pub fn radians(self) -> Radians {
29+
match self {
30+
Rotation::Floating(radians) | Rotation::Solid(radians) => radians,
31+
}
32+
}
33+
34+
/// Returns a mutable reference to the angle of the [`Rotation`] in [`Radians`].
35+
pub fn radians_mut(&mut self) -> &mut Radians {
36+
match self {
37+
Rotation::Floating(radians) | Rotation::Solid(radians) => radians,
38+
}
39+
}
40+
41+
/// Returns the angle of the [`Rotation`] in [`Degrees`].
42+
pub fn degrees(self) -> Degrees {
43+
Degrees(self.radians().0.to_degrees())
44+
}
45+
46+
/// Applies the [`Rotation`] to the given [`Size`], returning
47+
/// the minimum [`Size`] containing the rotated one.
48+
pub fn apply(self, size: Size) -> Size {
49+
match self {
50+
Self::Floating(_) => size,
51+
Self::Solid(rotation) => size.rotate(rotation),
52+
}
53+
}
54+
}
55+
56+
impl Default for Rotation {
57+
fn default() -> Self {
58+
Self::Floating(Radians(0.0))
59+
}
60+
}
61+
62+
impl From<Radians> for Rotation {
63+
fn from(radians: Radians) -> Self {
64+
Self::Floating(radians)
65+
}
66+
}
67+
68+
impl From<f32> for Rotation {
69+
fn from(radians: f32) -> Self {
70+
Self::Floating(Radians(radians))
71+
}
72+
}

core/src/size.rs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::Vector;
1+
use crate::{Radians, Vector};
22

33
/// An amount of space in 2 dimensions.
44
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
@@ -51,6 +51,19 @@ impl Size {
5151
height: self.height + other.height,
5252
}
5353
}
54+
55+
/// Rotates the given [`Size`] and returns the minimum [`Size`]
56+
/// containing it.
57+
pub fn rotate(self, rotation: Radians) -> Size {
58+
let radians = f32::from(rotation);
59+
60+
Size {
61+
width: (self.width * radians.cos()).abs()
62+
+ (self.height * radians.sin()).abs(),
63+
height: (self.width * radians.sin()).abs()
64+
+ (self.height * radians.cos()).abs(),
65+
}
66+
}
5467
}
5568

5669
impl<T> From<[T; 2]> for Size<T> {
@@ -113,3 +126,17 @@ where
113126
}
114127
}
115128
}
129+
130+
impl<T> std::ops::Mul<Vector<T>> for Size<T>
131+
where
132+
T: std::ops::Mul<Output = T> + Copy,
133+
{
134+
type Output = Size<T>;
135+
136+
fn mul(self, scale: Vector<T>) -> Self::Output {
137+
Size {
138+
width: self.width * scale.x,
139+
height: self.height * scale.y,
140+
}
141+
}
142+
}

core/src/svg.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//! Load and draw vector graphics.
2-
use crate::{Color, Rectangle, Size};
2+
use crate::{Color, Radians, Rectangle, Size};
33

44
use rustc_hash::FxHasher;
55
use std::borrow::Cow;
@@ -100,5 +100,6 @@ pub trait Renderer: crate::Renderer {
100100
handle: Handle,
101101
color: Option<Color>,
102102
bounds: Rectangle,
103+
rotation: Radians,
103104
);
104105
}

0 commit comments

Comments
 (0)