mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-12-23 10:11:54 +00:00
Implement bezier library's De Casteljau points function (#715)
* pre-rebase * broken wasm * hull lines * update rust lib description * update comments, handle match statement better, pass Point type to vue * cleanup * add linear case * More idiomatic code * Further simplifications to the algorithm and removal of more heap allocations * Rename to de_casteljau_points and use colors for the iterations * Small comment changes * Improve colors Co-authored-by: Jackie Chen <jackiechen73> Co-authored-by: Keavon Chambers <keavon@keavon.com> Co-authored-by: Hannah Li <hannahli2010@gmail.com>
This commit is contained in:
parent
dc0b38750c
commit
004c2aeff6
3 changed files with 57 additions and 0 deletions
|
|
@ -281,6 +281,29 @@ export default defineComponent({
|
|||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "De Casteljau Points",
|
||||
callback: (canvas: HTMLCanvasElement, bezier: WasmBezierInstance, options: Record<string, number>): void => {
|
||||
const hullPoints: Point[][] = JSON.parse(bezier.de_casteljau_points(options.t));
|
||||
hullPoints.reverse().forEach((iteration: Point[], iterationIndex) => {
|
||||
const colorLight = `hsl(${90 * iterationIndex}, 100%, 50%)`;
|
||||
|
||||
iteration.forEach((point: Point, index) => {
|
||||
// Skip the anchor and handle points which are already drawn in black
|
||||
if (iterationIndex !== hullPoints.length - 1) {
|
||||
drawPoint(getContextFromCanvas(canvas), point, 4, colorLight);
|
||||
}
|
||||
|
||||
if (index !== 0) {
|
||||
const prevPoint: Point = iteration[index - 1];
|
||||
drawLine(getContextFromCanvas(canvas), point, prevPoint, colorLight);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
template: markRaw(SliderExample),
|
||||
templateOptions: { sliders: [tSliderOptions] },
|
||||
},
|
||||
{
|
||||
name: "Rotate",
|
||||
callback: (canvas: HTMLCanvasElement, bezier: WasmBezierInstance, options: Record<string, number>): void => {
|
||||
|
|
|
|||
|
|
@ -138,4 +138,14 @@ impl WasmBezier {
|
|||
let bezier_points: Vec<Vec<Point>> = self.0.reduce(None).into_iter().map(bezier_to_points).collect();
|
||||
to_js_value(bezier_points)
|
||||
}
|
||||
|
||||
pub fn de_casteljau_points(&self, t: f64) -> JsValue {
|
||||
let hull = self
|
||||
.0
|
||||
.de_casteljau_points(t)
|
||||
.iter()
|
||||
.map(|level| level.iter().map(|&point| Point { x: point.x, y: point.y }).collect::<Vec<Point>>())
|
||||
.collect::<Vec<Vec<Point>>>();
|
||||
to_js_value(hull)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -631,6 +631,30 @@ impl Bezier {
|
|||
.collect::<Vec<DVec2>>()
|
||||
}
|
||||
|
||||
/// Returns a list of lists of points representing the De Casteljau points for all iterations at the point corresponding to `t` using De Casteljau's algorithm.
|
||||
/// The `i`th element of the list represents the set of points in the `i`th iteration.
|
||||
/// More information on the algorithm can be found in the [De Casteljau section](https://pomax.github.io/bezierinfo/#decasteljau) in Pomax's primer.
|
||||
pub fn de_casteljau_points(&self, t: f64) -> Vec<Vec<DVec2>> {
|
||||
let bezier_points = match self.handles {
|
||||
BezierHandles::Linear => vec![self.start, self.end],
|
||||
BezierHandles::Quadratic { handle } => vec![self.start, handle, self.end],
|
||||
BezierHandles::Cubic { handle_start, handle_end } => vec![self.start, handle_start, handle_end, self.end],
|
||||
};
|
||||
let mut de_casteljau_points = vec![bezier_points];
|
||||
let mut current_points = de_casteljau_points.last().unwrap();
|
||||
|
||||
// Iterate until one point is left, that point will be equal to `evaluate(t)`
|
||||
while current_points.len() > 1 {
|
||||
// Map from every adjacent pair of points to their respective midpoints, which decrements by 1 the number of points for the next iteration
|
||||
let next_points: Vec<DVec2> = current_points.as_slice().windows(2).map(|pair| DVec2::lerp(pair[0], pair[1], t)).collect();
|
||||
de_casteljau_points.push(next_points);
|
||||
|
||||
current_points = de_casteljau_points.last().unwrap();
|
||||
}
|
||||
|
||||
de_casteljau_points
|
||||
}
|
||||
|
||||
/// Determine if it is possible to scale the given curve, using the following conditions:
|
||||
/// 1. All the handles are located on a single side of the curve.
|
||||
/// 2. The on-curve point for `t = 0.5` must occur roughly in the center of the polygon defined by the curve's endpoint normals.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue