mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2025-12-23 10:11:54 +00:00
Bezier-rs: Add hash URLs for individual API examples (#968)
* Handle displaying single demos * Address comments * Update eslintrc * Add hash links * Use hash instead of pathname * Address small nits * Change url format * CSS improvements --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
This commit is contained in:
parent
875f2a5cd1
commit
ff51098c11
10 changed files with 209 additions and 87 deletions
|
|
@ -53,7 +53,7 @@ module.exports = {
|
|||
rules: {
|
||||
// Standard ESLint config
|
||||
indent: "off",
|
||||
quotes: ["error", "double"],
|
||||
quotes: ["error", "double", { allowTemplateLiterals: true }],
|
||||
camelcase: ["error", { properties: "always" }],
|
||||
"linebreak-style": ["error", "unix"],
|
||||
"eol-last": ["error", "always"],
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { WasmBezier } from "@/../wasm/pkg";
|
||||
import bezierFeatures, { BezierFeatureName } from "@/features/bezier-features";
|
||||
import bezierFeatures, { BezierFeatureKey } from "@/features/bezier-features";
|
||||
import { renderDemo } from "@/utils/render";
|
||||
import { getConstructorKey, getCurveType, BezierCallback, BezierCurveType, SliderOption, WasmBezierManipulatorKey, ComputeType, Demo } from "@/utils/types";
|
||||
|
||||
|
|
@ -18,7 +18,7 @@ class BezierDemo extends HTMLElement implements Demo {
|
|||
|
||||
points!: number[][];
|
||||
|
||||
name!: BezierFeatureName;
|
||||
key!: BezierFeatureKey;
|
||||
|
||||
sliderOptions!: SliderOption[];
|
||||
|
||||
|
|
@ -54,12 +54,12 @@ class BezierDemo extends HTMLElement implements Demo {
|
|||
connectedCallback(): void {
|
||||
this.title = this.getAttribute("title") || "";
|
||||
this.points = JSON.parse(this.getAttribute("points") || "[]");
|
||||
this.name = this.getAttribute("name") as BezierFeatureName;
|
||||
this.key = this.getAttribute("key") as BezierFeatureKey;
|
||||
this.sliderOptions = JSON.parse(this.getAttribute("sliderOptions") || "[]");
|
||||
this.triggerOnMouseMove = this.getAttribute("triggerOnMouseMove") === "true";
|
||||
this.computeType = (this.getAttribute("computetype") || "Parametric") as ComputeType;
|
||||
|
||||
this.callback = bezierFeatures[this.name].callback as BezierCallback;
|
||||
this.callback = bezierFeatures[this.key].callback as BezierCallback;
|
||||
const curveType = getCurveType(this.points.length);
|
||||
|
||||
this.manipulatorKeys = MANIPULATOR_KEYS_FROM_BEZIER_TYPE[curveType];
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { BezierFeatureName } from "@/features/bezier-features";
|
||||
import bezierFeatures, { BezierFeatureKey } from "@/features/bezier-features";
|
||||
import { renderDemoPane } from "@/utils/render";
|
||||
import { BezierCurveType, BEZIER_CURVE_TYPE, ComputeType, BezierDemoOptions, SliderOption, Demo, DemoPane, BezierDemoArgs } from "@/utils/types";
|
||||
|
||||
|
|
@ -28,7 +28,9 @@ const demoDefaults = {
|
|||
|
||||
class BezierDemoPane extends HTMLElement implements DemoPane {
|
||||
// Props
|
||||
name!: BezierFeatureName;
|
||||
key!: BezierFeatureKey;
|
||||
|
||||
name!: string;
|
||||
|
||||
demoOptions!: BezierDemoOptions;
|
||||
|
||||
|
|
@ -44,10 +46,11 @@ class BezierDemoPane extends HTMLElement implements DemoPane {
|
|||
computeType!: ComputeType;
|
||||
|
||||
connectedCallback(): void {
|
||||
this.id = `${Math.random()}`.substring(2);
|
||||
this.computeType = "Parametric";
|
||||
|
||||
this.name = (this.getAttribute("name") || "") as BezierFeatureName;
|
||||
this.key = (this.getAttribute("name") || "") as BezierFeatureKey;
|
||||
this.id = `bezier/${this.key}`;
|
||||
this.name = bezierFeatures[this.key].name;
|
||||
this.demoOptions = JSON.parse(this.getAttribute("demoOptions") || "[]");
|
||||
this.triggerOnMouseMove = this.getAttribute("triggerOnMouseMove") === "true";
|
||||
this.chooseComputeType = this.getAttribute("chooseComputeType") === "true";
|
||||
|
|
@ -74,7 +77,7 @@ class BezierDemoPane extends HTMLElement implements DemoPane {
|
|||
const bezierDemo = document.createElement("bezier-demo");
|
||||
bezierDemo.setAttribute("title", demo.title);
|
||||
bezierDemo.setAttribute("points", JSON.stringify(demo.points));
|
||||
bezierDemo.setAttribute("name", this.name);
|
||||
bezierDemo.setAttribute("key", this.key);
|
||||
bezierDemo.setAttribute("sliderOptions", JSON.stringify(demo.sliderOptions));
|
||||
bezierDemo.setAttribute("triggerOnMouseMove", String(this.triggerOnMouseMove));
|
||||
bezierDemo.setAttribute("computetype", this.computeType);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { WasmSubpath } from "@/../wasm/pkg";
|
||||
import subpathFeatures, { SubpathFeatureName } from "@/features/subpath-features";
|
||||
import subpathFeatures, { SubpathFeatureKey } from "@/features/subpath-features";
|
||||
import { renderDemo } from "@/utils/render";
|
||||
|
||||
import { SubpathCallback, WasmSubpathInstance, WasmSubpathManipulatorKey, SliderOption, ComputeType } from "@/utils/types";
|
||||
|
|
@ -13,7 +13,7 @@ class SubpathDemo extends HTMLElement {
|
|||
|
||||
triples!: (number[] | undefined)[][];
|
||||
|
||||
name!: SubpathFeatureName;
|
||||
key!: SubpathFeatureKey;
|
||||
|
||||
closed!: boolean;
|
||||
|
||||
|
|
@ -51,13 +51,13 @@ class SubpathDemo extends HTMLElement {
|
|||
connectedCallback(): void {
|
||||
this.title = this.getAttribute("title") || "";
|
||||
this.triples = JSON.parse(this.getAttribute("triples") || "[]");
|
||||
this.name = this.getAttribute("name") as SubpathFeatureName;
|
||||
this.key = this.getAttribute("key") as SubpathFeatureKey;
|
||||
this.sliderOptions = JSON.parse(this.getAttribute("sliderOptions") || "[]");
|
||||
this.triggerOnMouseMove = this.getAttribute("triggerOnMouseMove") === "true";
|
||||
this.closed = this.getAttribute("closed") === "true";
|
||||
this.computeType = (this.getAttribute("computetype") || "Parametric") as ComputeType;
|
||||
|
||||
this.callback = subpathFeatures[this.name].callback as SubpathCallback;
|
||||
this.callback = subpathFeatures[this.key].callback as SubpathCallback;
|
||||
this.subpath = WasmSubpath.from_triples(this.triples, this.closed) as WasmSubpathInstance;
|
||||
this.sliderData = Object.assign({}, ...this.sliderOptions.map((s) => ({ [s.variable]: s.default })));
|
||||
this.sliderUnits = Object.assign({}, ...this.sliderOptions.map((s) => ({ [s.variable]: s.unit })));
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
import { SubpathFeatureName } from "@/features/subpath-features";
|
||||
import subpathFeatures, { SubpathFeatureKey } from "@/features/subpath-features";
|
||||
import { renderDemoPane } from "@/utils/render";
|
||||
import { ComputeType, Demo, DemoPane, SliderOption, SubpathDemoArgs } from "@/utils/types";
|
||||
|
||||
class SubpathDemoPane extends HTMLElement implements DemoPane {
|
||||
// Props
|
||||
name!: SubpathFeatureName;
|
||||
key!: SubpathFeatureKey;
|
||||
|
||||
name!: string;
|
||||
|
||||
sliderOptions!: SliderOption[];
|
||||
|
||||
|
|
@ -45,10 +47,11 @@ class SubpathDemoPane extends HTMLElement implements DemoPane {
|
|||
closed: true,
|
||||
},
|
||||
];
|
||||
this.id = `${Math.random()}`.substring(2);
|
||||
this.computeType = "Parametric";
|
||||
|
||||
this.name = (this.getAttribute("name") || "") as SubpathFeatureName;
|
||||
this.key = (this.getAttribute("name") || "") as SubpathFeatureKey;
|
||||
this.id = `subpath/${this.key}`;
|
||||
this.name = subpathFeatures[this.key].name;
|
||||
this.sliderOptions = JSON.parse(this.getAttribute("sliderOptions") || "[]");
|
||||
this.triggerOnMouseMove = this.getAttribute("triggerOnMouseMove") === "true";
|
||||
this.chooseComputeType = this.getAttribute("chooseComputeType") === "true";
|
||||
|
|
@ -65,7 +68,7 @@ class SubpathDemoPane extends HTMLElement implements DemoPane {
|
|||
subpathDemo.setAttribute("title", demo.title);
|
||||
subpathDemo.setAttribute("triples", JSON.stringify(demo.triples));
|
||||
subpathDemo.setAttribute("closed", String(demo.closed));
|
||||
subpathDemo.setAttribute("name", this.name);
|
||||
subpathDemo.setAttribute("key", this.key);
|
||||
subpathDemo.setAttribute("sliderOptions", JSON.stringify(this.sliderOptions));
|
||||
subpathDemo.setAttribute("triggerOnMouseMove", String(this.triggerOnMouseMove));
|
||||
subpathDemo.setAttribute("computetype", this.computeType);
|
||||
|
|
|
|||
|
|
@ -3,10 +3,12 @@ import { tSliderOptions, tErrorOptions, tMinimumSeperationOptions } from "@/util
|
|||
import { ComputeType, BezierDemoOptions, WasmBezierInstance, BezierCallback } from "@/utils/types";
|
||||
|
||||
const bezierFeatures = {
|
||||
Constructor: {
|
||||
constructor: {
|
||||
name: "Constructor",
|
||||
callback: (bezier: WasmBezierInstance, _: Record<string, number>): string => bezier.to_svg(),
|
||||
},
|
||||
"Bezier Through Points": {
|
||||
"bezier-through-points": {
|
||||
name: "Bezier Through Points",
|
||||
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => {
|
||||
const points = JSON.parse(bezier.get_points());
|
||||
if (Object.values(options).length === 1) {
|
||||
|
|
@ -59,10 +61,12 @@ const bezierFeatures = {
|
|||
},
|
||||
},
|
||||
},
|
||||
Length: {
|
||||
length: {
|
||||
name: "Length",
|
||||
callback: (bezier: WasmBezierInstance, _: Record<string, number>): string => bezier.length(),
|
||||
},
|
||||
Evaluate: {
|
||||
evaluate: {
|
||||
name: "Evaluate",
|
||||
callback: (bezier: WasmBezierInstance, options: Record<string, number>, _: undefined, computeType: ComputeType): string => bezier.evaluate(options.computeArgument, computeType),
|
||||
demoOptions: {
|
||||
Quadratic: {
|
||||
|
|
@ -71,7 +75,8 @@ const bezierFeatures = {
|
|||
},
|
||||
chooseComputeType: true,
|
||||
},
|
||||
"Lookup Table": {
|
||||
"lookup-table": {
|
||||
name: "Lookup Table",
|
||||
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => bezier.compute_lookup_table(options.steps),
|
||||
demoOptions: {
|
||||
Quadratic: {
|
||||
|
|
@ -87,7 +92,8 @@ const bezierFeatures = {
|
|||
},
|
||||
},
|
||||
},
|
||||
Derivative: {
|
||||
derivative: {
|
||||
name: "Derivative",
|
||||
callback: (bezier: WasmBezierInstance, _: Record<string, number>): string => bezier.derivative(),
|
||||
demoOptions: {
|
||||
Linear: {
|
||||
|
|
@ -110,7 +116,8 @@ const bezierFeatures = {
|
|||
},
|
||||
},
|
||||
},
|
||||
Tangent: {
|
||||
tangent: {
|
||||
name: "Tangent",
|
||||
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => bezier.tangent(options.t),
|
||||
demoOptions: {
|
||||
Quadratic: {
|
||||
|
|
@ -118,7 +125,8 @@ const bezierFeatures = {
|
|||
},
|
||||
},
|
||||
},
|
||||
Normal: {
|
||||
normal: {
|
||||
name: "Normal",
|
||||
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => bezier.normal(options.t),
|
||||
demoOptions: {
|
||||
Quadratic: {
|
||||
|
|
@ -126,7 +134,8 @@ const bezierFeatures = {
|
|||
},
|
||||
},
|
||||
},
|
||||
Curvature: {
|
||||
curvature: {
|
||||
name: "Curvature",
|
||||
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => bezier.curvature(options.t),
|
||||
demoOptions: {
|
||||
Linear: {
|
||||
|
|
@ -137,7 +146,8 @@ const bezierFeatures = {
|
|||
},
|
||||
},
|
||||
},
|
||||
Split: {
|
||||
split: {
|
||||
name: "Split",
|
||||
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => bezier.split(options.t),
|
||||
demoOptions: {
|
||||
Quadratic: {
|
||||
|
|
@ -145,7 +155,8 @@ const bezierFeatures = {
|
|||
},
|
||||
},
|
||||
},
|
||||
Trim: {
|
||||
trim: {
|
||||
name: "Trim",
|
||||
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => bezier.trim(options.t1, options.t2),
|
||||
demoOptions: {
|
||||
Quadratic: {
|
||||
|
|
@ -168,12 +179,14 @@ const bezierFeatures = {
|
|||
},
|
||||
},
|
||||
},
|
||||
Project: {
|
||||
project: {
|
||||
name: "Project",
|
||||
callback: (bezier: WasmBezierInstance, _: Record<string, number>, mouseLocation?: [number, number]): string =>
|
||||
mouseLocation ? bezier.project(mouseLocation[0], mouseLocation[1]) : bezier.to_svg(),
|
||||
triggerOnMouseMove: true,
|
||||
},
|
||||
"Local Extrema": {
|
||||
"local-extrema": {
|
||||
name: "Local Extrema",
|
||||
callback: (bezier: WasmBezierInstance, _: Record<string, number>): string => bezier.local_extrema(),
|
||||
demoOptions: {
|
||||
Quadratic: {
|
||||
|
|
@ -193,10 +206,12 @@ const bezierFeatures = {
|
|||
},
|
||||
},
|
||||
},
|
||||
"Bounding Box": {
|
||||
"bounding-box": {
|
||||
name: "Bounding Box",
|
||||
callback: (bezier: WasmBezierInstance, _: Record<string, number>): string => bezier.bounding_box(),
|
||||
},
|
||||
Inflections: {
|
||||
inflections: {
|
||||
name: "Inflections",
|
||||
callback: (bezier: WasmBezierInstance, _: Record<string, number>): string => bezier.inflections(),
|
||||
demoOptions: {
|
||||
Linear: {
|
||||
|
|
@ -207,10 +222,12 @@ const bezierFeatures = {
|
|||
},
|
||||
},
|
||||
},
|
||||
Reduce: {
|
||||
reduce: {
|
||||
name: "Reduce",
|
||||
callback: (bezier: WasmBezierInstance): string => bezier.reduce(),
|
||||
},
|
||||
Offset: {
|
||||
offset: {
|
||||
name: "Offset",
|
||||
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => bezier.offset(options.distance),
|
||||
demoOptions: {
|
||||
Quadratic: {
|
||||
|
|
@ -226,7 +243,8 @@ const bezierFeatures = {
|
|||
},
|
||||
},
|
||||
},
|
||||
Outline: {
|
||||
outline: {
|
||||
name: "Outline",
|
||||
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => bezier.outline(options.distance),
|
||||
demoOptions: {
|
||||
Quadratic: {
|
||||
|
|
@ -242,7 +260,8 @@ const bezierFeatures = {
|
|||
},
|
||||
},
|
||||
},
|
||||
"Graduated Outline": {
|
||||
"graduated-outline": {
|
||||
name: "Graduated Outline",
|
||||
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => bezier.graduated_outline(options.start_distance, options.end_distance),
|
||||
demoOptions: {
|
||||
Quadratic: {
|
||||
|
|
@ -273,7 +292,8 @@ const bezierFeatures = {
|
|||
],
|
||||
},
|
||||
},
|
||||
"Skewed Outline": {
|
||||
"skewed-outline": {
|
||||
name: "Skewed Outline",
|
||||
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => bezier.skewed_outline(options.distance1, options.distance2, options.distance3, options.distance4),
|
||||
demoOptions: {
|
||||
Quadratic: {
|
||||
|
|
@ -310,7 +330,8 @@ const bezierFeatures = {
|
|||
},
|
||||
},
|
||||
},
|
||||
Arcs: {
|
||||
arcs: {
|
||||
name: "Arcs",
|
||||
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => bezier.arcs(options.error, options.max_iterations, options.strategy),
|
||||
demoOptions: ((): Omit<BezierDemoOptions, "Linear"> => {
|
||||
const sliderOptions = [
|
||||
|
|
@ -361,7 +382,8 @@ const bezierFeatures = {
|
|||
};
|
||||
})(),
|
||||
},
|
||||
"Intersect (Line Segment)": {
|
||||
"intersect-linear": {
|
||||
name: "Intersect (Line Segment)",
|
||||
callback: (bezier: WasmBezierInstance): string => {
|
||||
const line = [
|
||||
[150, 150],
|
||||
|
|
@ -370,7 +392,8 @@ const bezierFeatures = {
|
|||
return bezier.intersect_line_segment(line);
|
||||
},
|
||||
},
|
||||
"Intersect (Quadratic)": {
|
||||
"intersect-quadratic": {
|
||||
name: "Intersect (Quadratic)",
|
||||
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => {
|
||||
const quadratic = [
|
||||
[20, 80],
|
||||
|
|
@ -385,7 +408,8 @@ const bezierFeatures = {
|
|||
},
|
||||
},
|
||||
},
|
||||
"Intersect (Cubic)": {
|
||||
"intersect-cubic": {
|
||||
name: "Intersect (Cubic)",
|
||||
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => {
|
||||
const cubic = [
|
||||
[40, 20],
|
||||
|
|
@ -401,7 +425,8 @@ const bezierFeatures = {
|
|||
},
|
||||
},
|
||||
},
|
||||
"Intersect (Self)": {
|
||||
"intersect-self": {
|
||||
name: "Intersect (Self)",
|
||||
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => bezier.intersect_self(options.error),
|
||||
demoOptions: {
|
||||
Quadratic: {
|
||||
|
|
@ -417,14 +442,16 @@ const bezierFeatures = {
|
|||
},
|
||||
},
|
||||
},
|
||||
"Intersect (Rectangle)": {
|
||||
"intersect-rectangle": {
|
||||
name: "Intersect (Rectangle)",
|
||||
callback: (bezier: WasmBezierInstance): string =>
|
||||
bezier.intersect_rectangle([
|
||||
[50, 50],
|
||||
[150, 150],
|
||||
]),
|
||||
},
|
||||
Rotate: {
|
||||
rotate: {
|
||||
name: "Rotate",
|
||||
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => bezier.rotate(options.angle * Math.PI, 100, 100),
|
||||
demoOptions: {
|
||||
Quadratic: {
|
||||
|
|
@ -441,7 +468,8 @@ const bezierFeatures = {
|
|||
},
|
||||
},
|
||||
},
|
||||
"De Casteljau Points": {
|
||||
"de-casteljau-points": {
|
||||
name: "De Casteljau Points",
|
||||
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => bezier.de_casteljau_points(options.t),
|
||||
demoOptions: {
|
||||
Quadratic: {
|
||||
|
|
@ -451,11 +479,12 @@ const bezierFeatures = {
|
|||
},
|
||||
};
|
||||
|
||||
export type BezierFeatureName = keyof typeof bezierFeatures;
|
||||
export type BezierFeatureKey = keyof typeof bezierFeatures;
|
||||
export type BezierFeatureOptions = {
|
||||
name: string;
|
||||
callback: BezierCallback;
|
||||
demoOptions?: Partial<BezierDemoOptions>;
|
||||
triggerOnMouseMove?: boolean;
|
||||
chooseComputeType?: boolean;
|
||||
};
|
||||
export default bezierFeatures as Record<BezierFeatureName, BezierFeatureOptions>;
|
||||
export default bezierFeatures as Record<BezierFeatureKey, BezierFeatureOptions>;
|
||||
|
|
|
|||
|
|
@ -2,44 +2,53 @@ import { tSliderOptions } from "@/utils/options";
|
|||
import { ComputeType, SliderOption, SubpathCallback, WasmSubpathInstance } from "@/utils/types";
|
||||
|
||||
const subpathFeatures = {
|
||||
Constructor: {
|
||||
constructor: {
|
||||
name: "Constructor",
|
||||
callback: (subpath: WasmSubpathInstance): string => subpath.to_svg(),
|
||||
},
|
||||
Insert: {
|
||||
insert: {
|
||||
name: "Insert",
|
||||
callback: (subpath: WasmSubpathInstance, options: Record<string, number>, _: undefined, computeType: ComputeType): string => subpath.insert(options.computeArgument, computeType),
|
||||
sliderOptions: [{ ...tSliderOptions, variable: "computeArgument" }],
|
||||
// TODO: Uncomment this after implementing the Euclidean version
|
||||
// chooseComputeType: true,
|
||||
},
|
||||
Length: {
|
||||
length: {
|
||||
name: "Length",
|
||||
callback: (subpath: WasmSubpathInstance): string => subpath.length(),
|
||||
},
|
||||
Evaluate: {
|
||||
evaluate: {
|
||||
name: "Evaluate",
|
||||
callback: (subpath: WasmSubpathInstance, options: Record<string, number>, _: undefined, computeType: ComputeType): string => subpath.evaluate(options.computeArgument, computeType),
|
||||
sliderOptions: [{ ...tSliderOptions, variable: "computeArgument" }],
|
||||
chooseComputeType: true,
|
||||
},
|
||||
Project: {
|
||||
project: {
|
||||
name: "Project",
|
||||
callback: (subpath: WasmSubpathInstance, _: Record<string, number>, mouseLocation?: [number, number]): string =>
|
||||
mouseLocation ? subpath.project(mouseLocation[0], mouseLocation[1]) : subpath.to_svg(),
|
||||
triggerOnMouseMove: true,
|
||||
},
|
||||
Tangent: {
|
||||
tangent: {
|
||||
name: "Tangent",
|
||||
callback: (subpath: WasmSubpathInstance, options: Record<string, number>): string => subpath.tangent(options.t),
|
||||
sliderOptions: [tSliderOptions],
|
||||
},
|
||||
Normal: {
|
||||
normal: {
|
||||
name: "Normal",
|
||||
callback: (subpath: WasmSubpathInstance, options: Record<string, number>): string => subpath.normal(options.t),
|
||||
sliderOptions: [tSliderOptions],
|
||||
},
|
||||
"Intersect (Line Segment)": {
|
||||
"intersect-linear": {
|
||||
name: "Intersect (Line Segment)",
|
||||
callback: (subpath: WasmSubpathInstance): string =>
|
||||
subpath.intersect_line_segment([
|
||||
[150, 150],
|
||||
[20, 20],
|
||||
]),
|
||||
},
|
||||
"Intersect (Quadratic segment)": {
|
||||
"intersect-quadratic": {
|
||||
name: "Intersect (Quadratic segment)",
|
||||
callback: (subpath: WasmSubpathInstance): string =>
|
||||
subpath.intersect_quadratic_segment([
|
||||
[20, 80],
|
||||
|
|
@ -47,7 +56,8 @@ const subpathFeatures = {
|
|||
[90, 120],
|
||||
]),
|
||||
},
|
||||
"Intersect (Cubic segment)": {
|
||||
"intersect-cubic": {
|
||||
name: "Intersect (Cubic segment)",
|
||||
callback: (subpath: WasmSubpathInstance): string =>
|
||||
subpath.intersect_cubic_segment([
|
||||
[40, 20],
|
||||
|
|
@ -56,7 +66,8 @@ const subpathFeatures = {
|
|||
[175, 140],
|
||||
]),
|
||||
},
|
||||
Split: {
|
||||
split: {
|
||||
name: "Split",
|
||||
callback: (subpath: WasmSubpathInstance, options: Record<string, number>, _: undefined, computeType: ComputeType): string => subpath.split(options.computeArgument, computeType),
|
||||
sliderOptions: [{ ...tSliderOptions, variable: "computeArgument" }],
|
||||
// TODO: Uncomment this after implementing the Euclidean version
|
||||
|
|
@ -64,11 +75,12 @@ const subpathFeatures = {
|
|||
},
|
||||
};
|
||||
|
||||
export type SubpathFeatureName = keyof typeof subpathFeatures;
|
||||
export type SubpathFeatureKey = keyof typeof subpathFeatures;
|
||||
export type SubpathFeatureOptions = {
|
||||
name: string;
|
||||
callback: SubpathCallback;
|
||||
sliderOptions?: SliderOption[];
|
||||
triggerOnMouseMove?: boolean;
|
||||
chooseComputeType?: boolean;
|
||||
};
|
||||
export default subpathFeatures as Record<SubpathFeatureName, SubpathFeatureOptions>;
|
||||
export default subpathFeatures as Record<SubpathFeatureKey, SubpathFeatureOptions>;
|
||||
|
|
|
|||
|
|
@ -3,26 +3,11 @@ import BezierDemoPane from "@/components/BezierDemoPane";
|
|||
import SubpathDemo from "@/components/SubpathDemo";
|
||||
import SubpathDemoPane from "@/components/SubpathDemoPane";
|
||||
|
||||
import bezierFeatures, { BezierFeatureName } from "@/features/bezier-features";
|
||||
import subpathFeatures, { SubpathFeatureName } from "@/features/subpath-features";
|
||||
import bezierFeatures, { BezierFeatureKey } from "@/features/bezier-features";
|
||||
import subpathFeatures, { SubpathFeatureKey } from "@/features/subpath-features";
|
||||
|
||||
import "@/style.css";
|
||||
|
||||
window.document.title = "Bezier-rs Interactive Documentation";
|
||||
window.document.body.innerHTML = `
|
||||
<h1>Bezier-rs Interactive Documentation</h1>
|
||||
<p>
|
||||
This is the interactive documentation for the <a href="https://crates.io/crates/bezier-rs"><b>Bezier-rs</b></a> library. View the
|
||||
<a href="https://docs.rs/bezier-rs/latest/bezier_rs">crate documentation</a>
|
||||
for detailed function descriptions and API usage. Click and drag on the endpoints of the demo curves to visualize the various Bezier utilities and functions.
|
||||
</p>
|
||||
|
||||
<h2>Beziers</h2>
|
||||
<div id="bezier-demos"></div>
|
||||
<h2>Subpaths</h2>
|
||||
<div id="subpath-demos"></div>
|
||||
`.trim();
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"bezier-demo": BezierDemo;
|
||||
|
|
@ -32,13 +17,14 @@ declare global {
|
|||
}
|
||||
}
|
||||
|
||||
window.document.title = "Bezier-rs Interactive Documentation";
|
||||
|
||||
window.customElements.define("bezier-demo", BezierDemo);
|
||||
window.customElements.define("bezier-demo-pane", BezierDemoPane);
|
||||
window.customElements.define("subpath-demo", SubpathDemo);
|
||||
window.customElements.define("subpath-demo-pane", SubpathDemoPane);
|
||||
|
||||
const bezierDemos = document.getElementById("bezier-demos");
|
||||
(Object.keys(bezierFeatures) as BezierFeatureName[]).forEach((featureName) => {
|
||||
function renderBezierPane(featureName: BezierFeatureKey, container: HTMLElement | null): void {
|
||||
const feature = bezierFeatures[featureName];
|
||||
const demo = document.createElement("bezier-demo-pane");
|
||||
|
||||
|
|
@ -46,11 +32,10 @@ const bezierDemos = document.getElementById("bezier-demos");
|
|||
demo.setAttribute("demoOptions", JSON.stringify(feature.demoOptions || {}));
|
||||
demo.setAttribute("triggerOnMouseMove", String(feature.triggerOnMouseMove));
|
||||
demo.setAttribute("chooseComputeType", String(feature.chooseComputeType));
|
||||
bezierDemos?.append(demo);
|
||||
});
|
||||
container?.append(demo);
|
||||
}
|
||||
|
||||
const subpathDemos = document.getElementById("subpath-demos");
|
||||
(Object.keys(subpathFeatures) as SubpathFeatureName[]).forEach((featureName) => {
|
||||
function renderSubpathPane(featureName: SubpathFeatureKey, container: HTMLElement | null): void {
|
||||
const feature = subpathFeatures[featureName];
|
||||
const demo = document.createElement("subpath-demo-pane");
|
||||
|
||||
|
|
@ -58,5 +43,66 @@ const subpathDemos = document.getElementById("subpath-demos");
|
|||
demo.setAttribute("sliderOptions", JSON.stringify(feature.sliderOptions || []));
|
||||
demo.setAttribute("triggerOnMouseMove", String(feature.triggerOnMouseMove));
|
||||
demo.setAttribute("chooseComputeType", String(feature.chooseComputeType));
|
||||
subpathDemos?.append(demo);
|
||||
container?.append(demo);
|
||||
}
|
||||
|
||||
function isUrlSolo(url: string): boolean {
|
||||
const hash = url.split("#")?.[1];
|
||||
const splitHash = hash?.split("/");
|
||||
return splitHash?.length === 3 && splitHash?.[2] === "solo";
|
||||
}
|
||||
|
||||
window.addEventListener("hashchange", (e: Event): void => {
|
||||
const hashChangeEvent = e as HashChangeEvent;
|
||||
const isOldHashSolo = isUrlSolo(hashChangeEvent.oldURL);
|
||||
const isNewHashSolo = isUrlSolo(hashChangeEvent.newURL);
|
||||
const target = document.getElementById(window.location.hash.substring(1));
|
||||
// Determine whether the page needs to recompute which examples to show
|
||||
if (!target || isOldHashSolo !== isNewHashSolo) {
|
||||
renderExamples();
|
||||
}
|
||||
});
|
||||
|
||||
function renderExamples(): void {
|
||||
const hash = window.location.hash;
|
||||
const splitHash = hash.split("/");
|
||||
|
||||
// Determine which examples to render based on hash
|
||||
if (splitHash[0] === "#bezier" && splitHash[1] in bezierFeatures && splitHash[2] === "solo") {
|
||||
window.document.body.innerHTML = `<div id="bezier-demos"></div>`;
|
||||
renderBezierPane(splitHash[1] as BezierFeatureKey, document.getElementById("bezier-demos"));
|
||||
} else if (splitHash[0] === "#subpath" && splitHash[1] in subpathFeatures && splitHash[2] === "solo") {
|
||||
window.document.body.innerHTML = `<div id="subpath-demos"></div>`;
|
||||
renderSubpathPane(splitHash[1] as SubpathFeatureKey, document.getElementById("subpath-demos"));
|
||||
} else {
|
||||
window.document.body.innerHTML = `
|
||||
<h1>Bezier-rs Interactive Documentation</h1>
|
||||
<p>
|
||||
This is the interactive documentation for the <a href="https://crates.io/crates/bezier-rs"><b>Bezier-rs</b></a> library. View the
|
||||
<a href="https://docs.rs/bezier-rs/latest/bezier_rs">crate documentation</a>
|
||||
for detailed function descriptions and API usage. Click and drag on the endpoints of the demo curves to visualize the various Bezier utilities and functions.
|
||||
</p>
|
||||
|
||||
<h2>Beziers</h2>
|
||||
<div id="bezier-demos"></div>
|
||||
<h2>Subpaths</h2>
|
||||
<div id="subpath-demos"></div>
|
||||
`.trim();
|
||||
|
||||
const bezierDemos = document.getElementById("bezier-demos");
|
||||
const subpathDemos = document.getElementById("subpath-demos");
|
||||
|
||||
(Object.keys(bezierFeatures) as BezierFeatureKey[]).forEach((feature) => renderBezierPane(feature, bezierDemos));
|
||||
(Object.keys(subpathFeatures) as SubpathFeatureKey[]).forEach((feature) => renderSubpathPane(feature, subpathDemos));
|
||||
}
|
||||
|
||||
// Scroll to specified hash if it exists
|
||||
if (hash) {
|
||||
const target = document.getElementById(hash.substring(1));
|
||||
if (target) {
|
||||
target.scrollIntoView();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
renderExamples();
|
||||
|
|
|
|||
|
|
@ -1,11 +1,17 @@
|
|||
html,
|
||||
body {
|
||||
height: 100%;
|
||||
font-family: Arial, sans-serif;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
body > h1 {
|
||||
margin: 40px 0;
|
||||
}
|
||||
|
||||
body > h1 ~ :last-child {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
body > h1 + p {
|
||||
max-width: 768px;
|
||||
line-height: 1.4;
|
||||
|
|
@ -29,8 +35,24 @@ body > h2 {
|
|||
}
|
||||
|
||||
.demo-pane-header {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
margin-top: 2em;
|
||||
margin-bottom: 0;
|
||||
padding: 0 1em;
|
||||
}
|
||||
|
||||
.demo-pane-header a {
|
||||
display: none;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.demo-pane-header:hover a {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.demo-pane-container {
|
||||
|
|
|
|||
|
|
@ -42,9 +42,16 @@ export function renderDemoPane(demoPane: DemoPane): void {
|
|||
const container = document.createElement("div");
|
||||
container.className = "demo-pane-container";
|
||||
|
||||
const headerAnchorLink = document.createElement("a");
|
||||
headerAnchorLink.innerText = "#";
|
||||
const currentHash = window.location.hash.split("/");
|
||||
// Add href anchor if not on a solo example page
|
||||
if (currentHash.length !== 3 && currentHash[2] !== "solo") headerAnchorLink.href = `#${demoPane.id}`;
|
||||
|
||||
const header = document.createElement("h3");
|
||||
header.innerText = demoPane.name;
|
||||
header.className = "demo-pane-header";
|
||||
header.append(headerAnchorLink);
|
||||
|
||||
const computeTypeContainer = document.createElement("div");
|
||||
computeTypeContainer.className = "compute-type-choice";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue