Angle as proper type

This commit is contained in:
Dietrich 2022-08-20 15:33:47 +02:00
parent a5a1be2392
commit d16607ff4d
Signed by: dietrich
GPG Key ID: F0CE5A20AB5C4B27
9 changed files with 217 additions and 49 deletions

21
Cargo.lock generated
View File

@ -4,9 +4,9 @@ version = 3
[[package]] [[package]]
name = "ab_glyph" name = "ab_glyph"
version = "0.2.15" version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24606928a235e73cdef55a0c909719cadd72fce573e5713d58cb2952d8f5794c" checksum = "846ffacb9d0c8b879ef9e565b59e18fb76d6a61013e5bd24ecc659864e6b1a1f"
dependencies = [ dependencies = [
"ab_glyph_rasterizer", "ab_glyph_rasterizer",
"owned_ttf_parser", "owned_ttf_parser",
@ -594,9 +594,9 @@ dependencies = [
[[package]] [[package]]
name = "bevy_pbr" name = "bevy_pbr"
version = "0.8.0" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed9a81bbd02f5e0a57899a41aec37d9cb14965e1e4d510547f3f680323d05c0f" checksum = "176073021a4caeb8b448f24ce790fb57fde74b114f345064a8b102d2f7bed905"
dependencies = [ dependencies = [
"bevy_app", "bevy_app",
"bevy_asset", "bevy_asset",
@ -741,9 +741,9 @@ dependencies = [
[[package]] [[package]]
name = "bevy_sprite" name = "bevy_sprite"
version = "0.8.0" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f83dfe8897d6c0d9d5ce3818d49a13e58ae2b9b9ecf4f4bb85aa31bb0678f68" checksum = "69c419f3db09d7ac1f4d45e0874d349d5d6f47f48bc10d55cd0da36413e2331e"
dependencies = [ dependencies = [
"bevy_app", "bevy_app",
"bevy_asset", "bevy_asset",
@ -841,9 +841,9 @@ dependencies = [
[[package]] [[package]]
name = "bevy_ui" name = "bevy_ui"
version = "0.8.0" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac181a7b637da61fad72981ff9d2e5b899283ca7d54b2b7ea49c431121331c53" checksum = "062ce086de1a4a470e5df48cb5c16a1dc97ab610e635cafabdef26c4a1ef5756"
dependencies = [ dependencies = [
"bevy_app", "bevy_app",
"bevy_asset", "bevy_asset",
@ -883,9 +883,9 @@ dependencies = [
[[package]] [[package]]
name = "bevy_window" name = "bevy_window"
version = "0.8.0" version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3bdc3a220a9bb2fad9bd30d5f44c6645725398fe1bc588fc87abf09f092696e" checksum = "707dbbebfac72b1e63e874e7a11a345feab8c440355c0bd71e6dff26709fba9a"
dependencies = [ dependencies = [
"bevy_app", "bevy_app",
"bevy_ecs", "bevy_ecs",
@ -3238,6 +3238,7 @@ dependencies = [
"bevy-inspector-egui", "bevy-inspector-egui",
"bevy_prototype_lyon", "bevy_prototype_lyon",
"bevy_tweening", "bevy_tweening",
"num-traits",
] ]
[[package]] [[package]]

View File

@ -11,6 +11,7 @@ bevy = { version = "0.8", features = ["dynamic"] }
bevy-inspector-egui = "0.12.1" bevy-inspector-egui = "0.12.1"
bevy_prototype_lyon = "0.6" bevy_prototype_lyon = "0.6"
bevy_tweening = "0.5.0" bevy_tweening = "0.5.0"
num-traits = "0.2"
# Enable a small amount of optimization in debug mode # Enable a small amount of optimization in debug mode
[profile.dev] [profile.dev]

1
src/datatypes.rs Normal file
View File

@ -0,0 +1 @@
pub mod angle;

154
src/datatypes/angle.rs Normal file
View File

@ -0,0 +1,154 @@
use bevy_inspector_egui::Inspectable;
use std::ops::{Add, Div, Mul, Neg, Rem, Sub};
#[derive(Inspectable, Copy, Clone, Debug, PartialEq, Eq)]
pub enum AngleUnit<T: Default> {
Degrees(T),
Radians(T),
}
impl<T: Default> Default for AngleUnit<T> {
fn default() -> Self {
Self::Degrees(Default::default())
}
}
#[derive(Inspectable, Copy, Default, Clone, Debug, PartialEq, Eq)]
pub struct Angle<T: Default> {
value: AngleUnit<T>,
}
impl<T: Default + Clone + Rem<T, Output = T>> Rem<T> for Angle<T> {
type Output = Self;
fn rem(self, rhs: T) -> Self::Output {
match self.value {
AngleUnit::Degrees(v) => Self::Output::degrees(v % rhs),
AngleUnit::Radians(v) => Self::Output::radians(v % rhs),
}
}
}
impl<T: Default + Clone + Mul<T, Output = T>> Mul<T> for Angle<T> {
type Output = Self;
fn mul(self, rhs: T) -> Self::Output {
match self.value {
AngleUnit::Degrees(v) => Self::Output::degrees(v * rhs),
AngleUnit::Radians(v) => Self::Output::radians(v * rhs),
}
}
}
impl<T: Default + Clone + Div<T, Output = T>> Div<T> for Angle<T> {
type Output = Self;
fn div(self, rhs: T) -> Self::Output {
match self.value {
AngleUnit::Degrees(v) => Self::Output::degrees(v / rhs),
AngleUnit::Radians(v) => Self::Output::radians(v / rhs),
}
}
}
impl<T: Default + Clone + std::ops::Neg<Output = T>> Neg for Angle<T> {
type Output = Self;
fn neg(self) -> Self::Output {
match self.value {
AngleUnit::Degrees(v) => Self::Output::degrees(-v),
AngleUnit::Radians(v) => Self::Output::radians(-v),
}
}
}
impl<T: Default + Clone + std::ops::Neg<Output = T>> Neg for &Angle<T> {
type Output = Angle<T>;
fn neg(self) -> Self::Output {
match self.value.clone() {
AngleUnit::Degrees(v) => Self::Output::degrees(-v),
AngleUnit::Radians(v) => Self::Output::radians(-v),
}
}
}
impl<T: Default + Clone> Angle<T> {
pub fn degrees(value: T) -> Angle<T> {
Self {
value: AngleUnit::Degrees(value),
}
}
pub fn radians(value: T) -> Angle<T> {
Self {
value: AngleUnit::Radians(value),
}
}
pub fn value(&self) -> T {
match self.value.clone() {
AngleUnit::Degrees(v) => v,
AngleUnit::Radians(v) => v,
}
}
}
impl<T: Default + num_traits::float::Float> Angle<T> {
pub fn to_radians(self) -> Self {
match self.value {
AngleUnit::Degrees(v) => Self {
value: AngleUnit::Radians(v.to_radians()),
},
AngleUnit::Radians(_) => self,
}
}
}
impl<T: Add<Output = T> + Default + num_traits::float::Float> Add for Angle<T> {
type Output = Angle<T>;
fn add(self, rhs: Self) -> Self::Output {
match (self.value, rhs.value) {
(AngleUnit::Degrees(v), AngleUnit::Degrees(o)) => Self::Output {
value: AngleUnit::Degrees(v + o),
},
(AngleUnit::Degrees(v), AngleUnit::Radians(o)) => Self::Output {
value: AngleUnit::Radians(v.to_radians() + o),
},
(AngleUnit::Radians(v), AngleUnit::Degrees(o)) => Self::Output {
value: AngleUnit::Radians(v + o.to_radians()),
},
(AngleUnit::Radians(v), AngleUnit::Radians(o)) => Self::Output {
value: AngleUnit::Radians(v + o),
},
}
}
}
impl<T: Sub<Output = T> + Default + num_traits::float::Float> Sub for Angle<T> {
type Output = Angle<T>;
fn sub(self, rhs: Self) -> Self::Output {
match (self.value, rhs.value) {
(AngleUnit::Degrees(v), AngleUnit::Degrees(o)) => Self::Output {
value: AngleUnit::Degrees(v - o),
},
(AngleUnit::Degrees(v), AngleUnit::Radians(o)) => Self::Output {
value: AngleUnit::Radians(v.to_radians() - o),
},
(AngleUnit::Radians(v), AngleUnit::Degrees(o)) => Self::Output {
value: AngleUnit::Radians(v - o.to_radians()),
},
(AngleUnit::Radians(v), AngleUnit::Radians(o)) => Self::Output {
value: AngleUnit::Radians(v - o),
},
}
}
}
#[test]
fn convert_to_radians() {
let radi = Angle::radians(30f32.to_radians());
let degr = Angle::degrees(30f32);
let converted = degr.to_radians();
assert_eq!(radi, converted)
}

0
src/datatypes/length.rs Normal file
View File

View File

@ -1,3 +1,4 @@
mod datatypes;
mod debug; mod debug;
mod primitives; mod primitives;
mod turtle; mod turtle;

View File

@ -6,7 +6,7 @@ use bevy_prototype_lyon::{
}; };
use bevy_tweening::Lens; use bevy_tweening::Lens;
use crate::turtle::Angle; use crate::datatypes::angle::Angle;
pub(crate) struct LineAnimationLens { pub(crate) struct LineAnimationLens {
start: Vec2, start: Vec2,
@ -58,8 +58,8 @@ pub(crate) struct CircleAnimationLens {
pub start_pos: Vec2, pub start_pos: Vec2,
pub center: Vec2, pub center: Vec2,
pub radii: Vec2, pub radii: Vec2,
pub start: Angle, pub start: Angle<f32>,
pub end: Angle, pub end: Angle<f32>,
} }
impl Lens<Path> for CircleAnimationLens { impl Lens<Path> for CircleAnimationLens {
@ -70,7 +70,9 @@ impl Lens<Path> for CircleAnimationLens {
path_builder.arc( path_builder.arc(
self.center, self.center,
self.radii, self.radii,
(self.start.0 + ((self.end.0 - self.start.0) * ratio)).to_radians(), (self.start + ((self.end - self.start) * ratio))
.to_radians()
.value(),
0., 0.,
); );
let line = path_builder.build(); let line = path_builder.build();
@ -81,17 +83,17 @@ impl Lens<Path> for CircleAnimationLens {
pub(crate) struct CircleMovementLens { pub(crate) struct CircleMovementLens {
pub center: Vec2, pub center: Vec2,
pub start: Transform, pub start: Transform,
pub end: Angle, pub end: Angle<f32>,
} }
impl Lens<Transform> for CircleMovementLens { impl Lens<Transform> for CircleMovementLens {
fn lerp(&mut self, target: &mut Transform, ratio: f32) { fn lerp(&mut self, target: &mut Transform, ratio: f32) {
let angle = self.end.0 * ratio; let angle = self.end * ratio;
let mut rotated = self.start; let mut rotated = self.start;
rotated.rotate_around( rotated.rotate_around(
self.center.extend(0.), self.center.extend(0.),
Quat::from_rotation_z(angle.to_radians()), Quat::from_rotation_z(angle.to_radians().value()),
); );
*target = rotated; *target = rotated;
@ -114,7 +116,7 @@ impl TurtleDrawCircle {
pub(crate) fn new( pub(crate) fn new(
center: Vec2, center: Vec2,
radii: Vec2, radii: Vec2,
angle: Angle, angle: Angle<f32>,
index: u64, index: u64,
start: Vec2, start: Vec2,
end: Vec2, end: Vec2,
@ -122,7 +124,7 @@ impl TurtleDrawCircle {
let mut path_builder = PathBuilder::new(); let mut path_builder = PathBuilder::new();
path_builder.move_to(start); path_builder.move_to(start);
// The center point of the radius, then the radii in x and y direction, then the angle that will be drawn, then the x_rotation ? // The center point of the radius, then the radii in x and y direction, then the angle that will be drawn, then the x_rotation ?
path_builder.arc(center, radii, angle.0.to_radians(), 0.); path_builder.arc(center, radii, angle.to_radians().value(), 0.);
/* println!("The radiuses: {}", radii); /* println!("The radiuses: {}", radii);
path_builder.move_to(Vec2::ZERO); path_builder.move_to(Vec2::ZERO);

View File

@ -9,6 +9,7 @@ use bevy_tweening::{
}; };
use crate::{ use crate::{
datatypes::angle::Angle,
primitives::{CircleAnimationLens, LineAnimationLens, TurtleDrawCircle, TurtleDrawLine}, primitives::{CircleAnimationLens, LineAnimationLens, TurtleDrawCircle, TurtleDrawLine},
turtle_shapes, turtle_shapes,
}; };
@ -44,19 +45,19 @@ impl Default for Turtle {
TurtleCommand::Forward(Length(-100.)), TurtleCommand::Forward(Length(-100.)),
TurtleCommand::Circle { TurtleCommand::Circle {
radius: Length(150.), radius: Length(150.),
angle: Angle(30.), angle: Angle::degrees(30.),
}, },
TurtleCommand::Forward(Length(100.)), TurtleCommand::Forward(Length(100.)),
TurtleCommand::Circle { TurtleCommand::Circle {
radius: Length(70.), radius: Length(70.),
angle: Angle(60.), angle: Angle::degrees(60.),
}, },
TurtleCommand::Forward(Length(100.)), TurtleCommand::Forward(Length(100.)),
TurtleCommand::Right(Angle(90.)), TurtleCommand::Right(Angle::degrees(70.)),
//TurtleCommand::PenDown, //TurtleCommand::PenDown,
TurtleCommand::Circle { TurtleCommand::Circle {
radius: Length(30.), radius: Length(30.),
angle: Angle(360. - 46.), angle: Angle::degrees(360. - 46.),
}, },
/* TurtleCommand::Backward(Length(100.)), /* TurtleCommand::Backward(Length(100.)),
TurtleCommand::Right(Angle(90.)), TurtleCommand::Right(Angle(90.)),
@ -111,7 +112,7 @@ pub struct TurtleCommands {
#[derive(Inspectable)] #[derive(Inspectable)]
pub struct TurtleState { pub struct TurtleState {
pub start: Vec2, pub start: Vec2,
pub heading: f32, pub heading: Angle<f32>,
pub index: u64, pub index: u64,
pub drawing: bool, pub drawing: bool,
} }
@ -123,7 +124,7 @@ impl TurtleCommands {
lines: vec![], lines: vec![],
state: TurtleState { state: TurtleState {
start: Vec2::ZERO, start: Vec2::ZERO,
heading: 0f32.to_radians(), heading: Angle::degrees(0.),
index: 0, index: 0,
drawing: true, drawing: true,
}, },
@ -144,11 +145,11 @@ impl TurtleCommands {
TurtleCommand::Backward(Length(x)) => { TurtleCommand::Backward(Length(x)) => {
crate::turtle_movement::turtle_move(&mut self.state, -*x as f32) crate::turtle_movement::turtle_move(&mut self.state, -*x as f32)
} }
TurtleCommand::Left(Angle(x)) => { TurtleCommand::Left(angle) => {
crate::turtle_movement::turtle_turn(&mut self.state, *x as f32) crate::turtle_movement::turtle_turn(&mut self.state, *angle)
} }
TurtleCommand::Right(Angle(x)) => { TurtleCommand::Right(angle) => {
crate::turtle_movement::turtle_turn(&mut self.state, -*x as f32) crate::turtle_movement::turtle_turn(&mut self.state, -angle)
} }
TurtleCommand::PenUp => { TurtleCommand::PenUp => {
self.state.drawing = false; self.state.drawing = false;
@ -182,7 +183,7 @@ pub enum TurtleGraphElement {
end: Vec2, end: Vec2,
center: Vec2, center: Vec2,
radii: Vec2, radii: Vec2,
angle: Angle, angle: Angle<f32>,
}, },
#[default] #[default]
Noop, Noop,
@ -199,22 +200,20 @@ pub struct Colors {
#[derive(Inspectable, Default, Copy, Clone, Debug)] #[derive(Inspectable, Default, Copy, Clone, Debug)]
pub struct Length(f32); pub struct Length(f32);
#[derive(Inspectable, Default, Copy, Clone, Debug)]
pub struct Angle(pub f32);
#[derive(Component, Inspectable, Default)] #[derive(Component, Inspectable, Default)]
pub enum TurtleCommand { pub enum TurtleCommand {
Forward(Length), Forward(Length),
Backward(Length), Backward(Length),
Left(Angle), Left(Angle<f32>),
Right(Angle), Right(Angle<f32>),
PenUp, PenUp,
PenDown, PenDown,
#[default] #[default]
Pause, Pause,
Circle { Circle {
radius: Length, radius: Length,
angle: Angle, angle: Angle<f32>,
}, },
} }
@ -284,7 +283,7 @@ fn draw_lines(
start_pos: start, start_pos: start,
center, center,
radii, radii,
start: Angle(0.), start: Angle::degrees(0.),
end: angle, end: angle,
}, },
)); ));
@ -292,7 +291,7 @@ fn draw_lines(
.spawn_bundle(TurtleDrawCircle::new( .spawn_bundle(TurtleDrawCircle::new(
center, center,
radii, radii,
Angle(0.), Angle::degrees(0.),
tcmd.state.index, tcmd.state.index,
start, start,
end, end,
@ -324,7 +323,7 @@ fn keypresses(
let mut tcmd = tcmd.single_mut(); let mut tcmd = tcmd.single_mut();
tcmd.state = TurtleState { tcmd.state = TurtleState {
start: Vec2::ZERO, start: Vec2::ZERO,
heading: 0., heading: Angle::degrees(0.),
index: 0, index: 0,
drawing: true, drawing: true,
}; };

View File

@ -7,13 +7,14 @@ use bevy_tweening::{
}; };
use crate::{ use crate::{
datatypes::angle::Angle,
primitives::{CircleAnimationLens, CircleMovementLens}, primitives::{CircleAnimationLens, CircleMovementLens},
turtle::{Angle, TurtleGraphElement, TurtleState}, turtle::{TurtleGraphElement, TurtleState},
}; };
pub fn turtle_turn( pub fn turtle_turn(
state: &mut TurtleState, state: &mut TurtleState,
angle_to_turn: f32, angle_to_turn: Angle<f32>,
) -> (Option<Tween<Transform>>, Option<TurtleGraphElement>) { ) -> (Option<Tween<Transform>>, Option<TurtleGraphElement>) {
let start = state.heading; let start = state.heading;
let end = state.heading + (angle_to_turn * PI / 180.); let end = state.heading + (angle_to_turn * PI / 180.);
@ -24,7 +25,10 @@ pub fn turtle_turn(
// Animation time // Animation time
Duration::from_millis(500), Duration::from_millis(500),
// Rotate the turtle // Rotate the turtle
TransformRotateZLens { start, end }, TransformRotateZLens {
start: start.value(),
end: end.value(),
},
) )
.with_completed_event(state.index as u64); .with_completed_event(state.index as u64);
// Dont move and draw // Dont move and draw
@ -39,7 +43,7 @@ pub fn turtle_move(
length: f32, length: f32,
) -> (Option<Tween<Transform>>, Option<TurtleGraphElement>) { ) -> (Option<Tween<Transform>>, Option<TurtleGraphElement>) {
let start = state.start; let start = state.start;
let end = state.start + (Vec2::from_angle(state.heading) * length); let end = state.start + (Vec2::from_angle(state.heading.to_radians().value()) * length);
let turtle_movement_animation = Tween::new( let turtle_movement_animation = Tween::new(
// accelerate and decelerate // accelerate and decelerate
EaseFunction::QuadraticInOut, EaseFunction::QuadraticInOut,
@ -66,14 +70,15 @@ pub fn turtle_move(
pub fn turtle_circle( pub fn turtle_circle(
state: &mut TurtleState, state: &mut TurtleState,
radius: f32, radius: f32,
angle: Angle, angle: Angle<f32>,
) -> (Option<Tween<Transform>>, Option<TurtleGraphElement>) { ) -> (Option<Tween<Transform>>, Option<TurtleGraphElement>) {
let radius_tuple = Vec2::ONE * radius.abs(); let radius_tuple = Vec2::ONE * radius.abs();
let left_right = if radius >= 0. { 90f32 } else { -90. }; let left_right = Angle::degrees(if radius >= 0. { 90. } else { -90. });
println!("Heading: {}", state.heading); println!("Heading: {}", state.heading.value());
let center = state.start let center = state.start
+ (Vec2::new(radius.abs(), 0.) + (Vec2::new(radius.abs(), 0.).rotate(Vec2::from_angle(
.rotate(Vec2::from_angle(state.heading + left_right.to_radians()))); (state.heading + left_right.to_radians()).value(),
)));
let turtle_movement_animation = Tween::new( let turtle_movement_animation = Tween::new(
// accelerate and decelerate // accelerate and decelerate
@ -85,7 +90,7 @@ pub fn turtle_circle(
CircleMovementLens { CircleMovementLens {
start: Transform { start: Transform {
translation: state.start.extend(0.), translation: state.start.extend(0.),
rotation: Quat::from_rotation_z(state.heading), rotation: Quat::from_rotation_z(state.heading.to_radians().value()),
scale: Vec3::ONE, scale: Vec3::ONE,
}, },
end: angle, end: angle,
@ -105,6 +110,10 @@ pub fn turtle_circle(
} else { } else {
TurtleGraphElement::Noop TurtleGraphElement::Noop
}; };
// TODO update end_position : state.start = end; let end_pos = center
+ Vec2::new(radius.abs(), 0.).rotate(Vec2::from_angle(
(state.heading + angle).to_radians().value(),
));
state.start = end_pos;
(Some(turtle_movement_animation), Some(line)) (Some(turtle_movement_animation), Some(line))
} }