Skip to content

Commit 2724501

Browse files
committed
You can now throw as many rotations at it as you want
It handles it flawlessly (almost). It's quite fun to spin the cube!
1 parent f026333 commit 2724501

File tree

2 files changed

+90
-77
lines changed

2 files changed

+90
-77
lines changed

src/cube_rotation.rs

Lines changed: 78 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
1-
use std::cmp::Ordering;
1+
use std::collections::VecDeque;
22
use std::time::{Duration, Instant};
33

44
use bevy::prelude::*;
55
use derivative::Derivative;
66

77
use crate::MainCamera;
8-
use crate::utils::CartesianDirection;
8+
use crate::utils::{CartesianDirection, SeeDirection};
99

10-
#[derive(Debug, Default, Clone, Copy)]
10+
#[derive(Debug, Default, Clone)]
1111
pub(crate) struct RotationData {
1212
rotation_state: RotationState,
13-
future_rotation_state: RotationState,
1413
/// The rotation state that is currently targeted by the ongoing animation(s). An animation around the side face only sets the top to `Some`, and vice versa. This is to allow simultaneous animations of the side and the top, without interference once one of them reaches the goal.
15-
top_rotation_animation: Option<RotationAnimationData>,
16-
side_rotation_animation: Option<RotationAnimationData>,
14+
future_rotation_state: RotationState,
15+
/// List of ongoing animations, oldest animation is first
16+
animations: VecDeque<RotationAnimationData>,
1717
}
1818

1919
#[derive(Debug, Derivative, Clone, Copy, PartialEq, Eq)]
@@ -48,13 +48,43 @@ impl RotationState {
4848
}
4949
rotation_state
5050
}
51+
52+
/// Set the rotation state field from the see direction
53+
fn set_see_direction(&mut self, see_direction: SeeDirection, value: CartesianDirection) {
54+
match see_direction {
55+
SeeDirection::Top => self.top = value,
56+
SeeDirection::Left => self.side = value,
57+
_ => {}
58+
}
59+
}
60+
61+
/// Panics in some cases if the rotation state is not possible. That is, if the side and top are parallel
62+
fn get_see_direction(&self, see_direction: SeeDirection) -> CartesianDirection {
63+
match see_direction {
64+
SeeDirection::Top => self.top,
65+
SeeDirection::Left => self.side,
66+
SeeDirection::Right => self
67+
.top
68+
.cross(self.side)
69+
.expect("Rotation state is not possible"),
70+
SeeDirection::BackLeft => self
71+
.top
72+
.cross(self.side)
73+
.expect("Rotation state is not possible")
74+
.opposite(),
75+
SeeDirection::Bottom => self.top.opposite(),
76+
SeeDirection::BackRight => self.side.opposite(),
77+
}
78+
}
5179
}
5280

5381
#[derive(Debug, Clone, Copy)]
5482
pub(crate) struct RotationAnimationData {
5583
from: CartesianDirection,
5684
target: CartesianDirection,
5785
animation_started: Instant,
86+
/// What side seen from the camera that from and target are referring to
87+
side_changing: SeeDirection,
5888
}
5989

6090
impl RotationAnimationData {
@@ -97,26 +127,9 @@ pub(crate) fn iterate(
97127
let mut transform = camera.0;
98128
transform.translation = rotation_data.rotation_state.camera_location() * 2.;
99129

100-
// Needed to prevent dropping this value
101-
let animations = [
102-
rotation_data.top_rotation_animation,
103-
rotation_data.side_rotation_animation,
104-
];
105-
let mut animations = animations
106-
.iter()
107-
.flatten()
108-
.collect::<Vec<&RotationAnimationData>>();
109-
// Sort by when the animation started. If this is not done, the animations may be added in the wrong order resulting in wrong rotation
110-
animations.sort_by(|c1, c2| {
111-
if c1.animation_started < c2.animation_started {
112-
Ordering::Greater
113-
} else {
114-
Ordering::Less
115-
}
116-
});
117130
transform.translate_around(
118131
Vec3::ZERO,
119-
total_animation_rotation(&animations, rotation_duration),
132+
total_animation_rotation(&rotation_data.animations, rotation_duration),
120133
);
121134

122135
transform.look_at(
@@ -129,60 +142,48 @@ pub(crate) fn iterate(
129142
}
130143

131144
fn conclude_finished_animations(rotation_data: &mut RotationData, rotation_duration: Duration) {
132-
if let Some(animation) = &rotation_data.side_rotation_animation {
145+
let mut num_finished_animations = 0;
146+
for animation in &rotation_data.animations {
133147
if (Instant::now() - animation.animation_started) > rotation_duration {
134-
rotation_data.rotation_state.top = animation.target;
135-
rotation_data.side_rotation_animation = None;
148+
rotation_data
149+
.rotation_state
150+
.set_see_direction(animation.side_changing, animation.target);
151+
num_finished_animations += 1;
136152
}
137153
}
138154

139-
if let Some(animation) = &rotation_data.top_rotation_animation {
140-
if (Instant::now() - animation.animation_started) > rotation_duration {
141-
rotation_data.rotation_state.side = animation.target;
142-
rotation_data.top_rotation_animation = None;
143-
}
155+
for _ in 0..num_finished_animations {
156+
rotation_data.animations.pop_front();
144157
}
145158
}
146159

147160
fn input_handling(input: Res<Input<KeyCode>>, rotation_data: &mut RotationData) {
148-
if rotation_data.top_rotation_animation.is_none() {
149-
let rotation = if input.just_pressed(KeyCode::Right) {
150-
Some(rotation_data.future_rotation_state.top.opposite())
151-
} else if input.just_pressed(KeyCode::Left) {
152-
Some(rotation_data.future_rotation_state.top)
153-
} else {
154-
None
155-
};
156-
if let Some(rotation) = rotation {
157-
start_rotation(rotation_data, rotation);
158-
}
161+
let fs = rotation_data.future_rotation_state; // Shorthand
162+
if input.just_pressed(KeyCode::Right) {
163+
start_rotation(rotation_data, fs.top.opposite(), SeeDirection::Top);
164+
} else if input.just_pressed(KeyCode::Left) {
165+
start_rotation(rotation_data, fs.top, SeeDirection::Top);
159166
}
160-
161-
if rotation_data.side_rotation_animation.is_none() {
162-
let rotation = if input.just_pressed(KeyCode::Up) {
163-
Some(rotation_data.future_rotation_state.side.opposite())
164-
} else if input.just_pressed(KeyCode::Down) {
165-
Some(rotation_data.future_rotation_state.side)
166-
} else {
167-
None
168-
};
169-
if let Some(rotation) = rotation {
170-
start_rotation(rotation_data, rotation);
171-
}
167+
if input.just_pressed(KeyCode::Up) {
168+
start_rotation(rotation_data, fs.side.opposite(), SeeDirection::Left);
169+
} else if input.just_pressed(KeyCode::Down) {
170+
start_rotation(rotation_data, fs.side, SeeDirection::Left);
172171
}
173172
}
174173

175-
fn start_rotation(rotation_data: &mut RotationData, rotation: CartesianDirection) {
174+
/// @param see_direction: The side (seen from the camera) that this rotation is rotating around
175+
fn start_rotation(rotation_data: &mut RotationData, rotation: CartesianDirection, _see_direction: SeeDirection) {
176176
// If the rotation axis is not parallel to the top axis, then this rotation will modify the side axis
177177
if !rotation.is_parallel_to(rotation_data.future_rotation_state.side) {
178178
let target = rotation_data
179179
.future_rotation_state
180180
.after_rotation(rotation)
181181
.side;
182-
rotation_data.top_rotation_animation = Some(RotationAnimationData {
182+
rotation_data.animations.push_back(RotationAnimationData {
183183
from: rotation_data.future_rotation_state.side,
184184
target,
185185
animation_started: Instant::now(),
186+
side_changing: SeeDirection::Left,
186187
});
187188
rotation_data.future_rotation_state.side = target;
188189
}
@@ -192,42 +193,34 @@ fn start_rotation(rotation_data: &mut RotationData, rotation: CartesianDirection
192193
.future_rotation_state
193194
.after_rotation(rotation)
194195
.top;
195-
rotation_data.side_rotation_animation = Some(RotationAnimationData {
196+
rotation_data.animations.push_back(RotationAnimationData {
196197
from: rotation_data.future_rotation_state.top,
197198
target,
198199
animation_started: Instant::now(),
200+
side_changing: SeeDirection::Top,
199201
});
200202
rotation_data.future_rotation_state.top = target;
201203
}
202204
}
203205

204206
fn total_animation_rotation(
205-
animations: &[&RotationAnimationData],
207+
animations: &VecDeque<RotationAnimationData>,
206208
rotation_time: Duration,
207209
) -> Quat {
208210
let mut output = Quat::IDENTITY;
209-
// let mut output = animations.iter().flatten().last().map_or(Quat::IDENTITY, |p|p.partial_camera_translation(rotation_time));
210-
// Iterate without Nones
211-
for animation in animations {
211+
for animation in animations.iter().rev() {
212212
output *= animation.partial_camera_translation(rotation_time);
213213
}
214214
output
215215
}
216216

217217
fn camera_up_vector(rotation_data: &RotationData, rotation_time: Duration) -> Vec3 {
218218
let mut output = rotation_data.rotation_state.top.as_vec3();
219-
for animation in [
220-
rotation_data.top_rotation_animation,
221-
rotation_data.side_rotation_animation,
222-
]
223-
.iter()
224-
.flatten()
225-
{
226-
let top = rotation_data.rotation_state.top;
227-
if (animation.target.is_parallel_to(top) || animation.from.is_parallel_to(top))
219+
for animation in &rotation_data.animations {
220+
if (animation.side_changing == SeeDirection::Top || animation.side_changing == SeeDirection::Bottom)
228221
&& animation.target != animation.from
229222
{
230-
output = animation.camera_up_vector(rotation_time);
223+
output += animation.camera_up_vector(rotation_time) - animation.from.as_vec3();
231224
}
232225
}
233226
output
@@ -249,9 +242,6 @@ fn rotation_curve(time: f32) -> f32 {
249242
}
250243

251244
mod tests {
252-
#[test]
253-
fn camera_location_test() {}
254-
255245
#[test]
256246
fn rotation_state_after_rotation_test() {
257247
use super::RotationState;
@@ -262,4 +252,16 @@ mod tests {
262252
RotationState { top: X, side: Z }
263253
);
264254
}
265-
}
255+
256+
use super::*;
257+
use bevy::math::Quat;
258+
use std::time::Duration;
259+
260+
#[test]
261+
fn total_animation_rotation_with_no_animations() {
262+
let animations = VecDeque::<RotationAnimationData>::new();
263+
let rotation_time = Duration::from_secs(1);
264+
let result = total_animation_rotation(&animations, rotation_time);
265+
assert_eq!(result, Quat::IDENTITY);
266+
}
267+
}

src/utils.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ impl CartesianDirection {
206206
Self::Z | Self::NegZ => Self::Z,
207207
}
208208
}
209-
209+
210210
pub(crate) fn is_parallel_to(&self, other: CartesianDirection) -> bool {
211211
self.abs() == other.abs()
212212
}
@@ -272,3 +272,14 @@ impl CartesianDirection {
272272
out
273273
}
274274
}
275+
276+
/// Signifies the direction seen from the camera.
277+
#[derive(Debug, Clone, Copy, PartialEq)]
278+
pub(crate) enum SeeDirection {
279+
Top,
280+
Left,
281+
Right,
282+
Bottom,
283+
BackLeft,
284+
BackRight,
285+
}

0 commit comments

Comments
 (0)