diff --git a/website/other/bezier-rs-demos/.eslintrc.js b/website/other/bezier-rs-demos/.eslintrc.js index e3c46275d..58b5afe08 100644 --- a/website/other/bezier-rs-demos/.eslintrc.js +++ b/website/other/bezier-rs-demos/.eslintrc.js @@ -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"], diff --git a/website/other/bezier-rs-demos/src/components/BezierDemo.ts b/website/other/bezier-rs-demos/src/components/BezierDemo.ts index 25a546fc5..4f3bc0bc5 100644 --- a/website/other/bezier-rs-demos/src/components/BezierDemo.ts +++ b/website/other/bezier-rs-demos/src/components/BezierDemo.ts @@ -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]; diff --git a/website/other/bezier-rs-demos/src/components/BezierDemoPane.ts b/website/other/bezier-rs-demos/src/components/BezierDemoPane.ts index 39c2699b7..d3da73340 100644 --- a/website/other/bezier-rs-demos/src/components/BezierDemoPane.ts +++ b/website/other/bezier-rs-demos/src/components/BezierDemoPane.ts @@ -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); diff --git a/website/other/bezier-rs-demos/src/components/SubpathDemo.ts b/website/other/bezier-rs-demos/src/components/SubpathDemo.ts index cfc318d52..01bdd1f1c 100644 --- a/website/other/bezier-rs-demos/src/components/SubpathDemo.ts +++ b/website/other/bezier-rs-demos/src/components/SubpathDemo.ts @@ -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 }))); diff --git a/website/other/bezier-rs-demos/src/components/SubpathDemoPane.ts b/website/other/bezier-rs-demos/src/components/SubpathDemoPane.ts index 608c997a7..82ea8cf87 100644 --- a/website/other/bezier-rs-demos/src/components/SubpathDemoPane.ts +++ b/website/other/bezier-rs-demos/src/components/SubpathDemoPane.ts @@ -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); diff --git a/website/other/bezier-rs-demos/src/features/bezier-features.ts b/website/other/bezier-rs-demos/src/features/bezier-features.ts index 753960199..42f64ca72 100644 --- a/website/other/bezier-rs-demos/src/features/bezier-features.ts +++ b/website/other/bezier-rs-demos/src/features/bezier-features.ts @@ -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 => bezier.to_svg(), }, - "Bezier Through Points": { + "bezier-through-points": { + name: "Bezier Through Points", callback: (bezier: WasmBezierInstance, options: Record): 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 => bezier.length(), }, - Evaluate: { + evaluate: { + name: "Evaluate", callback: (bezier: WasmBezierInstance, options: Record, _: 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 => bezier.compute_lookup_table(options.steps), demoOptions: { Quadratic: { @@ -87,7 +92,8 @@ const bezierFeatures = { }, }, }, - Derivative: { + derivative: { + name: "Derivative", callback: (bezier: WasmBezierInstance, _: Record): string => bezier.derivative(), demoOptions: { Linear: { @@ -110,7 +116,8 @@ const bezierFeatures = { }, }, }, - Tangent: { + tangent: { + name: "Tangent", callback: (bezier: WasmBezierInstance, options: Record): string => bezier.tangent(options.t), demoOptions: { Quadratic: { @@ -118,7 +125,8 @@ const bezierFeatures = { }, }, }, - Normal: { + normal: { + name: "Normal", callback: (bezier: WasmBezierInstance, options: Record): string => bezier.normal(options.t), demoOptions: { Quadratic: { @@ -126,7 +134,8 @@ const bezierFeatures = { }, }, }, - Curvature: { + curvature: { + name: "Curvature", callback: (bezier: WasmBezierInstance, options: Record): string => bezier.curvature(options.t), demoOptions: { Linear: { @@ -137,7 +146,8 @@ const bezierFeatures = { }, }, }, - Split: { + split: { + name: "Split", callback: (bezier: WasmBezierInstance, options: Record): string => bezier.split(options.t), demoOptions: { Quadratic: { @@ -145,7 +155,8 @@ const bezierFeatures = { }, }, }, - Trim: { + trim: { + name: "Trim", callback: (bezier: WasmBezierInstance, options: Record): string => bezier.trim(options.t1, options.t2), demoOptions: { Quadratic: { @@ -168,12 +179,14 @@ const bezierFeatures = { }, }, }, - Project: { + project: { + name: "Project", callback: (bezier: WasmBezierInstance, _: Record, 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 => bezier.local_extrema(), demoOptions: { Quadratic: { @@ -193,10 +206,12 @@ const bezierFeatures = { }, }, }, - "Bounding Box": { + "bounding-box": { + name: "Bounding Box", callback: (bezier: WasmBezierInstance, _: Record): string => bezier.bounding_box(), }, - Inflections: { + inflections: { + name: "Inflections", callback: (bezier: WasmBezierInstance, _: Record): 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 => bezier.offset(options.distance), demoOptions: { Quadratic: { @@ -226,7 +243,8 @@ const bezierFeatures = { }, }, }, - Outline: { + outline: { + name: "Outline", callback: (bezier: WasmBezierInstance, options: Record): 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 => 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 => 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 => bezier.arcs(options.error, options.max_iterations, options.strategy), demoOptions: ((): Omit => { 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 => { const quadratic = [ [20, 80], @@ -385,7 +408,8 @@ const bezierFeatures = { }, }, }, - "Intersect (Cubic)": { + "intersect-cubic": { + name: "Intersect (Cubic)", callback: (bezier: WasmBezierInstance, options: Record): string => { const cubic = [ [40, 20], @@ -401,7 +425,8 @@ const bezierFeatures = { }, }, }, - "Intersect (Self)": { + "intersect-self": { + name: "Intersect (Self)", callback: (bezier: WasmBezierInstance, options: Record): 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 => 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 => 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; triggerOnMouseMove?: boolean; chooseComputeType?: boolean; }; -export default bezierFeatures as Record; +export default bezierFeatures as Record; diff --git a/website/other/bezier-rs-demos/src/features/subpath-features.ts b/website/other/bezier-rs-demos/src/features/subpath-features.ts index 3ded6296d..2991e7843 100644 --- a/website/other/bezier-rs-demos/src/features/subpath-features.ts +++ b/website/other/bezier-rs-demos/src/features/subpath-features.ts @@ -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, _: 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, _: undefined, computeType: ComputeType): string => subpath.evaluate(options.computeArgument, computeType), sliderOptions: [{ ...tSliderOptions, variable: "computeArgument" }], chooseComputeType: true, }, - Project: { + project: { + name: "Project", callback: (subpath: WasmSubpathInstance, _: Record, 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 => subpath.tangent(options.t), sliderOptions: [tSliderOptions], }, - Normal: { + normal: { + name: "Normal", callback: (subpath: WasmSubpathInstance, options: Record): 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, _: 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; +export default subpathFeatures as Record; diff --git a/website/other/bezier-rs-demos/src/main.ts b/website/other/bezier-rs-demos/src/main.ts index d18dc7a35..da97e0d38 100644 --- a/website/other/bezier-rs-demos/src/main.ts +++ b/website/other/bezier-rs-demos/src/main.ts @@ -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 = ` -

Bezier-rs Interactive Documentation

-

- This is the interactive documentation for the Bezier-rs library. View the - crate documentation - 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. -

- -

Beziers

-
-

Subpaths

-
-`.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 = `
`; + 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 = `
`; + renderSubpathPane(splitHash[1] as SubpathFeatureKey, document.getElementById("subpath-demos")); + } else { + window.document.body.innerHTML = ` +

Bezier-rs Interactive Documentation

+

+ This is the interactive documentation for the Bezier-rs library. View the + crate documentation + 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. +

+ +

Beziers

+
+

Subpaths

+
+ `.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(); diff --git a/website/other/bezier-rs-demos/src/style.css b/website/other/bezier-rs-demos/src/style.css index 70c685e70..f73455530 100644 --- a/website/other/bezier-rs-demos/src/style.css +++ b/website/other/bezier-rs-demos/src/style.css @@ -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 { diff --git a/website/other/bezier-rs-demos/src/utils/render.ts b/website/other/bezier-rs-demos/src/utils/render.ts index 6c75630f8..2f37fe2d2 100644 --- a/website/other/bezier-rs-demos/src/utils/render.ts +++ b/website/other/bezier-rs-demos/src/utils/render.ts @@ -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";