Skip to content
Merged
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -24,7 +24,8 @@
// ESLint config
"eslint.format.enable": true,
"eslint.workingDirectories": [
"./frontend"
"./frontend",
"./bezier-rs/docs/interactive-docs",
],
"eslint.validate": [
"javascript",
Expand Down
3 changes: 1 addition & 2 deletions bezier-rs/docs/interactive-docs/.eslintrc.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -65,8 +65,7 @@ module.exports = {
"no-bitwise": "off",
"no-shadow": "off",
"no-use-before-define": "off",
// TODO: Vetur cannot properly recognize paths using @ which contradicts this rule
// "no-restricted-imports": ["error", { patterns: [".*", "!@/*"] }],
"no-restricted-imports": ["error", { patterns: [".*", "!@/*"] }],

// TypeScript plugin config
"@typescript-eslint/indent": "off",
Expand Down
40 changes: 32 additions & 8 deletions bezier-rs/docs/interactive-docs/src/App.vue
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,24 +2,29 @@
<div class="App">
<h1>Bezier-rs Interactive Documentation</h1>
<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>
<ExamplePane />
<div v-for="feature in features" :key="feature.id">
<ExamplePane :name="feature.name" :callback="feature.callback" />
</div>
<div id="svg-test" />
</div>
</template>

<script lang="ts">
import { defineComponent } from "vue";

import ExamplePane from "./components/ExamplePane.vue";
import { drawText, getContextFromCanvas } from "@/utils/drawing";
import { WasmBezierInstance } from "@/utils/types";

import ExamplePane from "@/components/ExamplePane.vue";

// eslint-disable-next-line
const testBezierLib = async () => {
// TODO: Fix below
// 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)
// eslint-disable-next-line
import("../wasm/pkg").then((wasm) => {
// eslint-disable-next-line
const bezier = wasm.WasmBezier.new_quad(0, 0, 50, 0, 100, 100);
import("@/../wasm/pkg").then((wasm) => {
const bezier = wasm.WasmBezier.new_quad([
[0, 0],
[50, 0],
[100, 100],
]);
const svgContainer = document.getElementById("svg-test");
if (svgContainer) {
svgContainer.innerHTML = bezier.to_svg();
Expand All@@ -32,6 +37,25 @@ export default defineComponent({
components: {
ExamplePane,
},
data() {
return {
features: [
{
id: 0,
name: "Constructor",
// eslint-disable-next-line
callback: (): void => {},
},
{
id: 2,
name: "Length",
callback: (canvas: HTMLCanvasElement, bezier: WasmBezierInstance): void => {
drawText(getContextFromCanvas(canvas), `Length: ${bezier.length().toFixed(2)}`, 5, canvas.height - 7);
},
},
],
};
},
});
</script>

Expand Down
16 changes: 8 additions & 8 deletions bezier-rs/docs/interactive-docs/src/components/BezierDrawing.ts
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
import { drawBezier } from "@/utils/drawing";
import { Point, WasmBezierMutatorKey } from "@/utils/types";
import { drawBezier, getContextFromCanvas } from "@/utils/drawing";
import { BezierCallback, Point, WasmBezierMutatorKey } from "@/utils/types";
import { WasmBezierInstance } from "@/utils/wasm-comm";

class BezierDrawing {
Expand All@@ -15,8 +15,11 @@ class BezierDrawing {

bezier: WasmBezierInstance;

constructor(bezier: WasmBezierInstance) {
callback: BezierCallback;

constructor(bezier: WasmBezierInstance, callback: BezierCallback) {
this.bezier = bezier;
this.callback = callback;
this.points = bezier
.get_points()
.map((p) => JSON.parse(p))
Expand All@@ -37,11 +40,7 @@ class BezierDrawing {
this.canvas.width = 200;
this.canvas.height = 200;

const ctx = this.canvas.getContext("2d");
if (ctx == null) {
throw Error("Failed to create context");
}
this.ctx = ctx;
this.ctx = getContextFromCanvas(this.canvas);

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

Expand DownExpand Up@@ -100,6 +99,7 @@ class BezierDrawing {

updateBezier(): void {
drawBezier(this.ctx, this.points);
this.callback(this.canvas, this.bezier);
}

getCanvas(): HTMLCanvasElement {
Expand Down
25 changes: 18 additions & 7 deletions bezier-rs/docs/interactive-docs/src/components/Example.vue
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
<template>
<div>
<h3>{{ title }}</h3>
<figure ref="drawing"></figure>
<h4 class="example_header">{{ title }}</h4>
<figure class="example_figure" ref="drawing"></figure>
</div>
</template>

<script lang="ts">
import { defineComponent, PropType } from "vue";

import { WasmBezierInstance } from "../utils/wasm-comm";

import BezierDrawing from "./BezierDrawing";
import BezierDrawing from "@/components/BezierDrawing";
import { BezierCallback } from "@/utils/types";
import { WasmBezierInstance } from "@/utils/wasm-comm";

export default defineComponent({
name: "ExampleComponent",
Expand All@@ -20,14 +20,25 @@ export default defineComponent({
type: Object as PropType<WasmBezierInstance>,
required: true,
},
callback: {
type: Function as PropType<BezierCallback>,
required: true,
},
},
mounted() {
const bezierDrawing = new BezierDrawing(this.bezier);
const bezierDrawing = new BezierDrawing(this.bezier, this.callback);
const drawing = this.$refs.drawing as HTMLElement;
drawing.appendChild(bezierDrawing.getCanvas());
bezierDrawing.updateBezier();
},
});
</script>

<style scoped></style>
<style scoped>
.example_header {
margin-bottom: 0;
}
.example_figure {
margin-top: 0.5em;
}
</style>
48 changes: 35 additions & 13 deletions bezier-rs/docs/interactive-docs/src/components/ExamplePane.vue
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
<template>
<div class="example_row">
<div v-for="example in exampleData" :key="example.id">
<Example :title="example.title" :bezier="example.bezier" />
<div>
<h2 class="example_pane_header">{{ this.name }}</h2>
<div class="example_row">
<div v-for="example in exampleData" :key="example.id">
<Example :title="example.title" :bezier="example.bezier" :callback="callback" />
</div>
</div>
</div>
</template>

<script lang="ts">
import { defineComponent } from "vue";
import { defineComponent, PropType } from "vue";

import { WasmBezierInstance } from "../utils/wasm-comm";
import { BezierCallback } from "@/utils/types";
import { WasmBezierInstance } from "@/utils/wasm-comm";

import Example from "./Example.vue";
// import wasm from "bezier-rs-wasm";
import Example from "@/components/Example.vue";

type ExampleData = {
id: number;
Expand All@@ -25,24 +28,39 @@ export default defineComponent({
components: {
Example,
},
props: {
name: String,
callback: {
type: Function as PropType<BezierCallback>,
required: true,
},
},
data() {
return {
exampleData: [] as ExampleData[],
};
},
mounted() {
// eslint-disable-next-line
import("../../wasm/pkg").then((wasm) => {
import("@/../wasm/pkg").then((wasm) => {
this.exampleData = [
{
id: 0,
title: "Quadratic Bezier",
bezier: wasm.WasmBezier.new_quad(30, 30, 140, 20, 160, 170),
title: "Quadratic",
bezier: wasm.WasmBezier.new_quad([
[30, 30],
[140, 20],
[160, 170],
]),
},
{
id: 1,
title: "Cubic Bezier",
bezier: wasm.WasmBezier.new_cubic(30, 30, 60, 140, 150, 30, 160, 160),
title: "Cubic",
bezier: wasm.WasmBezier.new_cubic([
[30, 30],
[60, 140],
[150, 30],
[160, 160],
]),
},
];
});
Expand All@@ -56,4 +74,8 @@ export default defineComponent({
flex-direction: row;
justify-content: center;
}

.example_pane_header {
margin-bottom: 0;
}
</style>
2 changes: 1 addition & 1 deletion bezier-rs/docs/interactive-docs/src/main.ts
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
import { createApp } from "vue";

import App from "./App.vue";
import App from "@/App.vue";

createApp(App).mount("#app");
14 changes: 14 additions & 0 deletions bezier-rs/docs/interactive-docs/src/utils/drawing.ts
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
import { Point } from "@/utils/types";

export const getContextFromCanvas = (canvas: HTMLCanvasElement): CanvasRenderingContext2D => {
const ctx = canvas.getContext("2d");
if (ctx === null) {
throw Error("Failed to fetch context");
}
return ctx;
};

export const drawLine = (ctx: CanvasRenderingContext2D, p1: Point, p2: Point): void => {
ctx.strokeStyle = "grey";
ctx.lineWidth = 1;
Expand All@@ -25,6 +33,12 @@ export const drawPoint = (ctx: CanvasRenderingContext2D, p: Point): void => {
ctx.fill();
};

export const drawText = (ctx: CanvasRenderingContext2D, text: string, x: number, y: number): void => {
ctx.fillStyle = "black";
ctx.font = "16px Arial";
ctx.fillText(text, x, y);
};

export const drawBezier = (ctx: CanvasRenderingContext2D, points: Point[]): void => {
/* Until a bezier representation is finalized, treat the points as follows
points[0] = start point
Expand Down
4 changes: 3 additions & 1 deletion bezier-rs/docs/interactive-docs/src/utils/types.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -4,10 +4,12 @@ export type WasmBezierInstance = InstanceType<WasmRawInstance["WasmBezier"]>;
export type WasmBezierKey = keyof WasmBezierInstance;
export type WasmBezierMutatorKey = "set_start" | "set_handle1" | "set_handle2" | "set_end";

export type BezierCallback = (canvas: HTMLCanvasElement, bezier: WasmBezierInstance) => void;

export type Point = {
x: number;
y: number;
r: number;
mutator: WasmBezierMutatorKey;
selected?: boolean;
selected: boolean;
};
24 changes: 16 additions & 8 deletions bezier-rs/docs/interactive-docs/wasm/src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,32 +16,36 @@ pub struct WasmBezier {

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

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

pub fn set_start(&mut self, x: f64, y: f64) {
self.internal.set_start(DVec2::from((x, y)));
self.internal.set_start(DVec2::from((x, y)));
}

pub fn set_end(&mut self, x: f64, y: f64) {
self.internal.set_start( DVec2::from((x, y)));
self.internal.set_end(DVec2::from((x, y)));
}

pub fn set_handle1(&mut self, x: f64, y: f64) {
self.internal.set_handle1(DVec2::from((x, y)));
self.internal.set_handle1(DVec2::from((x, y)));
}

pub fn set_handle2(&mut self, x: f64, y: f64) {
self.internal.set_handle2(DVec2::from((x, y)));
self.internal.set_handle2(DVec2::from((x, y)));
}

pub fn get_points(&self) -> Vec<JsValue> {
Expand All@@ -56,4 +60,8 @@ impl WasmBezier {
pub fn to_svg(&self) -> String {
self.internal.to_svg()
}

pub fn length(&self) -> f64 {
self.internal.length()
}
}
Loading