Implement function to find inflection points of a Bezier curve (#712)
* Implement inflection function for bezier-rs * Swapped to explicit inflection formula Co-authored-by: Linda Zheng <ll2zheng@uwaterloo.ca> * Address Rob's comments * Fix axis align Co-authored-by: Linda Zheng <linda-zheng@users.noreply.github.com> * Address Keavon's comments * Fix linting Co-authored-by: Robert Nadal <Robnadal44@gmail.com> Co-authored-by: Hannah Li <hannahli2010@gmail.com> Co-authored-by: Linda Zheng <linda-zheng@users.noreply.github.com> Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
c05c93c8a2
commit
19483a9a35
|
|
@ -369,6 +369,18 @@ export default defineComponent({
|
|||
drawLine(context, maxPoint, { x: maxPoint.x, y: minPoint.y }, COLORS.NON_INTERACTIVE.STROKE_1);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Inflections",
|
||||
callback: (canvas: HTMLCanvasElement, bezier: WasmBezierInstance): void => {
|
||||
const context = getContextFromCanvas(canvas);
|
||||
const inflections: number[] = JSON.parse(bezier.inflections());
|
||||
inflections.forEach((t) => {
|
||||
const point = JSON.parse(bezier.evaluate(t));
|
||||
drawPoint(context, point, 4, COLORS.NON_INTERACTIVE.STROKE_1);
|
||||
});
|
||||
},
|
||||
curveDegrees: new Set([BezierCurveType.Cubic]),
|
||||
},
|
||||
],
|
||||
subpathFeatures: [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -156,4 +156,9 @@ impl WasmBezier {
|
|||
let bbox_points: [Point; 2] = self.0.bounding_box().map(|p| Point { x: p.x, y: p.y });
|
||||
to_js_value(bbox_points)
|
||||
}
|
||||
|
||||
pub fn inflections(&self) -> JsValue {
|
||||
let inflections = self.0.inflections();
|
||||
to_js_value(inflections)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -793,6 +793,45 @@ impl Bezier {
|
|||
|
||||
[endpoints_min, endpoints_max]
|
||||
}
|
||||
|
||||
// TODO: Use an `impl Iterator` return type instead of a `Vec`
|
||||
/// Returns list of `t`-values representing the inflection points of the curve.
|
||||
/// The inflection points are defined to be points at which the second derivative of the curve is equal to zero.
|
||||
pub fn unrestricted_inflections(&self) -> Vec<f64> {
|
||||
match self.handles {
|
||||
// There exists no inflection points for linear and quadratic beziers.
|
||||
BezierHandles::Linear => Vec::new(),
|
||||
BezierHandles::Quadratic { .. } => Vec::new(),
|
||||
BezierHandles::Cubic { .. } => {
|
||||
// Axis align the curve.
|
||||
let translated_bezier = self.translate(-self.start);
|
||||
let angle = translated_bezier.end.angle_between(DVec2::new(1., 0.));
|
||||
let rotated_bezier = translated_bezier.rotate(angle);
|
||||
if let BezierHandles::Cubic { handle_start, handle_end } = rotated_bezier.handles {
|
||||
// These formulas and naming conventions follows https://pomax.github.io/bezierinfo/#inflections
|
||||
let a = handle_end.x * handle_start.y;
|
||||
let b = rotated_bezier.end.x * handle_start.y;
|
||||
let c = handle_start.x * handle_end.y;
|
||||
let d = rotated_bezier.end.x * handle_end.y;
|
||||
|
||||
let x = -3. * a + 2. * b + 3. * c - d;
|
||||
let y = 3. * a - b - 3. * c;
|
||||
let z = c - a;
|
||||
|
||||
let discriminant = y * y - 4. * x * z;
|
||||
utils::solve_quadratic(discriminant, 2. * x, y, z)
|
||||
} else {
|
||||
unreachable!("shouldn't happen")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns list of `t`-values representing the inflection points of the curve.
|
||||
/// The list of `t`-values returned are filtered such that they fall within the range `[0, 1]`.
|
||||
pub fn inflections(&self) -> Vec<f64> {
|
||||
self.unrestricted_inflections().into_iter().filter(|&t| t > 0. && t < 1.).collect::<Vec<f64>>()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
|||
Loading…
Reference in New Issue