mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-09-25 13:28:11 +08:00
Support linear bezier curve segments in the Bezier math library (#717)
* Support Linear line segments, add linear section to interactive docs * Fix regression, customize points in UI examples, add optional subdivisions to length, minor refactors * Refactor ExamplePane, use better example curves * Update consts.rs comments * Code review changes * Address PR comments * Code review Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
committed by
Keavon Chambers
co-authored by
Keavon Chambers
parent
ad7097ea92
commit
00cc50d531
@@ -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"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -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<string, number>): 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<string, number>): 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<string, number>): 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: "π",
|
||||
},
|
||||
],
|
||||
|
||||
@@ -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 });
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<h4 class="example_header">{{ title }}</h4>
|
||||
<figure class="example_figure" ref="drawing"></figure>
|
||||
<h4 class="example-header">{{ title }}</h4>
|
||||
<figure class="example-figure" ref="drawing"></figure>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -54,10 +54,11 @@ export default defineComponent({
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.example_header {
|
||||
.example-header {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.example_figure {
|
||||
|
||||
.example-figure {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<h2 class="example_pane_header">{{ name }}</h2>
|
||||
<div class="example_row">
|
||||
<h2 class="example-pane-header">{{ name }}</h2>
|
||||
<div class="example-row">
|
||||
<div v-for="(example, index) in exampleData" :key="index">
|
||||
<component :is="template" :templateOptions="example.templateOptions" :title="example.title" :bezier="example.bezier" :callback="callback" :createThroughPoints="createThroughPoints" />
|
||||
</div>
|
||||
@@ -12,7 +12,7 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType, Component } from "vue";
|
||||
|
||||
import { BezierCallback, TemplateOption, WasmBezierInstance, WasmRawInstance } from "@/utils/types";
|
||||
import { BezierCallback, BezierCurveType, TemplateOption, WasmBezierConstructorKey, WasmBezierInstance, WasmRawInstance } from "@/utils/types";
|
||||
|
||||
import Example from "@/components/Example.vue";
|
||||
|
||||
@@ -22,13 +22,51 @@ type ExampleData = {
|
||||
templateOptions: TemplateOption;
|
||||
};
|
||||
|
||||
type CustomTemplateOptions = {
|
||||
[key in BezierCurveType]?: TemplateOption;
|
||||
};
|
||||
|
||||
type CustomPoints = {
|
||||
[key in BezierCurveType]?: number[][];
|
||||
};
|
||||
|
||||
const CurveTypeMapping = {
|
||||
[BezierCurveType.Linear]: {
|
||||
points: [
|
||||
[30, 60],
|
||||
[140, 120],
|
||||
],
|
||||
constructor: "new_linear" as WasmBezierConstructorKey,
|
||||
},
|
||||
[BezierCurveType.Quadratic]: {
|
||||
points: [
|
||||
[30, 50],
|
||||
[140, 30],
|
||||
[160, 170],
|
||||
],
|
||||
constructor: "new_quadratic" as WasmBezierConstructorKey,
|
||||
},
|
||||
[BezierCurveType.Cubic]: {
|
||||
points: [
|
||||
[30, 30],
|
||||
[60, 140],
|
||||
[150, 30],
|
||||
[160, 160],
|
||||
],
|
||||
constructor: "new_cubic" as WasmBezierConstructorKey,
|
||||
},
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
name: "ExamplePane",
|
||||
components: {
|
||||
Example,
|
||||
},
|
||||
props: {
|
||||
name: String,
|
||||
name: {
|
||||
type: String as PropType<string>,
|
||||
required: true,
|
||||
},
|
||||
callback: {
|
||||
type: Function as PropType<BezierCallback>,
|
||||
required: true,
|
||||
@@ -37,15 +75,26 @@ export default defineComponent({
|
||||
type: Object as PropType<Component>,
|
||||
default: Example,
|
||||
},
|
||||
templateOptions: Object as PropType<TemplateOption>,
|
||||
cubicOptions: {
|
||||
templateOptions: {
|
||||
type: Object as PropType<TemplateOption>,
|
||||
default: null,
|
||||
required: false,
|
||||
},
|
||||
customOptions: {
|
||||
type: Object as PropType<CustomTemplateOptions>,
|
||||
default: () => ({}),
|
||||
},
|
||||
createThroughPoints: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: false,
|
||||
},
|
||||
curveDegrees: {
|
||||
type: Set as PropType<Set<BezierCurveType>>,
|
||||
default: () => new Set(Object.values(BezierCurveType)),
|
||||
},
|
||||
customPoints: {
|
||||
type: Object as PropType<CustomPoints>,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -54,42 +103,32 @@ export default defineComponent({
|
||||
},
|
||||
mounted() {
|
||||
import("@/../wasm/pkg").then((wasm: WasmRawInstance) => {
|
||||
const quadraticPoints = [
|
||||
[30, 50],
|
||||
[140, 30],
|
||||
[160, 170],
|
||||
];
|
||||
const cubicPoints = [
|
||||
[30, 30],
|
||||
[60, 140],
|
||||
[150, 30],
|
||||
[160, 160],
|
||||
];
|
||||
this.exampleData = [
|
||||
{
|
||||
title: "Quadratic",
|
||||
bezier: wasm.WasmBezier.new_quadratic(quadraticPoints),
|
||||
templateOptions: this.templateOptions as TemplateOption,
|
||||
},
|
||||
{
|
||||
title: "Cubic",
|
||||
bezier: wasm.WasmBezier.new_cubic(cubicPoints),
|
||||
templateOptions: (this.cubicOptions || this.templateOptions) as TemplateOption,
|
||||
},
|
||||
];
|
||||
this.exampleData = [];
|
||||
// Only add example for BezierCurveType that is in the curveDegrees set
|
||||
Object.values(BezierCurveType).forEach((bezierType) => {
|
||||
if (this.curveDegrees.has(bezierType)) {
|
||||
const { points, constructor } = CurveTypeMapping[bezierType];
|
||||
this.exampleData.push({
|
||||
title: bezierType,
|
||||
// Use custom options if they were provided for the current BezierCurveType
|
||||
bezier: wasm.WasmBezier[constructor](this.customPoints[bezierType] || points),
|
||||
templateOptions: (this.customOptions[bezierType] || this.templateOptions) as TemplateOption,
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.example_row {
|
||||
.example-row {
|
||||
display: flex; /* or inline-flex */
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.example_pane_header {
|
||||
.example-pane-header {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div>
|
||||
<Example :title="title" :bezier="bezier" :callback="callback" :options="sliderData" :createThroughPoints="createThroughPoints" />
|
||||
<div v-for="(slider, index) in templateOptions.sliders" :key="index">
|
||||
<div class="slider_label">{{ slider.variable }} = {{ sliderData[slider.variable] }}{{ sliderUnits[slider.variable] }}</div>
|
||||
<div class="slider-label">{{ slider.variable }} = {{ sliderData[slider.variable] }}{{ sliderUnits[slider.variable] }}</div>
|
||||
<input class="slider" v-model.number="sliderData[slider.variable]" type="range" :step="slider.step" :min="slider.min" :max="slider.max" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -28,9 +28,9 @@ export const getContextFromCanvas = (canvas: HTMLCanvasElement): CanvasRendering
|
||||
return ctx;
|
||||
};
|
||||
|
||||
export const drawLine = (ctx: CanvasRenderingContext2D, point1: Point, point2: Point, strokeColor = COLORS.INTERACTIVE.STROKE_2): void => {
|
||||
export const drawLine = (ctx: CanvasRenderingContext2D, point1: Point, point2: Point, strokeColor = COLORS.INTERACTIVE.STROKE_2, lineWidth = 1): void => {
|
||||
ctx.strokeStyle = strokeColor;
|
||||
ctx.lineWidth = 1;
|
||||
ctx.lineWidth = lineWidth;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(point1.x, point1.y);
|
||||
@@ -59,6 +59,20 @@ export const drawText = (ctx: CanvasRenderingContext2D, text: string, x: number,
|
||||
ctx.fillText(text, x, y);
|
||||
};
|
||||
|
||||
export const drawCurve = (ctx: CanvasRenderingContext2D, points: Point[], strokeColor = COLORS.INTERACTIVE.STROKE_1): void => {
|
||||
ctx.strokeStyle = strokeColor;
|
||||
ctx.lineWidth = 2;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(points[0].x, points[0].y);
|
||||
if (points.length === 3) {
|
||||
ctx.quadraticCurveTo(points[1].x, points[1].y, points[2].x, points[2].y);
|
||||
} else {
|
||||
ctx.bezierCurveTo(points[1].x, points[1].y, points[2].x, points[2].y, points[3].x, points[3].y);
|
||||
}
|
||||
ctx.stroke();
|
||||
};
|
||||
|
||||
export const drawBezierHelper = (ctx: CanvasRenderingContext2D, bezier: WasmBezierInstance, bezierStyleConfig: Partial<BezierStyleConfig> = {}): void => {
|
||||
const points = bezier.get_points().map((p: string) => JSON.parse(p));
|
||||
drawBezier(ctx, points, null, bezierStyleConfig);
|
||||
@@ -95,27 +109,24 @@ export const drawBezier = (ctx: CanvasRenderingContext2D, points: Point[], dragI
|
||||
handleStart = points[1];
|
||||
handleEnd = points[2];
|
||||
end = points[3];
|
||||
} else {
|
||||
} else if (points.length === 3) {
|
||||
handleStart = points[1];
|
||||
handleEnd = handleStart;
|
||||
end = points[2];
|
||||
}
|
||||
|
||||
ctx.strokeStyle = styleConfig.curveStrokeColor;
|
||||
ctx.lineWidth = 2;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(points[0].x, points[0].y);
|
||||
if (points.length === 3) {
|
||||
ctx.quadraticCurveTo(handleStart.x, handleStart.y, end.x, end.y);
|
||||
} else {
|
||||
ctx.bezierCurveTo(handleStart.x, handleStart.y, handleEnd.x, handleEnd.y, end.x, end.y);
|
||||
handleStart = start;
|
||||
handleEnd = points[1];
|
||||
end = handleEnd;
|
||||
}
|
||||
ctx.stroke();
|
||||
|
||||
if (styleConfig.drawHandles) {
|
||||
drawLine(ctx, start, handleStart, styleConfig.handleLineStrokeColor);
|
||||
drawLine(ctx, end, handleEnd, styleConfig.handleLineStrokeColor);
|
||||
if (points.length === 2) {
|
||||
drawLine(ctx, start, end, styleConfig.curveStrokeColor, 2);
|
||||
} else {
|
||||
drawCurve(ctx, points, styleConfig.curveStrokeColor);
|
||||
if (styleConfig.drawHandles) {
|
||||
drawLine(ctx, start, handleStart, styleConfig.handleLineStrokeColor);
|
||||
drawLine(ctx, end, handleEnd, styleConfig.handleLineStrokeColor);
|
||||
}
|
||||
}
|
||||
|
||||
points.forEach((point, index) => {
|
||||
|
||||
@@ -2,7 +2,14 @@ export type WasmRawInstance = typeof import("../../wasm/pkg");
|
||||
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 WasmBezierConstructorKey = "new_linear" | "new_quadratic" | "new_cubic";
|
||||
export type WasmBezierManipulatorKey = "set_start" | "set_handle_start" | "set_handle_end" | "set_end";
|
||||
|
||||
export enum BezierCurveType {
|
||||
Linear = "Linear",
|
||||
Quadratic = "Quadratic",
|
||||
Cubic = "Cubic",
|
||||
}
|
||||
|
||||
export type BezierCallback = (canvas: HTMLCanvasElement, bezier: WasmBezierInstance, options: Record<string, number>, mouseLocation?: Point) => void;
|
||||
|
||||
@@ -25,7 +32,7 @@ export type Point = {
|
||||
};
|
||||
|
||||
export type BezierPoint = Point & {
|
||||
mutator: WasmBezierMutatorKey;
|
||||
manipulator: WasmBezierManipulatorKey;
|
||||
};
|
||||
|
||||
export type BezierStyleConfig = {
|
||||
|
||||
@@ -21,7 +21,7 @@ fn vec_to_point(p: &DVec2) -> JsValue {
|
||||
|
||||
/// 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()
|
||||
bezier.get_points().map(|point| Point { x: point.x, y: point.y }).collect()
|
||||
}
|
||||
|
||||
/// Serialize some data and then convert it to a JsValue.
|
||||
@@ -31,6 +31,12 @@ fn to_js_value<T: Serialize>(data: T) -> JsValue {
|
||||
|
||||
#[wasm_bindgen]
|
||||
impl WasmBezier {
|
||||
/// Expect js_points to be a list of 3 pairs.
|
||||
pub fn new_linear(js_points: &JsValue) -> WasmBezier {
|
||||
let points: [DVec2; 2] = js_points.into_serde().unwrap();
|
||||
WasmBezier(Bezier::from_linear_dvec2(points[0], points[1]))
|
||||
}
|
||||
|
||||
/// Expect js_points to be a list of 3 pairs.
|
||||
pub fn new_quadratic(js_points: &JsValue) -> WasmBezier {
|
||||
let points: [DVec2; 3] = js_points.into_serde().unwrap();
|
||||
@@ -70,7 +76,7 @@ impl WasmBezier {
|
||||
}
|
||||
|
||||
pub fn get_points(&self) -> Vec<JsValue> {
|
||||
self.0.get_points().iter().flatten().map(vec_to_point).collect()
|
||||
self.0.get_points().map(|point| vec_to_point(&point)).collect()
|
||||
}
|
||||
|
||||
pub fn to_svg(&self) -> String {
|
||||
@@ -78,17 +84,21 @@ impl WasmBezier {
|
||||
}
|
||||
|
||||
pub fn length(&self) -> f64 {
|
||||
self.0.length()
|
||||
self.0.length(None)
|
||||
}
|
||||
|
||||
pub fn compute(&self, t: f64) -> JsValue {
|
||||
vec_to_point(&self.0.compute(t))
|
||||
pub fn evaluate(&self, t: f64) -> JsValue {
|
||||
vec_to_point(&self.0.evaluate(t))
|
||||
}
|
||||
|
||||
pub fn compute_lookup_table(&self, steps: i32) -> Vec<JsValue> {
|
||||
self.0.compute_lookup_table(Some(steps)).iter().map(vec_to_point).collect()
|
||||
}
|
||||
|
||||
pub fn derivative(&self) -> Option<WasmBezier> {
|
||||
self.0.derivative().map(WasmBezier)
|
||||
}
|
||||
|
||||
pub fn tangent(&self, t: f64) -> JsValue {
|
||||
vec_to_point(&self.0.tangent(t))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user