mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-30 17:18:13 +08:00
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:
co-authored by
Linda Zheng
Linda Zheng
Robert Nadal
Hannah Li
Keavon Chambers
parent
a5bb153dab
commit
06524bac8d
@@ -369,6 +369,18 @@ export default defineComponent({
|
|||||||
drawLine(context, maxPoint, { x: maxPoint.x, y: minPoint.y }, COLORS.NON_INTERACTIVE.STROKE_1);
|
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: [
|
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 });
|
let bbox_points: [Point; 2] = self.0.bounding_box().map(|p| Point { x: p.x, y: p.y });
|
||||||
to_js_value(bbox_points)
|
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]
|
[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)]
|
#[cfg(test)]
|
||||||
|
|||||||
Reference in New Issue
Block a user