@oeave/bakery3 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +562 -2
  3. package/dist/animation-B1h0Ryvj.d.ts +97 -0
  4. package/dist/bake/index.d.ts +369 -0
  5. package/dist/bake/index.js +9 -0
  6. package/dist/bake/index.js.map +1 -0
  7. package/dist/bake-DaOlenyx.d.ts +1357 -0
  8. package/dist/catalog/index.d.ts +100 -0
  9. package/dist/catalog/index.js +154 -0
  10. package/dist/catalog/index.js.map +1 -0
  11. package/dist/catalog.gen-BM-aNf7n.d.ts +733 -0
  12. package/dist/chunk-25ZXSVQ3.js +1107 -0
  13. package/dist/chunk-25ZXSVQ3.js.map +1 -0
  14. package/dist/chunk-3G5QL4F4.js +145 -0
  15. package/dist/chunk-3G5QL4F4.js.map +1 -0
  16. package/dist/chunk-4E5VV4QY.js +12 -0
  17. package/dist/chunk-4E5VV4QY.js.map +1 -0
  18. package/dist/chunk-54WJDN5R.js +3286 -0
  19. package/dist/chunk-54WJDN5R.js.map +1 -0
  20. package/dist/chunk-6NYR73Y7.js +832 -0
  21. package/dist/chunk-6NYR73Y7.js.map +1 -0
  22. package/dist/chunk-6YVQANMW.js +1463 -0
  23. package/dist/chunk-6YVQANMW.js.map +1 -0
  24. package/dist/chunk-AF5BIELI.js +70 -0
  25. package/dist/chunk-AF5BIELI.js.map +1 -0
  26. package/dist/chunk-APBOWWSV.js +92 -0
  27. package/dist/chunk-APBOWWSV.js.map +1 -0
  28. package/dist/chunk-ARMTTZXZ.js +1157 -0
  29. package/dist/chunk-ARMTTZXZ.js.map +1 -0
  30. package/dist/chunk-CX4WTVGH.js +4492 -0
  31. package/dist/chunk-CX4WTVGH.js.map +1 -0
  32. package/dist/chunk-HQTUFVMM.js +1412 -0
  33. package/dist/chunk-HQTUFVMM.js.map +1 -0
  34. package/dist/chunk-MD2U6S4N.js +1952 -0
  35. package/dist/chunk-MD2U6S4N.js.map +1 -0
  36. package/dist/chunk-S4AJTLLN.js +23 -0
  37. package/dist/chunk-S4AJTLLN.js.map +1 -0
  38. package/dist/chunk-TGFNQBKC.js +551 -0
  39. package/dist/chunk-TGFNQBKC.js.map +1 -0
  40. package/dist/chunk-V3QINEEG.js +87 -0
  41. package/dist/chunk-V3QINEEG.js.map +1 -0
  42. package/dist/chunk-W6WQ5UP7.js +91 -0
  43. package/dist/chunk-W6WQ5UP7.js.map +1 -0
  44. package/dist/chunk-WBIAFIHV.js +346 -0
  45. package/dist/chunk-WBIAFIHV.js.map +1 -0
  46. package/dist/chunk-ZEBVAIJJ.js +156 -0
  47. package/dist/chunk-ZEBVAIJJ.js.map +1 -0
  48. package/dist/devtools/index.d.ts +536 -0
  49. package/dist/devtools/index.js +15 -0
  50. package/dist/devtools/index.js.map +1 -0
  51. package/dist/environments/index.d.ts +93 -0
  52. package/dist/environments/index.js +382 -0
  53. package/dist/environments/index.js.map +1 -0
  54. package/dist/hotspots/index.d.ts +111 -0
  55. package/dist/hotspots/index.js +285 -0
  56. package/dist/hotspots/index.js.map +1 -0
  57. package/dist/index.d.ts +944 -0
  58. package/dist/index.js +15 -0
  59. package/dist/index.js.map +1 -0
  60. package/dist/node/index.cjs +5115 -0
  61. package/dist/node/index.cjs.map +1 -0
  62. package/dist/node/index.d.cts +4862 -0
  63. package/dist/node/index.d.ts +576 -0
  64. package/dist/node/index.js +1279 -0
  65. package/dist/node/index.js.map +1 -0
  66. package/dist/prepare-C5qVvaw4.d.ts +112 -0
  67. package/dist/presets/index.d.ts +526 -0
  68. package/dist/presets/index.js +14 -0
  69. package/dist/presets/index.js.map +1 -0
  70. package/dist/r3f/index.d.ts +159 -0
  71. package/dist/r3f/index.js +592 -0
  72. package/dist/r3f/index.js.map +1 -0
  73. package/dist/room-S5TLCK4F.js +9 -0
  74. package/dist/room-S5TLCK4F.js.map +1 -0
  75. package/dist/rooms.gen-vhj6sFW5.d.ts +1434 -0
  76. package/dist/session-YCQS4MRH.js +9 -0
  77. package/dist/session-YCQS4MRH.js.map +1 -0
  78. package/dist/shapes/index.d.ts +222 -0
  79. package/dist/shapes/index.js +836 -0
  80. package/dist/shapes/index.js.map +1 -0
  81. package/dist/testRun-BIPXaVPb.d.ts +1130 -0
  82. package/dist/timeline-ChwgD7bT.d.ts +470 -0
  83. package/dist/tsl/index.d.ts +165 -0
  84. package/dist/tsl/index.js +310 -0
  85. package/dist/tsl/index.js.map +1 -0
  86. package/package.json +170 -4
@@ -0,0 +1,382 @@
1
+ import { Mesh, Group, MeshStandardMaterial, Color, HemisphereLight, CircleGeometry, SphereGeometry, FrontSide } from 'three';
2
+ import { MeshStandardNodeMaterial } from 'three/webgpu';
3
+ import { vec3, positionLocal, normalize, smoothstep, float, length, mix, exp, dot, sin, vec2, color } from 'three/tsl';
4
+
5
+ // src/environments/environments.ts
6
+ var ENVIRONMENT_SHAPES = ["sphere", "groundSphere", "floor"];
7
+ var ENVIRONMENT_SHADERS = [
8
+ "gradient",
9
+ "bands",
10
+ "blobs",
11
+ "horizon"
12
+ ];
13
+ var ENVIRONMENT_MODES = ["dark", "colorful", "light"];
14
+ var INK = "#06070d";
15
+ var CYAN = "#33d1ff";
16
+ var MAGENTA = "#ff2d95";
17
+ var VIOLET = "#8b5cf6";
18
+ var WARM = "#f4efe6";
19
+ var PALE = "#dfe4ec";
20
+ var SOFT_CYAN = "#9fe3ff";
21
+ var SOFT_MAGENTA = "#ffb3d6";
22
+ var ENVIRONMENT_PALETTES = {
23
+ dark: {
24
+ gradient: [INK, "#0e2a3a", CYAN],
25
+ bands: [INK, CYAN, MAGENTA],
26
+ blobs: [INK, CYAN, MAGENTA],
27
+ horizon: [INK, CYAN, MAGENTA]
28
+ },
29
+ colorful: {
30
+ gradient: [INK, MAGENTA, CYAN],
31
+ bands: [INK, CYAN, MAGENTA, VIOLET],
32
+ blobs: [INK, CYAN, MAGENTA, VIOLET],
33
+ horizon: [INK, MAGENTA, CYAN]
34
+ },
35
+ light: {
36
+ gradient: [PALE, WARM, SOFT_CYAN],
37
+ bands: [PALE, WARM, SOFT_CYAN],
38
+ blobs: [PALE, SOFT_CYAN, SOFT_MAGENTA, WARM],
39
+ horizon: [PALE, WARM, SOFT_MAGENTA]
40
+ }
41
+ };
42
+ var ENVIRONMENT_INTENSITY = {
43
+ dark: 3,
44
+ colorful: 2.5,
45
+ light: 1.2
46
+ };
47
+ var GROUND_LOOK = {
48
+ dark: { color: "#0b0c12", roughness: 0.28 },
49
+ colorful: { color: "#0b0c12", roughness: 0.25 },
50
+ light: { color: "#e4dfd6", roughness: 0.6 }
51
+ };
52
+ var DEFAULT_RADIUS = 12;
53
+ var GROUND_FRACTION = 0.3;
54
+ function createEnvironment(options) {
55
+ const spec = resolve(options);
56
+ const geometry = geometryFor(spec);
57
+ const material = materialFor(spec);
58
+ const mesh = new Mesh(geometry, material);
59
+ mesh.name = `environment-${spec.shape}`;
60
+ mesh.receiveShadow = true;
61
+ mesh.castShadow = false;
62
+ const group = new Group();
63
+ group.name = `bakery3-environment-${spec.shape}-${spec.shader}`;
64
+ group.userData.bakery3Preset = { environment: spec };
65
+ group.add(mesh);
66
+ if (options.fill) group.add(environmentFill(spec));
67
+ if (spec.shape === "groundSphere") {
68
+ const look = GROUND_LOOK[spec.mode];
69
+ const ground = new Mesh(
70
+ groundGeometry(spec.radius),
71
+ new MeshStandardMaterial({
72
+ color: look.color,
73
+ roughness: look.roughness,
74
+ metalness: 0
75
+ })
76
+ );
77
+ ground.name = "environment-ground";
78
+ ground.receiveShadow = true;
79
+ ground.castShadow = false;
80
+ group.add(ground);
81
+ }
82
+ return group;
83
+ }
84
+ function environmentFill(spec, intensity) {
85
+ const dark = spec.mode !== "light";
86
+ const dimmed = (spec.intensity ?? ENVIRONMENT_INTENSITY[spec.mode]) / ENVIRONMENT_INTENSITY[spec.mode];
87
+ const sky = new Color(dark ? spec.palette[1] : "#ffffff");
88
+ const ground = new Color(
89
+ dark ? spec.palette[2] ?? spec.palette[1] : "#8a8378"
90
+ );
91
+ const fill = new HemisphereLight(
92
+ sky,
93
+ ground,
94
+ (dark ? 0.9 : 0.6) * dimmed
95
+ );
96
+ fill.name = "environment-fill";
97
+ fill.userData.bakery3 = { exclude: true };
98
+ return fill;
99
+ }
100
+ function environmentSpecOf(object) {
101
+ const preset = object.userData?.bakery3Preset;
102
+ return preset?.environment;
103
+ }
104
+ var HEX = /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i;
105
+ function resolve(options) {
106
+ if (!ENVIRONMENT_SHAPES.includes(options.shape)) {
107
+ throw new Error(
108
+ `createEnvironment: unknown shape "${String(options.shape)}". One of: ${ENVIRONMENT_SHAPES.join(", ")}.`
109
+ );
110
+ }
111
+ if (!ENVIRONMENT_SHADERS.includes(options.shader)) {
112
+ throw new Error(
113
+ `createEnvironment: unknown shader "${String(options.shader)}". One of: ${ENVIRONMENT_SHADERS.join(", ")}.`
114
+ );
115
+ }
116
+ const mode = options.mode ?? "dark";
117
+ if (!ENVIRONMENT_MODES.includes(mode)) {
118
+ throw new Error(
119
+ `createEnvironment: unknown mode "${String(mode)}". One of: ${ENVIRONMENT_MODES.join(", ")}.`
120
+ );
121
+ }
122
+ const palette = options.palette ?? [
123
+ ...ENVIRONMENT_PALETTES[mode][options.shader]
124
+ ];
125
+ if (palette.length < 2 || palette.length > 4 || !palette.every((hex) => HEX.test(hex))) {
126
+ throw new Error(
127
+ 'createEnvironment: `palette` must be two to four CSS hex colors (e.g. ["#06070d", "#33d1ff"]).'
128
+ );
129
+ }
130
+ const intensity = options.intensity ?? ENVIRONMENT_INTENSITY[mode];
131
+ const radius = options.radius ?? DEFAULT_RADIUS;
132
+ if (!(intensity >= 0) || !(radius > 0)) {
133
+ throw new Error(
134
+ "createEnvironment: `intensity` must be \u2265 0 and `radius` > 0."
135
+ );
136
+ }
137
+ return {
138
+ shape: options.shape,
139
+ shader: options.shader,
140
+ mode,
141
+ palette,
142
+ intensity,
143
+ radius,
144
+ seed: options.seed ?? 1,
145
+ time: options.time ?? 0
146
+ };
147
+ }
148
+ function invert(geometry) {
149
+ const index = geometry.getIndex();
150
+ if (index) {
151
+ const a = index.array;
152
+ for (let i = 0; i + 2 < a.length; i += 3) {
153
+ const t = a[i + 1];
154
+ a[i + 1] = a[i + 2];
155
+ a[i + 2] = t;
156
+ }
157
+ index.needsUpdate = true;
158
+ }
159
+ geometry.computeVertexNormals();
160
+ return geometry;
161
+ }
162
+ function geometryFor(spec) {
163
+ const R = spec.radius;
164
+ switch (spec.shape) {
165
+ case "sphere":
166
+ return invert(new SphereGeometry(R, 96, 48));
167
+ case "groundSphere":
168
+ return invert(domeGeometry(R));
169
+ case "floor": {
170
+ const disc = new CircleGeometry(R, 128);
171
+ disc.rotateX(-Math.PI / 2);
172
+ return disc;
173
+ }
174
+ }
175
+ }
176
+ function domeGeometry(R) {
177
+ const g = GROUND_FRACTION;
178
+ const sphere = new SphereGeometry(
179
+ R,
180
+ 96,
181
+ 48,
182
+ 0,
183
+ Math.PI * 2,
184
+ 0,
185
+ Math.acos(-g)
186
+ );
187
+ sphere.translate(0, g * R, 0);
188
+ sphere.computeBoundingSphere();
189
+ return sphere;
190
+ }
191
+ function groundRadius(R) {
192
+ return R * Math.sqrt(1 - GROUND_FRACTION * GROUND_FRACTION);
193
+ }
194
+ function groundGeometry(R) {
195
+ const disc = new CircleGeometry(groundRadius(R), 128);
196
+ disc.rotateX(-Math.PI / 2);
197
+ return disc;
198
+ }
199
+ var rgb = (hex) => color(new Color(hex));
200
+ function materialFor(spec) {
201
+ const material = new MeshStandardNodeMaterial();
202
+ material.name = `bakery3-environment-${spec.shader}`;
203
+ material.roughness = 1;
204
+ material.metalness = 0;
205
+ material.emissive.set(0);
206
+ material.emissiveIntensity = 1;
207
+ if (spec.shape === "floor") {
208
+ floorMaterial(material, spec);
209
+ } else {
210
+ domeMaterial(material, spec);
211
+ }
212
+ return material;
213
+ }
214
+ function domeMaterial(material, spec) {
215
+ const R = spec.radius;
216
+ const centerY = spec.shape === "groundSphere" ? GROUND_FRACTION * R : 0;
217
+ const center = vec3(0, centerY, 0);
218
+ const local = positionLocal.sub(center);
219
+ const e = local.y.div(R);
220
+ const n = normalize(local);
221
+ let emission = domeShader(spec, e, n);
222
+ const base = spec.palette[0];
223
+ if (spec.shape === "groundSphere") {
224
+ emission = emission.mul(smoothstep(-0.3, -0.3 + 0.03, e));
225
+ }
226
+ material.colorNode = rgb(base);
227
+ material.emissiveNode = emission.mul(float(spec.intensity));
228
+ material.side = FrontSide;
229
+ }
230
+ function floorMaterial(material, spec) {
231
+ const R = spec.radius;
232
+ const p = positionLocal.xz.div(R);
233
+ const d = length(p);
234
+ const shaded = floorShader(spec, d, p);
235
+ const base = rgb(spec.palette[0]);
236
+ material.colorNode = mix(shaded, base, smoothstep(0.55, 0.95, d));
237
+ material.opacityNode = float(1).sub(smoothstep(0.6, 1, d));
238
+ material.transparent = true;
239
+ material.roughness = 0.9;
240
+ }
241
+ function domeShader(spec, e, n) {
242
+ const palette = spec.palette;
243
+ const random = mulberry32(spec.seed);
244
+ const azimuthPhase = spec.time * 0.1;
245
+ switch (spec.shader) {
246
+ case "gradient":
247
+ return ramp(palette, e, -0.4, 1);
248
+ case "bands": {
249
+ const bandCount = 4;
250
+ const k = bandCount * Math.PI;
251
+ const phase = spec.time * 0.35;
252
+ const colors = palette.slice(1);
253
+ const weight = mix(float(0.35), float(1), smoothstep(-0.6, 0.1, e));
254
+ let out = rgb(palette[0]);
255
+ colors.forEach((hex, j) => {
256
+ const offset = phase + 2 * Math.PI * j / colors.length;
257
+ const wave = sin(e.mul(k).add(offset)).mul(0.5).add(0.5);
258
+ const band = smoothstep(0.6, 0.95, wave).mul(weight);
259
+ out = out.add(rgb(hex).mul(band));
260
+ });
261
+ return out;
262
+ }
263
+ case "blobs": {
264
+ const colors = palette.slice(1);
265
+ const count = Math.max(3, colors.length);
266
+ let out = rgb(palette[0]);
267
+ for (let j = 0; j < count; j++) {
268
+ const azimuth = random() * Math.PI * 2 + azimuthPhase;
269
+ const elevation = 0.15 + random() * 0.6;
270
+ const sharpness = 7 + random() * 7;
271
+ const strength = 0.8 + random() * 0.5;
272
+ const direction = directionFrom(azimuth, elevation);
273
+ const blob = exp(dot(n, direction).sub(1).mul(sharpness)).mul(strength);
274
+ out = out.add(rgb(colors[j % colors.length]).mul(blob));
275
+ }
276
+ return out;
277
+ }
278
+ case "horizon": {
279
+ const ringCenter = 0.02;
280
+ const ringWidth = 0.025;
281
+ const ring = exp(
282
+ e.sub(ringCenter).pow(2).mul(-1 / (2 * ringWidth * ringWidth))
283
+ ).mul(2.5);
284
+ const azimuth = random() * Math.PI * 2 + azimuthPhase;
285
+ const key = directionFrom(azimuth, 0.45);
286
+ const glow = exp(dot(n, key).sub(1).mul(5)).mul(0.9);
287
+ const [dome, ringColor, glowColor = ringColor] = palette;
288
+ return rgb(dome).add(rgb(ringColor).mul(ring)).add(rgb(glowColor).mul(glow));
289
+ }
290
+ }
291
+ }
292
+ function floorShader(spec, d, p) {
293
+ const palette = spec.palette;
294
+ const random = mulberry32(spec.seed);
295
+ const azimuthPhase = spec.time * 0.1;
296
+ switch (spec.shader) {
297
+ case "gradient":
298
+ return ramp([...palette].reverse(), d, 0, 0.8);
299
+ case "bands": {
300
+ const rings = 3;
301
+ const k = rings * Math.PI * 2;
302
+ const phase = spec.time * 0.35;
303
+ const colors = palette.slice(1);
304
+ const weight = float(1).sub(smoothstep(0.3, 0.8, d));
305
+ let out = rgb(palette[0]);
306
+ colors.forEach((hex, j) => {
307
+ const offset = phase + 2 * Math.PI * j / colors.length;
308
+ const wave = sin(d.mul(k).add(offset)).mul(0.5).add(0.5);
309
+ const band = smoothstep(0.6, 0.95, wave).mul(weight);
310
+ out = out.add(rgb(hex).mul(band));
311
+ });
312
+ return out;
313
+ }
314
+ case "blobs": {
315
+ const colors = palette.slice(1);
316
+ const count = Math.max(3, colors.length);
317
+ let out = rgb(palette[0]);
318
+ for (let j = 0; j < count; j++) {
319
+ const azimuth = random() * Math.PI * 2 + azimuthPhase;
320
+ const distance = 0.15 + random() * 0.35;
321
+ const sigma = 0.12 + random() * 0.12;
322
+ const strength = 0.8 + random() * 0.5;
323
+ const at = vec2(
324
+ Math.cos(azimuth) * distance,
325
+ Math.sin(azimuth) * distance
326
+ );
327
+ const blob = exp(
328
+ length(p.sub(at)).pow(2).mul(-1 / (2 * sigma * sigma))
329
+ ).mul(strength);
330
+ out = out.add(rgb(colors[j % colors.length]).mul(blob));
331
+ }
332
+ return out;
333
+ }
334
+ case "horizon": {
335
+ const ringRadius = 0.5;
336
+ const ringWidth = 0.03;
337
+ const ring = exp(
338
+ d.sub(ringRadius).pow(2).mul(-1 / (2 * ringWidth * ringWidth))
339
+ );
340
+ const azimuth = random() * Math.PI * 2 + azimuthPhase;
341
+ const at = vec2(Math.cos(azimuth) * 0.3, Math.sin(azimuth) * 0.3);
342
+ const glow = exp(
343
+ length(p.sub(at)).pow(2).mul(-1 / (2 * 0.18 * 0.18))
344
+ ).mul(0.9);
345
+ const [floor, ringColor, glowColor = ringColor] = palette;
346
+ return rgb(floor).add(rgb(ringColor).mul(ring)).add(rgb(glowColor).mul(glow));
347
+ }
348
+ }
349
+ }
350
+ function ramp(palette, t, from, to) {
351
+ const steps = palette.length - 1;
352
+ const span = (to - from) / steps;
353
+ let out = rgb(palette[0]);
354
+ for (let i = 1; i <= steps; i++) {
355
+ const low = from + span * (i - 1);
356
+ const high = from + span * i;
357
+ out = mix(out, rgb(palette[i]), smoothstep(low, high, t));
358
+ }
359
+ return out;
360
+ }
361
+ function directionFrom(azimuth, elevation) {
362
+ const horizontal = Math.sqrt(Math.max(0, 1 - elevation * elevation));
363
+ return vec3(
364
+ Math.cos(azimuth) * horizontal,
365
+ elevation,
366
+ Math.sin(azimuth) * horizontal
367
+ );
368
+ }
369
+ function mulberry32(seed) {
370
+ let a = (Math.floor(seed) || 1) >>> 0;
371
+ return () => {
372
+ a = a + 1831565813 >>> 0;
373
+ let t = a;
374
+ t = Math.imul(t ^ t >>> 15, t | 1);
375
+ t ^= t + Math.imul(t ^ t >>> 7, t | 61);
376
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
377
+ };
378
+ }
379
+
380
+ export { ENVIRONMENT_MODES, ENVIRONMENT_SHADERS, ENVIRONMENT_SHAPES, createEnvironment, environmentSpecOf };
381
+ //# sourceMappingURL=index.js.map
382
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/environments/environments.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * The implementation behind `@oeave/bakery3/environments`. `index.ts` lists\n * what is public.\n */\n\nimport {\n FrontSide,\n CircleGeometry,\n Color,\n Group,\n HemisphereLight,\n Mesh,\n MeshStandardMaterial,\n SphereGeometry,\n type BufferGeometry,\n type Object3D,\n} from 'three';\nimport { MeshStandardNodeMaterial } from 'three/webgpu';\nimport {\n color,\n dot,\n exp,\n float,\n length,\n mix,\n normalize,\n positionLocal,\n sin,\n smoothstep,\n vec2,\n vec3,\n} from 'three/tsl';\n\n// ---------------------------------------------------------------------------\n// The vocabulary. Callers name shapes, shaders and modes by exactly these\n// strings, so they are a contract, not a style choice.\n// ---------------------------------------------------------------------------\n\nexport const ENVIRONMENT_SHAPES = ['sphere', 'groundSphere', 'floor'] as const;\nexport const ENVIRONMENT_SHADERS = [\n 'gradient',\n 'bands',\n 'blobs',\n 'horizon',\n] as const;\n/**\n * `dark`: an ink dome with colored light in it, over a dark, glossy ground,\n * the brand's own look. `colorful`: the same ground under more of that light.\n * `light`: a pale dome with soft color, over a warm matte ground.\n */\nexport const ENVIRONMENT_MODES = ['dark', 'colorful', 'light'] as const;\n\nexport type EnvironmentShape = (typeof ENVIRONMENT_SHAPES)[number];\nexport type EnvironmentShader = (typeof ENVIRONMENT_SHADERS)[number];\nexport type EnvironmentMode = (typeof ENVIRONMENT_MODES)[number];\n\nexport type EnvironmentOptions = {\n /**\n * `sphere`: an inward-facing dome of `radius` around the origin.\n * `groundSphere`: the same dome with its lower part flattened onto y = 0,\n * so shadows land on it. `floor`: a disc of `radius` at y = 0 whose color\n * and opacity fade with distance, so its edge never shows.\n */\n shape: EnvironmentShape;\n shader: EnvironmentShader;\n /**\n * Which palette and ground the environment defaults to. `dark` unless told\n * otherwise. A `palette` overrides the colors; the ground still follows\n * the mode.\n */\n mode?: EnvironmentMode;\n /**\n * Two to four CSS hex colors, darkest first. Each shader has a default\n * per mode. What each slot means:\n *\n * - `gradient`: bottom to top (center to rim on a floor)\n * - `bands`: the base, then the band colors in turn\n * - `blobs`: the base, then the blob colors in turn\n * - `horizon`: the dome, the horizon ring, the key glow\n */\n palette?: string[];\n /**\n * Emission strength, in the units the renderer traces with: a dome at\n * `1` emits the radiance an HDRI pixel of value 1 would. Meaningless for\n * the `floor` shape, which is lit, not lit from. Defaults per mode: a dark\n * dome needs its few bright patches to carry the whole scene, so it emits\n * harder (`3` dark, `2.5` colorful, `1.2` light).\n */\n intensity?: number;\n /** Dome or disc radius in meters. Default `12`. */\n radius?: number;\n /** Seeds blob placement and the key glow's side. Default `1`. */\n seed?: number;\n /**\n * A phase, in seconds, baked into the shader when it is built: it turns\n * the bands and rotates the blobs, and is the same number in the viewport\n * and in the render. Default `0`.\n */\n time?: number;\n /**\n * Add a viewport-only fill light. The dome is a real light in the render,\n * but a rasterizer takes no light from an emissive mesh, so without a fill\n * a product under a dark dome previews black. The fill is a hemisphere\n * light in the dome's own colors (sky and ground from the palette, white\n * over warm ground in light mode), marked `userData.bakery3.exclude` so it\n * never reaches the manifest. The two halves differ on the product on\n * purpose (a flat two-tone fill live, the dome's own light in the render).\n * The dome is the same in both; the glossy ground of a dark or colorful\n * dome is not, because in the render it reflects the dome (a colorful\n * horizon lays a band of its color across it) and a rasterizer reflects\n * no mesh. Default `false`.\n */\n fill?: boolean;\n};\n\n/**\n * The options an environment was built with, defaults filled in. The fill is\n * not part of it: it is a viewport aid, not the environment.\n */\nexport type EnvironmentSpec = Required<Omit<EnvironmentOptions, 'fill'>>;\n\nconst INK = '#06070d';\nconst CYAN = '#33d1ff';\nconst MAGENTA = '#ff2d95';\nconst VIOLET = '#8b5cf6';\nconst WARM = '#f4efe6';\nconst PALE = '#dfe4ec';\nconst SOFT_CYAN = '#9fe3ff';\nconst SOFT_MAGENTA = '#ffb3d6';\n\n/**\n * Default palettes per mode, darkest first. What each slot means:\n * gradient: bottom to top (center to rim on a floor)\n * bands: base, then the band colors in turn\n * blobs: base, then the blob colors in turn\n * horizon: dome, the horizon ring, the key glow\n *\n * No white in the dark and colorful sets on purpose: a warm patch on an ink\n * dome renders as a white light, and the look is color on dark.\n */\nexport const ENVIRONMENT_PALETTES: Record<\n EnvironmentMode,\n Record<EnvironmentShader, readonly string[]>\n> = {\n dark: {\n gradient: [INK, '#0e2a3a', CYAN],\n bands: [INK, CYAN, MAGENTA],\n blobs: [INK, CYAN, MAGENTA],\n horizon: [INK, CYAN, MAGENTA],\n },\n colorful: {\n gradient: [INK, MAGENTA, CYAN],\n bands: [INK, CYAN, MAGENTA, VIOLET],\n blobs: [INK, CYAN, MAGENTA, VIOLET],\n horizon: [INK, MAGENTA, CYAN],\n },\n light: {\n gradient: [PALE, WARM, SOFT_CYAN],\n bands: [PALE, WARM, SOFT_CYAN],\n blobs: [PALE, SOFT_CYAN, SOFT_MAGENTA, WARM],\n horizon: [PALE, WARM, SOFT_MAGENTA],\n },\n};\n\n/** Default emission per mode; see `EnvironmentOptions.intensity`. */\nexport const ENVIRONMENT_INTENSITY: Record<EnvironmentMode, number> = {\n dark: 3,\n colorful: 2.5,\n light: 1.2,\n};\n\n/**\n * The ground disc under a `groundSphere`: dark and glossy under a dark dome,\n * warm and matte under a light one.\n */\nconst GROUND_LOOK: Record<\n EnvironmentMode,\n { color: string; roughness: number }\n> = {\n dark: { color: '#0b0c12', roughness: 0.28 },\n colorful: { color: '#0b0c12', roughness: 0.25 },\n light: { color: '#e4dfd6', roughness: 0.6 },\n};\n\nconst DEFAULT_RADIUS = 12;\n/** How far below the dome's center the ground plane cuts a `groundSphere`,\n * as a fraction of the radius. The center then sits this high above y = 0. */\nconst GROUND_FRACTION = 0.3;\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Build an environment. Returns a `Group` holding one mesh, named\n * `bakery3-environment-<shape>-<shader>`, with the resolved options on\n * `userData.bakery3Preset.environment`. The product stands at the origin,\n * on y = 0.\n */\nexport function createEnvironment(options: EnvironmentOptions): Group {\n const spec = resolve(options);\n const geometry = geometryFor(spec);\n const material = materialFor(spec);\n\n const mesh = new Mesh(geometry, material);\n mesh.name = `environment-${spec.shape}`;\n mesh.receiveShadow = true;\n mesh.castShadow = false;\n\n const group = new Group();\n group.name = `bakery3-environment-${spec.shape}-${spec.shader}`;\n group.userData.bakery3Preset = { environment: spec };\n group.add(mesh);\n\n if (options.fill) group.add(environmentFill(spec));\n\n if (spec.shape === 'groundSphere') {\n // The ground is its own mesh with a plain PBR material, so it can be\n // glossy while the dome stays a pure emitter (a roughness that varies\n // across one material would need a graph the extractor does not carry).\n // It sits exactly on the dome's rim (see `domeGeometry`) at y = 0.\n const look = GROUND_LOOK[spec.mode];\n const ground = new Mesh(\n groundGeometry(spec.radius),\n new MeshStandardMaterial({\n color: look.color,\n roughness: look.roughness,\n metalness: 0,\n }),\n );\n ground.name = 'environment-ground';\n ground.receiveShadow = true;\n ground.castShadow = false;\n group.add(ground);\n }\n return group;\n}\n\n/**\n * The viewport-only fill for an environment; see `EnvironmentOptions.fill`,\n * which is how a developer asks for it.\n */\nexport function environmentFill(\n spec: Pick<EnvironmentSpec, 'mode' | 'palette'> & { intensity?: number },\n intensity?: number,\n): HemisphereLight {\n const dark = spec.mode !== 'light';\n // The fill stands in for the dome's light, so a dome turned down to a\n // third of its mode's default fills a third as hard.\n const dimmed =\n (spec.intensity ?? ENVIRONMENT_INTENSITY[spec.mode]) /\n ENVIRONMENT_INTENSITY[spec.mode];\n const sky = new Color(dark ? spec.palette[1]! : '#ffffff');\n const ground = new Color(\n dark ? (spec.palette[2] ?? spec.palette[1]!) : '#8a8378',\n );\n const fill = new HemisphereLight(\n sky,\n ground,\n intensity ?? (dark ? 0.9 : 0.6) * dimmed,\n );\n fill.name = 'environment-fill';\n fill.userData.bakery3 = { exclude: true };\n return fill;\n}\n\n/** The spec a `createEnvironment()` group was built with, or `undefined`\n * for anything else. */\nexport function environmentSpecOf(\n object: Object3D,\n): EnvironmentSpec | undefined {\n const preset = object.userData?.bakery3Preset as\n { environment?: EnvironmentSpec } | undefined;\n return preset?.environment;\n}\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\nconst HEX = /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i;\n\nfunction resolve(options: EnvironmentOptions): EnvironmentSpec {\n if (!ENVIRONMENT_SHAPES.includes(options.shape)) {\n throw new Error(\n `createEnvironment: unknown shape \"${String(options.shape)}\". One of: ${ENVIRONMENT_SHAPES.join(', ')}.`,\n );\n }\n if (!ENVIRONMENT_SHADERS.includes(options.shader)) {\n throw new Error(\n `createEnvironment: unknown shader \"${String(options.shader)}\". One of: ${ENVIRONMENT_SHADERS.join(', ')}.`,\n );\n }\n const mode = options.mode ?? 'dark';\n if (!ENVIRONMENT_MODES.includes(mode)) {\n throw new Error(\n `createEnvironment: unknown mode \"${String(mode)}\". One of: ${ENVIRONMENT_MODES.join(', ')}.`,\n );\n }\n const palette = options.palette ?? [\n ...ENVIRONMENT_PALETTES[mode][options.shader],\n ];\n if (\n palette.length < 2 ||\n palette.length > 4 ||\n !palette.every((hex) => HEX.test(hex))\n ) {\n throw new Error(\n 'createEnvironment: `palette` must be two to four CSS hex colors (e.g. [\"#06070d\", \"#33d1ff\"]).',\n );\n }\n const intensity = options.intensity ?? ENVIRONMENT_INTENSITY[mode];\n const radius = options.radius ?? DEFAULT_RADIUS;\n if (!(intensity >= 0) || !(radius > 0)) {\n throw new Error(\n 'createEnvironment: `intensity` must be ≥ 0 and `radius` > 0.',\n );\n }\n return {\n shape: options.shape,\n shader: options.shader,\n mode,\n palette,\n intensity,\n radius,\n seed: options.seed ?? 1,\n time: options.time ?? 0,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Geometry\n// ---------------------------------------------------------------------------\n\n/**\n * Turn a sphere inside out. A dome is seen from within, and three could draw\n * that with `side: BackSide`, but glTF has no back side: the exported sphere\n * would be single-sided with its backfaces invisible to camera rays, so the\n * emission would still light the product while the dome itself rendered\n * black. Flipping the winding makes it an ordinary front-facing mesh from\n * the inside, the same in the viewport and the render.\n */\nfunction invert(geometry: BufferGeometry): BufferGeometry {\n const index = geometry.getIndex();\n if (index) {\n const a = index.array;\n for (let i = 0; i + 2 < a.length; i += 3) {\n const t = a[i + 1]!;\n a[i + 1] = a[i + 2]!;\n a[i + 2] = t;\n }\n index.needsUpdate = true;\n }\n geometry.computeVertexNormals();\n return geometry;\n}\n\nfunction geometryFor(spec: EnvironmentSpec): BufferGeometry {\n const R = spec.radius;\n switch (spec.shape) {\n case 'sphere':\n return invert(new SphereGeometry(R, 96, 48));\n case 'groundSphere':\n return invert(domeGeometry(R));\n case 'floor': {\n // CircleGeometry lies in XY; rotate the vertex data itself so the disc\n // is the XZ plane at y = 0 and `positionLocal` reads floor coordinates.\n const disc = new CircleGeometry(R, 128);\n disc.rotateX(-Math.PI / 2);\n return disc;\n }\n }\n}\n\n/**\n * The dome of a `groundSphere`: the sphere cut off at the ground plane\n * (y = −g·R, g = GROUND_FRACTION), then lifted so that rim sits on y = 0.\n * The ground itself is a separate disc of the rim's radius (`groundGeometry`),\n * so the two meet in a continuous ring and a product at the origin casts its\n * shadow onto real geometry. Being plain vertex data, both trace exactly as\n * they preview.\n */\nfunction domeGeometry(R: number): BufferGeometry {\n const g = GROUND_FRACTION;\n // theta is measured from +y: y = R·cos(theta), so the cut is at acos(−g).\n const sphere = new SphereGeometry(\n R,\n 96,\n 48,\n 0,\n Math.PI * 2,\n 0,\n Math.acos(-g),\n );\n sphere.translate(0, g * R, 0);\n sphere.computeBoundingSphere();\n return sphere;\n}\n\n/** The radius of the ring where the dome meets the ground. */\nexport function groundRadius(R: number): number {\n return R * Math.sqrt(1 - GROUND_FRACTION * GROUND_FRACTION);\n}\n\nfunction groundGeometry(R: number): BufferGeometry {\n const disc = new CircleGeometry(groundRadius(R), 128);\n disc.rotateX(-Math.PI / 2);\n return disc;\n}\n\n// ---------------------------------------------------------------------------\n// Materials\n// ---------------------------------------------------------------------------\n\ntype Expr = ReturnType<typeof float>;\n\n/** Linear working-space color, as a graph constant. */\nconst rgb = (hex: string) => color(new Color(hex));\n\nfunction materialFor(spec: EnvironmentSpec): MeshStandardNodeMaterial {\n const material = new MeshStandardNodeMaterial();\n material.name = `bakery3-environment-${spec.shader}`;\n material.roughness = 1;\n material.metalness = 0;\n // Static emission stays off. The graph is the emission, and the intensity\n // is multiplied into it rather than left on `emissiveIntensity`: three's\n // node material ignores `emissiveIntensity` once `emissiveNode` is set,\n // while the renderer would still apply it, and the two halves would\n // disagree by exactly that factor.\n material.emissive.set(0x000000);\n material.emissiveIntensity = 1;\n\n if (spec.shape === 'floor') {\n floorMaterial(material, spec);\n } else {\n domeMaterial(material, spec);\n }\n return material;\n}\n\n/**\n * A dome. Shaded by elevation `e` (the sine of the angle above the dome's\n * center: −1 at the nadir, 0 at the horizon, 1 at the zenith) and by the\n * direction `n` from the center, for the placed features.\n */\nfunction domeMaterial(\n material: MeshStandardNodeMaterial,\n spec: EnvironmentSpec,\n): void {\n const R = spec.radius;\n const centerY = spec.shape === 'groundSphere' ? GROUND_FRACTION * R : 0;\n const center = vec3(0, centerY, 0);\n const local = positionLocal.sub(center);\n const e = local.y.div(R);\n const n = normalize(local);\n\n let emission = domeShader(spec, e, n);\n\n const base = spec.palette[0]!;\n if (spec.shape === 'groundSphere') {\n // The dome ends at the ground plane; its emission fades to nothing just\n // above the rim so the ring where it meets the ground disc never glows.\n const g = GROUND_FRACTION;\n emission = emission.mul(smoothstep(-g, -g + 0.03, e));\n }\n\n material.colorNode = rgb(base);\n material.emissiveNode = emission.mul(float(spec.intensity));\n // Front side on purpose: see `invert()`.\n material.side = FrontSide;\n}\n\n/**\n * A floor disc. Shaded by `d`, the distance from the origin as a fraction of\n * the radius (0 at the center, 1 at the rim), and by the floor point `p`.\n * The color is albedo (a floor is lit by whatever lights the scene), and it\n * blends into the base tone and fades out towards the rim so the edge never\n * shows. Both halves take the opacity graph: the extractor carries the\n * `opacity` slot and the renderer wires it to the surface's alpha.\n */\nfunction floorMaterial(\n material: MeshStandardNodeMaterial,\n spec: EnvironmentSpec,\n): void {\n const R = spec.radius;\n const p = positionLocal.xz.div(R);\n const d = length(p);\n\n const shaded = floorShader(spec, d, p);\n const base = rgb(spec.palette[0]!);\n material.colorNode = mix(shaded, base, smoothstep(0.55, 0.95, d));\n material.opacityNode = float(1).sub(smoothstep(0.6, 1, d));\n material.transparent = true;\n material.roughness = 0.9;\n}\n\n// ---------------------------------------------------------------------------\n// The four shaders, dome and floor readings of each\n// ---------------------------------------------------------------------------\n\nfunction domeShader(spec: EnvironmentSpec, e: Expr, n: Expr): Expr {\n const palette = spec.palette;\n const random = mulberry32(spec.seed);\n const azimuthPhase = spec.time * 0.1;\n\n switch (spec.shader) {\n case 'gradient':\n // Evenly spaced stops from just below the horizon to the zenith.\n return ramp(palette, e, -0.4, 1);\n\n case 'bands': {\n // Soft horizontal bands from a sine of elevation, one sine per band\n // color, phase-shifted so the colors take turns. Bands below the\n // horizon are dimmed: the light should come from above.\n const bandCount = 4;\n const k = bandCount * Math.PI;\n const phase = spec.time * 0.35;\n const colors = palette.slice(1);\n const weight = mix(float(0.35), float(1), smoothstep(-0.6, 0.1, e));\n let out: Expr = rgb(palette[0]!);\n colors.forEach((hex, j) => {\n const offset = phase + (2 * Math.PI * j) / colors.length;\n const wave = sin(e.mul(k).add(offset)).mul(0.5).add(0.5);\n const band = smoothstep(0.6, 0.95, wave).mul(weight);\n out = out.add(rgb(hex).mul(band));\n });\n return out;\n }\n\n case 'blobs': {\n // A few soft blobs above the horizon, placed by the seed. Each is a\n // falloff of the angle to its direction: exp((n·d − 1)·sharpness).\n const colors = palette.slice(1);\n const count = Math.max(3, colors.length);\n let out: Expr = rgb(palette[0]!);\n for (let j = 0; j < count; j++) {\n const azimuth = random() * Math.PI * 2 + azimuthPhase;\n const elevation = 0.15 + random() * 0.6;\n const sharpness = 7 + random() * 7;\n const strength = 0.8 + random() * 0.5;\n const direction = directionFrom(azimuth, elevation);\n const blob = exp(dot(n, direction).sub(1).mul(sharpness)).mul(strength);\n out = out.add(rgb(colors[j % colors.length]!).mul(blob));\n }\n return out;\n }\n\n case 'horizon': {\n // A dark dome, a thin bright ring just above the horizon, and a soft\n // key glow to one side. The ring is a Gaussian in elevation.\n const ringCenter = 0.02;\n const ringWidth = 0.025;\n const ring = exp(\n e\n .sub(ringCenter)\n .pow(2)\n .mul(-1 / (2 * ringWidth * ringWidth)),\n ).mul(2.5);\n const azimuth = random() * Math.PI * 2 + azimuthPhase;\n const key = directionFrom(azimuth, 0.45);\n const glow = exp(dot(n, key).sub(1).mul(5)).mul(0.9);\n const [dome, ringColor, glowColor = ringColor] = palette as [\n string,\n string,\n string?,\n ];\n return rgb(dome)\n .add(rgb(ringColor).mul(ring))\n .add(rgb(glowColor!).mul(glow));\n }\n }\n}\n\nfunction floorShader(spec: EnvironmentSpec, d: Expr, p: Expr): Expr {\n const palette = spec.palette;\n const random = mulberry32(spec.seed);\n const azimuthPhase = spec.time * 0.1;\n\n switch (spec.shader) {\n case 'gradient':\n // The bright end under the product, the base tone at the rim: the\n // palette read top-down, since the floor has no up.\n return ramp([...palette].reverse(), d, 0, 0.8);\n\n case 'bands': {\n // Concentric rings, fading towards the rim.\n const rings = 3;\n const k = rings * Math.PI * 2;\n const phase = spec.time * 0.35;\n const colors = palette.slice(1);\n const weight = float(1).sub(smoothstep(0.3, 0.8, d));\n let out: Expr = rgb(palette[0]!);\n colors.forEach((hex, j) => {\n const offset = phase + (2 * Math.PI * j) / colors.length;\n const wave = sin(d.mul(k).add(offset)).mul(0.5).add(0.5);\n const band = smoothstep(0.6, 0.95, wave).mul(weight);\n out = out.add(rgb(hex).mul(band));\n });\n return out;\n }\n\n case 'blobs': {\n // Soft patches on the floor around the product: Gaussians of the\n // distance to a seeded point.\n const colors = palette.slice(1);\n const count = Math.max(3, colors.length);\n let out: Expr = rgb(palette[0]!);\n for (let j = 0; j < count; j++) {\n const azimuth = random() * Math.PI * 2 + azimuthPhase;\n const distance = 0.15 + random() * 0.35;\n const sigma = 0.12 + random() * 0.12;\n const strength = 0.8 + random() * 0.5;\n const at = vec2(\n Math.cos(azimuth) * distance,\n Math.sin(azimuth) * distance,\n );\n const blob = exp(\n length(p.sub(at))\n .pow(2)\n .mul(-1 / (2 * sigma * sigma)),\n ).mul(strength);\n out = out.add(rgb(colors[j % colors.length]!).mul(blob));\n }\n return out;\n }\n\n case 'horizon': {\n // A bright ring around the product and a key patch to one side.\n const ringRadius = 0.5;\n const ringWidth = 0.03;\n const ring = exp(\n d\n .sub(ringRadius)\n .pow(2)\n .mul(-1 / (2 * ringWidth * ringWidth)),\n );\n const azimuth = random() * Math.PI * 2 + azimuthPhase;\n const at = vec2(Math.cos(azimuth) * 0.3, Math.sin(azimuth) * 0.3);\n const glow = exp(\n length(p.sub(at))\n .pow(2)\n .mul(-1 / (2 * 0.18 * 0.18)),\n ).mul(0.9);\n const [floor, ringColor, glowColor = ringColor] = palette as [\n string,\n string,\n string?,\n ];\n return rgb(floor)\n .add(rgb(ringColor).mul(ring))\n .add(rgb(glowColor!).mul(glow));\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** A chain of smoothstep mixes through the palette, stops evenly spaced\n * between `from` and `to` along `t`. */\nfunction ramp(\n palette: readonly string[],\n t: Expr,\n from: number,\n to: number,\n): Expr {\n const steps = palette.length - 1;\n const span = (to - from) / steps;\n let out: Expr = rgb(palette[0]!);\n for (let i = 1; i <= steps; i++) {\n const low = from + span * (i - 1);\n const high = from + span * i;\n out = mix(out, rgb(palette[i]!), smoothstep(low, high, t));\n }\n return out;\n}\n\n/** A unit direction from an azimuth (radians, around +y) and an elevation\n * given as the sine of the angle above the horizon. */\nfunction directionFrom(azimuth: number, elevation: number): Expr {\n const horizontal = Math.sqrt(Math.max(0, 1 - elevation * elevation));\n return vec3(\n Math.cos(azimuth) * horizontal,\n elevation,\n Math.sin(azimuth) * horizontal,\n );\n}\n\n/** A small deterministic PRNG: the same seed places the same blobs in the\n * viewport, the manifest and a render months later. */\nfunction mulberry32(seed: number): () => number {\n let a = (Math.floor(seed) || 1) >>> 0;\n return () => {\n a = (a + 0x6d2b79f5) >>> 0;\n let t = a;\n t = Math.imul(t ^ (t >>> 15), t | 1);\n t ^= t + Math.imul(t ^ (t >>> 7), t | 61);\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n };\n}\n"]}
@@ -0,0 +1,111 @@
1
+ import { Group, Camera, Vector3, Object3D } from 'three';
2
+
3
+ /**
4
+ * The implementation behind `@oeave/bakery3/hotspots`. `index.ts` lists what
5
+ * is public; the orb's parts and the brand color are here for the SDK's own
6
+ * pointer handling and the monorepo's tools (`internal.ts`).
7
+ */
8
+
9
+ /** `[x, y, z]`, in meters: the protocol's own vector shape. */
10
+ type Vec3 = [number, number, number];
11
+ type HotspotOptions = {
12
+ /** Stable identity: it names the group and travels with `hotspotSpecOf`. */
13
+ id: string;
14
+ /** A caption for the app to show; the orb itself draws no text. */
15
+ label?: string;
16
+ /** Where the orb is, in the parent's space (world space without a parent). */
17
+ position: Vec3 | Vector3;
18
+ /**
19
+ * A surface point on the model, in the same space as `position`. When given
20
+ * the hotspot is anchored: a thin line is drawn from here to the orb.
21
+ */
22
+ anchor?: Vec3 | Vector3;
23
+ /**
24
+ * Attach to this object so the hotspot follows it. Without one the caller
25
+ * adds `hotspot.object` to the scene themselves.
26
+ */
27
+ parent?: Object3D;
28
+ /** CSS color of the orb, halo and line. Default the brand cyan, `#33d1ff`. */
29
+ color?: string;
30
+ /**
31
+ * How the orb is sized. `screen` (default) keeps it the same size on screen
32
+ * wherever it is: `size` is then a fraction of the viewport height, and
33
+ * `attachHotspots` (or `fit()`) re-fits it every frame from the camera.
34
+ * `world` is a fixed radius in meters, which shrinks with distance.
35
+ */
36
+ sizing?: 'screen' | 'world';
37
+ /** Orb radius: a fraction of viewport height (`screen`, default 0.012) or meters (`world`, default 0.03). */
38
+ size?: number;
39
+ /** Whether the halo breathes. Default true. */
40
+ pulse?: boolean;
41
+ };
42
+ type Hotspot = {
43
+ readonly id: string;
44
+ readonly label: string | undefined;
45
+ /**
46
+ * The group holding everything (the orb, and the tether when anchored),
47
+ * placed at the orb; excluded from capture, named `bakery3-hotspot-<id>`.
48
+ */
49
+ readonly object: Group;
50
+ /** Whether `setSelected(true)` is in effect. */
51
+ readonly selected: boolean;
52
+ /** Whether the cursor is over it; set by `attachHotspots`. */
53
+ readonly hovered: boolean;
54
+ setSelected(on: boolean): void;
55
+ /** Advance the pulse; `seconds` is any monotonic clock. */
56
+ update(seconds: number): void;
57
+ /**
58
+ * Scale the orb so it projects to its `size` for this camera. Called by
59
+ * `attachHotspots.update()`; a host without pointer handling calls it
60
+ * itself once per frame. No-op for `sizing: 'world'`.
61
+ */
62
+ fit(camera: Camera): void;
63
+ /** Detach from its parent and free every geometry and material. */
64
+ dispose(): void;
65
+ };
66
+ /** A hotspot as data, in world space: what to keep with a capture. */
67
+ type HotspotSpec = {
68
+ id: string;
69
+ label?: string;
70
+ position: Vec3;
71
+ anchor?: Vec3;
72
+ };
73
+ declare function createHotspot(options: HotspotOptions): Hotspot;
74
+ /**
75
+ * A hotspot as plain data, in world space, ready for `productState`.
76
+ *
77
+ * Reads through the parents so a hotspot riding on a product that has been
78
+ * moved reports where it actually is, not where it was authored.
79
+ */
80
+ declare function hotspotSpecOf(hotspot: Hotspot): HotspotSpec;
81
+ type AttachHotspotsOptions = {
82
+ hotspots: Hotspot[];
83
+ camera: Camera;
84
+ /** The renderer's canvas, or whatever element receives the pointer. */
85
+ domElement: HTMLElement;
86
+ /** Selection changed: the hotspot now selected, or null when cleared. */
87
+ onSelect?(hotspot: Hotspot | null): void;
88
+ /** The pointer moved onto (or off) a hotspot. */
89
+ onHover?(hotspot: Hotspot | null): void;
90
+ };
91
+ type AttachedHotspots = {
92
+ /** Advance every hotspot's pulse. Call once per frame. */
93
+ update(seconds: number): void;
94
+ /** The hotspot currently selected, if any. */
95
+ readonly selected: Hotspot | null;
96
+ /** Select (or clear) programmatically; fires `onSelect` like a click would. */
97
+ select(hotspot: Hotspot | null): void;
98
+ /** Remove the listeners. Does not dispose the hotspots. */
99
+ dispose(): void;
100
+ };
101
+ /**
102
+ * Wire hover and click for a set of hotspots on one canvas.
103
+ *
104
+ * Raycasts the orbs (core and halo, so the target is a little generous) from
105
+ * the pointer through `camera`; sets `cursor: pointer` over one; toggles
106
+ * selection on click, one at a time. Nothing here is a render loop: call
107
+ * `update()` from yours.
108
+ */
109
+ declare function attachHotspots(options: AttachHotspotsOptions): AttachedHotspots;
110
+
111
+ export { type AttachHotspotsOptions, type AttachedHotspots, type Hotspot, type HotspotOptions, type HotspotSpec, type Vec3, attachHotspots, createHotspot, hotspotSpecOf };