diff --git a/website/other/bezier-rs-demos/src/demos.ts b/website/other/bezier-rs-demos/src/demos.ts deleted file mode 100644 index 83515f6cac..0000000000 --- a/website/other/bezier-rs-demos/src/demos.ts +++ /dev/null @@ -1,114 +0,0 @@ -import { WasmSubpath, WasmBezier } from "@/../wasm/pkg"; -import type { BezierFeatureKey } from "@/features-bezier"; -import bezierFeatures from "@/features-bezier"; -import type { SubpathFeatureKey } from "@/features-subpath"; -import subpathFeatures from "@/features-subpath"; -import type { WasmSubpathInstance, WasmSubpathManipulatorKey, InputOption, DemoData, DemoDataBezier, DemoDataSubpath } from "@/types"; -import { POINT_INDEX_TO_MANIPULATOR, getConstructorKey, getCurveType, MANIPULATOR_KEYS_FROM_BEZIER_TYPE } from "@/types"; - -export function demoBezier(title: string, points: number[][], key: BezierFeatureKey, inputOptions: InputOption[], triggerOnMouseMove: boolean): DemoDataBezier { - return { - kind: "bezier", - title, - element: document.createElement("div"), - inputOptions, - locked: false, - triggerOnMouseMove, - sliderData: Object.assign({}, ...inputOptions.map((s) => ({ [s.variable]: s.default }))), - sliderUnits: Object.assign({}, ...inputOptions.map((s) => ({ [s.variable]: s.unit }))), - activePointIndex: undefined as number | undefined, - manipulatorKeys: MANIPULATOR_KEYS_FROM_BEZIER_TYPE[getCurveType(points.length)], - bezier: WasmBezier[getConstructorKey(getCurveType(points.length))](points), - points, - callback: bezierFeatures[key].callback, - }; -} - -export function demoSubpath(title: string, triples: (number[] | undefined)[][], key: SubpathFeatureKey, closed: boolean, inputOptions: InputOption[], triggerOnMouseMove: boolean): DemoDataSubpath { - return { - kind: "subpath", - title, - element: document.createElement("div"), - inputOptions, - locked: false, - triggerOnMouseMove, - sliderData: Object.assign({}, ...inputOptions.map((s) => ({ [s.variable]: s.default }))), - sliderUnits: Object.assign({}, ...inputOptions.map((s) => ({ [s.variable]: s.unit }))), - activePointIndex: undefined as number | undefined, - activeManipulatorIndex: undefined as number | undefined, - manipulatorKeys: undefined as undefined | WasmSubpathManipulatorKey[], - subpath: WasmSubpath.from_triples(triples, closed) as WasmSubpathInstance, - triples, - callback: subpathFeatures[key].callback, - }; -} - -export function updateDemoSVG(data: DemoData, figure: HTMLElement, mouseLocation?: [number, number]) { - if (data.kind === "subpath") figure.innerHTML = data.callback(data.subpath, data.sliderData, mouseLocation); - if (data.kind === "bezier") figure.innerHTML = data.callback(data.bezier, data.sliderData, mouseLocation); -} - -export function onMouseDown(data: DemoData, e: MouseEvent) { - const SELECTABLE_RANGE = 10; - - if (data.kind === "bezier") { - const SELECTABLE_RANGE = 10; - - const distances = data.points.flatMap((point, pointIndex) => { - if (!point) return []; - const distance = Math.sqrt(Math.pow(e.offsetX - point[0], 2) + Math.pow(e.offsetY - point[1], 2)); - return distance < SELECTABLE_RANGE ? [{ pointIndex, distance }] : []; - }); - const closest = distances.sort((a, b) => a.distance - b.distance)[0]; - if (closest) data.activePointIndex = closest.pointIndex; - } - - if (data.kind === "subpath") { - const distances = data.triples.flatMap((triple, manipulatorIndex) => - triple.flatMap((point, pointIndex) => { - if (!point) return []; - const distance = Math.sqrt(Math.pow(e.offsetX - point[0], 2) + Math.pow(e.offsetY - point[1], 2)); - return distance < SELECTABLE_RANGE ? [{ manipulatorIndex, pointIndex, distance }] : []; - }), - ); - const closest = distances.sort((a, b) => a.distance - b.distance)[0]; - if (closest) { - data.activeManipulatorIndex = closest.manipulatorIndex; - data.activePointIndex = closest.pointIndex; - } - } -} - -export function onMouseMove(data: DemoData, e: MouseEvent) { - if (data.locked || !(e.currentTarget instanceof HTMLElement)) return; - data.locked = true; - - if (data.kind === "bezier") { - if (data.activePointIndex !== undefined) { - data.bezier[data.manipulatorKeys[data.activePointIndex]](e.offsetX, e.offsetY); - data.points[data.activePointIndex] = [e.offsetX, e.offsetY]; - - updateDemoSVG(data, e.currentTarget); - } else if (data.triggerOnMouseMove) { - updateDemoSVG(data, e.currentTarget, [e.offsetX, e.offsetY]); - } - } - - if (data.kind === "subpath") { - if (data.activeManipulatorIndex !== undefined && data.activePointIndex !== undefined) { - data.subpath[POINT_INDEX_TO_MANIPULATOR[data.activePointIndex]](data.activeManipulatorIndex, e.offsetX, e.offsetY); - data.triples[data.activeManipulatorIndex][data.activePointIndex] = [e.offsetX, e.offsetY]; - - updateDemoSVG(data, e.currentTarget); - } else if (data.triggerOnMouseMove) { - updateDemoSVG(data, e.currentTarget, [e.offsetX, e.offsetY]); - } - } - - data.locked = false; -} - -export function onMouseUp(data: DemoData) { - data.activePointIndex = undefined; - if (data.kind === "subpath") data.activeManipulatorIndex = undefined; -} diff --git a/website/other/bezier-rs-demos/src/main.ts b/website/other/bezier-rs-demos/src/main.ts index cdc7ce31ac..d01826a0f4 100644 --- a/website/other/bezier-rs-demos/src/main.ts +++ b/website/other/bezier-rs-demos/src/main.ts @@ -1,11 +1,10 @@ -import { default as init } from "@/../wasm/pkg"; -import { demoBezier, demoSubpath, onMouseDown, onMouseMove, onMouseUp, updateDemoSVG } from "@/demos"; +import { default as init, WasmSubpath, WasmBezier } from "@/../wasm/pkg"; import bezierFeatures from "@/features-bezier"; import type { BezierFeatureKey, BezierFeatureOptions } from "@/features-bezier"; import subpathFeatures from "@/features-subpath"; import type { SubpathFeatureKey, SubpathFeatureOptions } from "@/features-subpath"; -import { BEZIER_CURVE_TYPE, getBezierDemoPointDefaults, getSubpathDemoArgs } from "@/types"; -import type { DemoArgs, BezierCurveType, BezierDemoArgs, SubpathDemoArgs, DemoData } from "@/types"; +import type { DemoArgs, BezierCurveType, BezierDemoArgs, SubpathDemoArgs, DemoData, WasmSubpathInstance, WasmSubpathManipulatorKey, InputOption, DemoDataBezier, DemoDataSubpath } from "@/types"; +import { BEZIER_CURVE_TYPE, getBezierDemoPointDefaults, getSubpathDemoArgs, POINT_INDEX_TO_MANIPULATOR, getConstructorKey, getCurveType, MANIPULATOR_KEYS_FROM_BEZIER_TYPE } from "@/types"; init().then(renderPage); @@ -101,6 +100,103 @@ function subpathDemoGroup(key: SubpathFeatureKey, options: SubpathFeatureOptions return renderDemoGroup(`subpath/${key}`, subpathFeatures[key].name, getSubpathDemoArgs(), buildDemo); } +function demoBezier(title: string, points: number[][], key: BezierFeatureKey, inputOptions: InputOption[], triggerOnMouseMove: boolean): DemoDataBezier { + return { + kind: "bezier", + title, + element: document.createElement("div"), + inputOptions, + locked: false, + triggerOnMouseMove, + sliderData: Object.assign({}, ...inputOptions.map((s) => ({ [s.variable]: s.default }))), + sliderUnits: Object.assign({}, ...inputOptions.map((s) => ({ [s.variable]: s.unit }))), + activePointIndex: undefined as number | undefined, + manipulatorKeys: MANIPULATOR_KEYS_FROM_BEZIER_TYPE[getCurveType(points.length)], + bezier: WasmBezier[getConstructorKey(getCurveType(points.length))](points), + points, + callback: bezierFeatures[key].callback, + }; +} + +function demoSubpath(title: string, triples: (number[] | undefined)[][], key: SubpathFeatureKey, closed: boolean, inputOptions: InputOption[], triggerOnMouseMove: boolean): DemoDataSubpath { + return { + kind: "subpath", + title, + element: document.createElement("div"), + inputOptions, + locked: false, + triggerOnMouseMove, + sliderData: Object.assign({}, ...inputOptions.map((s) => ({ [s.variable]: s.default }))), + sliderUnits: Object.assign({}, ...inputOptions.map((s) => ({ [s.variable]: s.unit }))), + activePointIndex: undefined as number | undefined, + activeManipulatorIndex: undefined as number | undefined, + manipulatorKeys: undefined as undefined | WasmSubpathManipulatorKey[], + subpath: WasmSubpath.from_triples(triples, closed) as WasmSubpathInstance, + triples, + callback: subpathFeatures[key].callback, + }; +} + +function updateDemoSVG(data: DemoData, figure: HTMLElement, mouseLocation?: [number, number]) { + if (data.kind === "subpath") figure.innerHTML = data.callback(data.subpath, data.sliderData, mouseLocation); + if (data.kind === "bezier") figure.innerHTML = data.callback(data.bezier, data.sliderData, mouseLocation); +} + +function onMouseDown(data: DemoData, e: MouseEvent) { + const SELECTABLE_RANGE = 10; + + let distances; + if (data.kind === "bezier") { + distances = data.points.flatMap((point, pointIndex) => { + if (!point) return []; + const distance = Math.sqrt(Math.pow(e.offsetX - point[0], 2) + Math.pow(e.offsetY - point[1], 2)); + return distance < SELECTABLE_RANGE ? [{ manipulatorIndex: undefined, pointIndex, distance }] : []; + }); + } else if (data.kind === "subpath") { + distances = data.triples.flatMap((triple, manipulatorIndex) => + triple.flatMap((point, pointIndex) => { + if (!point) return []; + const distance = Math.sqrt(Math.pow(e.offsetX - point[0], 2) + Math.pow(e.offsetY - point[1], 2)); + return distance < SELECTABLE_RANGE ? [{ manipulatorIndex, pointIndex, distance }] : []; + }), + ); + } else { + return; + } + + const closest = distances.sort((a, b) => a.distance - b.distance)[0]; + if (closest) { + if (data.kind === "subpath") data.activeManipulatorIndex = closest.manipulatorIndex; + data.activePointIndex = closest.pointIndex; + } +} + +function onMouseMove(data: DemoData, e: MouseEvent) { + if (data.locked || !(e.currentTarget instanceof HTMLElement)) return; + data.locked = true; + + if (data.kind === "bezier" && data.activePointIndex !== undefined) { + data.bezier[data.manipulatorKeys[data.activePointIndex]](e.offsetX, e.offsetY); + data.points[data.activePointIndex] = [e.offsetX, e.offsetY]; + + updateDemoSVG(data, e.currentTarget); + } else if (data.kind === "subpath" && data.activePointIndex !== undefined && data.activeManipulatorIndex !== undefined) { + data.subpath[POINT_INDEX_TO_MANIPULATOR[data.activePointIndex]](data.activeManipulatorIndex, e.offsetX, e.offsetY); + data.triples[data.activeManipulatorIndex][data.activePointIndex] = [e.offsetX, e.offsetY]; + + updateDemoSVG(data, e.currentTarget); + } else if (data.triggerOnMouseMove) { + updateDemoSVG(data, e.currentTarget, [e.offsetX, e.offsetY]); + } + + data.locked = false; +} + +function onMouseUp(data: DemoData) { + data.activePointIndex = undefined; + if (data.kind === "subpath") data.activeManipulatorIndex = undefined; +} + function renderDemoGroup(id: string, name: string, demos: T[], buildDemo: (demo: T) => DemoData): HTMLDivElement { const demoGroup = document.createElement("div"); demoGroup.className = "demo-group-container"; diff --git a/website/other/bezier-rs-demos/src/types.ts b/website/other/bezier-rs-demos/src/types.ts index a690016013..dc96de89dc 100644 --- a/website/other/bezier-rs-demos/src/types.ts +++ b/website/other/bezier-rs-demos/src/types.ts @@ -1,14 +1,12 @@ import type * as WasmPkg from "@/../wasm/pkg"; -export type WasmRawInstance = typeof WasmPkg; +type WasmRawInstance = typeof WasmPkg; export type WasmBezierInstance = InstanceType; -export type WasmBezierKey = keyof WasmBezierInstance; -export type WasmBezierConstructorKey = "new_linear" | "new_quadratic" | "new_cubic"; -export type WasmBezierManipulatorKey = "set_start" | "set_handle_start" | "set_handle_end" | "set_end"; - export type WasmSubpathInstance = InstanceType; export type WasmSubpathManipulatorKey = "set_anchor" | "set_in_handle" | "set_out_handle"; +type WasmBezierConstructorKey = "new_linear" | "new_quadratic" | "new_cubic"; +type WasmBezierManipulatorKey = "set_start" | "set_handle_start" | "set_handle_end" | "set_end"; type DemoDataCommon = { title: string; @@ -51,10 +49,6 @@ export type BezierDemoOptions = { }; }; -export type SubpathInputOption = InputOption & { - isDisabledForClosed?: boolean; -}; - export type InputOption = { variable: string; min?: number; @@ -66,6 +60,9 @@ export type InputOption = { options?: string[]; disabled?: boolean; }; +export type SubpathInputOption = InputOption & { + isDisabledForClosed?: boolean; +}; export function getCurveType(numPoints: number): BezierCurveType { const mapping: Record = { @@ -106,8 +103,8 @@ export type SubpathDemoArgs = { export const BEZIER_T_VALUE_VARIANTS = ["Parametric", "Euclidean"] as const; export const SUBPATH_T_VALUE_VARIANTS = ["GlobalParametric", "GlobalEuclidean"] as const; -export const CAP_VARIANTS = ["Butt", "Round", "Square"] as const; -export const JOIN_VARIANTS = ["Bevel", "Miter", "Round"] as const; +const CAP_VARIANTS = ["Butt", "Round", "Square"] as const; +const JOIN_VARIANTS = ["Bevel", "Miter", "Round"] as const; export const POINT_INDEX_TO_MANIPULATOR: WasmSubpathManipulatorKey[] = ["set_anchor", "set_in_handle", "set_out_handle"];