mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-25 09:18:12 +08:00
Bezier derivative and normal implementation (#679)
* added ui for derivative impl * Add derivative computation for bezier-rs library * integrate devivative ui with library * Add implementation for the normal function * Update rustdoc comments * Rename handles and getters, add tangent function * Rename variables, address nits Co-authored-by: Rob Nadal <robnadal44@gmail.com> Co-authored-by: Thomas Cheng <contact.chengthomas@gmail.com> Co-authored-by: ll2zheng <Linda Zheng>
This commit is contained in:
co-authored by
Rob Nadal
Thomas Cheng
ll2zheng
parent
2052c00010
commit
a4a174db04
@@ -1,5 +1,12 @@
|
||||
import { Point } from "@/utils/types";
|
||||
|
||||
const RADIUS_SIZE = {
|
||||
large: 5,
|
||||
small: 3,
|
||||
};
|
||||
|
||||
export const getPointSizeByIndex = (index: number, numPoints: number): number => RADIUS_SIZE[index === 0 || index === numPoints - 1 ? "large" : "small"];
|
||||
|
||||
export const getContextFromCanvas = (canvas: HTMLCanvasElement): CanvasRenderingContext2D => {
|
||||
const ctx = canvas.getContext("2d");
|
||||
if (ctx === null) {
|
||||
@@ -8,28 +15,28 @@ export const getContextFromCanvas = (canvas: HTMLCanvasElement): CanvasRendering
|
||||
return ctx;
|
||||
};
|
||||
|
||||
export const drawLine = (ctx: CanvasRenderingContext2D, p1: Point, p2: Point): void => {
|
||||
ctx.strokeStyle = "grey";
|
||||
export const drawLine = (ctx: CanvasRenderingContext2D, point1: Point, point2: Point, strokeColor = "gray"): void => {
|
||||
ctx.strokeStyle = strokeColor;
|
||||
ctx.lineWidth = 1;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(p1.x, p1.y);
|
||||
ctx.lineTo(p2.x, p2.y);
|
||||
ctx.moveTo(point1.x, point1.y);
|
||||
ctx.lineTo(point2.x, point2.y);
|
||||
ctx.stroke();
|
||||
};
|
||||
|
||||
export const drawPoint = (ctx: CanvasRenderingContext2D, p: Point, stroke = "black"): void => {
|
||||
export const drawPoint = (ctx: CanvasRenderingContext2D, point: Point, radius: number, strokeColor = "black"): void => {
|
||||
// Outline the point
|
||||
ctx.strokeStyle = p.selected ? "blue" : stroke;
|
||||
ctx.lineWidth = p.r / 3;
|
||||
ctx.strokeStyle = strokeColor;
|
||||
ctx.lineWidth = radius / 3;
|
||||
ctx.beginPath();
|
||||
ctx.arc(p.x, p.y, p.r, 0, 2 * Math.PI, false);
|
||||
ctx.arc(point.x, point.y, radius, 0, 2 * Math.PI, false);
|
||||
ctx.stroke();
|
||||
|
||||
// Fill the point (hiding any overlapping lines)
|
||||
ctx.fillStyle = "white";
|
||||
ctx.beginPath();
|
||||
ctx.arc(p.x, p.y, p.r * (2 / 3), 0, 2 * Math.PI, false);
|
||||
ctx.arc(point.x, point.y, radius * (2 / 3), 0, 2 * Math.PI, false);
|
||||
ctx.fill();
|
||||
};
|
||||
|
||||
@@ -39,24 +46,24 @@ export const drawText = (ctx: CanvasRenderingContext2D, text: string, x: number,
|
||||
ctx.fillText(text, x, y);
|
||||
};
|
||||
|
||||
export const drawBezier = (ctx: CanvasRenderingContext2D, points: Point[]): void => {
|
||||
export const drawBezier = (ctx: CanvasRenderingContext2D, points: Point[], dragIndex: number | null = null): void => {
|
||||
/* Until a bezier representation is finalized, treat the points as follows
|
||||
points[0] = start point
|
||||
points[1] = handle 1
|
||||
points[2] = (optional) handle 2
|
||||
points[1] = handle start
|
||||
points[2] = (optional) handle end
|
||||
points[3] = end point
|
||||
*/
|
||||
const start = points[0];
|
||||
let end = null;
|
||||
let handle1 = null;
|
||||
let handle2 = null;
|
||||
let handleStart = null;
|
||||
let handleEnd = null;
|
||||
if (points.length === 4) {
|
||||
handle1 = points[1];
|
||||
handle2 = points[2];
|
||||
handleStart = points[1];
|
||||
handleEnd = points[2];
|
||||
end = points[3];
|
||||
} else {
|
||||
handle1 = points[1];
|
||||
handle2 = handle1;
|
||||
handleStart = points[1];
|
||||
handleEnd = handleStart;
|
||||
end = points[2];
|
||||
}
|
||||
|
||||
@@ -66,16 +73,16 @@ export const drawBezier = (ctx: CanvasRenderingContext2D, points: Point[]): void
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(points[0].x, points[0].y);
|
||||
if (points.length === 3) {
|
||||
ctx.quadraticCurveTo(handle1.x, handle1.y, end.x, end.y);
|
||||
ctx.quadraticCurveTo(handleStart.x, handleStart.y, end.x, end.y);
|
||||
} else {
|
||||
ctx.bezierCurveTo(handle1.x, handle1.y, handle2.x, handle2.y, end.x, end.y);
|
||||
ctx.bezierCurveTo(handleStart.x, handleStart.y, handleEnd.x, handleEnd.y, end.x, end.y);
|
||||
}
|
||||
ctx.stroke();
|
||||
|
||||
drawLine(ctx, start, handle1);
|
||||
drawLine(ctx, end, handle2);
|
||||
drawLine(ctx, start, handleStart);
|
||||
drawLine(ctx, end, handleEnd);
|
||||
|
||||
points.forEach((point) => {
|
||||
drawPoint(ctx, point);
|
||||
points.forEach((point, index) => {
|
||||
drawPoint(ctx, point, getPointSizeByIndex(index, points.length), index === dragIndex ? "Blue" : "Black");
|
||||
});
|
||||
};
|
||||
|
||||
@@ -2,14 +2,15 @@ export type WasmRawInstance = typeof import("../../wasm/pkg");
|
||||
export type WasmBezierInstance = InstanceType<WasmRawInstance["WasmBezier"]>;
|
||||
|
||||
export type WasmBezierKey = keyof WasmBezierInstance;
|
||||
export type WasmBezierMutatorKey = "set_start" | "set_handle1" | "set_handle2" | "set_end";
|
||||
export type WasmBezierMutatorKey = "set_start" | "set_handle_start" | "set_handle_end" | "set_end";
|
||||
|
||||
export type BezierCallback = (canvas: HTMLCanvasElement, bezier: WasmBezierInstance, options: string) => void;
|
||||
|
||||
export type Point = {
|
||||
x: number;
|
||||
y: number;
|
||||
r: number;
|
||||
};
|
||||
|
||||
export type BezierPoint = Point & {
|
||||
mutator: WasmBezierMutatorKey;
|
||||
selected: boolean;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user