From 9532f538a392503be0799f75dfbe0fb99e192c8b Mon Sep 17 00:00:00 2001 From: gabsi26 Date: Mon, 23 May 2022 15:45:27 +0200 Subject: [PATCH 1/4] Create `Angle` type to differentiate explicitly between degrees and radians. --- crates/fj/src/angle.rs | 101 +++++++++++++++++++++++++++++++++++++++++ crates/fj/src/lib.rs | 3 +- 2 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 crates/fj/src/angle.rs diff --git a/crates/fj/src/angle.rs b/crates/fj/src/angle.rs new file mode 100644 index 000000000..4bef7caac --- /dev/null +++ b/crates/fj/src/angle.rs @@ -0,0 +1,101 @@ +use std::f64::consts::PI; + +/// An angle +#[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))] +pub struct Angle { + // The value of the angle in radians + rad: f64, +} + +impl Angle { + /// Create a new angle specified in radians + pub fn from_rad(rad: f64) -> Self { + Self { + rad: Self::wrap(rad), + } + } + /// Create a new angle specified in degrees + pub fn from_deg(deg: f64) -> Self { + Self::from_rad(deg * PI / 180.) + } + /// Retrieve value of angle as radians + pub fn rad(&self) -> f64 { + self.rad + } + /// Retrieve value of angle as degrees + pub fn deg(&self) -> f64 { + self.rad / PI * 180. + } + + // ensures that the angle is always 0 <= a < 2*pi + fn wrap(rad: f64) -> f64 { + let modulo = rad % (2. * PI); + if modulo < 0. { + modulo * -1. + } else { + modulo + } + } + + // ensures that the angle is always 0 <= a < 2*pi + fn wrap_assign(&mut self) { + self.rad = Self::wrap(self.rad); + } +} + +impl std::ops::Add for Angle { + type Output = Angle; + fn add(self, rhs: Self) -> Self::Output { + Self::from_rad(self.rad + rhs.rad) + } +} + +impl std::ops::AddAssign for Angle { + fn add_assign(&mut self, rhs: Self) { + self.rad += rhs.rad; + self.wrap_assign() + } +} + +impl std::ops::Sub for Angle { + type Output = Angle; + fn sub(self, rhs: Self) -> Self::Output { + Self::from_rad(self.rad - rhs.rad) + } +} + +impl std::ops::SubAssign for Angle { + fn sub_assign(&mut self, rhs: Self) { + self.rad -= rhs.rad; + self.wrap_assign() + } +} + +impl std::ops::Mul for Angle { + type Output = Angle; + fn mul(self, rhs: Self) -> Self::Output { + Self::from_rad(self.rad * rhs.rad) + } +} + +impl std::ops::MulAssign for Angle { + fn mul_assign(&mut self, rhs: Self) { + self.rad *= rhs.rad; + self.wrap_assign() + } +} + +impl std::ops::Div for Angle { + type Output = Angle; + fn div(self, rhs: Self) -> Self::Output { + Self::from_rad(self.rad / rhs.rad) + } +} + +impl std::ops::DivAssign for Angle { + fn div_assign(&mut self, rhs: Self) { + self.rad /= rhs.rad; + self.wrap_assign() + } +} diff --git a/crates/fj/src/lib.rs b/crates/fj/src/lib.rs index 4214800e3..c65b29a88 100644 --- a/crates/fj/src/lib.rs +++ b/crates/fj/src/lib.rs @@ -20,10 +20,11 @@ pub mod syntax; +mod angle; mod shape_2d; mod shape_3d; -pub use self::{shape_2d::*, shape_3d::*}; +pub use self::{angle::*, shape_2d::*, shape_3d::*}; /// A shape #[derive(Clone, Debug)] From 907364a70fd4815b8dbc9b5a40bc6159590ec766 Mon Sep 17 00:00:00 2001 From: gabsi26 Date: Mon, 23 May 2022 15:45:35 +0200 Subject: [PATCH 2/4] Use `Angle` type anywhere a raw f64 angle was used previously. Most likely many places have been missed since --- crates/fj-operations/src/transform.rs | 2 +- crates/fj/src/shape_3d.rs | 4 ++-- crates/fj/src/syntax.rs | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/fj-operations/src/transform.rs b/crates/fj-operations/src/transform.rs index 79055cfbd..454a0e1eb 100644 --- a/crates/fj-operations/src/transform.rs +++ b/crates/fj-operations/src/transform.rs @@ -29,5 +29,5 @@ impl ToShape for fj::Transform { fn transform(transform: &fj::Transform) -> Transform { let axis = Vector::from(transform.axis).normalize(); Transform::translation(transform.offset) - * Transform::rotation(axis * transform.angle) + * Transform::rotation(axis * transform.angle.rad()) } diff --git a/crates/fj/src/shape_3d.rs b/crates/fj/src/shape_3d.rs index 29c66f31e..2376fa157 100644 --- a/crates/fj/src/shape_3d.rs +++ b/crates/fj/src/shape_3d.rs @@ -1,4 +1,4 @@ -use crate::{Shape, Shape2d}; +use crate::{Angle, Shape, Shape2d}; /// A 3-dimensional shape #[derive(Clone, Debug)] @@ -72,7 +72,7 @@ pub struct Transform { pub axis: [f64; 3], /// The angle of the rotation - pub angle: f64, + pub angle: Angle, /// The offset of the translation pub offset: [f64; 3], diff --git a/crates/fj/src/syntax.rs b/crates/fj/src/syntax.rs index fc67d01a7..34fb0c076 100644 --- a/crates/fj/src/syntax.rs +++ b/crates/fj/src/syntax.rs @@ -99,7 +99,7 @@ pub trait Transform { /// /// Create a rotation that rotates `shape` by `angle` around an axis defined /// by `axis`. - fn rotate(&self, axis: [f64; 3], angle: f64) -> crate::Transform; + fn rotate(&self, axis: [f64; 3], angle: crate::Angle) -> crate::Transform; /// Create a translation /// @@ -111,7 +111,7 @@ impl Transform for T where T: Clone + Into, { - fn rotate(&self, axis: [f64; 3], angle: f64) -> crate::Transform { + fn rotate(&self, axis: [f64; 3], angle: crate::Angle) -> crate::Transform { let shape = self.clone().into(); crate::Transform { shape, @@ -126,7 +126,7 @@ where crate::Transform { shape, axis: [1., 0., 0.], - angle: 0., + angle: crate::Angle::from_rad(0.), offset, } } From da1118ad465cd18bbbfe878860f631c238f2ad94 Mon Sep 17 00:00:00 2001 From: gabsi26 Date: Mon, 23 May 2022 15:54:10 +0200 Subject: [PATCH 3/4] Use `Angle` type in star model --- models/star/src/lib.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/models/star/src/lib.rs b/models/star/src/lib.rs index e17caa351..371da98c3 100644 --- a/models/star/src/lib.rs +++ b/models/star/src/lib.rs @@ -1,3 +1,4 @@ +use fj::Angle; use std::{collections::HashMap, f64::consts::PI}; #[no_mangle] @@ -31,7 +32,7 @@ pub extern "C" fn model(args: &HashMap) -> fj::Shape { // gives us the angle and radius for each vertex. let num_vertices = num_points * 2; let vertex_iter = (0..num_vertices).map(|i| { - let angle = 2. * PI / num_vertices as f64 * i as f64; + let angle = Angle::from_rad(2. * PI / num_vertices as f64 * i as f64); let radius = if i % 2 == 0 { r1 } else { r2 }; (angle, radius) }); @@ -41,7 +42,7 @@ pub extern "C" fn model(args: &HashMap) -> fj::Shape { let mut outer = Vec::new(); let mut inner = Vec::new(); for (angle, radius) in vertex_iter { - let (sin, cos) = angle.sin_cos(); + let (sin, cos) = angle.rad().sin_cos(); let x = cos * radius; let y = sin * radius; From 010a2650ae8141835da9121167023eaee0921af4 Mon Sep 17 00:00:00 2001 From: Hanno Braun Date: Mon, 23 May 2022 16:19:34 +0200 Subject: [PATCH 4/4] Use standard library methods to simplify code --- crates/fj/src/angle.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/fj/src/angle.rs b/crates/fj/src/angle.rs index 4bef7caac..5ad919afa 100644 --- a/crates/fj/src/angle.rs +++ b/crates/fj/src/angle.rs @@ -17,7 +17,7 @@ impl Angle { } /// Create a new angle specified in degrees pub fn from_deg(deg: f64) -> Self { - Self::from_rad(deg * PI / 180.) + Self::from_rad(deg.to_radians()) } /// Retrieve value of angle as radians pub fn rad(&self) -> f64 { @@ -25,7 +25,7 @@ impl Angle { } /// Retrieve value of angle as degrees pub fn deg(&self) -> f64 { - self.rad / PI * 180. + self.rad.to_degrees() } // ensures that the angle is always 0 <= a < 2*pi