Skip to content

Commit 3b0b721

Browse files
committed
PARTIAL
1 parent 8cd93f7 commit 3b0b721

8 files changed

Lines changed: 818 additions & 0 deletions

File tree

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import type { Meta, StoryObj } from "@storybook/react";
2+
3+
import { FilterOBB } from "../../src/components/filter-obb";
4+
import {
5+
CONCAVE,
6+
CONVEX,
7+
CONVEX_CIRCLE,
8+
LIP,
9+
} from "../../src/helpers/surface-equations";
10+
import {
11+
defaultFilterArgs,
12+
FilterShowcase,
13+
filterArgTypes,
14+
type SharedFilterProps,
15+
} from "../helpers";
16+
17+
const FilterOBBStory = (props: SharedFilterProps) => (
18+
<FilterShowcase>
19+
{(id) => <FilterOBB id={id} scaleRatio={1} pixelRatio={6} {...props} />}
20+
</FilterShowcase>
21+
);
22+
23+
const meta = {
24+
title: "Filters/Filter OBB (ObjectBoundingBox)",
25+
component: FilterOBBStory,
26+
argTypes: filterArgTypes,
27+
args: defaultFilterArgs,
28+
} satisfies Meta<typeof FilterOBBStory>;
29+
30+
export default meta;
31+
32+
type Story = StoryObj<typeof meta>;
33+
34+
export const Convex: Story = {
35+
args: { bezelHeightFn: CONVEX },
36+
};
37+
38+
export const ConvexCircle: Story = {
39+
args: { bezelHeightFn: CONVEX_CIRCLE },
40+
};
41+
42+
export const Concave: Story = {
43+
args: { bezelHeightFn: CONCAVE },
44+
};
45+
46+
export const Lip: Story = {
47+
args: { bezelHeightFn: LIP },
48+
};
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import type { Meta, StoryObj } from "@storybook/react";
2+
3+
import { FilterPolar } from "../../src/components/filter-polar";
4+
import {
5+
CONCAVE,
6+
CONVEX,
7+
CONVEX_CIRCLE,
8+
LIP,
9+
} from "../../src/helpers/surface-equations";
10+
import {
11+
defaultFilterArgs,
12+
FilterShowcase,
13+
filterArgTypes,
14+
type SharedFilterProps,
15+
} from "../helpers";
16+
17+
type FilterPolarStoryProps = Omit<
18+
SharedFilterProps,
19+
"specularOpacity" | "specularAngle"
20+
>;
21+
22+
const FilterPolarStory = (props: FilterPolarStoryProps) => (
23+
<FilterShowcase>
24+
{(id) => <FilterPolar id={id} pixelRatio={6} {...props} />}
25+
</FilterShowcase>
26+
);
27+
28+
const {
29+
specularOpacity: _a,
30+
specularAngle: _b,
31+
...polarArgTypes
32+
} = filterArgTypes;
33+
const {
34+
specularOpacity: _c,
35+
specularAngle: _d,
36+
...polarArgs
37+
} = defaultFilterArgs;
38+
39+
const meta = {
40+
title: "Filters/Filter Polar (Debug)",
41+
component: FilterPolarStory,
42+
argTypes: polarArgTypes,
43+
args: polarArgs,
44+
} satisfies Meta<typeof FilterPolarStory>;
45+
46+
export default meta;
47+
48+
type Story = StoryObj<typeof meta>;
49+
50+
export const Convex: Story = {
51+
args: { bezelHeightFn: CONVEX },
52+
};
53+
54+
export const ConvexCircle: Story = {
55+
args: { bezelHeightFn: CONVEX_CIRCLE },
56+
};
57+
58+
export const Concave: Story = {
59+
args: { bezelHeightFn: CONCAVE },
60+
};
61+
62+
export const Lip: Story = {
63+
args: { bezelHeightFn: LIP },
64+
};
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import type { Meta, StoryObj } from "@storybook/react";
2+
3+
import { Filter } from "../../src/components/filter";
4+
import {
5+
CONCAVE,
6+
CONVEX,
7+
CONVEX_CIRCLE,
8+
LIP,
9+
} from "../../src/helpers/surface-equations";
10+
import {
11+
defaultFilterArgs,
12+
FilterShowcase,
13+
filterArgTypes,
14+
type SharedFilterProps,
15+
} from "../helpers";
16+
17+
const FilterStory = (props: SharedFilterProps) => (
18+
<FilterShowcase>
19+
{(id) => (
20+
<Filter
21+
id={id}
22+
scaleRatio={1}
23+
pixelRatio={6}
24+
width={400}
25+
height={300}
26+
{...props}
27+
/>
28+
)}
29+
</FilterShowcase>
30+
);
31+
32+
const meta = {
33+
title: "Filters/Filter (Explicit Size)",
34+
component: FilterStory,
35+
argTypes: filterArgTypes,
36+
args: defaultFilterArgs,
37+
} satisfies Meta<typeof FilterStory>;
38+
39+
export default meta;
40+
41+
type Story = StoryObj<typeof meta>;
42+
43+
export const Convex: Story = {
44+
args: { bezelHeightFn: CONVEX },
45+
};
46+
47+
export const ConvexCircle: Story = {
48+
args: { bezelHeightFn: CONVEX_CIRCLE },
49+
};
50+
51+
export const Concave: Story = {
52+
args: { bezelHeightFn: CONCAVE },
53+
};
54+
55+
export const Lip: Story = {
56+
args: { bezelHeightFn: LIP },
57+
};
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import { useId } from "react";
2+
3+
import type { SurfaceFnDef } from "../src/helpers/surface-equations";
4+
import { CONVEX } from "../src/helpers/surface-equations";
5+
6+
export type SharedFilterProps = {
7+
blur: number;
8+
radius: number;
9+
glassThickness: number;
10+
bezelWidth: number;
11+
refractiveIndex: number;
12+
specularOpacity: number;
13+
specularAngle: number;
14+
bezelHeightFn: SurfaceFnDef;
15+
};
16+
17+
export const defaultFilterArgs: SharedFilterProps = {
18+
blur: 2,
19+
radius: 20,
20+
glassThickness: 70,
21+
bezelWidth: 30,
22+
refractiveIndex: 1.5,
23+
specularOpacity: 0.9,
24+
specularAngle: 2,
25+
bezelHeightFn: CONVEX,
26+
};
27+
28+
export const filterArgTypes = {
29+
blur: { control: { type: "range" as const, min: 0, max: 20, step: 0.5 } },
30+
radius: { control: { type: "range" as const, min: 0, max: 100, step: 1 } },
31+
glassThickness: {
32+
control: { type: "range" as const, min: 0, max: 300, step: 1 },
33+
},
34+
bezelWidth: {
35+
control: { type: "range" as const, min: 0, max: 100, step: 1 },
36+
},
37+
refractiveIndex: {
38+
control: { type: "range" as const, min: 1, max: 3, step: 0.01 },
39+
},
40+
specularOpacity: {
41+
control: { type: "range" as const, min: 0, max: 1, step: 0.01 },
42+
},
43+
specularAngle: {
44+
control: { type: "range" as const, min: 0, max: 6.28, step: 0.01 },
45+
},
46+
bezelHeightFn: { table: { disable: true } },
47+
};
48+
49+
/**
50+
* Wrapper that renders a filter + a div with that filter applied, over a colorful background.
51+
*/
52+
export const FilterShowcase: React.FC<{
53+
children: (id: string) => React.ReactNode;
54+
}> = ({ children }) => {
55+
const filterId = useId();
56+
57+
return (
58+
<div
59+
style={{
60+
position: "relative",
61+
width: "100%",
62+
height: "100vh",
63+
background:
64+
"repeating-conic-gradient(#e66465 0% 25%, #9198e5 0% 50%) 0 / 40px 40px",
65+
display: "flex",
66+
alignItems: "center",
67+
justifyContent: "center",
68+
}}
69+
>
70+
{children(filterId)}
71+
72+
<div
73+
style={{
74+
width: 400,
75+
height: 300,
76+
backdropFilter: `url(#${filterId})`,
77+
borderRadius: 20,
78+
backgroundColor: "rgba(255, 255, 255, 0.15)",
79+
display: "flex",
80+
alignItems: "center",
81+
justifyContent: "center",
82+
fontSize: 18,
83+
fontWeight: 600,
84+
color: "#333",
85+
}}
86+
>
87+
Refractive Glass
88+
</div>
89+
</div>
90+
);
91+
};
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import type { Meta, StoryObj } from "@storybook/react";
2+
import { useEffect, useRef } from "react";
3+
4+
import type { SurfaceFnDef } from "../../src/helpers/surface-equations";
5+
import {
6+
CONCAVE,
7+
CONVEX,
8+
CONVEX_CIRCLE,
9+
LIP,
10+
} from "../../src/helpers/surface-equations";
11+
import {
12+
calculateDisplacementMap,
13+
calculateDisplacementMapRadius,
14+
} from "../../src/maps/displacement-map";
15+
16+
type Props = {
17+
radius: number;
18+
glassThickness: number;
19+
bezelWidth: number;
20+
refractiveIndex: number;
21+
bezelHeightFn: SurfaceFnDef;
22+
pixelRatio: number;
23+
};
24+
25+
const DisplacementMapVis = ({
26+
radius,
27+
glassThickness,
28+
bezelWidth,
29+
refractiveIndex,
30+
bezelHeightFn,
31+
pixelRatio,
32+
}: Props) => {
33+
const canvasRef = useRef<HTMLCanvasElement>(null);
34+
35+
useEffect(() => {
36+
const canvas = canvasRef.current;
37+
if (!canvas) {
38+
return;
39+
}
40+
41+
const cornerWidth = Math.max(radius, bezelWidth);
42+
const imageSide = cornerWidth * 2 + 1;
43+
44+
const map = calculateDisplacementMapRadius(
45+
glassThickness,
46+
bezelWidth,
47+
bezelHeightFn,
48+
refractiveIndex,
49+
);
50+
const maximumDisplacement = Math.max(...map.map(Math.abs));
51+
52+
const imageData = calculateDisplacementMap({
53+
width: imageSide,
54+
height: imageSide,
55+
radius,
56+
bezelWidth,
57+
precomputedDisplacementMap: map,
58+
maximumDisplacement,
59+
pixelRatio,
60+
});
61+
62+
canvas.width = imageData.width;
63+
canvas.height = imageData.height;
64+
const ctx = canvas.getContext("2d")!;
65+
ctx.putImageData(imageData, 0, 0);
66+
}, [
67+
radius,
68+
glassThickness,
69+
bezelWidth,
70+
refractiveIndex,
71+
bezelHeightFn,
72+
pixelRatio,
73+
]);
74+
75+
return (
76+
<div style={{ padding: 32, background: "#1a1a2e" }}>
77+
<p style={{ color: "#ccc", marginBottom: 8, fontFamily: "monospace" }}>
78+
Red = X displacement, Green = Y displacement (128 = neutral)
79+
</p>
80+
<canvas
81+
ref={canvasRef}
82+
style={{
83+
imageRendering: "pixelated",
84+
width: 500,
85+
height: 500,
86+
border: "1px solid #444",
87+
}}
88+
/>
89+
</div>
90+
);
91+
};
92+
93+
const meta = {
94+
title: "Internals/Displacement Map",
95+
component: DisplacementMapVis,
96+
argTypes: {
97+
radius: { control: { type: "range", min: 0, max: 100, step: 1 } },
98+
glassThickness: { control: { type: "range", min: 0, max: 300, step: 1 } },
99+
bezelWidth: { control: { type: "range", min: 0, max: 100, step: 1 } },
100+
refractiveIndex: {
101+
control: { type: "range", min: 1, max: 3, step: 0.01 },
102+
},
103+
pixelRatio: { control: { type: "range", min: 1, max: 12, step: 1 } },
104+
bezelHeightFn: { table: { disable: true } },
105+
},
106+
args: {
107+
radius: 20,
108+
glassThickness: 70,
109+
bezelWidth: 30,
110+
refractiveIndex: 1.5,
111+
pixelRatio: 6,
112+
bezelHeightFn: CONVEX,
113+
},
114+
} satisfies Meta<typeof DisplacementMapVis>;
115+
116+
export default meta;
117+
118+
type Story = StoryObj<typeof meta>;
119+
120+
export const Convex: Story = { args: { bezelHeightFn: CONVEX } };
121+
export const ConvexCircle: Story = { args: { bezelHeightFn: CONVEX_CIRCLE } };
122+
export const Concave: Story = { args: { bezelHeightFn: CONCAVE } };
123+
export const Lip: Story = { args: { bezelHeightFn: LIP } };

0 commit comments

Comments
 (0)