mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-30 10:28:12 +08:00
Bezier split and trim implementation (#680)
* Added split implementation and UI Co-authored-by: Thomas Cheng <Androxium@users.noreply.github.com> * Added bezier split impl * Adjust struct traits * Implement trim and adjust FE code to handle multiple sliders per feature Co-authored-by: Thomas Cheng <contact.chengthomas@gmail.com> Co-authored-by: Rob Nadal <robnadal44@gmail.com> * Fix edge case in trim and add rust doc comments * Stylistic changes per review * More stylistic changes per review * replaced last explicit color usages Co-authored-by: Robert Nadal <Robnadal44@gmail.com> Co-authored-by: Thomas Cheng <Androxium@users.noreply.github.com> Co-authored-by: Thomas Cheng <contact.chengthomas@gmail.com>
This commit is contained in:
co-authored by
Thomas Cheng
Thomas Cheng
Rob Nadal
parent
55444eea86
commit
79944b54c8
@@ -1,11 +1,22 @@
|
||||
import { Point } from "@/utils/types";
|
||||
import { Point, WasmBezierInstance } from "@/utils/types";
|
||||
|
||||
const RADIUS_SIZE = {
|
||||
large: 5,
|
||||
small: 3,
|
||||
const HANDLE_RADIUS_FACTOR = 2 / 3;
|
||||
const DEFAULT_ENDPOINT_RADIUS = 5;
|
||||
|
||||
export const COLORS = {
|
||||
CANVAS: "white",
|
||||
INTERACTIVE: {
|
||||
STROKE_1: "black",
|
||||
STROKE_2: "grey",
|
||||
SELECTED: "blue",
|
||||
},
|
||||
NON_INTERACTIVE: {
|
||||
STROKE_1: "red",
|
||||
STROKE_2: "orange",
|
||||
},
|
||||
};
|
||||
|
||||
export const getPointSizeByIndex = (index: number, numPoints: number): number => RADIUS_SIZE[index === 0 || index === numPoints - 1 ? "large" : "small"];
|
||||
export const getPointSizeByIndex = (index: number, numPoints: number, radius = DEFAULT_ENDPOINT_RADIUS): number => (index === 0 || index === numPoints - 1 ? radius : radius * HANDLE_RADIUS_FACTOR);
|
||||
|
||||
export const getContextFromCanvas = (canvas: HTMLCanvasElement): CanvasRenderingContext2D => {
|
||||
const ctx = canvas.getContext("2d");
|
||||
@@ -15,7 +26,7 @@ export const getContextFromCanvas = (canvas: HTMLCanvasElement): CanvasRendering
|
||||
return ctx;
|
||||
};
|
||||
|
||||
export const drawLine = (ctx: CanvasRenderingContext2D, point1: Point, point2: Point, strokeColor = "gray"): void => {
|
||||
export const drawLine = (ctx: CanvasRenderingContext2D, point1: Point, point2: Point, strokeColor = COLORS.INTERACTIVE.STROKE_2): void => {
|
||||
ctx.strokeStyle = strokeColor;
|
||||
ctx.lineWidth = 1;
|
||||
|
||||
@@ -25,7 +36,7 @@ export const drawLine = (ctx: CanvasRenderingContext2D, point1: Point, point2: P
|
||||
ctx.stroke();
|
||||
};
|
||||
|
||||
export const drawPoint = (ctx: CanvasRenderingContext2D, point: Point, radius: number, strokeColor = "black"): void => {
|
||||
export const drawPoint = (ctx: CanvasRenderingContext2D, point: Point, radius: number, strokeColor = COLORS.INTERACTIVE.STROKE_1): void => {
|
||||
// Outline the point
|
||||
ctx.strokeStyle = strokeColor;
|
||||
ctx.lineWidth = radius / 3;
|
||||
@@ -34,25 +45,29 @@ export const drawPoint = (ctx: CanvasRenderingContext2D, point: Point, radius: n
|
||||
ctx.stroke();
|
||||
|
||||
// Fill the point (hiding any overlapping lines)
|
||||
ctx.fillStyle = "white";
|
||||
ctx.fillStyle = COLORS.CANVAS;
|
||||
ctx.beginPath();
|
||||
ctx.arc(point.x, point.y, radius * (2 / 3), 0, 2 * Math.PI, false);
|
||||
ctx.arc(point.x, point.y, radius * HANDLE_RADIUS_FACTOR, 0, 2 * Math.PI, false);
|
||||
ctx.fill();
|
||||
};
|
||||
|
||||
export const drawText = (ctx: CanvasRenderingContext2D, text: string, x: number, y: number): void => {
|
||||
ctx.fillStyle = "black";
|
||||
export const drawText = (ctx: CanvasRenderingContext2D, text: string, x: number, y: number, textColor = COLORS.INTERACTIVE.STROKE_1): void => {
|
||||
ctx.fillStyle = textColor;
|
||||
ctx.font = "16px Arial";
|
||||
ctx.fillText(text, x, y);
|
||||
};
|
||||
|
||||
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 start
|
||||
points[2] = (optional) handle end
|
||||
points[3] = end point
|
||||
*/
|
||||
export const drawBezierHelper = (ctx: CanvasRenderingContext2D, bezier: WasmBezierInstance, strokeColor = COLORS.INTERACTIVE.STROKE_1, radius = DEFAULT_ENDPOINT_RADIUS): void => {
|
||||
const points = bezier.get_points().map((p: string) => JSON.parse(p));
|
||||
drawBezier(ctx, points, null, strokeColor, radius);
|
||||
};
|
||||
|
||||
export const drawBezier = (ctx: CanvasRenderingContext2D, points: Point[], dragIndex: number | null = null, strokeColor = COLORS.INTERACTIVE.STROKE_1, radius = DEFAULT_ENDPOINT_RADIUS): void => {
|
||||
// Points passed to drawBezier are interpreted as follows
|
||||
// points[0] = start point
|
||||
// points[1] = handle start
|
||||
// points[2] = (optional) handle end
|
||||
// points[3] = end point
|
||||
const start = points[0];
|
||||
let end = null;
|
||||
let handleStart = null;
|
||||
@@ -67,7 +82,7 @@ export const drawBezier = (ctx: CanvasRenderingContext2D, points: Point[], dragI
|
||||
end = points[2];
|
||||
}
|
||||
|
||||
ctx.strokeStyle = "black";
|
||||
ctx.strokeStyle = strokeColor;
|
||||
ctx.lineWidth = 2;
|
||||
|
||||
ctx.beginPath();
|
||||
@@ -79,10 +94,10 @@ export const drawBezier = (ctx: CanvasRenderingContext2D, points: Point[], dragI
|
||||
}
|
||||
ctx.stroke();
|
||||
|
||||
drawLine(ctx, start, handleStart);
|
||||
drawLine(ctx, end, handleEnd);
|
||||
drawLine(ctx, start, handleStart, strokeColor);
|
||||
drawLine(ctx, end, handleEnd, strokeColor);
|
||||
|
||||
points.forEach((point, index) => {
|
||||
drawPoint(ctx, point, getPointSizeByIndex(index, points.length), index === dragIndex ? "Blue" : "Black");
|
||||
drawPoint(ctx, point, getPointSizeByIndex(index, points.length, radius), index === dragIndex ? COLORS.INTERACTIVE.SELECTED : strokeColor);
|
||||
});
|
||||
};
|
||||
|
||||
@@ -4,7 +4,15 @@ export type WasmBezierInstance = InstanceType<WasmRawInstance["WasmBezier"]>;
|
||||
export type WasmBezierKey = keyof WasmBezierInstance;
|
||||
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 BezierCallback = (canvas: HTMLCanvasElement, bezier: WasmBezierInstance, options: Record<string, number>) => void;
|
||||
|
||||
export type SliderOption = {
|
||||
min: number;
|
||||
max: number;
|
||||
step: number;
|
||||
default: number;
|
||||
variable: string;
|
||||
};
|
||||
|
||||
export type Point = {
|
||||
x: number;
|
||||
|
||||
Reference in New Issue
Block a user