diff --git a/bezier-rs/docs/interactive-docs/src/App.vue b/bezier-rs/docs/interactive-docs/src/App.vue index a5f738adf..03e7b22e4 100644 --- a/bezier-rs/docs/interactive-docs/src/App.vue +++ b/bezier-rs/docs/interactive-docs/src/App.vue @@ -9,7 +9,9 @@ :name="feature.name" :callback="feature.callback" :createThroughPoints="feature.createThroughPoints" - :cubicOptions="feature.cubicOptions" + :curveDegrees="feature.curveDegrees" + :customPoints="feature.customPoints" + :customOptions="feature.customOptions" /> @@ -19,7 +21,7 @@ import { defineComponent, markRaw } from "vue"; import { drawText, drawPoint, drawBezier, drawLine, getContextFromCanvas, drawBezierHelper, COLORS } from "@/utils/drawing"; -import { Point, WasmBezierInstance } from "@/utils/types"; +import { BezierCurveType, Point, WasmBezierInstance } from "@/utils/types"; import ExamplePane from "@/components/ExamplePane.vue"; import SliderExample from "@/components/SliderExample.vue"; @@ -51,6 +53,7 @@ export default defineComponent({ name: "Bezier Through Points", // eslint-disable-next-line callback: (): void => {}, + curveDegrees: new Set([BezierCurveType.Quadratic, BezierCurveType.Cubic]), createThroughPoints: true, template: markRaw(SliderExample), templateOptions: { @@ -64,22 +67,31 @@ export default defineComponent({ }, ], }, - cubicOptions: { - sliders: [ - { - min: 0.01, - max: 0.99, - step: 0.01, - default: 0.5, - variable: "t", - }, - { - min: 0, - max: 100, - step: 5, - default: 10, - variable: "midpoint separation", - }, + customOptions: { + [BezierCurveType.Cubic]: { + sliders: [ + { + min: 0.01, + max: 0.99, + step: 0.01, + default: 0.5, + variable: "t", + }, + { + min: 0, + max: 100, + step: 2, + default: 30, + variable: "midpoint separation", + }, + ], + }, + }, + customPoints: { + [BezierCurveType.Quadratic]: [ + [30, 50], + [120, 70], + [160, 170], ], }, }, @@ -90,9 +102,9 @@ export default defineComponent({ }, }, { - name: "Compute", + name: "Evaluate", callback: (canvas: HTMLCanvasElement, bezier: WasmBezierInstance, options: Record): void => { - const point = JSON.parse(bezier.compute(options.t)); + const point = JSON.parse(bezier.evaluate(options.t)); drawPoint(getContextFromCanvas(canvas), point, 4, COLORS.NON_INTERACTIVE.STROKE_1); }, template: markRaw(SliderExample), @@ -121,12 +133,42 @@ export default defineComponent({ ], }, }, + { + name: "Derivative", + callback: (canvas: HTMLCanvasElement, bezier: WasmBezierInstance): void => { + const context = getContextFromCanvas(canvas); + + const derivativeBezier = bezier.derivative(); + if (derivativeBezier) { + const points: Point[] = derivativeBezier.get_points().map((p) => JSON.parse(p)); + if (points.length === 2) { + drawLine(context, points[0], points[1], COLORS.NON_INTERACTIVE.STROKE_1); + } else { + drawBezier(context, points, null, { curveStrokeColor: COLORS.NON_INTERACTIVE.STROKE_1, radius: 3.5 }); + } + } + }, + curveDegrees: new Set([BezierCurveType.Quadratic, BezierCurveType.Cubic]), + customPoints: { + [BezierCurveType.Quadratic]: [ + [30, 40], + [110, 50], + [120, 130], + ], + [BezierCurveType.Cubic]: [ + [50, 50], + [60, 100], + [100, 140], + [140, 150], + ], + }, + }, { name: "Tangent", callback: (canvas: HTMLCanvasElement, bezier: WasmBezierInstance, options: Record): void => { const context = getContextFromCanvas(canvas); - const intersection = JSON.parse(bezier.compute(options.t)); + const intersection = JSON.parse(bezier.evaluate(options.t)); const tangent = JSON.parse(bezier.tangent(options.t)); const tangentEnd = { @@ -146,7 +188,7 @@ export default defineComponent({ callback: (canvas: HTMLCanvasElement, bezier: WasmBezierInstance, options: Record): void => { const context = getContextFromCanvas(canvas); - const intersection = JSON.parse(bezier.compute(options.t)); + const intersection = JSON.parse(bezier.evaluate(options.t)); const normal = JSON.parse(bezier.normal(options.t)); const normalEnd = { @@ -218,13 +260,26 @@ export default defineComponent({ const extrema: number[][] = JSON.parse(bezier.local_extrema()); extrema.forEach((tValues, index) => { tValues.forEach((t) => { - const point: Point = JSON.parse(bezier.compute(t)); + const point: Point = JSON.parse(bezier.evaluate(t)); drawPoint(context, point, 4, dimensionColors[index]); }); }); drawText(getContextFromCanvas(canvas), "X extrema", 5, canvas.height - 20, dimensionColors[0]); drawText(getContextFromCanvas(canvas), "Y extrema", 5, canvas.height - 5, dimensionColors[1]); }, + customPoints: { + [BezierCurveType.Quadratic]: [ + [40, 40], + [160, 30], + [110, 150], + ], + [BezierCurveType.Cubic]: [ + [160, 180], + [170, 10], + [30, 90], + [180, 160], + ], + }, }, { name: "Rotate", @@ -243,8 +298,8 @@ export default defineComponent({ variable: "angle", min: 0, max: 2, - step: 1 / 16, - default: 1 / 8, + step: 1 / 50, + default: 0.12, unit: "π", }, ], diff --git a/bezier-rs/docs/interactive-docs/src/components/BezierDrawing.ts b/bezier-rs/docs/interactive-docs/src/components/BezierDrawing.ts index 8e17fdcb4..0a551a005 100644 --- a/bezier-rs/docs/interactive-docs/src/components/BezierDrawing.ts +++ b/bezier-rs/docs/interactive-docs/src/components/BezierDrawing.ts @@ -1,13 +1,19 @@ import { WasmBezier } from "@/../wasm/pkg"; + import { COLORS, drawBezier, drawPoint, getContextFromCanvas, getPointSizeByIndex } from "@/utils/drawing"; -import { BezierCallback, BezierPoint, BezierStyleConfig, Point, WasmBezierMutatorKey, WasmBezierInstance } from "@/utils/types"; +import { BezierCallback, BezierPoint, BezierStyleConfig, Point, WasmBezierManipulatorKey, WasmBezierInstance } from "@/utils/types"; // Offset to increase selectable range, used to make points easier to grab const FUDGE_FACTOR = 3; -class BezierDrawing { - static indexToMutator: WasmBezierMutatorKey[] = ["set_start", "set_handle_start", "set_handle_end", "set_end"]; +// Given the number of points in the curve, map the index of a point to the correct manipulator key +const MANIPULATOR_KEYS_FROM_BEZIER_TYPE: { [k: number]: WasmBezierManipulatorKey[] } = { + 2: ["set_start", "set_end"], + 3: ["set_start", "set_handle_start", "set_end"], + 4: ["set_start", "set_handle_start", "set_handle_end", "set_end"], +}; +class BezierDrawing { points: BezierPoint[]; canvas: HTMLCanvasElement; @@ -37,7 +43,7 @@ class BezierDrawing { y: p.y, r: getPointSizeByIndex(i, points.length), selected: false, - mutator: BezierDrawing.indexToMutator[points.length === 3 && i > 1 ? i + 1 : i], + manipulator: MANIPULATOR_KEYS_FROM_BEZIER_TYPE[points.length][i], })); if (this.createThroughPoints && this.points.length === 4) { @@ -70,10 +76,7 @@ class BezierDrawing { } mouseMoveHandler(evt: MouseEvent): void { - if (evt.buttons === 0) { - this.deselectPointHandler(); - return; - } + if (evt.buttons === 0) this.deselectPointHandler(); const mx = evt.offsetX; const my = evt.offsetY; @@ -84,7 +87,7 @@ class BezierDrawing { const selectedPoint = this.points[this.dragIndex]; selectedPoint.x = mx; selectedPoint.y = my; - this.bezier[selectedPoint.mutator](selectedPoint.x, selectedPoint.y); + this.bezier[selectedPoint.manipulator](selectedPoint.x, selectedPoint.y); } } this.updateBezier({ x: mx, y: my }); diff --git a/bezier-rs/docs/interactive-docs/src/components/Example.vue b/bezier-rs/docs/interactive-docs/src/components/Example.vue index 46405d201..ec5dce8ae 100644 --- a/bezier-rs/docs/interactive-docs/src/components/Example.vue +++ b/bezier-rs/docs/interactive-docs/src/components/Example.vue @@ -1,7 +1,7 @@ @@ -54,10 +54,11 @@ export default defineComponent({ diff --git a/bezier-rs/docs/interactive-docs/src/components/ExamplePane.vue b/bezier-rs/docs/interactive-docs/src/components/ExamplePane.vue index a62422bea..1cded33da 100644 --- a/bezier-rs/docs/interactive-docs/src/components/ExamplePane.vue +++ b/bezier-rs/docs/interactive-docs/src/components/ExamplePane.vue @@ -1,7 +1,7 @@