Make certain Bezier function parameters optional and other refactors (#713)

* Make certain parameters optional

* Use builder pattern for project function's optional parameters

* Address comments posted in bezier-math-lib discord channel

* Minor changes to text

* Address PR comments

* Fix index.html

* Nit

* Replace builder pattern with simple struct

* Move constants to a separate file

Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
Hannah Li
2022-07-06 14:02:52 -04:00
committed by GitHub
co-authored by Keavon Chambers
parent 9ed9db7009
commit f3d5028107
10 changed files with 155 additions and 125 deletions
+24 -47
View File
@@ -12,8 +12,6 @@
:cubicOptions="feature.cubicOptions"
/>
</div>
<br />
<div id="svg-test" />
</div>
</template>
@@ -26,21 +24,6 @@ import { Point, WasmBezierInstance } from "@/utils/types";
import ExamplePane from "@/components/ExamplePane.vue";
import SliderExample from "@/components/SliderExample.vue";
// eslint-disable-next-line
const testBezierLib = async () => {
import("@/../wasm/pkg").then((wasm) => {
const bezier = wasm.WasmBezier.new_quadratic([
[0, 0],
[50, 0],
[100, 100],
]);
const svgContainer = document.getElementById("svg-test");
if (svgContainer) {
svgContainer.innerHTML = bezier.to_svg();
}
});
};
const tSliderOptions = {
min: 0,
max: 1,
@@ -49,6 +32,8 @@ const tSliderOptions = {
variable: "t",
};
const SCALE_UNIT_VECTOR_FACTOR = 50;
export default defineComponent({
name: "App",
components: {
@@ -63,7 +48,7 @@ export default defineComponent({
callback: (): void => {},
},
{
name: "Bezier through points",
name: "Bezier Through Points",
// eslint-disable-next-line
callback: (): void => {},
createThroughPoints: true,
@@ -137,26 +122,20 @@ export default defineComponent({
},
},
{
name: "Derivative",
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 derivative = JSON.parse(bezier.derivative(options.t));
const curveFactor = bezier.get_points().length - 1;
const tangent = JSON.parse(bezier.tangent(options.t));
const tangentStart = {
x: intersection.x - derivative.x / curveFactor,
y: intersection.y - derivative.y / curveFactor,
};
const tangentEnd = {
x: intersection.x + derivative.x / curveFactor,
y: intersection.y + derivative.y / curveFactor,
x: intersection.x + tangent.x * SCALE_UNIT_VECTOR_FACTOR,
y: intersection.y + tangent.y * SCALE_UNIT_VECTOR_FACTOR,
};
drawLine(context, tangentStart, tangentEnd, COLORS.NON_INTERACTIVE.STROKE_1);
drawPoint(context, tangentStart, 3, COLORS.NON_INTERACTIVE.STROKE_1);
drawPoint(context, intersection, 3, COLORS.NON_INTERACTIVE.STROKE_1);
drawLine(context, intersection, tangentEnd, COLORS.NON_INTERACTIVE.STROKE_1);
drawPoint(context, tangentEnd, 3, COLORS.NON_INTERACTIVE.STROKE_1);
},
template: markRaw(SliderExample),
@@ -170,18 +149,13 @@ export default defineComponent({
const intersection = JSON.parse(bezier.compute(options.t));
const normal = JSON.parse(bezier.normal(options.t));
const normalStart = {
x: intersection.x - normal.x * 20,
y: intersection.y - normal.y * 20,
};
const normalEnd = {
x: intersection.x + normal.x * 20,
y: intersection.y + normal.y * 20,
x: intersection.x - normal.x * SCALE_UNIT_VECTOR_FACTOR,
y: intersection.y - normal.y * SCALE_UNIT_VECTOR_FACTOR,
};
drawLine(context, normalStart, normalEnd, COLORS.NON_INTERACTIVE.STROKE_1);
drawPoint(context, normalStart, 3, COLORS.NON_INTERACTIVE.STROKE_1);
drawPoint(context, intersection, 3, COLORS.NON_INTERACTIVE.STROKE_1);
drawLine(context, intersection, normalEnd, COLORS.NON_INTERACTIVE.STROKE_1);
drawPoint(context, normalEnd, 3, COLORS.NON_INTERACTIVE.STROKE_1);
},
template: markRaw(SliderExample),
@@ -240,14 +214,16 @@ export default defineComponent({
name: "Local Extrema",
callback: (canvas: HTMLCanvasElement, bezier: WasmBezierInstance): void => {
const context = getContextFromCanvas(canvas);
const dimensionColors = [COLORS.NON_INTERACTIVE.STROKE_1, COLORS.NON_INTERACTIVE.STROKE_2];
const dimensionColors = ["red", "green"];
const extrema: number[][] = JSON.parse(bezier.local_extrema());
extrema.forEach((tValues, index) => {
tValues.forEach((t) => {
const point = JSON.parse(bezier.compute(t));
const point: Point = JSON.parse(bezier.compute(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]);
},
},
{
@@ -255,7 +231,7 @@ export default defineComponent({
callback: (canvas: HTMLCanvasElement, bezier: WasmBezierInstance, options: Record<string, number>): void => {
const context = getContextFromCanvas(canvas);
const rotatedBezier = bezier
.rotate((options.angle * Math.PI) / 180)
.rotate(options.angle * Math.PI)
.get_points()
.map((p) => JSON.parse(p));
drawBezier(context, rotatedBezier, null, { curveStrokeColor: COLORS.NON_INTERACTIVE.STROKE_1, radius: 3.5 });
@@ -265,25 +241,26 @@ export default defineComponent({
sliders: [
{
variable: "angle",
min: -90,
max: 90,
step: 5,
default: 15,
min: 0,
max: 2,
step: 1 / 16,
default: 1 / 8,
unit: "π",
},
],
},
},
{
name: "Line Intersection",
name: "Intersect Line Segment",
callback: (canvas: HTMLCanvasElement, bezier: WasmBezierInstance): void => {
const context = getContextFromCanvas(canvas);
const line = [
{ x: 150, y: 150 },
{ x: 30, y: 30 },
{ x: 20, y: 20 },
];
const mappedLine = line.map((p) => [p.x, p.y]);
drawLine(context, line[0], line[1], COLORS.NON_INTERACTIVE.STROKE_1);
const intersections: Point[] = bezier.line_intersection(mappedLine).map((p) => JSON.parse(p));
const intersections: Point[] = bezier.intersect_line_segment(mappedLine).map((p) => JSON.parse(p));
intersections.forEach((p: Point) => {
drawPoint(context, p, 3, COLORS.NON_INTERACTIVE.STROKE_2);
});
@@ -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] }}</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>
@@ -43,6 +43,7 @@ export default defineComponent({
const sliders = this.templateOptions.sliders;
return {
sliderData: Object.assign({}, ...sliders.map((s) => ({ [s.variable]: s.default }))),
sliderUnits: Object.assign({}, ...sliders.map((s) => ({ [s.variable]: s.unit }))),
};
},
});
@@ -12,6 +12,7 @@ export type SliderOption = {
step: number;
default: number;
variable: string;
unit?: string;
};
export type TemplateOption = {