mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-28 16:08:11 +08:00
Implement Bezier curve reduce function (#711)
* fixed extrema bug and added reduce impl Co-authored-by: Rob Nadal <RobNadal@users.noreply.github.com> Co-authored-by: Linda Zheng <ll2zheng@uwaterloo.ca> Co-authored-by: Hannah Li <hannahli2010@gmail.com> * Stylistic changes related to reduce * Fixed reduce splitting bug causing panic * Added shortcuts and simplified reduce * Stylistic changes per review * address comments * Removed color gradient function and added consts * Tweaks * Change colors faster * Don't drop on mouseout Co-authored-by: Thomas Cheng <contact.chengthomas@gmail.com> Co-authored-by: Rob Nadal <RobNadal@users.noreply.github.com> Co-authored-by: Linda Zheng <ll2zheng@uwaterloo.ca> Co-authored-by: Hannah Li <hannahli2010@gmail.com> Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
co-authored by
Rob Nadal
Linda Zheng
Hannah Li
Thomas Cheng
Keavon Chambers
parent
f3d5028107
commit
b4ada6affd
@@ -266,6 +266,16 @@ export default defineComponent({
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Reduce",
|
||||
callback: (canvas: HTMLCanvasElement, bezier: WasmBezierInstance): void => {
|
||||
const context = getContextFromCanvas(canvas);
|
||||
const curves: Point[][] = JSON.parse(bezier.reduce());
|
||||
curves.forEach((points, index) => {
|
||||
drawBezier(context, points, null, { curveStrokeColor: `hsl(${40 * index}, 100%, 50%)`, radius: 3.5, drawHandles: false });
|
||||
});
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
|
||||
@@ -59,10 +59,9 @@ class BezierDrawing {
|
||||
|
||||
this.dragIndex = null; // Index of the point being moved
|
||||
|
||||
this.canvas.addEventListener("mousedown", this.mouseDownHandler.bind(this));
|
||||
this.canvas.addEventListener("mousemove", this.mouseMoveHandler.bind(this));
|
||||
this.canvas.addEventListener("mouseup", this.deselectPointHandler.bind(this));
|
||||
this.canvas.addEventListener("mouseout", this.deselectPointHandler.bind(this));
|
||||
this.canvas.addEventListener("mousedown", (e) => this.mouseDownHandler(e));
|
||||
this.canvas.addEventListener("mousemove", (e) => this.mouseMoveHandler(e));
|
||||
this.canvas.addEventListener("mouseup", () => this.deselectPointHandler());
|
||||
this.updateBezier();
|
||||
}
|
||||
|
||||
@@ -71,6 +70,11 @@ class BezierDrawing {
|
||||
}
|
||||
|
||||
mouseMoveHandler(evt: MouseEvent): void {
|
||||
if (evt.buttons === 0) {
|
||||
this.deselectPointHandler();
|
||||
return;
|
||||
}
|
||||
|
||||
const mx = evt.offsetX;
|
||||
const my = evt.offsetY;
|
||||
|
||||
|
||||
@@ -70,9 +70,10 @@ export const drawBezier = (ctx: CanvasRenderingContext2D, points: Point[], dragI
|
||||
handleStrokeColor: COLORS.INTERACTIVE.STROKE_1,
|
||||
handleLineStrokeColor: COLORS.INTERACTIVE.STROKE_1,
|
||||
radius: DEFAULT_ENDPOINT_RADIUS,
|
||||
drawHandles: true,
|
||||
...bezierStyleConfig,
|
||||
};
|
||||
// if the handle or handle line colors are not specified, use the same colour as the rest of the curve
|
||||
// If the handle or handle line colors are not specified, use the same color as the rest of the curve
|
||||
if (bezierStyleConfig.curveStrokeColor) {
|
||||
if (!bezierStyleConfig.handleStrokeColor) {
|
||||
styleConfig.handleStrokeColor = bezierStyleConfig.curveStrokeColor;
|
||||
@@ -112,11 +113,15 @@ export const drawBezier = (ctx: CanvasRenderingContext2D, points: Point[], dragI
|
||||
}
|
||||
ctx.stroke();
|
||||
|
||||
drawLine(ctx, start, handleStart, styleConfig.handleLineStrokeColor);
|
||||
drawLine(ctx, end, handleEnd, styleConfig.handleLineStrokeColor);
|
||||
if (styleConfig.drawHandles) {
|
||||
drawLine(ctx, start, handleStart, styleConfig.handleLineStrokeColor);
|
||||
drawLine(ctx, end, handleEnd, styleConfig.handleLineStrokeColor);
|
||||
}
|
||||
|
||||
points.forEach((point, index) => {
|
||||
const strokeColor = isIndexFirstOrLast(index, points.length) ? styleConfig.curveStrokeColor : styleConfig.handleStrokeColor;
|
||||
drawPoint(ctx, point, getPointSizeByIndex(index, points.length, styleConfig.radius), index === dragIndex ? COLORS.INTERACTIVE.SELECTED : strokeColor);
|
||||
if (styleConfig.drawHandles || isIndexFirstOrLast(index, points.length)) {
|
||||
const strokeColor = isIndexFirstOrLast(index, points.length) ? styleConfig.curveStrokeColor : styleConfig.handleStrokeColor;
|
||||
drawPoint(ctx, point, getPointSizeByIndex(index, points.length, styleConfig.radius), index === dragIndex ? COLORS.INTERACTIVE.SELECTED : strokeColor);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -33,4 +33,5 @@ export type BezierStyleConfig = {
|
||||
handleStrokeColor: string;
|
||||
handleLineStrokeColor: string;
|
||||
radius: number;
|
||||
drawHandles: boolean;
|
||||
};
|
||||
|
||||
@@ -15,10 +15,20 @@ struct Point {
|
||||
pub struct WasmBezier(Bezier);
|
||||
|
||||
/// Convert a `DVec2` into a `JsValue`.
|
||||
pub fn vec_to_point(p: &DVec2) -> JsValue {
|
||||
fn vec_to_point(p: &DVec2) -> JsValue {
|
||||
JsValue::from_serde(&serde_json::to_string(&Point { x: p.x, y: p.y }).unwrap()).unwrap()
|
||||
}
|
||||
|
||||
/// Convert a bezier to a list of points.
|
||||
fn bezier_to_points(bezier: Bezier) -> Vec<Point> {
|
||||
bezier.get_points().iter().flatten().map(|point| Point { x: point.x, y: point.y }).collect()
|
||||
}
|
||||
|
||||
/// Serialize some data and then convert it to a JsValue.
|
||||
fn to_js_value<T: Serialize>(data: T) -> JsValue {
|
||||
JsValue::from_serde(&serde_json::to_string(&data).unwrap()).unwrap()
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
impl WasmBezier {
|
||||
/// Expect js_points to be a list of 3 pairs.
|
||||
@@ -88,11 +98,8 @@ impl WasmBezier {
|
||||
}
|
||||
|
||||
pub fn split(&self, t: f64) -> JsValue {
|
||||
let bezier_points: [Vec<Point>; 2] = self
|
||||
.0
|
||||
.split(t)
|
||||
.map(|bezier| bezier.get_points().iter().flatten().map(|point| Point { x: point.x, y: point.y }).collect());
|
||||
JsValue::from_serde(&serde_json::to_string(&bezier_points).unwrap()).unwrap()
|
||||
let bezier_points: [Vec<Point>; 2] = self.0.split(t).map(bezier_to_points);
|
||||
to_js_value(bezier_points)
|
||||
}
|
||||
|
||||
pub fn trim(&self, t1: f64, t2: f64) -> WasmBezier {
|
||||
@@ -105,7 +112,7 @@ impl WasmBezier {
|
||||
|
||||
pub fn local_extrema(&self) -> JsValue {
|
||||
let local_extrema = self.0.local_extrema();
|
||||
JsValue::from_serde(&serde_json::to_string(&local_extrema).unwrap()).unwrap()
|
||||
to_js_value(local_extrema)
|
||||
}
|
||||
|
||||
pub fn rotate(&self, angle: f64) -> WasmBezier {
|
||||
@@ -116,4 +123,9 @@ impl WasmBezier {
|
||||
let line: [DVec2; 2] = js_points.into_serde().unwrap();
|
||||
self.0.intersect_line_segment(line).iter().map(|&p| vec_to_point(&p)).collect::<Vec<JsValue>>()
|
||||
}
|
||||
|
||||
pub fn reduce(&self) -> JsValue {
|
||||
let bezier_points: Vec<Vec<Point>> = self.0.reduce(None).into_iter().map(bezier_to_points).collect();
|
||||
to_js_value(bezier_points)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user