Bezier-rs: Add joins and caps to offsets and outlines (#1083)

* Intial work

* Improve miter and add round join

* Get arcs to go opposite direction

* Add cap and other refactors

* Rename joint to join, fix some bugs

* Fix single point issue

* Clean up

* Fix iframe sizes and update UI

* Address comments and handle single point outline

* Rename variables, fix branches in outline

* Address comments
This commit is contained in:
Hannah Li
2023-03-27 16:25:08 -04:00
committed by GitHub
parent 2291442186
commit 93821f3e3b
18 changed files with 684 additions and 173 deletions
@@ -1,6 +1,6 @@
import subpathFeatures, { SubpathFeatureKey } from "@graphite/features/subpath-features";
import { renderDemoPane } from "@graphite/utils/render";
import { Demo, DemoPane, InputOption, SubpathDemoArgs } from "@graphite/utils/types";
import { Demo, DemoPane, SubpathDemoArgs, SubpathInputOption } from "@graphite/utils/types";
class SubpathDemoPane extends HTMLElement implements DemoPane {
// Props
@@ -8,7 +8,7 @@ class SubpathDemoPane extends HTMLElement implements DemoPane {
name!: string;
inputOptions!: InputOption[];
inputOptions!: SubpathInputOption[];
triggerOnMouseMove!: boolean;
@@ -62,7 +62,12 @@ class SubpathDemoPane extends HTMLElement implements DemoPane {
subpathDemo.setAttribute("triples", JSON.stringify(demo.triples));
subpathDemo.setAttribute("closed", String(demo.closed));
subpathDemo.setAttribute("key", this.key);
subpathDemo.setAttribute("inputOptions", JSON.stringify(this.inputOptions));
const inputOptions = this.inputOptions.map((option) => ({
...option,
disabled: option.isDisabledForClosed && demo.closed,
}));
subpathDemo.setAttribute("inputOptions", JSON.stringify(inputOptions));
subpathDemo.setAttribute("triggerOnMouseMove", String(this.triggerOnMouseMove));
return subpathDemo;
}
@@ -1,5 +1,5 @@
import { WasmBezier } from "@graphite/../wasm/pkg";
import { tSliderOptions, bezierTValueVariantOptions, errorOptions, minimumSeparationOptions } from "@graphite/utils/options";
import { capOptions, tSliderOptions, bezierTValueVariantOptions, errorOptions, minimumSeparationOptions } from "@graphite/utils/options";
import { BezierDemoOptions, WasmBezierInstance, BezierCallback, InputOption, BEZIER_T_VALUE_VARIANTS } from "@graphite/utils/types";
const bezierFeatures = {
@@ -251,7 +251,7 @@ const bezierFeatures = {
},
outline: {
name: "Outline",
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => bezier.outline(options.distance),
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => bezier.outline(options.distance, options.cap),
demoOptions: {
Quadratic: {
inputOptions: [
@@ -262,13 +262,14 @@ const bezierFeatures = {
step: 1,
default: 15,
},
capOptions,
],
},
},
},
"graduated-outline": {
name: "Graduated Outline",
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => bezier.graduated_outline(options.start_distance, options.end_distance),
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => bezier.graduated_outline(options.start_distance, options.end_distance, options.cap),
demoOptions: {
Quadratic: {
inputOptions: [
@@ -286,6 +287,7 @@ const bezierFeatures = {
step: 1,
default: 15,
},
capOptions,
],
},
},
@@ -300,7 +302,8 @@ const bezierFeatures = {
},
"skewed-outline": {
name: "Skewed Outline",
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string => bezier.skewed_outline(options.distance1, options.distance2, options.distance3, options.distance4),
callback: (bezier: WasmBezierInstance, options: Record<string, number>): string =>
bezier.skewed_outline(options.distance1, options.distance2, options.distance3, options.distance4, options.cap),
demoOptions: {
Quadratic: {
inputOptions: [
@@ -332,6 +335,7 @@ const bezierFeatures = {
step: 1,
default: 5,
},
capOptions,
],
},
},
@@ -1,5 +1,5 @@
import { tSliderOptions, subpathTValueVariantOptions, intersectionErrorOptions, minimumSeparationOptions } from "@graphite/utils/options";
import { InputOption, SubpathCallback, WasmSubpathInstance, SUBPATH_T_VALUE_VARIANTS } from "@graphite/utils/types";
import { capOptions, joinOptions, tSliderOptions, subpathTValueVariantOptions, intersectionErrorOptions, minimumSeparationOptions } from "@graphite/utils/options";
import { SubpathCallback, SubpathInputOption, WasmSubpathInstance, SUBPATH_T_VALUE_VARIANTS } from "@graphite/utils/types";
const subpathFeatures = {
constructor: {
@@ -107,7 +107,7 @@ const subpathFeatures = {
},
offset: {
name: "Offset",
callback: (subpath: WasmSubpathInstance, options: Record<string, number>): string => subpath.offset(options.distance),
callback: (subpath: WasmSubpathInstance, options: Record<string, number>): string => subpath.offset(options.distance, options.join),
inputOptions: [
{
variable: "distance",
@@ -116,11 +116,12 @@ const subpathFeatures = {
step: 1,
default: 10,
},
joinOptions,
],
},
outline: {
name: "Outline",
callback: (subpath: WasmSubpathInstance, options: Record<string, number>): string => subpath.outline(options.distance),
callback: (subpath: WasmSubpathInstance, options: Record<string, number>): string => subpath.outline(options.distance, options.join, options.cap),
inputOptions: [
{
variable: "distance",
@@ -129,6 +130,8 @@ const subpathFeatures = {
step: 1,
default: 10,
},
joinOptions,
{ ...capOptions, isDisabledForClosed: true },
],
},
};
@@ -137,7 +140,7 @@ export type SubpathFeatureKey = keyof typeof subpathFeatures;
export type SubpathFeatureOptions = {
name: string;
callback: SubpathCallback;
inputOptions?: InputOption[];
inputOptions?: SubpathInputOption[];
triggerOnMouseMove?: boolean;
};
export default subpathFeatures as Record<SubpathFeatureKey, SubpathFeatureOptions>;
@@ -45,3 +45,17 @@ export const subpathTValueVariantOptions = {
inputType: "dropdown",
options: SUBPATH_T_VALUE_VARIANTS,
};
export const joinOptions = {
variable: "join",
default: 0,
inputType: "dropdown",
options: ["Bevel", "Miter", "Round"],
};
export const capOptions = {
variable: "cap",
default: 0,
inputType: "dropdown",
options: ["Butt", "Round", "Square"],
};
@@ -43,6 +43,10 @@ export function renderDemo(demo: Demo): void {
selectInput.append(option);
});
if (inputOption.disabled) {
selectInput.disabled = true;
}
selectInput.addEventListener("change", (event: Event): void => {
demo.sliderData[inputOption.variable] = Number((event.target as HTMLInputElement).value);
demo.drawDemo(figure);
@@ -22,6 +22,10 @@ export type BezierDemoOptions = {
};
};
export type SubpathInputOption = InputOption & {
isDisabledForClosed?: boolean;
};
export type InputOption = {
variable: string;
min?: number;
@@ -31,6 +35,7 @@ export type InputOption = {
unit?: string | string[];
inputType?: "slider" | "dropdown";
options?: string[];
disabled?: boolean;
};
export function getCurveType(numPoints: number): BezierCurveType {