diff --git a/src/kernel/geometry/curves/circle.rs b/src/kernel/geometry/curves/circle.rs index cfa96b4a5..6cad83bd4 100644 --- a/src/kernel/geometry/curves/circle.rs +++ b/src/kernel/geometry/curves/circle.rs @@ -35,13 +35,13 @@ impl Circle { /// Converts the provided point into curve coordinates between `0.` /// (inclusive) and `PI * 2.` (exclusive). /// - /// Ignores the radius, meaning points that are not on the circle will be - /// converted to the curve coordinate of their projection on the circle. + /// Projects the point onto the circle before computing curve coordinate, + /// ignoring the radius. This is done to make this method robust against + /// floating point accuracy issues. /// - /// This is done to make this method robust against floating point accuracy - /// issues. Callers are advised to be careful about the points they pass, as - /// the point not being on the circle, intended or not, will not result in - /// an error. + /// Callers are advised to be careful about the points they pass, as the + /// point not being on the curve, intentional or not, will not result in an + /// error. pub fn point_model_to_curve(&self, point: &Point<3>) -> Point<1> { let v = point - self.center; let atan = f64::atan2(v.y, v.x); @@ -49,6 +49,24 @@ impl Circle { point![coord] } + /// Convert a point on the curve into model coordinates + pub fn point_curve_to_model(&self, point: &Point<1>) -> Point<3> { + self.center + self.vector_curve_to_model(&point.coords) + } + + /// Convert a vector on the curve into model coordinates + pub fn vector_curve_to_model(&self, point: &Vector<1>) -> Vector<3> { + let radius = self.radius.magnitude(); + let angle = point.x; + + let (sin, cos) = angle.sin_cos(); + + let x = cos * radius; + let y = sin * radius; + + vector![x, y, 0.] + } + pub fn approx(&self, tolerance: f64, out: &mut Vec>) { let radius = self.radius.magnitude(); @@ -62,14 +80,7 @@ impl Circle { for i in 0..n { let angle = 2. * PI / n as f64 * i as f64; - - let (sin, cos) = angle.sin_cos(); - - let x = cos * radius; - let y = sin * radius; - - let point = self.center + vector![x, y, 0.]; - + let point = self.point_curve_to_model(&point![angle]); out.push(point); } } @@ -93,7 +104,7 @@ mod tests { use super::Circle; #[test] - fn test_point_model_to_curve() { + fn point_model_to_curve() { let circle = Circle { center: point![1., 2., 3.], radius: vector![1., 0.], @@ -118,7 +129,7 @@ mod tests { } #[test] - fn test_number_of_vertices() { + fn number_of_vertices() { verify_result(50., 100., 3); verify_result(10., 100., 7); verify_result(1., 100., 23); diff --git a/src/kernel/geometry/curves/line.rs b/src/kernel/geometry/curves/line.rs index c9d6fa45b..449ba2467 100644 --- a/src/kernel/geometry/curves/line.rs +++ b/src/kernel/geometry/curves/line.rs @@ -30,13 +30,12 @@ impl Line { /// Convert a point in model coordinates to curve coordinates /// - /// Ignores the distance of the point to the line, meaning points on the - /// line will be converted to the curve coordinates of their projection on - /// the line. + /// Projects the point onto the line before computing curve coordinate. This + /// is done to make this method robust against floating point accuracy + /// issues. /// - /// This is done to make this method robust against floating point accuracy - /// issues. Callers are advised to be careful about the points they pass, as - /// the point not being on the line, intended or not, will not result in an + /// Callers are advised to be careful about the points they pass, as the + /// point not being on the line, intentional or not, will never result in an /// error. pub fn point_model_to_curve(&self, point: &Point<3>) -> Point<1> { let p = point - self.origin; @@ -46,6 +45,19 @@ impl Line { point![t] } + + /// Convert a point on the curve into model coordinates + #[cfg(test)] + pub fn point_curve_to_model(&self, point: &Point<1>) -> Point<3> { + self.origin + self.vector_curve_to_model(&point.coords) + } + + /// Convert a vector on the curve into model coordinates + #[cfg(test)] + pub fn vector_curve_to_model(&self, point: &Vector<1>) -> Vector<3> { + let t = point.x; + self.direction * t + } } impl AbsDiffEq for Line { @@ -74,7 +86,7 @@ mod tests { use super::Line; #[test] - fn test_transform() { + fn transform() { let line = Line { origin: point![1., 0., 0.], direction: vector![0., 1., 0.], @@ -96,7 +108,7 @@ mod tests { } #[test] - fn test_point_model_to_curve() { + fn point_model_to_curve() { let line = Line { origin: point![1., 0., 0.], direction: vector![2., 0., 0.], @@ -108,7 +120,7 @@ mod tests { verify(line, 2.); fn verify(line: Line, t: f64) { - let point = line.origin + line.direction * t; + let point = line.point_curve_to_model(&point![t]); let t_result = line.point_model_to_curve(&point); assert_eq!(point![t], t_result); diff --git a/src/kernel/geometry/curves/mod.rs b/src/kernel/geometry/curves/mod.rs index 2afbfa1a8..8745429c6 100644 --- a/src/kernel/geometry/curves/mod.rs +++ b/src/kernel/geometry/curves/mod.rs @@ -36,8 +36,8 @@ impl Curve { #[must_use] pub fn transform(self, transform: &Isometry) -> Self { match self { - Self::Circle(circle) => Self::Circle(circle.transform(transform)), - Self::Line(line) => Self::Line(line.transform(transform)), + Self::Circle(curve) => Self::Circle(curve.transform(transform)), + Self::Line(curve) => Self::Line(curve.transform(transform)), #[cfg(test)] Self::Mock { .. } => todo!(), @@ -46,18 +46,17 @@ impl Curve { /// Convert a point in model coordinates to curve coordinates /// - /// Whether the point is actually on the curve or not will be ignored. The - /// curve coordinates of the projection of the point on the curve will be - /// returned. - /// + /// Projects the point onto the curve before computing curve coordinate. /// This is done to make this method robust against floating point accuracy - /// issues. Callers are advised to be careful about the points they pass, as - /// the point not being on the curve, intended or not, will not result in an - /// error. + /// issues. + /// + /// Callers are advised to be careful about the points they pass, as the + /// point not being on the curve, intentional or not, will never result in + /// an error. pub fn point_model_to_curve(&self, point: &Point<3>) -> Point<1> { match self { - Self::Circle(circle) => circle.point_model_to_curve(point), - Self::Line(line) => line.point_model_to_curve(point), + Self::Circle(curve) => curve.point_model_to_curve(point), + Self::Line(curve) => curve.point_model_to_curve(point), #[cfg(test)] Self::Mock { coords, .. } => coords.borrow_mut().remove(0),