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:
Hannah Li
2022-06-27 19:33:56 -04:00
committed by GitHub
co-authored by Thomas Cheng Thomas Cheng Rob Nadal
parent 55444eea86
commit 79944b54c8
8 changed files with 224 additions and 102 deletions
@@ -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;