mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-06 11:48:11 +08:00
Bezier-rs: Remove unused legacy drawing components and structs (#853)
* Removed unused legacy drawing components * Removed Point abstraction from frontend code * Code review fixes * Helper lambdas to functions Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
committed by
Keavon Chambers
co-authored by
Keavon Chambers
parent
2994afa6b8
commit
a220bfa759
@@ -1,150 +0,0 @@
|
||||
import { COLORS, drawBezier, drawPoint, getContextFromCanvas, getPointSizeByIndex } from "@/utils/drawing";
|
||||
import { Callback, BezierPoint, BezierStyleConfig, Point, WasmBezierManipulatorKey, WasmBezierInstance } from "@/utils/types";
|
||||
|
||||
// Offset to increase selectable range, used to make points easier to grab
|
||||
const FUDGE_FACTOR = 3;
|
||||
|
||||
// Given the number of points in the curve, map the index of a point to the correct manipulator key
|
||||
const MANIPULATOR_KEYS_FROM_BEZIER_TYPE: { [k: number]: WasmBezierManipulatorKey[] } = {
|
||||
2: ["set_start", "set_end"],
|
||||
3: ["set_start", "set_handle_start", "set_end"],
|
||||
4: ["set_start", "set_handle_start", "set_handle_end", "set_end"],
|
||||
};
|
||||
|
||||
class BezierDrawing {
|
||||
points: BezierPoint[];
|
||||
|
||||
canvas: HTMLCanvasElement;
|
||||
|
||||
ctx: CanvasRenderingContext2D;
|
||||
|
||||
dragIndex: number | null;
|
||||
|
||||
bezier: WasmBezierInstance;
|
||||
|
||||
callback: Callback;
|
||||
|
||||
options: Record<string, number>;
|
||||
|
||||
createThroughPoints: boolean;
|
||||
|
||||
constructor(bezier: WasmBezierInstance, callback: Callback, options: Record<string, number>, createThroughPoints = false) {
|
||||
this.bezier = bezier;
|
||||
this.callback = callback;
|
||||
this.options = options;
|
||||
this.createThroughPoints = createThroughPoints;
|
||||
const bezierPoints: Point[] = JSON.parse(bezier.get_points());
|
||||
this.points = bezierPoints.map((p, i, points) => ({
|
||||
x: p.x,
|
||||
y: p.y,
|
||||
r: getPointSizeByIndex(i, points.length),
|
||||
selected: false,
|
||||
manipulator: MANIPULATOR_KEYS_FROM_BEZIER_TYPE[points.length][i],
|
||||
}));
|
||||
|
||||
if (this.createThroughPoints && this.points.length === 4) {
|
||||
// Use the first handler as the middle point
|
||||
this.points = [this.points[0], this.points[1], this.points[3]];
|
||||
}
|
||||
|
||||
const canvas = document.createElement("canvas");
|
||||
if (canvas === null) {
|
||||
throw Error("Failed to create canvas");
|
||||
}
|
||||
this.canvas = canvas;
|
||||
|
||||
this.canvas.style.border = "solid 1px black";
|
||||
this.canvas.width = 200;
|
||||
this.canvas.height = 200;
|
||||
|
||||
this.ctx = getContextFromCanvas(this.canvas);
|
||||
|
||||
this.dragIndex = null; // Index of the point being moved
|
||||
|
||||
this.canvas.addEventListener("mousedown", (e) => this.mouseDownHandler(e));
|
||||
this.canvas.addEventListener("mousemove", (e) => this.mouseMoveHandler(e));
|
||||
this.canvas.addEventListener("mouseup", () => this.deselectPointHandler());
|
||||
this.updateBezier();
|
||||
}
|
||||
|
||||
clearFigure(): void {
|
||||
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||
}
|
||||
|
||||
mouseMoveHandler(evt: MouseEvent): void {
|
||||
if (evt.buttons === 0) this.deselectPointHandler();
|
||||
|
||||
const mx = evt.offsetX;
|
||||
const my = evt.offsetY;
|
||||
|
||||
if (this.dragIndex !== null) {
|
||||
const selectableRange = getPointSizeByIndex(this.dragIndex, this.points.length);
|
||||
if (mx - selectableRange > 0 && my - selectableRange > 0 && mx + selectableRange < this.canvas.width && my + selectableRange < this.canvas.height) {
|
||||
const selectedPoint = this.points[this.dragIndex];
|
||||
selectedPoint.x = mx;
|
||||
selectedPoint.y = my;
|
||||
this.bezier[selectedPoint.manipulator](selectedPoint.x, selectedPoint.y);
|
||||
}
|
||||
}
|
||||
this.updateBezier({ x: mx, y: my });
|
||||
}
|
||||
|
||||
mouseDownHandler(evt: MouseEvent): void {
|
||||
const mx = evt.offsetX;
|
||||
const my = evt.offsetY;
|
||||
for (let i = 0; i < this.points.length; i += 1) {
|
||||
const selectableRange = getPointSizeByIndex(i, this.points.length) + FUDGE_FACTOR;
|
||||
if (Math.abs(mx - this.points[i].x) < selectableRange && Math.abs(my - this.points[i].y) < selectableRange) {
|
||||
this.dragIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.updateBezier();
|
||||
}
|
||||
|
||||
deselectPointHandler(): void {
|
||||
if (this.dragIndex !== undefined) {
|
||||
this.dragIndex = null;
|
||||
this.updateBezier();
|
||||
}
|
||||
}
|
||||
|
||||
updateBezier(mouseLocation?: Point, options: Record<string, number> = {}): void {
|
||||
this.clearFigure();
|
||||
if (Object.values(options).length !== 0) {
|
||||
this.options = options;
|
||||
}
|
||||
this.clearFigure();
|
||||
|
||||
// For the create through points cases, we store a bezier where the handle is actually the point that the curve should pass through
|
||||
// This is so that we can re-use the drag and drop logic, while simply drawing the desired bezier instead
|
||||
const pointsToDraw = this.points;
|
||||
|
||||
let styleConfig: Partial<BezierStyleConfig> = {
|
||||
handleLineStrokeColor: COLORS.INTERACTIVE.STROKE_2,
|
||||
};
|
||||
let dragIndex = this.dragIndex;
|
||||
if (this.createThroughPoints) {
|
||||
if (this.dragIndex === 1) {
|
||||
// Do not propagate dragIndex when the the non-endpoint is moved
|
||||
dragIndex = null;
|
||||
} else if (this.dragIndex === 2 && pointsToDraw.length === 4) {
|
||||
// For the cubic case, we want to propagate the drag index when the end point is moved, but need to adjust the index
|
||||
dragIndex = 3;
|
||||
}
|
||||
styleConfig = { handleLineStrokeColor: COLORS.NON_INTERACTIVE.STROKE_1, handleStrokeColor: COLORS.NON_INTERACTIVE.STROKE_1 };
|
||||
}
|
||||
drawBezier(this.ctx, pointsToDraw, dragIndex, styleConfig);
|
||||
if (this.createThroughPoints) {
|
||||
// Draw the point that the curve was drawn through
|
||||
drawPoint(this.ctx, this.points[1], getPointSizeByIndex(1, this.points.length), this.dragIndex === 1 ? COLORS.INTERACTIVE.SELECTED : COLORS.INTERACTIVE.STROKE_1);
|
||||
}
|
||||
this.callback(this.canvas, this.bezier, this.options, mouseLocation);
|
||||
}
|
||||
|
||||
getCanvas(): HTMLCanvasElement {
|
||||
return this.canvas;
|
||||
}
|
||||
}
|
||||
|
||||
export default BezierDrawing;
|
||||
@@ -86,7 +86,7 @@ export default defineComponent({
|
||||
this.mutablePoints[this.activeIndex] = [mx, my];
|
||||
this.bezierSVG = this.callback(this.bezier, this.sliderData);
|
||||
} else if (this.triggerOnMouseMove) {
|
||||
this.bezierSVG = this.callback(this.bezier, this.sliderData, { x: mx, y: my });
|
||||
this.bezierSVG = this.callback(this.bezier, this.sliderData, [mx, my]);
|
||||
}
|
||||
},
|
||||
getSliderValue: (sliderValue: number, sliderUnit?: string | string[]) => (Array.isArray(sliderUnit) ? sliderUnit[sliderValue] : sliderUnit),
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<h4 class="example-header">{{ title }}</h4>
|
||||
<figure class="example-figure" ref="drawing"></figure>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType } from "vue";
|
||||
|
||||
import BezierDrawing from "@/components/BezierDrawing";
|
||||
import { Callback, WasmBezierInstance } from "@/utils/types";
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
title: String,
|
||||
bezier: {
|
||||
type: Object as PropType<WasmBezierInstance>,
|
||||
required: true,
|
||||
},
|
||||
callback: {
|
||||
type: Function as PropType<Callback>,
|
||||
required: true,
|
||||
},
|
||||
options: {
|
||||
type: Object as PropType<Record<string, number>>,
|
||||
default: () => ({}),
|
||||
},
|
||||
createThroughPoints: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
bezierDrawing: new BezierDrawing(this.bezier, this.callback, this.options, this.createThroughPoints),
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
const drawing = this.$refs.drawing as HTMLElement;
|
||||
drawing.appendChild(this.bezierDrawing.getCanvas());
|
||||
this.bezierDrawing.updateBezier();
|
||||
},
|
||||
watch: {
|
||||
options: {
|
||||
deep: true,
|
||||
handler() {
|
||||
this.bezierDrawing.updateBezier(undefined, this.options);
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.example-header {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.example-figure {
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
</style>
|
||||
@@ -1,133 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<h3 class="example-pane-header">{{ name }}</h3>
|
||||
<div class="example-row">
|
||||
<div v-for="(example, index) in exampleData" :key="index">
|
||||
<component :is="template" :templateOptions="example.templateOptions" :title="example.title" :bezier="example.bezier" :callback="callback" :createThroughPoints="createThroughPoints" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType, Component } from "vue";
|
||||
|
||||
import { BezierCallback, BezierCurveType, TemplateOption, WasmBezierConstructorKey, WasmBezierInstance, WasmRawInstance } from "@/utils/types";
|
||||
|
||||
import Example from "@/components/Example.vue";
|
||||
|
||||
type ExampleData = {
|
||||
title: string;
|
||||
bezier: WasmBezierInstance;
|
||||
templateOptions: TemplateOption;
|
||||
};
|
||||
|
||||
type CustomTemplateOptions = {
|
||||
[key in BezierCurveType]?: TemplateOption;
|
||||
};
|
||||
|
||||
type CustomPoints = {
|
||||
[key in BezierCurveType]?: number[][];
|
||||
};
|
||||
|
||||
const CurveTypeMapping = {
|
||||
[BezierCurveType.Linear]: {
|
||||
points: [
|
||||
[30, 60],
|
||||
[140, 120],
|
||||
],
|
||||
constructor: "new_linear" as WasmBezierConstructorKey,
|
||||
},
|
||||
[BezierCurveType.Quadratic]: {
|
||||
points: [
|
||||
[30, 50],
|
||||
[140, 30],
|
||||
[160, 170],
|
||||
],
|
||||
constructor: "new_quadratic" as WasmBezierConstructorKey,
|
||||
},
|
||||
[BezierCurveType.Cubic]: {
|
||||
points: [
|
||||
[30, 30],
|
||||
[60, 140],
|
||||
[150, 30],
|
||||
[160, 160],
|
||||
],
|
||||
constructor: "new_cubic" as WasmBezierConstructorKey,
|
||||
},
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
name: {
|
||||
type: String as PropType<string>,
|
||||
required: true,
|
||||
},
|
||||
callback: {
|
||||
type: Function as PropType<BezierCallback>,
|
||||
required: true,
|
||||
},
|
||||
template: {
|
||||
type: Object as PropType<Component>,
|
||||
default: Example,
|
||||
},
|
||||
templateOptions: {
|
||||
type: Object as PropType<TemplateOption>,
|
||||
required: false,
|
||||
},
|
||||
customOptions: {
|
||||
type: Object as PropType<CustomTemplateOptions>,
|
||||
default: () => ({}),
|
||||
},
|
||||
createThroughPoints: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: false,
|
||||
},
|
||||
curveDegrees: {
|
||||
type: Set as PropType<Set<BezierCurveType>>,
|
||||
default: () => new Set(Object.values(BezierCurveType)),
|
||||
},
|
||||
customPoints: {
|
||||
type: Object as PropType<CustomPoints>,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
exampleData: [] as ExampleData[],
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
import("@/../wasm/pkg").then((wasm: WasmRawInstance) => {
|
||||
this.exampleData = [];
|
||||
// Only add example for BezierCurveType that is in the curveDegrees set
|
||||
Object.values(BezierCurveType).forEach((bezierType) => {
|
||||
if (this.curveDegrees.has(bezierType)) {
|
||||
const { points, constructor } = CurveTypeMapping[bezierType];
|
||||
this.exampleData.push({
|
||||
title: bezierType,
|
||||
// Use custom options if they were provided for the current BezierCurveType
|
||||
bezier: wasm.WasmBezier[constructor](this.customPoints[bezierType] || points),
|
||||
templateOptions: (this.customOptions[bezierType] || this.templateOptions) as TemplateOption,
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
components: {
|
||||
Example,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.example-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.example-pane-header {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -1,54 +0,0 @@
|
||||
<template>
|
||||
<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] }}{{ getSliderValue(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>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType } from "vue";
|
||||
|
||||
import { BezierCallback, TemplateOption, WasmBezierInstance } from "@/utils/types";
|
||||
|
||||
import Example from "@/components/Example.vue";
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
title: String,
|
||||
bezier: {
|
||||
type: Object as PropType<WasmBezierInstance>,
|
||||
required: true,
|
||||
},
|
||||
callback: {
|
||||
type: Function as PropType<BezierCallback>,
|
||||
required: true,
|
||||
},
|
||||
templateOptions: {
|
||||
type: Object as PropType<TemplateOption>,
|
||||
default: () => ({}),
|
||||
},
|
||||
createThroughPoints: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
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 }))),
|
||||
};
|
||||
},
|
||||
components: {
|
||||
Example,
|
||||
},
|
||||
methods: {
|
||||
getSliderValue: (sliderValue: number, sliderUnit?: string | string[]) => (Array.isArray(sliderUnit) ? sliderUnit[sliderValue] : sliderUnit),
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
Reference in New Issue
Block a user