Skip to content

Commit 94544ba

Browse files
hannahli2010AndroxiumRobNadal
authored
Bezier length implementation (#676)
* added approximated length func to bezier class * Added length functionality to interactive documentation. Co-authored-by: Hannah Li <[email protected]> * fixed typo * fix quadratic check * Modified length function to use new length impl Co-authored-by: Hannah Li <[email protected]> * Fixed errors in length function Co-authored-by: Hannah Li <[email protected]> * Refactor WasmBezier constructor interface * Adjust eslint settings * fixed bug with end point * Replaced missing example headers * Refactor to better use DVec2 * Additional minor changes * Adjust variable names Co-authored-by: Thomas Cheng <[email protected]> Co-authored-by: Robert Nadal <[email protected]>
1 parent 7265ee9 commit 94544ba

File tree

11 files changed

+192
-91
lines changed

11 files changed

+192
-91
lines changed

.vscode/settings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
// ESLint config
2525
"eslint.format.enable": true,
2626
"eslint.workingDirectories": [
27-
"./frontend"
27+
"./frontend",
28+
"./bezier-rs/docs/interactive-docs",
2829
],
2930
"eslint.validate": [
3031
"javascript",

bezier-rs/docs/interactive-docs/.eslintrc.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,7 @@ module.exports = {
6565
"no-bitwise": "off",
6666
"no-shadow": "off",
6767
"no-use-before-define": "off",
68-
// TODO: Vetur cannot properly recognize paths using @ which contradicts this rule
69-
// "no-restricted-imports": ["error", { patterns: [".*", "!@/*"] }],
68+
"no-restricted-imports": ["error", { patterns: [".*", "!@/*"] }],
7069

7170
// TypeScript plugin config
7271
"@typescript-eslint/indent": "off",

bezier-rs/docs/interactive-docs/src/App.vue

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,29 @@
22
<div class="App">
33
<h1>Bezier-rs Interactive Documentation</h1>
44
<p>This is the interactive documentation for the <b>bezier-rs</b> library. Click and drag on the endpoints of the example curves to visualize the various Bezier utilities and functions.</p>
5-
<ExamplePane />
5+
<div v-for="feature in features" :key="feature.id">
6+
<ExamplePane :name="feature.name" :callback="feature.callback" />
7+
</div>
68
<div id="svg-test" />
79
</div>
810
</template>
911

1012
<script lang="ts">
1113
import { defineComponent } from "vue";
1214
13-
import ExamplePane from "./components/ExamplePane.vue";
15+
import { drawText, getContextFromCanvas } from "@/utils/drawing";
16+
import { WasmBezierInstance } from "@/utils/types";
17+
18+
import ExamplePane from "@/components/ExamplePane.vue";
1419
1520
// eslint-disable-next-line
1621
const testBezierLib = async () => {
17-
// TODO: Fix below
18-
// eslint seems to think this pkg is the one in the frontend folder, not the one in interactive-docs (which is not what is actually imported)
19-
// eslint-disable-next-line
20-
import("../wasm/pkg").then((wasm) => {
21-
// eslint-disable-next-line
22-
const bezier = wasm.WasmBezier.new_quad(0, 0, 50, 0, 100, 100);
22+
import("@/../wasm/pkg").then((wasm) => {
23+
const bezier = wasm.WasmBezier.new_quad([
24+
[0, 0],
25+
[50, 0],
26+
[100, 100],
27+
]);
2328
const svgContainer = document.getElementById("svg-test");
2429
if (svgContainer) {
2530
svgContainer.innerHTML = bezier.to_svg();
@@ -32,6 +37,25 @@ export default defineComponent({
3237
components: {
3338
ExamplePane,
3439
},
40+
data() {
41+
return {
42+
features: [
43+
{
44+
id: 0,
45+
name: "Constructor",
46+
// eslint-disable-next-line
47+
callback: (): void => {},
48+
},
49+
{
50+
id: 2,
51+
name: "Length",
52+
callback: (canvas: HTMLCanvasElement, bezier: WasmBezierInstance): void => {
53+
drawText(getContextFromCanvas(canvas), `Length: ${bezier.length().toFixed(2)}`, 5, canvas.height - 7);
54+
},
55+
},
56+
],
57+
};
58+
},
3559
});
3660
</script>
3761

bezier-rs/docs/interactive-docs/src/components/BezierDrawing.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { drawBezier } from "@/utils/drawing";
2-
import { Point, WasmBezierMutatorKey } from "@/utils/types";
1+
import { drawBezier, getContextFromCanvas } from "@/utils/drawing";
2+
import { BezierCallback, Point, WasmBezierMutatorKey } from "@/utils/types";
33
import { WasmBezierInstance } from "@/utils/wasm-comm";
44

55
class BezierDrawing {
@@ -15,8 +15,11 @@ class BezierDrawing {
1515

1616
bezier: WasmBezierInstance;
1717

18-
constructor(bezier: WasmBezierInstance) {
18+
callback: BezierCallback;
19+
20+
constructor(bezier: WasmBezierInstance, callback: BezierCallback) {
1921
this.bezier = bezier;
22+
this.callback = callback;
2023
this.points = bezier
2124
.get_points()
2225
.map((p) => JSON.parse(p))
@@ -37,11 +40,7 @@ class BezierDrawing {
3740
this.canvas.width = 200;
3841
this.canvas.height = 200;
3942

40-
const ctx = this.canvas.getContext("2d");
41-
if (ctx == null) {
42-
throw Error("Failed to create context");
43-
}
44-
this.ctx = ctx;
43+
this.ctx = getContextFromCanvas(this.canvas);
4544

4645
this.dragIndex = null; // Index of the point being moved
4746

@@ -100,6 +99,7 @@ class BezierDrawing {
10099

101100
updateBezier(): void {
102101
drawBezier(this.ctx, this.points);
102+
this.callback(this.canvas, this.bezier);
103103
}
104104

105105
getCanvas(): HTMLCanvasElement {
Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
<template>
22
<div>
3-
<h3>{{ title }}</h3>
4-
<figure ref="drawing"></figure>
3+
<h4 class="example_header">{{ title }}</h4>
4+
<figure class="example_figure" ref="drawing"></figure>
55
</div>
66
</template>
77

88
<script lang="ts">
99
import { defineComponent, PropType } from "vue";
1010
11-
import { WasmBezierInstance } from "../utils/wasm-comm";
12-
13-
import BezierDrawing from "./BezierDrawing";
11+
import BezierDrawing from "@/components/BezierDrawing";
12+
import { BezierCallback } from "@/utils/types";
13+
import { WasmBezierInstance } from "@/utils/wasm-comm";
1414
1515
export default defineComponent({
1616
name: "ExampleComponent",
@@ -20,14 +20,25 @@ export default defineComponent({
2020
type: Object as PropType<WasmBezierInstance>,
2121
required: true,
2222
},
23+
callback: {
24+
type: Function as PropType<BezierCallback>,
25+
required: true,
26+
},
2327
},
2428
mounted() {
25-
const bezierDrawing = new BezierDrawing(this.bezier);
29+
const bezierDrawing = new BezierDrawing(this.bezier, this.callback);
2630
const drawing = this.$refs.drawing as HTMLElement;
2731
drawing.appendChild(bezierDrawing.getCanvas());
2832
bezierDrawing.updateBezier();
2933
},
3034
});
3135
</script>
3236

33-
<style scoped></style>
37+
<style scoped>
38+
.example_header {
39+
margin-bottom: 0;
40+
}
41+
.example_figure {
42+
margin-top: 0.5em;
43+
}
44+
</style>
Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
<template>
2-
<div class="example_row">
3-
<div v-for="example in exampleData" :key="example.id">
4-
<Example :title="example.title" :bezier="example.bezier" />
2+
<div>
3+
<h2 class="example_pane_header">{{ this.name }}</h2>
4+
<div class="example_row">
5+
<div v-for="example in exampleData" :key="example.id">
6+
<Example :title="example.title" :bezier="example.bezier" :callback="callback" />
7+
</div>
58
</div>
69
</div>
710
</template>
811

912
<script lang="ts">
10-
import { defineComponent } from "vue";
13+
import { defineComponent, PropType } from "vue";
1114
12-
import { WasmBezierInstance } from "../utils/wasm-comm";
15+
import { BezierCallback } from "@/utils/types";
16+
import { WasmBezierInstance } from "@/utils/wasm-comm";
1317
14-
import Example from "./Example.vue";
15-
// import wasm from "bezier-rs-wasm";
18+
import Example from "@/components/Example.vue";
1619
1720
type ExampleData = {
1821
id: number;
@@ -25,24 +28,39 @@ export default defineComponent({
2528
components: {
2629
Example,
2730
},
31+
props: {
32+
name: String,
33+
callback: {
34+
type: Function as PropType<BezierCallback>,
35+
required: true,
36+
},
37+
},
2838
data() {
2939
return {
3040
exampleData: [] as ExampleData[],
3141
};
3242
},
3343
mounted() {
34-
// eslint-disable-next-line
35-
import("../../wasm/pkg").then((wasm) => {
44+
import("@/../wasm/pkg").then((wasm) => {
3645
this.exampleData = [
3746
{
3847
id: 0,
39-
title: "Quadratic Bezier",
40-
bezier: wasm.WasmBezier.new_quad(30, 30, 140, 20, 160, 170),
48+
title: "Quadratic",
49+
bezier: wasm.WasmBezier.new_quad([
50+
[30, 30],
51+
[140, 20],
52+
[160, 170],
53+
]),
4154
},
4255
{
4356
id: 1,
44-
title: "Cubic Bezier",
45-
bezier: wasm.WasmBezier.new_cubic(30, 30, 60, 140, 150, 30, 160, 160),
57+
title: "Cubic",
58+
bezier: wasm.WasmBezier.new_cubic([
59+
[30, 30],
60+
[60, 140],
61+
[150, 30],
62+
[160, 160],
63+
]),
4664
},
4765
];
4866
});
@@ -56,4 +74,8 @@ export default defineComponent({
5674
flex-direction: row;
5775
justify-content: center;
5876
}
77+
78+
.example_pane_header {
79+
margin-bottom: 0;
80+
}
5981
</style>
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { createApp } from "vue";
22

3-
import App from "./App.vue";
3+
import App from "@/App.vue";
44

55
createApp(App).mount("#app");

bezier-rs/docs/interactive-docs/src/utils/drawing.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
import { Point } from "@/utils/types";
22

3+
export const getContextFromCanvas = (canvas: HTMLCanvasElement): CanvasRenderingContext2D => {
4+
const ctx = canvas.getContext("2d");
5+
if (ctx === null) {
6+
throw Error("Failed to fetch context");
7+
}
8+
return ctx;
9+
};
10+
311
export const drawLine = (ctx: CanvasRenderingContext2D, p1: Point, p2: Point): void => {
412
ctx.strokeStyle = "grey";
513
ctx.lineWidth = 1;
@@ -25,6 +33,12 @@ export const drawPoint = (ctx: CanvasRenderingContext2D, p: Point): void => {
2533
ctx.fill();
2634
};
2735

36+
export const drawText = (ctx: CanvasRenderingContext2D, text: string, x: number, y: number): void => {
37+
ctx.fillStyle = "black";
38+
ctx.font = "16px Arial";
39+
ctx.fillText(text, x, y);
40+
};
41+
2842
export const drawBezier = (ctx: CanvasRenderingContext2D, points: Point[]): void => {
2943
/* Until a bezier representation is finalized, treat the points as follows
3044
points[0] = start point

bezier-rs/docs/interactive-docs/src/utils/types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ export type WasmBezierInstance = InstanceType<WasmRawInstance["WasmBezier"]>;
44
export type WasmBezierKey = keyof WasmBezierInstance;
55
export type WasmBezierMutatorKey = "set_start" | "set_handle1" | "set_handle2" | "set_end";
66

7+
export type BezierCallback = (canvas: HTMLCanvasElement, bezier: WasmBezierInstance) => void;
8+
79
export type Point = {
810
x: number;
911
y: number;
1012
r: number;
1113
mutator: WasmBezierMutatorKey;
12-
selected?: boolean;
14+
selected: boolean;
1315
};

bezier-rs/docs/interactive-docs/wasm/src/lib.rs

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,32 +16,36 @@ pub struct WasmBezier {
1616

1717
#[wasm_bindgen]
1818
impl WasmBezier {
19-
pub fn new_quad(x1: f64, y1: f64, x2: f64, y2: f64, x3: f64, y3: f64) -> WasmBezier {
19+
/// Expect js_points to be a list of 3 pairs
20+
pub fn new_quad(js_points: &JsValue) -> WasmBezier {
21+
let points: [DVec2; 3] = js_points.into_serde().unwrap();
2022
WasmBezier {
21-
internal: Bezier::from_quadratic_coordinates(x1, y1, x2, y2, x3, y3),
23+
internal: Bezier::from_quadratic_dvec2(points[0], points[1], points[2]),
2224
}
2325
}
2426

25-
pub fn new_cubic(x1: f64, y1: f64, x2: f64, y2: f64, x3: f64, y3: f64, x4: f64, y4: f64) -> WasmBezier {
27+
/// Expect js_points to be a list of 4 pairs
28+
pub fn new_cubic(js_points: &JsValue) -> WasmBezier {
29+
let points: [DVec2; 4] = js_points.into_serde().unwrap();
2630
WasmBezier {
27-
internal: Bezier::from_cubic_coordinates(x1, y1, x2, y2, x3, y3, x4, y4),
31+
internal: Bezier::from_cubic_dvec2(points[0], points[1], points[2], points[3]),
2832
}
2933
}
3034

3135
pub fn set_start(&mut self, x: f64, y: f64) {
32-
self.internal.set_start( DVec2::from((x, y)) );
36+
self.internal.set_start(DVec2::from((x, y)));
3337
}
3438

3539
pub fn set_end(&mut self, x: f64, y: f64) {
36-
self.internal.set_start( DVec2::from((x, y)) );
40+
self.internal.set_end(DVec2::from((x, y)));
3741
}
3842

3943
pub fn set_handle1(&mut self, x: f64, y: f64) {
40-
self.internal.set_handle1( DVec2::from((x, y)) );
44+
self.internal.set_handle1(DVec2::from((x, y)));
4145
}
4246

4347
pub fn set_handle2(&mut self, x: f64, y: f64) {
44-
self.internal.set_handle2( DVec2::from((x, y)) );
48+
self.internal.set_handle2(DVec2::from((x, y)));
4549
}
4650

4751
pub fn get_points(&self) -> Vec<JsValue> {
@@ -56,4 +60,8 @@ impl WasmBezier {
5660
pub fn to_svg(&self) -> String {
5761
self.internal.to_svg()
5862
}
63+
64+
pub fn length(&self) -> f64 {
65+
self.internal.length()
66+
}
5967
}

0 commit comments

Comments
 (0)