Bezier-rs: Updated Bezier function signatures to accept TValue (#967)

* Create helper for converting d to t values

* Add euclidean option for tangent and normal

* Modified bezier functions signatures to accept ComputeType

* Stylistic changes per review

* Added ComputeType documentation

* Renamed ComputeType to TValue

* Fixed comments

* Fixed failing unit tests

* Code review

* Fix comments in code review

* Renamed compute_type_to_parametric to t_value_to_parametric

---------

Co-authored-by: Linda Zheng <thelindazheng@gmail.com>
Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
Rob Nadal
2023-02-13 12:31:51 -05:00
committed by Keavon Chambers
co-authored by Linda Zheng Keavon Chambers
parent f0ad4c91d3
commit a64c856ec4
25 changed files with 456 additions and 433 deletions
+78 -75
View File
@@ -1,14 +1,15 @@
use super::*;
use crate::utils::{f64_compare, ComputeType};
use crate::utils::{f64_compare, TValue};
use glam::DMat2;
use std::f64::consts::PI;
/// Functionality that transform Beziers, such as split, reduce, offset, etc.
impl Bezier {
/// Returns the pair of Bezier curves that result from splitting the original curve at the point corresponding to `t`.
pub fn split(&self, t: f64) -> [Bezier; 2] {
let split_point = self.evaluate(ComputeType::Parametric(t));
/// Returns the pair of Bezier curves that result from splitting the original curve at the point `t` along the curve.
pub fn split(&self, t: TValue) -> [Bezier; 2] {
let t = self.t_value_to_parametric(t);
let split_point = self.evaluate(TValue::Parametric(t));
match self.handles {
BezierHandles::Linear => [Bezier::from_linear_dvec2(self.start, split_point), Bezier::from_linear_dvec2(split_point, self.end)],
@@ -49,11 +50,13 @@ impl Bezier {
}
}
/// Returns the Bezier curve representing the sub-curve starting at the point corresponding to `t1` and ending at the point corresponding to `t2`.
pub fn trim(&self, t1: f64, t2: f64) -> Bezier {
/// Returns the Bezier curve representing the sub-curve starting at the point `t1` and ending at the point `t2` along the curve.
/// When `t1 < t2`, returns the reversed sub-curve starting at `t2` and ending at `t1`.
pub fn trim(&self, t1: TValue, t2: TValue) -> Bezier {
let (t1, t2) = (self.t_value_to_parametric(t1), self.t_value_to_parametric(t2));
// If t1 is equal to t2, return a bezier comprised entirely of the same point
if f64_compare(t1, t2, MAX_ABSOLUTE_DIFFERENCE) {
let point = self.evaluate(ComputeType::Parametric(t1));
let point = self.evaluate(TValue::Parametric(t1));
return match self.handles {
BezierHandles::Linear => Bezier::from_linear_dvec2(point, point),
BezierHandles::Quadratic { handle: _ } => Bezier::from_quadratic_dvec2(point, point, point),
@@ -63,7 +66,7 @@ impl Bezier {
// Depending on the order of `t1` and `t2`, determine which half of the split we need to keep
let t1_split_side = usize::from(t1 <= t2);
let t2_split_side = usize::from(t1 > t2);
let bezier_starting_at_t1 = self.split(t1)[t1_split_side];
let bezier_starting_at_t1 = self.split(TValue::Parametric(t1))[t1_split_side];
// Adjust the ratio `t2` to its corresponding value on the new curve that was split on `t1`
let adjusted_t2 = if t1 < t2 || t1 == 0. {
// Case where we took the split from t1 to the end
@@ -73,7 +76,7 @@ impl Bezier {
// Case where we took the split from the beginning to `t1`
t2 / t1
};
let result = bezier_starting_at_t1.split(adjusted_t2)[t2_split_side];
let result = bezier_starting_at_t1.split(TValue::Parametric(adjusted_t2))[t2_split_side];
if t2 < t1 {
return result.reverse();
}
@@ -132,8 +135,8 @@ impl Bezier {
}
}
// Verify the angle formed by the endpoint normals is sufficiently small, ensuring the on-curve point for `t = 0.5` occurs roughly in the center of the polygon.
let normal_0 = self.normal(0.);
let normal_1 = self.normal(1.);
let normal_0 = self.normal(TValue::Parametric(0.));
let normal_1 = self.normal(TValue::Parametric(1.));
let endpoint_normal_angle = (normal_0.x * normal_1.x + normal_0.y * normal_1.y).acos();
endpoint_normal_angle < SCALABLE_CURVE_MAX_ENDPOINT_NORMAL_ANGLE
}
@@ -169,7 +172,7 @@ impl Bezier {
extrema.windows(2).for_each(|t_pair| {
let t_subcurve_start = t_pair[0];
let t_subcurve_end = t_pair[1];
let subcurve = self.trim(t_subcurve_start, t_subcurve_end);
let subcurve = self.trim(TValue::Parametric(t_subcurve_start), TValue::Parametric(t_subcurve_end));
// Perform no processing on the subcurve if it's already scalable.
if subcurve.is_scalable() {
result_beziers.push(subcurve);
@@ -177,7 +180,7 @@ impl Bezier {
return;
}
// According to <https://pomax.github.io/bezierinfo/#offsetting>, it is generally sufficient to split subcurves with no local extrema at `t = 0.5` to generate two scalable segments.
let [first_half, second_half] = subcurve.split(0.5);
let [first_half, second_half] = subcurve.split(TValue::Parametric(0.5));
if first_half.is_scalable() && second_half.is_scalable() {
result_beziers.push(first_half);
result_beziers.push(second_half);
@@ -191,14 +194,14 @@ impl Bezier {
let mut t1 = 0.;
let mut t2 = step_size;
while t2 <= 1. + step_size {
segment = subcurve.trim(t1, f64::min(t2, 1.));
segment = subcurve.trim(TValue::Parametric(t1), TValue::Parametric(f64::min(t2, 1.)));
if !segment.is_scalable() {
t2 -= step_size;
// If the previous step does not exist, the start of the subcurve is irreducible.
// Otherwise, add the valid segment from the previous step to the result.
if f64::abs(t1 - t2) >= step_size {
segment = subcurve.trim(t1, t2);
segment = subcurve.trim(TValue::Parametric(t1), TValue::Parametric(t2));
result_beziers.push(segment);
result_t_values.push(t_subcurve_start + t2 * (t_subcurve_end - t_subcurve_start));
} else {
@@ -210,7 +213,7 @@ impl Bezier {
}
// Collect final remainder of the curve.
if t1 < 1. {
segment = subcurve.trim(t1, 1.);
segment = subcurve.trim(TValue::Parametric(t1), TValue::Parametric(1.));
if segment.is_scalable() {
result_beziers.push(segment);
result_t_values.push(t_subcurve_end);
@@ -236,8 +239,8 @@ impl Bezier {
fn scale(&self, distance: f64) -> Bezier {
assert!(self.is_scalable(), "The curve provided to scale is not scalable. Reduce the curve first.");
let normal_start = self.normal(0.);
let normal_end = self.normal(1.);
let normal_start = self.normal(TValue::Parametric(0.));
let normal_end = self.normal(TValue::Parametric(1.));
// If normal unit vectors are equal, then the lines are parallel
if normal_start.abs_diff_eq(normal_end, MAX_ABSOLUTE_DIFFERENCE) {
@@ -263,30 +266,30 @@ impl Bezier {
pub fn graduated_scale(&self, start_distance: f64, end_distance: f64) -> Bezier {
assert!(self.is_scalable(), "The curve provided to scale is not scalable. Reduce the curve first.");
let normal_start = self.normal(0.);
let normal_end = self.normal(1.);
let normal_start = self.normal(TValue::Parametric(0.));
let normal_end = self.normal(TValue::Parametric(1.));
// If normal unit vectors are equal, then the lines are parallel
if normal_start.abs_diff_eq(normal_end, MAX_ABSOLUTE_DIFFERENCE) {
let transformed_start = utils::scale_point_from_direction_vector(self.start, self.normal(0.), false, start_distance);
let transformed_end = utils::scale_point_from_direction_vector(self.end, self.normal(1.), false, end_distance);
let transformed_start = utils::scale_point_from_direction_vector(self.start, self.normal(TValue::Parametric(0.)), false, start_distance);
let transformed_end = utils::scale_point_from_direction_vector(self.end, self.normal(TValue::Parametric(1.)), false, end_distance);
return match self.handles {
BezierHandles::Linear => Bezier::from_linear_dvec2(transformed_start, transformed_end),
BezierHandles::Quadratic { handle } => {
let handle_closest_t = self.project(handle, ProjectionOptions::default());
let handle_scale_distance = (1. - handle_closest_t) * start_distance + handle_closest_t * end_distance;
let transformed_handle = utils::scale_point_from_direction_vector(handle, self.normal(handle_closest_t), false, handle_scale_distance);
let transformed_handle = utils::scale_point_from_direction_vector(handle, self.normal(TValue::Parametric(handle_closest_t)), false, handle_scale_distance);
Bezier::from_quadratic_dvec2(transformed_start, transformed_handle, transformed_end)
}
BezierHandles::Cubic { handle_start, handle_end } => {
let handle_start_closest_t = self.project(handle_start, ProjectionOptions::default());
let handle_start_scale_distance = (1. - handle_start_closest_t) * start_distance + handle_start_closest_t * end_distance;
let transformed_handle_start = utils::scale_point_from_direction_vector(handle_start, self.normal(handle_start_closest_t), false, handle_start_scale_distance);
let transformed_handle_start = utils::scale_point_from_direction_vector(handle_start, self.normal(TValue::Parametric(handle_start_closest_t)), false, handle_start_scale_distance);
let handle_end_closest_t = self.project(handle_start, ProjectionOptions::default());
let handle_end_scale_distance = (1. - handle_end_closest_t) * start_distance + handle_end_closest_t * end_distance;
let transformed_handle_end = utils::scale_point_from_direction_vector(handle_end, self.normal(handle_end_closest_t), false, handle_end_scale_distance);
let transformed_handle_end = utils::scale_point_from_direction_vector(handle_end, self.normal(TValue::Parametric(handle_end_closest_t)), false, handle_end_scale_distance);
Bezier::from_cubic_dvec2(transformed_start, transformed_handle_start, transformed_handle_end, transformed_end)
}
};
@@ -399,7 +402,7 @@ impl Bezier {
match maximize_arcs {
ArcStrategy::Automatic => {
let (auto_arcs, final_low_t) = self.approximate_curve_with_arcs(0., 1., error, max_iterations, true);
let arc_approximations = self.split(final_low_t)[1].arcs(ArcsOptions {
let arc_approximations = self.split(TValue::Parametric(final_low_t))[1].arcs(ArcsOptions {
strategy: ArcStrategy::FavorCorrectness,
error,
max_iterations,
@@ -444,9 +447,9 @@ impl Bezier {
// Inner loop to find the next maximal segment of the curve that can be approximated with a circular arc
while iterations <= max_iterations {
iterations += 1;
let p1 = self.evaluate(ComputeType::Parametric(low));
let p2 = self.evaluate(ComputeType::Parametric(middle));
let p3 = self.evaluate(ComputeType::Parametric(high));
let p1 = self.evaluate(TValue::Parametric(low));
let p2 = self.evaluate(TValue::Parametric(middle));
let p3 = self.evaluate(TValue::Parametric(high));
let wrapped_center = utils::compute_circle_center_from_points(p1, p2, p3);
// If the segment is linear, move on to next segment
@@ -486,8 +489,8 @@ impl Bezier {
};
// Use points in between low, middle, and high to evaluate how well the arc approximates the curve
let e1 = self.evaluate(ComputeType::Parametric((low + middle) / 2.));
let e2 = self.evaluate(ComputeType::Parametric((middle + high) / 2.));
let e1 = self.evaluate(TValue::Parametric((low + middle) / 2.));
let e2 = self.evaluate(TValue::Parametric((middle + high) / 2.));
// Iterate until we find the largest good approximation such that the next iteration is not a good approximation with an arc
if utils::f64_compare(radius, e1.distance(center), error) && utils::f64_compare(radius, e2.distance(center), error) {
@@ -537,7 +540,7 @@ impl Bezier {
#[cfg(test)]
mod tests {
use crate::utils::ComputeType;
use crate::utils::TValue;
use super::compare::{compare_arcs, compare_vector_of_beziers};
use super::*;
@@ -545,37 +548,37 @@ mod tests {
#[test]
fn test_split() {
let line = Bezier::from_linear_coordinates(25., 25., 75., 75.);
let [part1, part2] = line.split(0.5);
let [part1, part2] = line.split(TValue::Parametric(0.5));
assert_eq!(part1.start(), line.start());
assert_eq!(part1.end(), line.evaluate(ComputeType::Parametric(0.5)));
assert_eq!(part1.evaluate(ComputeType::Parametric(0.5)), line.evaluate(ComputeType::Parametric(0.25)));
assert_eq!(part1.end(), line.evaluate(TValue::Parametric(0.5)));
assert_eq!(part1.evaluate(TValue::Parametric(0.5)), line.evaluate(TValue::Parametric(0.25)));
assert_eq!(part2.start(), line.evaluate(ComputeType::Parametric(0.5)));
assert_eq!(part2.start(), line.evaluate(TValue::Parametric(0.5)));
assert_eq!(part2.end(), line.end());
assert_eq!(part2.evaluate(ComputeType::Parametric(0.5)), line.evaluate(ComputeType::Parametric(0.75)));
assert_eq!(part2.evaluate(TValue::Parametric(0.5)), line.evaluate(TValue::Parametric(0.75)));
let quad_bezier = Bezier::from_quadratic_coordinates(10., 10., 50., 50., 90., 10.);
let [part3, part4] = quad_bezier.split(0.5);
let [part3, part4] = quad_bezier.split(TValue::Parametric(0.5));
assert_eq!(part3.start(), quad_bezier.start());
assert_eq!(part3.end(), quad_bezier.evaluate(ComputeType::Parametric(0.5)));
assert_eq!(part3.evaluate(ComputeType::Parametric(0.5)), quad_bezier.evaluate(ComputeType::Parametric(0.25)));
assert_eq!(part3.end(), quad_bezier.evaluate(TValue::Parametric(0.5)));
assert_eq!(part3.evaluate(TValue::Parametric(0.5)), quad_bezier.evaluate(TValue::Parametric(0.25)));
assert_eq!(part4.start(), quad_bezier.evaluate(ComputeType::Parametric(0.5)));
assert_eq!(part4.start(), quad_bezier.evaluate(TValue::Parametric(0.5)));
assert_eq!(part4.end(), quad_bezier.end());
assert_eq!(part4.evaluate(ComputeType::Parametric(0.5)), quad_bezier.evaluate(ComputeType::Parametric(0.75)));
assert_eq!(part4.evaluate(TValue::Parametric(0.5)), quad_bezier.evaluate(TValue::Parametric(0.75)));
let cubic_bezier = Bezier::from_cubic_coordinates(10., 10., 50., 50., 90., 10., 40., 50.);
let [part5, part6] = cubic_bezier.split(0.5);
let [part5, part6] = cubic_bezier.split(TValue::Parametric(0.5));
assert_eq!(part5.start(), cubic_bezier.start());
assert_eq!(part5.end(), cubic_bezier.evaluate(ComputeType::Parametric(0.5)));
assert_eq!(part5.evaluate(ComputeType::Parametric(0.5)), cubic_bezier.evaluate(ComputeType::Parametric(0.25)));
assert_eq!(part5.end(), cubic_bezier.evaluate(TValue::Parametric(0.5)));
assert_eq!(part5.evaluate(TValue::Parametric(0.5)), cubic_bezier.evaluate(TValue::Parametric(0.25)));
assert_eq!(part6.start(), cubic_bezier.evaluate(ComputeType::Parametric(0.5)));
assert_eq!(part6.start(), cubic_bezier.evaluate(TValue::Parametric(0.5)));
assert_eq!(part6.end(), cubic_bezier.end());
assert_eq!(part6.evaluate(ComputeType::Parametric(0.5)), cubic_bezier.evaluate(ComputeType::Parametric(0.75)));
assert_eq!(part6.evaluate(TValue::Parametric(0.5)), cubic_bezier.evaluate(TValue::Parametric(0.75)));
}
#[test]
@@ -586,24 +589,24 @@ mod tests {
let bezier_quadratic = Bezier::from_quadratic_dvec2(start, DVec2::new(140., 30.), end);
// Test splitting a quadratic bezier at the startpoint
let [point_bezier1, remainder1] = bezier_quadratic.split(0.);
let [point_bezier1, remainder1] = bezier_quadratic.split(TValue::Parametric(0.));
assert_eq!(point_bezier1, Bezier::from_quadratic_dvec2(start, start, start));
assert!(remainder1.abs_diff_eq(&bezier_quadratic, MAX_ABSOLUTE_DIFFERENCE));
// Test splitting a quadratic bezier at the endpoint
let [remainder2, point_bezier2] = bezier_quadratic.split(1.);
let [remainder2, point_bezier2] = bezier_quadratic.split(TValue::Parametric(1.));
assert_eq!(point_bezier2, Bezier::from_quadratic_dvec2(end, end, end));
assert!(remainder2.abs_diff_eq(&bezier_quadratic, MAX_ABSOLUTE_DIFFERENCE));
let bezier_cubic = Bezier::from_cubic_dvec2(start, DVec2::new(60., 140.), DVec2::new(150., 30.), end);
// Test splitting a cubic bezier at the startpoint
let [point_bezier3, remainder3] = bezier_cubic.split(0.);
let [point_bezier3, remainder3] = bezier_cubic.split(TValue::Parametric(0.));
assert_eq!(point_bezier3, Bezier::from_cubic_dvec2(start, start, start, start));
assert!(remainder3.abs_diff_eq(&bezier_cubic, MAX_ABSOLUTE_DIFFERENCE));
// Test splitting a cubic bezier at the endpoint
let [remainder4, point_bezier4] = bezier_cubic.split(1.);
let [remainder4, point_bezier4] = bezier_cubic.split(TValue::Parametric(1.));
assert_eq!(point_bezier4, Bezier::from_cubic_dvec2(end, end, end, end));
assert!(remainder4.abs_diff_eq(&bezier_cubic, MAX_ABSOLUTE_DIFFERENCE));
}
@@ -611,39 +614,39 @@ mod tests {
#[test]
fn test_trim() {
let line = Bezier::from_linear_coordinates(80., 80., 40., 40.);
let trimmed1 = line.trim(0.25, 0.75);
let trimmed1 = line.trim(TValue::Parametric(0.25), TValue::Parametric(0.75));
assert_eq!(trimmed1.start(), line.evaluate(ComputeType::Parametric(0.25)));
assert_eq!(trimmed1.end(), line.evaluate(ComputeType::Parametric(0.75)));
assert_eq!(trimmed1.evaluate(ComputeType::Parametric(0.5)), line.evaluate(ComputeType::Parametric(0.5)));
assert_eq!(trimmed1.start(), line.evaluate(TValue::Parametric(0.25)));
assert_eq!(trimmed1.end(), line.evaluate(TValue::Parametric(0.75)));
assert_eq!(trimmed1.evaluate(TValue::Parametric(0.5)), line.evaluate(TValue::Parametric(0.5)));
let quadratic_bezier = Bezier::from_quadratic_coordinates(80., 80., 40., 40., 70., 70.);
let trimmed2 = quadratic_bezier.trim(0.25, 0.75);
let trimmed2 = quadratic_bezier.trim(TValue::Parametric(0.25), TValue::Parametric(0.75));
assert_eq!(trimmed2.start(), quadratic_bezier.evaluate(ComputeType::Parametric(0.25)));
assert_eq!(trimmed2.end(), quadratic_bezier.evaluate(ComputeType::Parametric(0.75)));
assert_eq!(trimmed2.evaluate(ComputeType::Parametric(0.5)), quadratic_bezier.evaluate(ComputeType::Parametric(0.5)));
assert_eq!(trimmed2.start(), quadratic_bezier.evaluate(TValue::Parametric(0.25)));
assert_eq!(trimmed2.end(), quadratic_bezier.evaluate(TValue::Parametric(0.75)));
assert_eq!(trimmed2.evaluate(TValue::Parametric(0.5)), quadratic_bezier.evaluate(TValue::Parametric(0.5)));
let cubic_bezier = Bezier::from_cubic_coordinates(80., 80., 40., 40., 70., 70., 150., 150.);
let trimmed3 = cubic_bezier.trim(0.25, 0.75);
let trimmed3 = cubic_bezier.trim(TValue::Parametric(0.25), TValue::Parametric(0.75));
assert_eq!(trimmed3.start(), cubic_bezier.evaluate(ComputeType::Parametric(0.25)));
assert_eq!(trimmed3.end(), cubic_bezier.evaluate(ComputeType::Parametric(0.75)));
assert_eq!(trimmed3.evaluate(ComputeType::Parametric(0.5)), cubic_bezier.evaluate(ComputeType::Parametric(0.5)));
assert_eq!(trimmed3.start(), cubic_bezier.evaluate(TValue::Parametric(0.25)));
assert_eq!(trimmed3.end(), cubic_bezier.evaluate(TValue::Parametric(0.75)));
assert_eq!(trimmed3.evaluate(TValue::Parametric(0.5)), cubic_bezier.evaluate(TValue::Parametric(0.5)));
}
#[test]
fn test_trim_t2_greater_than_t1() {
// Test trimming quadratic curve when t2 > t1
let bezier_quadratic = Bezier::from_quadratic_coordinates(30., 50., 140., 30., 160., 170.);
let trim1 = bezier_quadratic.trim(0.25, 0.75);
let trim2 = bezier_quadratic.trim(0.75, 0.25).reverse();
let trim1 = bezier_quadratic.trim(TValue::Parametric(0.25), TValue::Parametric(0.75));
let trim2 = bezier_quadratic.trim(TValue::Parametric(0.75), TValue::Parametric(0.25)).reverse();
assert!(trim1.abs_diff_eq(&trim2, MAX_ABSOLUTE_DIFFERENCE));
// Test trimming cubic curve when t2 > t1
let bezier_cubic = Bezier::from_cubic_coordinates(30., 30., 60., 140., 150., 30., 160., 160.);
let trim3 = bezier_cubic.trim(0.25, 0.75);
let trim4 = bezier_cubic.trim(0.75, 0.25).reverse();
let trim3 = bezier_cubic.trim(TValue::Parametric(0.25), TValue::Parametric(0.75));
let trim4 = bezier_cubic.trim(TValue::Parametric(0.75), TValue::Parametric(0.25)).reverse();
assert!(trim3.abs_diff_eq(&trim4, MAX_ABSOLUTE_DIFFERENCE));
}
@@ -704,7 +707,7 @@ mod tests {
assert!(reduced_curves
.iter()
.zip(helper_t_values.windows(2))
.all(|(curve, t_pair)| curve.abs_diff_eq(&bezier.trim(t_pair[0], t_pair[1]), MAX_ABSOLUTE_DIFFERENCE)))
.all(|(curve, t_pair)| curve.abs_diff_eq(&bezier.trim(TValue::Parametric(t_pair[0]), TValue::Parametric(t_pair[1])), MAX_ABSOLUTE_DIFFERENCE)))
}
#[test]
@@ -744,23 +747,23 @@ mod tests {
// Assert the first length-wise piece of the outline is 10 units from the line
assert!(f64_compare(
outline[0].evaluate(ComputeType::Parametric(0.25)).distance(line.evaluate(ComputeType::Parametric(0.25))),
outline[0].evaluate(TValue::Parametric(0.25)).distance(line.evaluate(TValue::Parametric(0.25))),
10.,
MAX_ABSOLUTE_DIFFERENCE
)); // f64
// Assert the first cap touches the line end point at the halfway point
assert!(outline[1].evaluate(ComputeType::Parametric(0.5)).abs_diff_eq(line.end(), MAX_ABSOLUTE_DIFFERENCE));
assert!(outline[1].evaluate(TValue::Parametric(0.5)).abs_diff_eq(line.end(), MAX_ABSOLUTE_DIFFERENCE));
// Assert the second length-wise piece of the outline is 10 units from the line
assert!(f64_compare(
outline[2].evaluate(ComputeType::Parametric(0.25)).distance(line.evaluate(ComputeType::Parametric(0.75))),
outline[2].evaluate(TValue::Parametric(0.25)).distance(line.evaluate(TValue::Parametric(0.75))),
10.,
MAX_ABSOLUTE_DIFFERENCE
)); // f64
// Assert the second cap touches the line start point at the halfway point
assert!(outline[3].evaluate(ComputeType::Parametric(0.5)).abs_diff_eq(line.start(), MAX_ABSOLUTE_DIFFERENCE));
assert!(outline[3].evaluate(TValue::Parametric(0.5)).abs_diff_eq(line.start(), MAX_ABSOLUTE_DIFFERENCE));
}
#[test]
@@ -778,17 +781,17 @@ mod tests {
// Assert the scaled bezier is 30 units from the line
assert!(f64_compare(
scaled_bezier.evaluate(ComputeType::Parametric(0.)).distance(bezier.evaluate(ComputeType::Parametric(0.))),
scaled_bezier.evaluate(TValue::Parametric(0.)).distance(bezier.evaluate(TValue::Parametric(0.))),
30.,
MAX_ABSOLUTE_DIFFERENCE
));
assert!(f64_compare(
scaled_bezier.evaluate(ComputeType::Parametric(1.)).distance(bezier.evaluate(ComputeType::Parametric(1.))),
scaled_bezier.evaluate(TValue::Parametric(1.)).distance(bezier.evaluate(TValue::Parametric(1.))),
30.,
MAX_ABSOLUTE_DIFFERENCE
));
assert!(f64_compare(
scaled_bezier.evaluate(ComputeType::Parametric(0.5)).distance(bezier.evaluate(ComputeType::Parametric(0.5))),
scaled_bezier.evaluate(TValue::Parametric(0.5)).distance(bezier.evaluate(TValue::Parametric(0.5))),
30.,
MAX_ABSOLUTE_DIFFERENCE
));