@3driseai/3drise-core 1.0.9 → 1.0.11

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 (52) hide show
  1. package/dist/data/effectsDefaults.d.ts.map +1 -1
  2. package/dist/data/effectsDefaults.js +32 -8
  3. package/dist/shaders/ChladniGridMaterial.d.ts +77 -0
  4. package/dist/shaders/ChladniGridMaterial.d.ts.map +1 -0
  5. package/dist/shaders/ChladniGridMaterial.js +301 -0
  6. package/dist/shaders/FlowNetGridMaterial.d.ts +74 -0
  7. package/dist/shaders/FlowNetGridMaterial.d.ts.map +1 -0
  8. package/dist/shaders/FlowNetGridMaterial.js +269 -0
  9. package/dist/shaders/GirihGridMaterial.d.ts +77 -0
  10. package/dist/shaders/GirihGridMaterial.d.ts.map +1 -0
  11. package/dist/shaders/GirihGridMaterial.js +464 -0
  12. package/dist/shaders/GravWaveGridMaterial.d.ts +77 -0
  13. package/dist/shaders/GravWaveGridMaterial.d.ts.map +1 -0
  14. package/dist/shaders/GravWaveGridMaterial.js +479 -0
  15. package/dist/shaders/HyperbolicGridMaterial.d.ts +74 -0
  16. package/dist/shaders/HyperbolicGridMaterial.d.ts.map +1 -0
  17. package/dist/shaders/HyperbolicGridMaterial.js +298 -0
  18. package/dist/shaders/ReactionDiffusionGridMaterial.d.ts +80 -0
  19. package/dist/shaders/ReactionDiffusionGridMaterial.d.ts.map +1 -0
  20. package/dist/shaders/ReactionDiffusionGridMaterial.js +247 -0
  21. package/dist/shaders/WallpaperGridMaterial.d.ts +134 -0
  22. package/dist/shaders/WallpaperGridMaterial.d.ts.map +1 -0
  23. package/dist/shaders/WallpaperGridMaterial.js +433 -0
  24. package/dist/shaders/index.d.ts +7 -0
  25. package/dist/shaders/index.d.ts.map +1 -1
  26. package/dist/shaders/index.js +7 -0
  27. package/dist/types/gallery.d.ts +21 -0
  28. package/dist/types/gallery.d.ts.map +1 -1
  29. package/dist/types/generativeEffects.d.ts +270 -2
  30. package/dist/types/generativeEffects.d.ts.map +1 -1
  31. package/dist/types/mesh.d.ts +3 -0
  32. package/dist/types/mesh.d.ts.map +1 -1
  33. package/dist/utils/GalleryCreator.d.ts +4 -75
  34. package/dist/utils/GalleryCreator.d.ts.map +1 -1
  35. package/dist/utils/GalleryCreator.js +12 -461
  36. package/dist/utils/LayoutCreator.d.ts +125 -0
  37. package/dist/utils/LayoutCreator.d.ts.map +1 -0
  38. package/dist/utils/LayoutCreator.js +521 -0
  39. package/dist/utils/galleriesUtils.d.ts.map +1 -1
  40. package/dist/utils/galleriesUtils.js +3 -2
  41. package/dist/utils/handleTransformEffects.d.ts.map +1 -1
  42. package/dist/utils/handleTransformEffects.js +5 -3
  43. package/dist/utils/index.d.ts +2 -0
  44. package/dist/utils/index.d.ts.map +1 -1
  45. package/dist/utils/index.js +2 -0
  46. package/dist/utils/modelCache.d.ts +78 -0
  47. package/dist/utils/modelCache.d.ts.map +1 -0
  48. package/dist/utils/modelCache.js +164 -0
  49. package/dist/utils/utils.d.ts +8 -0
  50. package/dist/utils/utils.d.ts.map +1 -1
  51. package/dist/utils/utils.js +17 -3
  52. package/package.json +1 -1
@@ -0,0 +1,521 @@
1
+ import { modeGroupOf } from "./surfaceUtils.js";
2
+ export class LayoutCreator {
3
+ /**
4
+ * Creates a scattered gallery layout with concentric circles
5
+ * Center image + rings of images around it, with side images rotated inward
6
+ */
7
+ static scattered(count, spacing = 3.5) {
8
+ const numImages = count;
9
+ const frames = [];
10
+ const actualCount = numImages;
11
+ if (actualCount === 0)
12
+ return frames;
13
+ // First image at center
14
+ frames.push({
15
+ position: [0, 0, 0],
16
+ rotation: [0, 0, 0],
17
+ });
18
+ let imageIndex = 1;
19
+ let ringNumber = 1;
20
+ // Distribute remaining images across rings
21
+ while (imageIndex < actualCount) {
22
+ const radius = ringNumber * spacing;
23
+ // Each ring can hold more images as radius increases
24
+ const imagesInRing = Math.min(Math.ceil(6 + ringNumber * 2), // 8, 10, 12, 14... images per ring
25
+ actualCount - imageIndex);
26
+ for (let i = 0; i < imagesInRing && imageIndex < actualCount; i++) {
27
+ const angle = (i / imagesInRing) * Math.PI * 2;
28
+ const x = Math.cos(angle) * radius;
29
+ const z = Math.sin(angle) * radius;
30
+ frames.push({
31
+ position: [x, 0, z],
32
+ rotation: [0, 0, 0], // All images face forward for now
33
+ });
34
+ imageIndex++;
35
+ }
36
+ ringNumber++;
37
+ }
38
+ return frames;
39
+ }
40
+ /**
41
+ * Creates a grid/tile layout with frames arranged in rows and columns
42
+ */
43
+ static tiles(count, columns = 3, spacing = 2.5) {
44
+ const frames = [];
45
+ const totalImages = count;
46
+ const rows = Math.ceil(totalImages / columns);
47
+ // Calculate centering offset to position gallery center at [0, 0, 0]
48
+ const totalWidth = (columns - 1) * spacing;
49
+ const totalHeight = (rows - 1) * spacing;
50
+ const offsetX = -totalWidth / 2;
51
+ const offsetY = -totalHeight / 2;
52
+ for (let i = 0; i < totalImages; i++) {
53
+ const row = Math.floor(i / columns);
54
+ const col = i % columns;
55
+ frames.push({
56
+ position: [
57
+ offsetX + col * spacing,
58
+ offsetY + row * spacing,
59
+ 0, // Centered at origin
60
+ ],
61
+ rotation: [0, 0, 0],
62
+ });
63
+ }
64
+ return frames;
65
+ }
66
+ /**
67
+ * Creates a horizontal row layout
68
+ */
69
+ static horizontal(count, spacing = 2.5, startX = 0) {
70
+ const frames = [];
71
+ const totalImages = count;
72
+ // Calculate centering offset to position gallery center at [0, 0, 0]
73
+ const totalWidth = (totalImages - 1) * spacing;
74
+ const offsetX = startX - totalWidth / 2;
75
+ for (let i = 0; i < totalImages; i++) {
76
+ frames.push({
77
+ position: [offsetX + i * spacing, 0, 0],
78
+ rotation: [0, 0, 0],
79
+ });
80
+ }
81
+ return frames;
82
+ }
83
+ /**
84
+ * Creates a vertical column layout
85
+ */
86
+ static vertical(count, spacing = 2.0, startY = 0) {
87
+ const frames = [];
88
+ const totalImages = count;
89
+ // Calculate centering offset to position gallery center at [0, 0, 0]
90
+ const totalHeight = (totalImages - 1) * spacing;
91
+ const offsetY = startY - totalHeight / 2;
92
+ for (let i = 0; i < totalImages; i++) {
93
+ frames.push({
94
+ position: [0, offsetY + i * spacing, 0],
95
+ rotation: [0, 0, 0],
96
+ });
97
+ }
98
+ return frames;
99
+ }
100
+ /**
101
+ * Creates a circular layout with images arranged in a ring
102
+ */
103
+ static circular(count, radius = 5) {
104
+ const frames = [];
105
+ const totalImages = count;
106
+ for (let i = 0; i < totalImages; i++) {
107
+ const angle = (i / totalImages) * Math.PI * 2;
108
+ const x = Math.cos(angle) * radius;
109
+ const z = Math.sin(angle) * radius;
110
+ frames.push({
111
+ position: [x, 0, z],
112
+ rotation: [0, -angle, 0], // Face inward
113
+ });
114
+ }
115
+ return frames;
116
+ }
117
+ /**
118
+ * Creates a spiral layout ascending or expanding outward
119
+ */
120
+ static spiral(count, radius = 5, rotations = 2, heightIncrement = 0.5) {
121
+ const frames = [];
122
+ const totalImages = count;
123
+ for (let i = 0; i < totalImages; i++) {
124
+ const progress = i / totalImages;
125
+ const angle = progress * rotations * Math.PI * 2;
126
+ const currentRadius = radius * (1 - progress * 0.3); // Spiral inward slightly
127
+ const x = Math.cos(angle) * currentRadius;
128
+ const z = Math.sin(angle) * currentRadius;
129
+ const y = progress * totalImages * heightIncrement - (totalImages * heightIncrement) / 2;
130
+ frames.push({
131
+ position: [x, y, z],
132
+ rotation: [0, -angle, 0],
133
+ });
134
+ }
135
+ return frames;
136
+ }
137
+ /**
138
+ * Creates an arc/curved layout
139
+ */
140
+ static arc(count, radius = 5, arcAngle = Math.PI) {
141
+ const frames = [];
142
+ const totalImages = count;
143
+ for (let i = 0; i < totalImages; i++) {
144
+ const progress = i / (totalImages - 1 || 1);
145
+ const angle = -arcAngle / 2 + progress * arcAngle;
146
+ const x = Math.sin(angle) * radius;
147
+ const z = Math.cos(angle) * radius;
148
+ frames.push({
149
+ position: [x, 0, z],
150
+ rotation: [0, -angle, 0],
151
+ });
152
+ }
153
+ return frames;
154
+ }
155
+ /**
156
+ * Creates a pyramid stack layout
157
+ */
158
+ static pyramid(count, layers = 3, spacing = 2.5) {
159
+ const frames = [];
160
+ let imageIndex = 0;
161
+ for (let layer = layers; layer > 0; layer--) {
162
+ const itemsInLayer = layer;
163
+ const layerY = (layers - layer) * spacing - (layers * spacing) / 2;
164
+ const layerWidth = (itemsInLayer - 1) * spacing;
165
+ const offsetX = -layerWidth / 2;
166
+ for (let i = 0; i < itemsInLayer && imageIndex < count; i++) {
167
+ frames.push({
168
+ position: [offsetX + i * spacing, layerY, 0],
169
+ rotation: [0, 0, 0],
170
+ });
171
+ imageIndex++;
172
+ }
173
+ }
174
+ return frames;
175
+ }
176
+ /**
177
+ * Creates a helix (DNA-style spiral)
178
+ */
179
+ static helix(count, radius = 3, rotations = 3, heightIncrement = 0.4) {
180
+ const frames = [];
181
+ const totalImages = count;
182
+ for (let i = 0; i < totalImages; i++) {
183
+ const progress = i / totalImages;
184
+ const angle = progress * rotations * Math.PI * 2;
185
+ const x = Math.cos(angle) * radius;
186
+ const z = Math.sin(angle) * radius;
187
+ const y = progress * totalImages * heightIncrement - (totalImages * heightIncrement) / 2;
188
+ frames.push({
189
+ position: [x, y, z],
190
+ rotation: [0, -angle - Math.PI / 2, 0], // Tangent to helix
191
+ });
192
+ }
193
+ return frames;
194
+ }
195
+ /**
196
+ * Creates a 3D box layout with images on all 6 faces
197
+ */
198
+ static box(count, size = 5) {
199
+ const frames = [];
200
+ const imagesPerFace = Math.ceil(count / 6);
201
+ let imageIndex = 0;
202
+ // Define the 6 faces: Front, Back, Right, Left, Top, Bottom
203
+ const faceConfigs = [
204
+ { name: 'Front', basePos: [0, 0, size], baseRot: [0, 0, 0] },
205
+ { name: 'Back', basePos: [0, 0, -size], baseRot: [0, Math.PI, 0] },
206
+ { name: 'Right', basePos: [size, 0, 0], baseRot: [0, Math.PI / 2, 0] },
207
+ { name: 'Left', basePos: [-size, 0, 0], baseRot: [0, -Math.PI / 2, 0] },
208
+ { name: 'Top', basePos: [0, size, 0], baseRot: [-Math.PI / 2, 0, 0] },
209
+ { name: 'Bottom', basePos: [0, -size, 0], baseRot: [Math.PI / 2, 0, 0] }
210
+ ];
211
+ for (let faceIndex = 0; faceIndex < 6 && imageIndex < count; faceIndex++) {
212
+ const config = faceConfigs[faceIndex];
213
+ const itemsOnFace = Math.min(imagesPerFace, count - imageIndex);
214
+ const cols = Math.ceil(Math.sqrt(itemsOnFace));
215
+ const rows = Math.ceil(itemsOnFace / cols);
216
+ const spacing = (size * 1.8) / Math.max(cols, rows);
217
+ for (let i = 0; i < itemsOnFace; i++) {
218
+ const row = Math.floor(i / cols);
219
+ const col = i % cols;
220
+ const offsetX = (col - (cols - 1) / 2) * spacing;
221
+ const offsetY = (row - (rows - 1) / 2) * spacing;
222
+ // Calculate position based on face orientation
223
+ let position;
224
+ if (faceIndex === 0)
225
+ position = [offsetX, offsetY, size]; // Front
226
+ else if (faceIndex === 1)
227
+ position = [-offsetX, offsetY, -size]; // Back (mirror X)
228
+ else if (faceIndex === 2)
229
+ position = [size, offsetY, -offsetX]; // Right (X fixed, offset in -Z)
230
+ else if (faceIndex === 3)
231
+ position = [-size, offsetY, offsetX]; // Left (X fixed, offset in +Z)
232
+ else if (faceIndex === 4)
233
+ position = [offsetX, size, -offsetY]; // Top (Y fixed, offset in -Z)
234
+ else
235
+ position = [offsetX, -size, offsetY]; // Bottom (Y fixed, offset in +Z)
236
+ frames.push({
237
+ position,
238
+ rotation: config.baseRot,
239
+ });
240
+ imageIndex++;
241
+ }
242
+ }
243
+ return frames;
244
+ }
245
+ /**
246
+ * Creates a sphere layout with images distributed evenly on a sphere surface
247
+ * Uses Fibonacci sphere algorithm for even distribution
248
+ */
249
+ static sphere(count, radius = 5) {
250
+ const frames = [];
251
+ const totalImages = count;
252
+ const goldenRatio = (1 + Math.sqrt(5)) / 2;
253
+ const angleIncrement = Math.PI * 2 * goldenRatio;
254
+ for (let i = 0; i < totalImages; i++) {
255
+ // Fibonacci sphere distribution
256
+ const t = i / totalImages;
257
+ const inclination = Math.acos(1 - 2 * t); // 0 to PI (theta - angle from north pole)
258
+ const azimuth = angleIncrement * i; // phi - angle around equator
259
+ // Convert spherical to Cartesian coordinates
260
+ const x = radius * Math.sin(inclination) * Math.cos(azimuth);
261
+ const y = radius * Math.cos(inclination);
262
+ const z = radius * Math.sin(inclination) * Math.sin(azimuth);
263
+ frames.push({
264
+ position: [x, y, z],
265
+ rotation: [0, 0, 0],
266
+ });
267
+ }
268
+ return frames;
269
+ }
270
+ /**
271
+ * Creates a fan/radial layout
272
+ */
273
+ static fan(count, radius = 5, arcAngle = Math.PI * 1.5) {
274
+ const frames = [];
275
+ const totalImages = count;
276
+ for (let i = 0; i < totalImages; i++) {
277
+ const progress = i / (totalImages - 1 || 1);
278
+ const angle = -arcAngle / 2 + progress * arcAngle;
279
+ const x = Math.sin(angle) * radius;
280
+ const z = Math.cos(angle) * radius;
281
+ frames.push({
282
+ position: [x, 0, z],
283
+ rotation: [0, -angle, 0], // All face center
284
+ });
285
+ }
286
+ return frames;
287
+ }
288
+ /**
289
+ * Creates a wave pattern layout
290
+ */
291
+ static wave(count, amplitude = 2, frequency = 2, spacing = 1.5) {
292
+ const frames = [];
293
+ const totalImages = count;
294
+ const totalWidth = (totalImages - 1) * spacing;
295
+ const offsetX = -totalWidth / 2;
296
+ for (let i = 0; i < totalImages; i++) {
297
+ const x = offsetX + i * spacing;
298
+ const progress = i / totalImages;
299
+ const y = Math.sin(progress * frequency * Math.PI * 2) * amplitude;
300
+ frames.push({
301
+ position: [x, y, 0],
302
+ rotation: [0, 0, 0],
303
+ });
304
+ }
305
+ return frames;
306
+ }
307
+ /**
308
+ * Creates a tunnel/perspective layout
309
+ */
310
+ static tunnel(count, depth = 20, scaleFactor = 0.8) {
311
+ const frames = [];
312
+ const totalImages = count;
313
+ for (let i = 0; i < totalImages; i++) {
314
+ const progress = i / totalImages;
315
+ const z = -progress * depth;
316
+ const scale = Math.pow(scaleFactor, i);
317
+ frames.push({
318
+ position: [0, 0, z],
319
+ rotation: [0, 0, 0],
320
+ scale: [scale, scale, scale],
321
+ });
322
+ }
323
+ return frames;
324
+ }
325
+ /**
326
+ * Creates a tunnel-pyramid layout combining depth perspective with pyramid stacking
327
+ * Starts with 1 frame at front, expands as it recedes. All at y=0
328
+ */
329
+ static tunnelPyramid(count, depth = 20, layers = 6, spacing = 2.5) {
330
+ const frames = [];
331
+ let imageIndex = 0;
332
+ let currentLayer = 1;
333
+ // Continue adding layers until all images are placed
334
+ while (imageIndex < count) {
335
+ const itemsInLayer = currentLayer;
336
+ const layerProgress = (currentLayer - 1) / (layers - 1 || 1);
337
+ const z = -layerProgress * depth;
338
+ const layerWidth = (itemsInLayer - 1) * spacing;
339
+ const offsetX = -layerWidth / 2;
340
+ for (let i = 0; i < itemsInLayer && imageIndex < count; i++) {
341
+ frames.push({
342
+ position: [offsetX + i * spacing, 0, z], // Y always 0
343
+ rotation: [0, 0, 0],
344
+ });
345
+ imageIndex++;
346
+ }
347
+ currentLayer++;
348
+ }
349
+ return frames;
350
+ }
351
+ /**
352
+ * Creates a tunnel-wave layout combining depth perspective with wave pattern
353
+ * Wave pattern in X axis, all at y=0, receding in Z
354
+ */
355
+ static tunnelWave(count, depth = 20, amplitude = 2, frequency = 2, spacing = 1.5) {
356
+ const frames = [];
357
+ const totalImages = count;
358
+ for (let i = 0; i < totalImages; i++) {
359
+ const progress = i / totalImages;
360
+ const x = Math.sin(progress * frequency * Math.PI * 2) * amplitude;
361
+ const z = -progress * depth;
362
+ frames.push({
363
+ position: [x, 0, z], // Y always 0, wave in X
364
+ rotation: [0, 0, 0],
365
+ });
366
+ }
367
+ return frames;
368
+ }
369
+ /**
370
+ * Creates a random/organic layout
371
+ */
372
+ static random(count, bounds = 8, seed = 1) {
373
+ const frames = [];
374
+ const totalImages = count;
375
+ const rand = seededRandom(seed);
376
+ for (let i = 0; i < totalImages; i++) {
377
+ const x = (rand() - 0.5) * bounds * 2;
378
+ const y = (rand() - 0.5) * bounds;
379
+ const z = (rand() - 0.5) * bounds * 2;
380
+ const rotY = rand() * Math.PI * 2;
381
+ frames.push({
382
+ position: [x, y, z],
383
+ rotation: [0, rotY, 0],
384
+ });
385
+ }
386
+ return frames;
387
+ }
388
+ /**
389
+ * Slots for `count` copies in the given layout. Reads only the fields the layout uses
390
+ * (see GALLERY_LAYOUT_CONFIGS); anything missing falls back to that layout's default.
391
+ */
392
+ static generate(count, settings) {
393
+ const n = Math.max(0, Math.floor(count || 0));
394
+ switch (settings.layout) {
395
+ case 'scattered':
396
+ return this.scattered(n, settings.spacing || 3.5);
397
+ case 'tiles':
398
+ return this.tiles(n, settings.columns || 3, settings.spacing || 2.5);
399
+ case 'horizontal':
400
+ return this.horizontal(n, settings.spacing || 2.5, settings.startX || 0);
401
+ case 'vertical':
402
+ return this.vertical(n, settings.spacing || 2.0, settings.startY || 0);
403
+ case 'circular':
404
+ return this.circular(n, settings.radius || 5);
405
+ case 'spiral':
406
+ return this.spiral(n, settings.radius || 5, settings.rotations || 2, settings.heightIncrement || 0.5);
407
+ case 'arc':
408
+ return this.arc(n, settings.radius || 5, settings.arcAngle || Math.PI);
409
+ case 'pyramid':
410
+ return this.pyramid(n, settings.layers || 3, settings.spacing || 2.5);
411
+ case 'helix':
412
+ return this.helix(n, settings.radius || 3, settings.rotations || 3, settings.heightIncrement || 0.4);
413
+ case 'box':
414
+ return this.box(n, settings.size || 5);
415
+ case 'sphere':
416
+ return this.sphere(n, settings.radius || 5);
417
+ case 'fan':
418
+ return this.fan(n, settings.radius || 5, settings.arcAngle || Math.PI * 1.5);
419
+ case 'wave':
420
+ return this.wave(n, settings.amplitude || 2, settings.frequency || 2, settings.spacing || 1.5);
421
+ case 'tunnel':
422
+ return this.tunnel(n, settings.depth || 20, settings.scaleFactor || 0.8);
423
+ case 'tunnelPyramid':
424
+ return this.tunnelPyramid(n, settings.depth || 20, settings.layers || 3, settings.spacing || 2.5);
425
+ case 'tunnelWave':
426
+ return this.tunnelWave(n, settings.depth || 20, settings.amplitude || 2, settings.frequency || 2, settings.spacing || 1.5);
427
+ case 'random':
428
+ return this.random(n, settings.bounds || 8, settings.seed ?? 1);
429
+ default:
430
+ return this.scattered(n);
431
+ }
432
+ }
433
+ }
434
+ export const generateLayoutSlots = (count, settings) => LayoutCreator.generate(count, settings);
435
+ // =============================================================================
436
+ // Object layout (meshSettings.layout) — repeat a scene object in a gallery layout
437
+ // =============================================================================
438
+ /** Object types whose renderer output ObjectNode can repeat across layout slots. */
439
+ export const LAYOUT_CAPABLE_TYPES = new Set(['mesh', 'model', 'effect', 'light']);
440
+ export const DEFAULT_OBJECT_LAYOUT = {
441
+ enabled: false,
442
+ layout: 'circular',
443
+ count: 6,
444
+ seed: 1,
445
+ columns: 3,
446
+ spacing: 3.5,
447
+ startX: 0,
448
+ startY: 0,
449
+ radius: 5,
450
+ size: 5,
451
+ rotations: 2,
452
+ heightIncrement: 0.5,
453
+ arcAngle: Math.PI,
454
+ amplitude: 2,
455
+ frequency: 2,
456
+ layers: 3,
457
+ depth: 20,
458
+ scaleFactor: 0.8,
459
+ bounds: 8,
460
+ };
461
+ /**
462
+ * Saved objects predate this field. Absent means no layout at all — never the default —
463
+ * and a partial block is merged over the defaults so an added knob fills in.
464
+ */
465
+ export const normalizeObjectLayout = (layout) => {
466
+ if (!layout || typeof layout !== 'object')
467
+ return null;
468
+ return { ...DEFAULT_OBJECT_LAYOUT, ...layout };
469
+ };
470
+ /**
471
+ * Per-type ceilings on layout copies. Every copy is a full render of the object, so these
472
+ * track what each copy costs:
473
+ * - light: each one adds per-fragment cost to every lit material, and changing the light
474
+ * count recompiles every material (three.js keys programs on light counts).
475
+ * - effect: each copy is a whole particle / shader effect.
476
+ * - mesh in a points mode: each copy resamples its points (volumeFill raycasts in render).
477
+ * - model: geometry is shared with the cache master, but every part of every copy is its
478
+ * own draw call with its own material clone.
479
+ */
480
+ export const LAYOUT_COUNT_LIMITS = {
481
+ mesh: 100,
482
+ meshPoints: 12,
483
+ model: 30,
484
+ effect: 24,
485
+ light: 8,
486
+ };
487
+ /** Light kinds a layout makes sense for: N directional copies are one brighter light, and
488
+ * an ambient light has no position. */
489
+ export const LAYOUT_LIGHT_KINDS = new Set(['point', 'spot']);
490
+ /** Whether this object can be repeated, and how many times. One answer for the viewer and the panel. */
491
+ export const getObjectLayoutSupport = (obj) => {
492
+ switch (obj.type) {
493
+ case 'mesh':
494
+ return modeGroupOf(obj.modeConfig?.mode) === 'points'
495
+ ? { allowed: true, maxCount: LAYOUT_COUNT_LIMITS.meshPoints }
496
+ : { allowed: true, maxCount: LAYOUT_COUNT_LIMITS.mesh };
497
+ case 'model':
498
+ return { allowed: true, maxCount: LAYOUT_COUNT_LIMITS.model };
499
+ case 'effect':
500
+ return { allowed: true, maxCount: LAYOUT_COUNT_LIMITS.effect };
501
+ case 'light': {
502
+ const kind = obj.config?.type;
503
+ return kind && LAYOUT_LIGHT_KINDS.has(kind)
504
+ ? { allowed: true, maxCount: LAYOUT_COUNT_LIMITS.light }
505
+ : { allowed: false, maxCount: 0, reason: 'Layouts work with point and spot lights only.' };
506
+ }
507
+ default:
508
+ return { allowed: false, maxCount: 0, reason: 'This object type cannot be repeated in a layout.' };
509
+ }
510
+ };
511
+ /** mulberry32 — deterministic, so a random layout keeps its shape across re-renders. */
512
+ function seededRandom(seed) {
513
+ let a = (Math.floor(seed) || 1) >>> 0;
514
+ return () => {
515
+ a = (a + 0x6D2B79F5) >>> 0;
516
+ let t = a;
517
+ t = Math.imul(t ^ (t >>> 15), t | 1);
518
+ t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
519
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
520
+ };
521
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"galleriesUtils.d.ts","sourceRoot":"","sources":["../../src/utils/galleriesUtils.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,qBAAqB,EAAE,KAAK,SAAS,EAAE,KAAK,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAKtH,eAAO,MAAM,gBAAgB,EAAE,MAAM,EAqBpC,CAAC;AACF,eAAO,MAAM,2BAA2B,GAAI,KAAK,MAAM,KAAG,MAAM,EAM/D,CAAC;AAIF,eAAO,MAAM,wBAAwB,GAAI,QAAQ,aAAa,KAAG,qBAuBhE,CAAC;AACF,eAAO,MAAM,2BAA2B,GAAI,iBAAiB,qBAAqB,EAAE,cAAc,OAAO,CAAC,qBAAqB,CAAC,KAAG;IAC3H,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,cAAc,EAAE,qBAAqB,CAAC;CA8B7C,CAAC;AAIF,eAAO,MAAM,mBAAmB,EAAE,aAgEjC,CAAC"}
1
+ {"version":3,"file":"galleriesUtils.d.ts","sourceRoot":"","sources":["../../src/utils/galleriesUtils.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,qBAAqB,EAAE,KAAK,SAAS,EAAE,KAAK,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAKtH,eAAO,MAAM,gBAAgB,EAAE,MAAM,EAqBpC,CAAC;AACF,eAAO,MAAM,2BAA2B,GAAI,KAAK,MAAM,KAAG,MAAM,EAM/D,CAAC;AAIF,eAAO,MAAM,wBAAwB,GAAI,QAAQ,aAAa,KAAG,qBAwBhE,CAAC;AACF,eAAO,MAAM,2BAA2B,GAAI,iBAAiB,qBAAqB,EAAE,cAAc,OAAO,CAAC,qBAAqB,CAAC,KAAG;IAC3H,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,cAAc,EAAE,qBAAqB,CAAC;CA8B7C,CAAC;AAIF,eAAO,MAAM,mBAAmB,EAAE,aAgEjC,CAAC"}
@@ -48,6 +48,7 @@ export const getInitialLayoutSettings = (layout) => {
48
48
  startX: 0,
49
49
  startY: 0,
50
50
  radius: 5,
51
+ size: 5,
51
52
  rotations: 2,
52
53
  heightIncrement: 0.5,
53
54
  arcAngle: Math.PI,
@@ -63,7 +64,6 @@ export const getInitialLayoutSettings = (layout) => {
63
64
  return getLayoutSettings(layout, galleryState);
64
65
  };
65
66
  export const updateCurrentLayoutSettings = (currentSettings, newSettings) => {
66
- console.log('Updating layout settings for gallery:', currentSettings);
67
67
  const galleryState = {
68
68
  geometryType: newSettings?.geometryType ?? currentSettings.geometryType,
69
69
  columns: newSettings?.columns ?? currentSettings.columns ?? 3,
@@ -73,6 +73,8 @@ export const updateCurrentLayoutSettings = (currentSettings, newSettings) => {
73
73
  startX: newSettings?.startX ?? currentSettings.startX ?? 0,
74
74
  startY: newSettings?.startY ?? currentSettings.startY ?? 0,
75
75
  radius: newSettings?.radius ?? currentSettings.radius ?? 5,
76
+ size: newSettings?.size ?? currentSettings.size ?? 5,
77
+ seed: newSettings?.seed ?? currentSettings.seed ?? 1,
76
78
  rotations: newSettings?.rotations ?? currentSettings.rotations ?? 2,
77
79
  heightIncrement: newSettings?.heightIncrement ?? currentSettings.heightIncrement ?? 0.5,
78
80
  arcAngle: newSettings?.arcAngle ?? currentSettings.arcAngle ?? Math.PI,
@@ -89,7 +91,6 @@ export const updateCurrentLayoutSettings = (currentSettings, newSettings) => {
89
91
  const frames = GalleryCreator.generateGallery(currentSettings.imageUrls && currentSettings.imageUrls.length > 0
90
92
  ? currentSettings.imageUrls
91
93
  : createNumberOfDefaultImages(galleryState.numImages ?? 20), galleryState);
92
- const layoutSettings = getLayoutSettings(currentSettings.layout, galleryState);
93
94
  return { frames, layoutSettings: galleryState };
94
95
  };
95
96
  // =============================================================================
@@ -1 +1 @@
1
- {"version":3,"file":"handleTransformEffects.d.ts","sourceRoot":"","sources":["../../src/utils/handleTransformEffects.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAKtC,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACvE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,MAAM,WAAW,yBAAyB;IACtC,MAAM,EAAE,QAAQ,CAAC;IACjB,YAAY,EAAE,GAAG,CAAC;IAClB,YAAY,EAAE,OAAO,CAAC;CACzB;AAMD,eAAO,MAAM,uBAAuB,QAAQ,CAAC;AAS7C;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,GAAG,UAAU,GAAG,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,IAAI,CAmBxN;AAID;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,yBAAyB,EAAE,UAAU,EAAE,gBAAgB,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAoDrJ;AAID;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,iBAAiB,GAAG,iBAAiB,EAAE,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE;IACtJ,OAAO,EAAE,WAAW,GAAG,IAAI,CAAC;CAC/B,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAqBjC"}
1
+ {"version":3,"file":"handleTransformEffects.d.ts","sourceRoot":"","sources":["../../src/utils/handleTransformEffects.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAKtC,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACvE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,MAAM,WAAW,yBAAyB;IACtC,MAAM,EAAE,QAAQ,CAAC;IACjB,YAAY,EAAE,GAAG,CAAC;IAClB,YAAY,EAAE,OAAO,CAAC;CACzB;AAMD,eAAO,MAAM,uBAAuB,QAAQ,CAAC;AAS7C;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,GAAG,UAAU,GAAG,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,IAAI,CAmBxN;AAID;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,yBAAyB,EAAE,UAAU,EAAE,gBAAgB,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAsDrJ;AAID;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,iBAAiB,GAAG,iBAAiB,EAAE,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE;IACtJ,OAAO,EAAE,WAAW,GAAG,IAAI,CAAC;CAC/B,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAqBjC"}
@@ -83,9 +83,11 @@ export function handleTransformAnimations(ctx, animations, currentTime, hovered)
83
83
  const targetsToAnimate = [];
84
84
  if (objectIds && objectIds.length > 0) {
85
85
  // Animate specific children by name
86
- objectIds.forEach((name) => {
87
- const child = object.getObjectByName(name);
88
- if (child) {
86
+ // Every match, not getObjectByName's first: a model repeated by
87
+ // meshSettings.layout has the same part name once per copy.
88
+ const names = new Set(objectIds);
89
+ object.traverse((child) => {
90
+ if (child.name && names.has(child.name)) {
89
91
  targetsToAnimate.push(child);
90
92
  }
91
93
  });
@@ -4,10 +4,12 @@ export * from './frameAnimations';
4
4
  export * from './particleAnimations';
5
5
  export * from './galleriesUtils';
6
6
  export * from './GalleryCreator';
7
+ export * from './LayoutCreator';
7
8
  export * from './GalleryLayouts';
8
9
  export * from './gridUtils';
9
10
  export * from './idUtils';
10
11
  export * from './loadingManager';
12
+ export * from './modelCache';
11
13
  export * from './materialApplicationUtils';
12
14
  export * from './materialUtils';
13
15
  export * from './meshUtils';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,kBAAkB,CAAC;AACjC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,yBAAyB,CAAC;AACxC,cAAc,YAAY,CAAC;AAC3B,cAAc,SAAS,CAAC;AACxB,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,0BAA0B,CAAC;AACzC,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,kBAAkB,CAAC;AACjC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,cAAc,CAAC;AAC7B,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,mBAAmB,CAAC;AAClC,cAAc,sBAAsB,CAAC;AACrC,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,kBAAkB,CAAC;AACjC,cAAc,cAAc,CAAC;AAC7B,cAAc,4BAA4B,CAAC;AAC3C,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,yBAAyB,CAAC;AACxC,cAAc,YAAY,CAAC;AAC3B,cAAc,SAAS,CAAC;AACxB,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,0BAA0B,CAAC;AACzC,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,kBAAkB,CAAC;AACjC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,cAAc,CAAC;AAC7B,cAAc,uBAAuB,CAAC;AACtC,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC"}
@@ -4,10 +4,12 @@ export * from './frameAnimations.js';
4
4
  export * from './particleAnimations.js';
5
5
  export * from './galleriesUtils.js';
6
6
  export * from './GalleryCreator.js';
7
+ export * from './LayoutCreator.js';
7
8
  export * from './GalleryLayouts.js';
8
9
  export * from './gridUtils.js';
9
10
  export * from './idUtils.js';
10
11
  export * from './loadingManager.js';
12
+ export * from './modelCache.js';
11
13
  export * from './materialApplicationUtils.js';
12
14
  export * from './materialUtils.js';
13
15
  export * from './meshUtils.js';