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

View File

@ -11,6 +11,7 @@ bevy = { version = "0.8", features = ["dynamic"] }
bevy-inspector-egui = "0.12.1"
bevy_prototype_lyon = "0.6"
bevy_tweening = "0.5.0"
num-traits = "0.2"
# Enable a small amount of optimization in debug mode
[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 primitives;
mod turtle;

View File

@ -6,7 +6,7 @@ use bevy_prototype_lyon::{
};
use bevy_tweening::Lens;
use crate::turtle::Angle;
use crate::datatypes::angle::Angle;
pub(crate) struct LineAnimationLens {
start: Vec2,
@ -58,8 +58,8 @@ pub(crate) struct CircleAnimationLens {
pub start_pos: Vec2,
pub center: Vec2,
pub radii: Vec2,
pub start: Angle,
pub end: Angle,
pub start: Angle<f32>,
pub end: Angle<f32>,
}
impl Lens<Path> for CircleAnimationLens {
@ -70,7 +70,9 @@ impl Lens<Path> for CircleAnimationLens {
path_builder.arc(
self.center,
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.,
);
let line = path_builder.build();
@ -81,17 +83,17 @@ impl Lens<Path> for CircleAnimationLens {
pub(crate) struct CircleMovementLens {
pub center: Vec2,
pub start: Transform,
pub end: Angle,
pub end: Angle<f32>,
}
impl Lens<Transform> for CircleMovementLens {
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;
rotated.rotate_around(
self.center.extend(0.),
Quat::from_rotation_z(angle.to_radians()),
Quat::from_rotation_z(angle.to_radians().value()),
);
*target = rotated;
@ -114,7 +116,7 @@ impl TurtleDrawCircle {
pub(crate) fn new(
center: Vec2,
radii: Vec2,
angle: Angle,
angle: Angle<f32>,
index: u64,
start: Vec2,
end: Vec2,
@ -122,7 +124,7 @@ impl TurtleDrawCircle {
let mut path_builder = PathBuilder::new();
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 ?
path_builder.arc(center, radii, angle.0.to_radians(), 0.);
path_builder.arc(center, radii, angle.to_radians().value(), 0.);
/* println!("The radiuses: {}", radii);
path_builder.move_to(Vec2::ZERO);

View File

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

View File

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