Skip to content

Add get and getLUT functions #677

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jun 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 46 additions & 3 deletions bezier-rs/docs/interactive-docs/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,21 @@
<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>
<div v-for="feature in features" :key="feature.id">
<ExamplePane :name="feature.name" :callback="feature.callback" />
<ExamplePane :template="feature.template" :templateOptions="feature.templateOptions" :name="feature.name" :callback="feature.callback" />
</div>
<br />
<div id="svg-test" />
</div>
</template>

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

import { drawText, getContextFromCanvas } from "@/utils/drawing";
import { drawText, drawPoint, getContextFromCanvas } from "@/utils/drawing";
import { WasmBezierInstance } from "@/utils/types";

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

// eslint-disable-next-line
const testBezierLib = async () => {
Expand Down Expand Up @@ -53,6 +55,47 @@ export default defineComponent({
drawText(getContextFromCanvas(canvas), `Length: ${bezier.length().toFixed(2)}`, 5, canvas.height - 7);
},
},
{
id: 3,
name: "Compute",
callback: (canvas: HTMLCanvasElement, bezier: WasmBezierInstance, options: string): void => {
const point = JSON.parse(bezier.compute(parseFloat(options)));
point.r = 4;
point.selected = false;
drawPoint(getContextFromCanvas(canvas), point, "DarkBlue");
},
template: markRaw(SliderExample),
templateOptions: {
min: 0,
max: 1,
step: 0.01,
default: 0.5,
variable: "t",
},
},
{
id: 4,
name: "Lookup Table",
callback: (canvas: HTMLCanvasElement, bezier: WasmBezierInstance, options: string): void => {
const lookupPoints = bezier.compute_lookup_table(Number(options));
lookupPoints.forEach((serPoint, index) => {
if (index !== 0 && index !== lookupPoints.length - 1) {
const point = JSON.parse(serPoint);
point.r = 3;
point.selected = false;
drawPoint(getContextFromCanvas(canvas), point, "DarkBlue");
}
});
},
template: markRaw(SliderExample),
templateOptions: {
min: 2,
max: 15,
step: 1,
default: 5,
variable: "Steps",
},
},
],
};
},
Expand Down
13 changes: 10 additions & 3 deletions bezier-rs/docs/interactive-docs/src/components/BezierDrawing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@ class BezierDrawing {

callback: BezierCallback;

constructor(bezier: WasmBezierInstance, callback: BezierCallback) {
options: string;

constructor(bezier: WasmBezierInstance, callback: BezierCallback, options: string) {
this.bezier = bezier;
this.callback = callback;
this.options = options;
this.points = bezier
.get_points()
.map((p) => JSON.parse(p))
Expand Down Expand Up @@ -97,9 +100,13 @@ class BezierDrawing {
}
}

updateBezier(): void {
updateBezier(options = ""): void {
if (options !== "") {
this.options = options;
}
this.ctx.clearRect(1, 1, this.canvas.width - 2, this.canvas.height - 2);
drawBezier(this.ctx, this.points);
this.callback(this.canvas, this.bezier);
this.callback(this.canvas, this.bezier, this.options);
}

getCanvas(): HTMLCanvasElement {
Expand Down
19 changes: 16 additions & 3 deletions bezier-rs/docs/interactive-docs/src/components/Example.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ import { WasmBezierInstance } from "@/utils/wasm-comm";

export default defineComponent({
name: "ExampleComponent",
data() {
return {
bezierDrawing: new BezierDrawing(this.bezier, this.callback, this.options),
};
},
props: {
title: String,
bezier: {
Expand All @@ -24,12 +29,20 @@ export default defineComponent({
type: Function as PropType<BezierCallback>,
required: true,
},
options: {
type: String,
default: "",
},
},
mounted() {
const bezierDrawing = new BezierDrawing(this.bezier, this.callback);
const drawing = this.$refs.drawing as HTMLElement;
drawing.appendChild(bezierDrawing.getCanvas());
bezierDrawing.updateBezier();
drawing.appendChild(this.bezierDrawing.getCanvas());
this.bezierDrawing.updateBezier();
},
watch: {
options() {
this.bezierDrawing.updateBezier(this.options);
},
},
});
</script>
Expand Down
11 changes: 8 additions & 3 deletions bezier-rs/docs/interactive-docs/src/components/ExamplePane.vue
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
<template>
<div>
<h2 class="example_pane_header">{{ this.name }}</h2>
<h2 class="example_pane_header">{{ name }}</h2>
<div class="example_row">
<div v-for="example in exampleData" :key="example.id">
<Example :title="example.title" :bezier="example.bezier" :callback="callback" />
<component :is="template" :templateOptions="templateOptions" :title="example.title" :bezier="example.bezier" :callback="callback" />
</div>
</div>
</div>
</template>

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

import { BezierCallback } from "@/utils/types";
import { WasmBezierInstance } from "@/utils/wasm-comm";
Expand All @@ -34,6 +34,11 @@ export default defineComponent({
type: Function as PropType<BezierCallback>,
required: true,
},
template: {
type: Object as PropType<Component>,
default: Example,
},
templateOptions: Object,
},
data() {
return {
Expand Down
45 changes: 45 additions & 0 deletions bezier-rs/docs/interactive-docs/src/components/SliderExample.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<template>
<div>
<Example :title="title" :bezier="bezier" :callback="callback" :options="value.toString()" />
<div class="slider_label">{{ templateOptions.variable }} = {{ value }}</div>
<input class="slider" v-model="value" type="range" :step="templateOptions.step" :min="templateOptions.min" :max="templateOptions.max" />
</div>
</template>

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

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

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

export default defineComponent({
name: "SliderExample",
components: {
Example,
},
props: {
title: String,
bezier: {
type: Object as PropType<WasmBezierInstance>,
required: true,
},
callback: {
type: Function as PropType<BezierCallback>,
required: true,
},
templateOptions: {
type: Object,
default: () => ({}),
},
},
data() {
return {
value: this.templateOptions.default,
};
},
});
</script>

<style scoped></style>
4 changes: 2 additions & 2 deletions bezier-rs/docs/interactive-docs/src/utils/drawing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ export const drawLine = (ctx: CanvasRenderingContext2D, p1: Point, p2: Point): v
ctx.stroke();
};

export const drawPoint = (ctx: CanvasRenderingContext2D, p: Point): void => {
export const drawPoint = (ctx: CanvasRenderingContext2D, p: Point, stroke = "black"): void => {
// Outline the point
ctx.strokeStyle = p.selected ? "blue" : "black";
ctx.strokeStyle = p.selected ? "blue" : stroke;
ctx.lineWidth = p.r / 3;
ctx.beginPath();
ctx.arc(p.x, p.y, p.r, 0, 2 * Math.PI, false);
Expand Down
2 changes: 1 addition & 1 deletion bezier-rs/docs/interactive-docs/src/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ 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 BezierCallback = (canvas: HTMLCanvasElement, bezier: WasmBezierInstance, options: string) => void;

export type Point = {
x: number;
Expand Down
19 changes: 13 additions & 6 deletions bezier-rs/docs/interactive-docs/wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ pub struct WasmBezier {
internal: Bezier,
}

pub fn vec_to_point(p: &DVec2) -> JsValue {
JsValue::from_serde(&serde_json::to_string(&Point { x: p[0], y: p[1] }).unwrap()).unwrap()
}

#[wasm_bindgen]
impl WasmBezier {
/// Expect js_points to be a list of 3 pairs
Expand Down Expand Up @@ -49,12 +53,7 @@ impl WasmBezier {
}

pub fn get_points(&self) -> Vec<JsValue> {
self.internal
.get_points()
.iter()
.flatten()
.map(|p| JsValue::from_serde(&serde_json::to_string(&Point { x: p[0], y: p[1] }).unwrap()).unwrap())
.collect()
self.internal.get_points().iter().flatten().map(vec_to_point).collect()
}

pub fn to_svg(&self) -> String {
Expand All @@ -64,4 +63,12 @@ impl WasmBezier {
pub fn length(&self) -> f64 {
self.internal.length()
}

pub fn compute(&self, t: f64) -> JsValue {
vec_to_point(&self.internal.compute(t))
}

pub fn compute_lookup_table(&self, steps: i32) -> Vec<JsValue> {
self.internal.compute_lookup_table(Some(steps)).iter().map(vec_to_point).collect()
}
}
37 changes: 25 additions & 12 deletions bezier-rs/lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,9 @@ impl Bezier {

/// Calculate the point on the curve based on the t-value provided
/// basis code based off of pseudocode found here: https://pomax.github.io/bezierinfo/#explanation
pub fn get_basis(&self, t: f64) -> DVec2 {
pub fn compute(&self, t: f64) -> DVec2 {
assert!((0.0..=1.0).contains(&t));

let t_squared = t * t;
let one_minus_t = 1.0 - t;
let squared_one_minus_t = one_minus_t * one_minus_t;
Expand All @@ -171,28 +173,39 @@ impl Bezier {
}
}

/// Return a selection of equidistant points on the bezier curve
/// If no value is provided for `steps`, then the function will default `steps` to be 10
pub fn compute_lookup_table(&self, steps: Option<i32>) -> Vec<DVec2> {
let steps_unwrapped = steps.unwrap_or(10);
let ratio: f64 = 1.0 / (steps_unwrapped as f64);
let mut steps_array = Vec::with_capacity((steps_unwrapped + 1) as usize);

for t in 0..steps_unwrapped + 1 {
steps_array.push(self.compute(f64::from(t) * ratio))
}

steps_array
}

/// Return an approximation of the length of the bezier curve
/// code example taken from: https://gamedev.stackexchange.com/questions/5373/moving-ships-between-two-planets-along-a-bezier-missing-some-equations-for-acce/5427#5427
pub fn length(&self) -> f64 {
// We will use an approximate approach where
// we split the curve into many subdivisions
// and calculate the euclidean distance between the two endpoints of the subdivision
const SUBDIVISIONS: i32 = 1000;
const RATIO: f64 = 1.0 / (SUBDIVISIONS as f64);

// start_point tracks the starting point of the subdivision
let mut start_point = self.get_basis(0.0);
let mut length_subtotal = 0.0;
let lookup_table = self.compute_lookup_table(Some(SUBDIVISIONS));
let mut approx_curve_length = 0.0;
let mut prev_point = lookup_table[0];
// calculate approximate distance between subdivision
for subdivision in 1..SUBDIVISIONS + 1 {
// get end point of the subdivision
let end_point = self.get_basis(f64::from(subdivision) * RATIO);
for curr_point in lookup_table.iter().skip(1) {
// calculate distance of subdivision
length_subtotal += (start_point - end_point).length();
// update start_point for next subdivision
start_point = end_point;
approx_curve_length += (*curr_point - prev_point).length();
// update the prev point
prev_point = *curr_point;
}

length_subtotal
approx_curve_length
}
}