mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-10 11:10:55 +08:00
Bezier-rs: Add Euclidean parameterization to Bezier::evaluate (#828)
* Add enum to evaluate for differenciating compute type * Add euclidean parameterization and update styling in the UI Co-authored-by: Rob Nadal <robnadal44@gmail.com> * Update usage of evaluate in graphite * Add description * Code review changes * Update tests * Improve ComputeType ergonomics * Large code review/cleanup pass Co-authored-by: Rob Nadal <robnadal44@gmail.com> Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
committed by
Keavon Chambers
co-authored by
Rob Nadal
Keavon Chambers
parent
a220bfa759
commit
01a9724389
@@ -1,9 +1,11 @@
|
||||
use crate::utils::{f64_compare, ComputeType};
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Functionality relating to looking up properties of the `Bezier` or points along the `Bezier`.
|
||||
impl Bezier {
|
||||
/// Calculate the point on the curve based on the `t`-value provided.
|
||||
pub(crate) fn unrestricted_evaluate(&self, t: f64) -> DVec2 {
|
||||
pub(crate) fn unrestricted_parametric_evaluate(&self, t: f64) -> DVec2 {
|
||||
// Basis code based off of pseudocode found here: <https://pomax.github.io/bezierinfo/#explanation>.
|
||||
|
||||
let t_squared = t * t;
|
||||
@@ -21,11 +23,48 @@ impl Bezier {
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate the point along the curve that is a factor of `d` away from the start.
|
||||
pub(crate) fn unrestricted_euclidean_evaluate(&self, d: f64, error: f64) -> DVec2 {
|
||||
if let BezierHandles::Linear = self.handles {
|
||||
return self.unrestricted_parametric_evaluate(d);
|
||||
}
|
||||
|
||||
let mut low = 0.;
|
||||
let mut mid = 0.;
|
||||
let mut high = 1.;
|
||||
let total_length = self.length(None);
|
||||
|
||||
while low < high {
|
||||
mid = (low + high) / 2.;
|
||||
let test_d = self.trim(0., mid).length(None) / total_length;
|
||||
if f64_compare(test_d, d, error) {
|
||||
break;
|
||||
} else if test_d < d {
|
||||
low = mid;
|
||||
} else {
|
||||
high = mid;
|
||||
}
|
||||
}
|
||||
self.unrestricted_parametric_evaluate(mid)
|
||||
}
|
||||
|
||||
/// Calculate the point on the curve based on the `t`-value provided.
|
||||
/// Expects `t` to be within the inclusive range `[0, 1]`.
|
||||
pub fn evaluate(&self, t: f64) -> DVec2 {
|
||||
assert!((0.0..=1.).contains(&t));
|
||||
self.unrestricted_evaluate(t)
|
||||
pub fn evaluate(&self, t: ComputeType) -> DVec2 {
|
||||
match t {
|
||||
ComputeType::Parametric(t) => {
|
||||
assert!((0.0..=1.).contains(&t));
|
||||
self.unrestricted_parametric_evaluate(t)
|
||||
}
|
||||
ComputeType::Euclidean(t) => {
|
||||
assert!((0.0..=1.).contains(&t));
|
||||
self.unrestricted_euclidean_evaluate(t, 0.0001)
|
||||
}
|
||||
ComputeType::EuclideanWithinError { t, epsilon } => {
|
||||
assert!((0.0..=1.).contains(&t));
|
||||
self.unrestricted_euclidean_evaluate(t, epsilon)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a selection of equidistant points on the bezier curve.
|
||||
@@ -36,7 +75,7 @@ impl Bezier {
|
||||
let mut steps_array = Vec::with_capacity(steps_unwrapped + 1);
|
||||
|
||||
for t in 0..steps_unwrapped + 1 {
|
||||
steps_array.push(self.evaluate(f64::from(t as i32) * ratio))
|
||||
steps_array.push(self.evaluate(ComputeType::Parametric(f64::from(t as i32) * ratio)))
|
||||
}
|
||||
|
||||
steps_array
|
||||
@@ -123,7 +162,7 @@ impl Bezier {
|
||||
if step_index == 0 {
|
||||
distance = *table_distance;
|
||||
} else {
|
||||
distance = point.distance(self.evaluate(iterator_t));
|
||||
distance = point.distance(self.evaluate(ComputeType::Parametric(iterator_t)));
|
||||
*table_distance = distance;
|
||||
}
|
||||
if distance < new_minimum_distance {
|
||||
@@ -173,23 +212,29 @@ mod tests {
|
||||
let p4 = DVec2::new(30., 21.);
|
||||
|
||||
let bezier1 = Bezier::from_quadratic_dvec2(p1, p2, p3);
|
||||
assert_eq!(bezier1.evaluate(0.5), DVec2::new(12.5, 6.25));
|
||||
assert_eq!(bezier1.evaluate(ComputeType::Parametric(0.5)), DVec2::new(12.5, 6.25));
|
||||
|
||||
let bezier2 = Bezier::from_cubic_dvec2(p1, p2, p3, p4);
|
||||
assert_eq!(bezier2.evaluate(0.5), DVec2::new(16.5, 9.625));
|
||||
assert_eq!(bezier2.evaluate(ComputeType::Parametric(0.5)), DVec2::new(16.5, 9.625));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_compute_lookup_table() {
|
||||
let bezier1 = Bezier::from_quadratic_coordinates(10., 10., 30., 30., 50., 10.);
|
||||
let lookup_table1 = bezier1.compute_lookup_table(Some(2));
|
||||
assert_eq!(lookup_table1, vec![bezier1.start(), bezier1.evaluate(0.5), bezier1.end()]);
|
||||
assert_eq!(lookup_table1, vec![bezier1.start(), bezier1.evaluate(ComputeType::Parametric(0.5)), bezier1.end()]);
|
||||
|
||||
let bezier2 = Bezier::from_cubic_coordinates(10., 10., 30., 30., 70., 70., 90., 10.);
|
||||
let lookup_table2 = bezier2.compute_lookup_table(Some(4));
|
||||
assert_eq!(
|
||||
lookup_table2,
|
||||
vec![bezier2.start(), bezier2.evaluate(0.25), bezier2.evaluate(0.5), bezier2.evaluate(0.75), bezier2.end()]
|
||||
vec![
|
||||
bezier2.start(),
|
||||
bezier2.evaluate(ComputeType::Parametric(0.25)),
|
||||
bezier2.evaluate(ComputeType::Parametric(0.50)),
|
||||
bezier2.evaluate(ComputeType::Parametric(0.75)),
|
||||
bezier2.end()
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user