mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-04 06:28:11 +08:00
Bezier split and trim implementation (#680)
* Added split implementation and UI Co-authored-by: Thomas Cheng <Androxium@users.noreply.github.com> * Added bezier split impl * Adjust struct traits * Implement trim and adjust FE code to handle multiple sliders per feature Co-authored-by: Thomas Cheng <contact.chengthomas@gmail.com> Co-authored-by: Rob Nadal <robnadal44@gmail.com> * Fix edge case in trim and add rust doc comments * Stylistic changes per review * More stylistic changes per review * replaced last explicit color usages Co-authored-by: Robert Nadal <Robnadal44@gmail.com> Co-authored-by: Thomas Cheng <Androxium@users.noreply.github.com> Co-authored-by: Thomas Cheng <contact.chengthomas@gmail.com>
This commit is contained in:
co-authored by
Thomas Cheng
Thomas Cheng
Rob Nadal
parent
55444eea86
commit
79944b54c8
@@ -2,7 +2,7 @@
|
||||
<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>
|
||||
<div v-for="feature in features" :key="feature.id">
|
||||
<div v-for="(feature, index) in features" :key="index">
|
||||
<ExamplePane :template="feature.template" :templateOptions="feature.templateOptions" :name="feature.name" :callback="feature.callback" />
|
||||
</div>
|
||||
<br />
|
||||
@@ -13,7 +13,7 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent, markRaw } from "vue";
|
||||
|
||||
import { drawText, drawPoint, drawLine, getContextFromCanvas } from "@/utils/drawing";
|
||||
import { drawText, drawPoint, drawBezier, drawLine, getContextFromCanvas, drawBezierHelper, COLORS } from "@/utils/drawing";
|
||||
import { WasmBezierInstance } from "@/utils/types";
|
||||
|
||||
import ExamplePane from "@/components/ExamplePane.vue";
|
||||
@@ -51,57 +51,55 @@ export default defineComponent({
|
||||
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);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "Compute",
|
||||
callback: (canvas: HTMLCanvasElement, bezier: WasmBezierInstance, options: string): void => {
|
||||
const point = JSON.parse(bezier.compute(parseFloat(options)));
|
||||
drawPoint(getContextFromCanvas(canvas), point, 4, "Red");
|
||||
callback: (canvas: HTMLCanvasElement, bezier: WasmBezierInstance, options: Record<string, number>): void => {
|
||||
const point = JSON.parse(bezier.compute(options.t));
|
||||
drawPoint(getContextFromCanvas(canvas), point, 4, COLORS.NON_INTERACTIVE.STROKE_1);
|
||||
},
|
||||
template: markRaw(SliderExample),
|
||||
templateOptions: tSliderOptions,
|
||||
templateOptions: { sliders: [tSliderOptions] },
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: "Lookup Table",
|
||||
callback: (canvas: HTMLCanvasElement, bezier: WasmBezierInstance, options: string): void => {
|
||||
const lookupPoints = bezier.compute_lookup_table(Number(options));
|
||||
callback: (canvas: HTMLCanvasElement, bezier: WasmBezierInstance, options: Record<string, number>): void => {
|
||||
const lookupPoints = bezier.compute_lookup_table(options.steps);
|
||||
lookupPoints.forEach((serialisedPoint, index) => {
|
||||
if (index !== 0 && index !== lookupPoints.length - 1) {
|
||||
drawPoint(getContextFromCanvas(canvas), JSON.parse(serialisedPoint), 3, "Red");
|
||||
drawPoint(getContextFromCanvas(canvas), JSON.parse(serialisedPoint), 3, COLORS.NON_INTERACTIVE.STROKE_1);
|
||||
}
|
||||
});
|
||||
},
|
||||
template: markRaw(SliderExample),
|
||||
templateOptions: {
|
||||
min: 2,
|
||||
max: 15,
|
||||
step: 1,
|
||||
default: 5,
|
||||
variable: "Steps",
|
||||
sliders: [
|
||||
{
|
||||
min: 2,
|
||||
max: 15,
|
||||
step: 1,
|
||||
default: 5,
|
||||
variable: "steps",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: "Derivative",
|
||||
callback: (canvas: HTMLCanvasElement, bezier: WasmBezierInstance, options: string): void => {
|
||||
const t = parseFloat(options);
|
||||
callback: (canvas: HTMLCanvasElement, bezier: WasmBezierInstance, options: Record<string, number>): void => {
|
||||
const context = getContextFromCanvas(canvas);
|
||||
|
||||
const intersection = JSON.parse(bezier.compute(t));
|
||||
const derivative = JSON.parse(bezier.derivative(t));
|
||||
const intersection = JSON.parse(bezier.compute(options.t));
|
||||
const derivative = JSON.parse(bezier.derivative(options.t));
|
||||
const curveFactor = bezier.get_points().length - 1;
|
||||
|
||||
const tangentStart = {
|
||||
@@ -113,23 +111,21 @@ export default defineComponent({
|
||||
y: intersection.y + derivative.y / curveFactor,
|
||||
};
|
||||
|
||||
drawLine(context, tangentStart, tangentEnd, "Red");
|
||||
drawPoint(context, tangentStart, 3, "Red");
|
||||
drawPoint(context, intersection, 3, "Red");
|
||||
drawPoint(context, tangentEnd, 3, "Red");
|
||||
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);
|
||||
drawPoint(context, tangentEnd, 3, COLORS.NON_INTERACTIVE.STROKE_1);
|
||||
},
|
||||
template: markRaw(SliderExample),
|
||||
templateOptions: tSliderOptions,
|
||||
templateOptions: { sliders: [tSliderOptions] },
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: "Normal",
|
||||
callback: (canvas: HTMLCanvasElement, bezier: WasmBezierInstance, options: string): void => {
|
||||
const t = parseFloat(options);
|
||||
callback: (canvas: HTMLCanvasElement, bezier: WasmBezierInstance, options: Record<string, number>): void => {
|
||||
const context = getContextFromCanvas(canvas);
|
||||
|
||||
const intersection = JSON.parse(bezier.compute(t));
|
||||
const normal = JSON.parse(bezier.normal(t));
|
||||
const intersection = JSON.parse(bezier.compute(options.t));
|
||||
const normal = JSON.parse(bezier.normal(options.t));
|
||||
|
||||
const normalStart = {
|
||||
x: intersection.x - normal.x * 20,
|
||||
@@ -140,13 +136,52 @@ export default defineComponent({
|
||||
y: intersection.y + normal.y * 20,
|
||||
};
|
||||
|
||||
drawLine(context, normalStart, normalEnd, "Red");
|
||||
drawPoint(context, normalStart, 3, "Red");
|
||||
drawPoint(context, intersection, 3, "Red");
|
||||
drawPoint(context, normalEnd, 3, "Red");
|
||||
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);
|
||||
drawPoint(context, normalEnd, 3, COLORS.NON_INTERACTIVE.STROKE_1);
|
||||
},
|
||||
template: markRaw(SliderExample),
|
||||
templateOptions: tSliderOptions,
|
||||
templateOptions: { sliders: [tSliderOptions] },
|
||||
},
|
||||
{
|
||||
name: "Split",
|
||||
callback: (canvas: HTMLCanvasElement, bezier: WasmBezierInstance, options: Record<string, number>): void => {
|
||||
const context = getContextFromCanvas(canvas);
|
||||
const bezierPairPoints = JSON.parse(bezier.split(options.t));
|
||||
|
||||
drawBezier(context, bezierPairPoints[0], null, COLORS.NON_INTERACTIVE.STROKE_2, 3.5);
|
||||
drawBezier(context, bezierPairPoints[1], null, COLORS.NON_INTERACTIVE.STROKE_1, 3.5);
|
||||
},
|
||||
template: markRaw(SliderExample),
|
||||
templateOptions: { sliders: [tSliderOptions] },
|
||||
},
|
||||
{
|
||||
name: "Trim",
|
||||
callback: (canvas: HTMLCanvasElement, bezier: WasmBezierInstance, options: Record<string, number>): void => {
|
||||
const context = getContextFromCanvas(canvas);
|
||||
const trimmedBezier = bezier.trim(options.t1, options.t2);
|
||||
drawBezierHelper(context, trimmedBezier, COLORS.NON_INTERACTIVE.STROKE_1, 3.5);
|
||||
},
|
||||
template: markRaw(SliderExample),
|
||||
templateOptions: {
|
||||
sliders: [
|
||||
{
|
||||
variable: "t1",
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.01,
|
||||
default: 0.25,
|
||||
},
|
||||
{
|
||||
variable: "t2",
|
||||
min: 0,
|
||||
max: 1,
|
||||
step: 0.01,
|
||||
default: 0.75,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user