@3driseai/3drise-core 1.0.10 → 1.0.12
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.
- package/dist/data/effectsDefaults.d.ts.map +1 -1
- package/dist/data/effectsDefaults.js +32 -8
- package/dist/data/index.d.ts +1 -0
- package/dist/data/index.d.ts.map +1 -1
- package/dist/data/index.js +1 -0
- package/dist/data/particlesDefaults.d.ts +6 -0
- package/dist/data/particlesDefaults.d.ts.map +1 -0
- package/dist/data/particlesDefaults.js +22 -0
- package/dist/shaders/ChladniGridMaterial.d.ts +77 -0
- package/dist/shaders/ChladniGridMaterial.d.ts.map +1 -0
- package/dist/shaders/ChladniGridMaterial.js +301 -0
- package/dist/shaders/FlowNetGridMaterial.d.ts +74 -0
- package/dist/shaders/FlowNetGridMaterial.d.ts.map +1 -0
- package/dist/shaders/FlowNetGridMaterial.js +269 -0
- package/dist/shaders/GirihGridMaterial.d.ts +77 -0
- package/dist/shaders/GirihGridMaterial.d.ts.map +1 -0
- package/dist/shaders/GirihGridMaterial.js +464 -0
- package/dist/shaders/GravWaveGridMaterial.d.ts +77 -0
- package/dist/shaders/GravWaveGridMaterial.d.ts.map +1 -0
- package/dist/shaders/GravWaveGridMaterial.js +479 -0
- package/dist/shaders/HyperbolicGridMaterial.d.ts +74 -0
- package/dist/shaders/HyperbolicGridMaterial.d.ts.map +1 -0
- package/dist/shaders/HyperbolicGridMaterial.js +298 -0
- package/dist/shaders/ReactionDiffusionGridMaterial.d.ts +80 -0
- package/dist/shaders/ReactionDiffusionGridMaterial.d.ts.map +1 -0
- package/dist/shaders/ReactionDiffusionGridMaterial.js +247 -0
- package/dist/shaders/WallpaperGridMaterial.d.ts +134 -0
- package/dist/shaders/WallpaperGridMaterial.d.ts.map +1 -0
- package/dist/shaders/WallpaperGridMaterial.js +433 -0
- package/dist/shaders/index.d.ts +7 -0
- package/dist/shaders/index.d.ts.map +1 -1
- package/dist/shaders/index.js +7 -0
- package/dist/types/gallery.d.ts +21 -0
- package/dist/types/gallery.d.ts.map +1 -1
- package/dist/types/generativeEffects.d.ts +270 -2
- package/dist/types/generativeEffects.d.ts.map +1 -1
- package/dist/types/generativeParticles.d.ts +253 -0
- package/dist/types/generativeParticles.d.ts.map +1 -0
- package/dist/types/generativeParticles.js +6 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/mesh.d.ts +3 -0
- package/dist/types/mesh.d.ts.map +1 -1
- package/dist/types/objectSettings.d.ts +2 -1
- package/dist/types/objectSettings.d.ts.map +1 -1
- package/dist/utils/GalleryCreator.d.ts +4 -75
- package/dist/utils/GalleryCreator.d.ts.map +1 -1
- package/dist/utils/GalleryCreator.js +12 -461
- package/dist/utils/LayoutCreator.d.ts +125 -0
- package/dist/utils/LayoutCreator.d.ts.map +1 -0
- package/dist/utils/LayoutCreator.js +521 -0
- package/dist/utils/galleriesUtils.d.ts.map +1 -1
- package/dist/utils/galleriesUtils.js +3 -2
- package/dist/utils/handleTransformEffects.d.ts.map +1 -1
- package/dist/utils/handleTransformEffects.js +5 -3
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/package.json +1 -1
|
@@ -1,465 +1,16 @@
|
|
|
1
|
+
import { LayoutCreator } from "./LayoutCreator.js";
|
|
2
|
+
/**
|
|
3
|
+
* Gallery frames: the layout slots from LayoutCreator with one image url stamped on each.
|
|
4
|
+
* The layout math itself lives in LayoutCreator so scene objects can share it.
|
|
5
|
+
*/
|
|
1
6
|
export class GalleryCreator {
|
|
2
|
-
/**
|
|
3
|
-
* Creates a scattered gallery layout with concentric circles
|
|
4
|
-
* Center image + rings of images around it, with side images rotated inward
|
|
5
|
-
*/
|
|
6
|
-
static scattered(imageUrls, spacing = 3.5, count) {
|
|
7
|
-
const numImages = count || imageUrls.length;
|
|
8
|
-
const frames = [];
|
|
9
|
-
const actualCount = Math.min(numImages, imageUrls.length);
|
|
10
|
-
if (actualCount === 0)
|
|
11
|
-
return frames;
|
|
12
|
-
// First image at center
|
|
13
|
-
frames.push({
|
|
14
|
-
url: imageUrls[0],
|
|
15
|
-
position: [0, 0, 0],
|
|
16
|
-
rotation: [0, 0, 0],
|
|
17
|
-
videoSrc: null,
|
|
18
|
-
});
|
|
19
|
-
let imageIndex = 1;
|
|
20
|
-
let ringNumber = 1;
|
|
21
|
-
// Distribute remaining images across rings
|
|
22
|
-
while (imageIndex < actualCount) {
|
|
23
|
-
const radius = ringNumber * spacing;
|
|
24
|
-
// Each ring can hold more images as radius increases
|
|
25
|
-
const imagesInRing = Math.min(Math.ceil(6 + ringNumber * 2), // 8, 10, 12, 14... images per ring
|
|
26
|
-
actualCount - imageIndex);
|
|
27
|
-
for (let i = 0; i < imagesInRing && imageIndex < actualCount; i++) {
|
|
28
|
-
const angle = (i / imagesInRing) * Math.PI * 2;
|
|
29
|
-
const x = Math.cos(angle) * radius;
|
|
30
|
-
const z = Math.sin(angle) * radius;
|
|
31
|
-
frames.push({
|
|
32
|
-
url: imageUrls[imageIndex],
|
|
33
|
-
position: [x, 0, z],
|
|
34
|
-
rotation: [0, 0, 0], // All images face forward for now
|
|
35
|
-
videoSrc: null,
|
|
36
|
-
});
|
|
37
|
-
imageIndex++;
|
|
38
|
-
}
|
|
39
|
-
ringNumber++;
|
|
40
|
-
}
|
|
41
|
-
return frames;
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Creates a grid/tile layout with frames arranged in rows and columns
|
|
45
|
-
*/
|
|
46
|
-
static tiles(imageUrls, columns = 3, spacing = 2.5) {
|
|
47
|
-
const frames = [];
|
|
48
|
-
const totalImages = imageUrls.length;
|
|
49
|
-
const rows = Math.ceil(totalImages / columns);
|
|
50
|
-
// Calculate centering offset to position gallery center at [0, 0, 0]
|
|
51
|
-
const totalWidth = (columns - 1) * spacing;
|
|
52
|
-
const totalHeight = (rows - 1) * spacing;
|
|
53
|
-
const offsetX = -totalWidth / 2;
|
|
54
|
-
const offsetY = -totalHeight / 2;
|
|
55
|
-
for (let i = 0; i < totalImages; i++) {
|
|
56
|
-
const row = Math.floor(i / columns);
|
|
57
|
-
const col = i % columns;
|
|
58
|
-
frames.push({
|
|
59
|
-
url: imageUrls[i],
|
|
60
|
-
position: [
|
|
61
|
-
offsetX + col * spacing,
|
|
62
|
-
offsetY + row * spacing,
|
|
63
|
-
0, // Centered at origin
|
|
64
|
-
],
|
|
65
|
-
rotation: [0, 0, 0],
|
|
66
|
-
videoSrc: null,
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
return frames;
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* Creates a horizontal row layout
|
|
73
|
-
*/
|
|
74
|
-
static horizontal(imageUrls, spacing = 2.5, startX = 0) {
|
|
75
|
-
const frames = [];
|
|
76
|
-
const totalImages = imageUrls.length;
|
|
77
|
-
// Calculate centering offset to position gallery center at [0, 0, 0]
|
|
78
|
-
const totalWidth = (totalImages - 1) * spacing;
|
|
79
|
-
const offsetX = startX - totalWidth / 2;
|
|
80
|
-
for (let i = 0; i < totalImages; i++) {
|
|
81
|
-
frames.push({
|
|
82
|
-
url: imageUrls[i],
|
|
83
|
-
position: [offsetX + i * spacing, 0, 0],
|
|
84
|
-
rotation: [0, 0, 0],
|
|
85
|
-
videoSrc: null,
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
return frames;
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Creates a vertical column layout
|
|
92
|
-
*/
|
|
93
|
-
static vertical(imageUrls, spacing = 2.0, startY = 0) {
|
|
94
|
-
const frames = [];
|
|
95
|
-
const totalImages = imageUrls.length;
|
|
96
|
-
// Calculate centering offset to position gallery center at [0, 0, 0]
|
|
97
|
-
const totalHeight = (totalImages - 1) * spacing;
|
|
98
|
-
const offsetY = startY - totalHeight / 2;
|
|
99
|
-
for (let i = 0; i < totalImages; i++) {
|
|
100
|
-
frames.push({
|
|
101
|
-
url: imageUrls[i],
|
|
102
|
-
position: [0, offsetY + i * spacing, 0],
|
|
103
|
-
rotation: [0, 0, 0],
|
|
104
|
-
videoSrc: null,
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
return frames;
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* Creates a circular layout with images arranged in a ring
|
|
111
|
-
*/
|
|
112
|
-
static circular(imageUrls, radius = 5) {
|
|
113
|
-
const frames = [];
|
|
114
|
-
const totalImages = imageUrls.length;
|
|
115
|
-
for (let i = 0; i < totalImages; i++) {
|
|
116
|
-
const angle = (i / totalImages) * Math.PI * 2;
|
|
117
|
-
const x = Math.cos(angle) * radius;
|
|
118
|
-
const z = Math.sin(angle) * radius;
|
|
119
|
-
frames.push({
|
|
120
|
-
url: imageUrls[i],
|
|
121
|
-
position: [x, 0, z],
|
|
122
|
-
rotation: [0, -angle, 0], // Face inward
|
|
123
|
-
videoSrc: null,
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
return frames;
|
|
127
|
-
}
|
|
128
|
-
/**
|
|
129
|
-
* Creates a spiral layout ascending or expanding outward
|
|
130
|
-
*/
|
|
131
|
-
static spiral(imageUrls, radius = 5, rotations = 2, heightIncrement = 0.5) {
|
|
132
|
-
const frames = [];
|
|
133
|
-
const totalImages = imageUrls.length;
|
|
134
|
-
for (let i = 0; i < totalImages; i++) {
|
|
135
|
-
const progress = i / totalImages;
|
|
136
|
-
const angle = progress * rotations * Math.PI * 2;
|
|
137
|
-
const currentRadius = radius * (1 - progress * 0.3); // Spiral inward slightly
|
|
138
|
-
const x = Math.cos(angle) * currentRadius;
|
|
139
|
-
const z = Math.sin(angle) * currentRadius;
|
|
140
|
-
const y = progress * totalImages * heightIncrement - (totalImages * heightIncrement) / 2;
|
|
141
|
-
frames.push({
|
|
142
|
-
url: imageUrls[i],
|
|
143
|
-
position: [x, y, z],
|
|
144
|
-
rotation: [0, -angle, 0],
|
|
145
|
-
videoSrc: null,
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
return frames;
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* Creates an arc/curved layout
|
|
152
|
-
*/
|
|
153
|
-
static arc(imageUrls, radius = 5, arcAngle = Math.PI) {
|
|
154
|
-
const frames = [];
|
|
155
|
-
const totalImages = imageUrls.length;
|
|
156
|
-
for (let i = 0; i < totalImages; i++) {
|
|
157
|
-
const progress = i / (totalImages - 1 || 1);
|
|
158
|
-
const angle = -arcAngle / 2 + progress * arcAngle;
|
|
159
|
-
const x = Math.sin(angle) * radius;
|
|
160
|
-
const z = Math.cos(angle) * radius;
|
|
161
|
-
frames.push({
|
|
162
|
-
url: imageUrls[i],
|
|
163
|
-
position: [x, 0, z],
|
|
164
|
-
rotation: [0, -angle, 0],
|
|
165
|
-
videoSrc: null,
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
return frames;
|
|
169
|
-
}
|
|
170
|
-
/**
|
|
171
|
-
* Creates a pyramid stack layout
|
|
172
|
-
*/
|
|
173
|
-
static pyramid(imageUrls, layers = 3, spacing = 2.5) {
|
|
174
|
-
const frames = [];
|
|
175
|
-
let imageIndex = 0;
|
|
176
|
-
for (let layer = layers; layer > 0; layer--) {
|
|
177
|
-
const itemsInLayer = layer;
|
|
178
|
-
const layerY = (layers - layer) * spacing - (layers * spacing) / 2;
|
|
179
|
-
const layerWidth = (itemsInLayer - 1) * spacing;
|
|
180
|
-
const offsetX = -layerWidth / 2;
|
|
181
|
-
for (let i = 0; i < itemsInLayer && imageIndex < imageUrls.length; i++) {
|
|
182
|
-
frames.push({
|
|
183
|
-
url: imageUrls[imageIndex],
|
|
184
|
-
position: [offsetX + i * spacing, layerY, 0],
|
|
185
|
-
rotation: [0, 0, 0],
|
|
186
|
-
videoSrc: null,
|
|
187
|
-
});
|
|
188
|
-
imageIndex++;
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
return frames;
|
|
192
|
-
}
|
|
193
|
-
/**
|
|
194
|
-
* Creates a helix (DNA-style spiral)
|
|
195
|
-
*/
|
|
196
|
-
static helix(imageUrls, radius = 3, rotations = 3, heightIncrement = 0.4) {
|
|
197
|
-
const frames = [];
|
|
198
|
-
const totalImages = imageUrls.length;
|
|
199
|
-
for (let i = 0; i < totalImages; i++) {
|
|
200
|
-
const progress = i / totalImages;
|
|
201
|
-
const angle = progress * rotations * Math.PI * 2;
|
|
202
|
-
const x = Math.cos(angle) * radius;
|
|
203
|
-
const z = Math.sin(angle) * radius;
|
|
204
|
-
const y = progress * totalImages * heightIncrement - (totalImages * heightIncrement) / 2;
|
|
205
|
-
frames.push({
|
|
206
|
-
url: imageUrls[i],
|
|
207
|
-
position: [x, y, z],
|
|
208
|
-
rotation: [0, -angle - Math.PI / 2, 0], // Tangent to helix
|
|
209
|
-
videoSrc: null,
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
return frames;
|
|
213
|
-
}
|
|
214
|
-
/**
|
|
215
|
-
* Creates a 3D box layout with images on all 6 faces
|
|
216
|
-
*/
|
|
217
|
-
static box(imageUrls, size = 5) {
|
|
218
|
-
const frames = [];
|
|
219
|
-
const imagesPerFace = Math.ceil(imageUrls.length / 6);
|
|
220
|
-
let imageIndex = 0;
|
|
221
|
-
// Define the 6 faces: Front, Back, Right, Left, Top, Bottom
|
|
222
|
-
const faceConfigs = [
|
|
223
|
-
{ name: 'Front', basePos: [0, 0, size], baseRot: [0, 0, 0] },
|
|
224
|
-
{ name: 'Back', basePos: [0, 0, -size], baseRot: [0, Math.PI, 0] },
|
|
225
|
-
{ name: 'Right', basePos: [size, 0, 0], baseRot: [0, Math.PI / 2, 0] },
|
|
226
|
-
{ name: 'Left', basePos: [-size, 0, 0], baseRot: [0, -Math.PI / 2, 0] },
|
|
227
|
-
{ name: 'Top', basePos: [0, size, 0], baseRot: [-Math.PI / 2, 0, 0] },
|
|
228
|
-
{ name: 'Bottom', basePos: [0, -size, 0], baseRot: [Math.PI / 2, 0, 0] }
|
|
229
|
-
];
|
|
230
|
-
for (let faceIndex = 0; faceIndex < 6 && imageIndex < imageUrls.length; faceIndex++) {
|
|
231
|
-
const config = faceConfigs[faceIndex];
|
|
232
|
-
const itemsOnFace = Math.min(imagesPerFace, imageUrls.length - imageIndex);
|
|
233
|
-
const cols = Math.ceil(Math.sqrt(itemsOnFace));
|
|
234
|
-
const rows = Math.ceil(itemsOnFace / cols);
|
|
235
|
-
const spacing = (size * 1.8) / Math.max(cols, rows);
|
|
236
|
-
for (let i = 0; i < itemsOnFace; i++) {
|
|
237
|
-
const row = Math.floor(i / cols);
|
|
238
|
-
const col = i % cols;
|
|
239
|
-
const offsetX = (col - (cols - 1) / 2) * spacing;
|
|
240
|
-
const offsetY = (row - (rows - 1) / 2) * spacing;
|
|
241
|
-
// Calculate position based on face orientation
|
|
242
|
-
let position;
|
|
243
|
-
if (faceIndex === 0)
|
|
244
|
-
position = [offsetX, offsetY, size]; // Front
|
|
245
|
-
else if (faceIndex === 1)
|
|
246
|
-
position = [-offsetX, offsetY, -size]; // Back (mirror X)
|
|
247
|
-
else if (faceIndex === 2)
|
|
248
|
-
position = [size, offsetY, -offsetX]; // Right (X fixed, offset in -Z)
|
|
249
|
-
else if (faceIndex === 3)
|
|
250
|
-
position = [-size, offsetY, offsetX]; // Left (X fixed, offset in +Z)
|
|
251
|
-
else if (faceIndex === 4)
|
|
252
|
-
position = [offsetX, size, -offsetY]; // Top (Y fixed, offset in -Z)
|
|
253
|
-
else
|
|
254
|
-
position = [offsetX, -size, offsetY]; // Bottom (Y fixed, offset in +Z)
|
|
255
|
-
frames.push({
|
|
256
|
-
url: imageUrls[imageIndex],
|
|
257
|
-
position,
|
|
258
|
-
rotation: config.baseRot,
|
|
259
|
-
videoSrc: null,
|
|
260
|
-
});
|
|
261
|
-
imageIndex++;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
return frames;
|
|
265
|
-
}
|
|
266
|
-
/**
|
|
267
|
-
* Creates a sphere layout with images distributed evenly on a sphere surface
|
|
268
|
-
* Uses Fibonacci sphere algorithm for even distribution
|
|
269
|
-
*/
|
|
270
|
-
static sphere(imageUrls, radius = 5) {
|
|
271
|
-
const frames = [];
|
|
272
|
-
const totalImages = imageUrls.length;
|
|
273
|
-
const goldenRatio = (1 + Math.sqrt(5)) / 2;
|
|
274
|
-
const angleIncrement = Math.PI * 2 * goldenRatio;
|
|
275
|
-
for (let i = 0; i < totalImages; i++) {
|
|
276
|
-
// Fibonacci sphere distribution
|
|
277
|
-
const t = i / totalImages;
|
|
278
|
-
const inclination = Math.acos(1 - 2 * t); // 0 to PI (theta - angle from north pole)
|
|
279
|
-
const azimuth = angleIncrement * i; // phi - angle around equator
|
|
280
|
-
// Convert spherical to Cartesian coordinates
|
|
281
|
-
const x = radius * Math.sin(inclination) * Math.cos(azimuth);
|
|
282
|
-
const y = radius * Math.cos(inclination);
|
|
283
|
-
const z = radius * Math.sin(inclination) * Math.sin(azimuth);
|
|
284
|
-
frames.push({
|
|
285
|
-
url: imageUrls[i],
|
|
286
|
-
position: [x, y, z],
|
|
287
|
-
rotation: [0, 0, 0],
|
|
288
|
-
videoSrc: null,
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
return frames;
|
|
292
|
-
}
|
|
293
|
-
/**
|
|
294
|
-
* Creates a fan/radial layout
|
|
295
|
-
*/
|
|
296
|
-
static fan(imageUrls, radius = 5, arcAngle = Math.PI * 1.5) {
|
|
297
|
-
const frames = [];
|
|
298
|
-
const totalImages = imageUrls.length;
|
|
299
|
-
for (let i = 0; i < totalImages; i++) {
|
|
300
|
-
const progress = i / (totalImages - 1 || 1);
|
|
301
|
-
const angle = -arcAngle / 2 + progress * arcAngle;
|
|
302
|
-
const x = Math.sin(angle) * radius;
|
|
303
|
-
const z = Math.cos(angle) * radius;
|
|
304
|
-
frames.push({
|
|
305
|
-
url: imageUrls[i],
|
|
306
|
-
position: [x, 0, z],
|
|
307
|
-
rotation: [0, -angle, 0], // All face center
|
|
308
|
-
videoSrc: null,
|
|
309
|
-
});
|
|
310
|
-
}
|
|
311
|
-
return frames;
|
|
312
|
-
}
|
|
313
|
-
/**
|
|
314
|
-
* Creates a wave pattern layout
|
|
315
|
-
*/
|
|
316
|
-
static wave(imageUrls, amplitude = 2, frequency = 2, spacing = 1.5) {
|
|
317
|
-
const frames = [];
|
|
318
|
-
const totalImages = imageUrls.length;
|
|
319
|
-
const totalWidth = (totalImages - 1) * spacing;
|
|
320
|
-
const offsetX = -totalWidth / 2;
|
|
321
|
-
for (let i = 0; i < totalImages; i++) {
|
|
322
|
-
const x = offsetX + i * spacing;
|
|
323
|
-
const progress = i / totalImages;
|
|
324
|
-
const y = Math.sin(progress * frequency * Math.PI * 2) * amplitude;
|
|
325
|
-
frames.push({
|
|
326
|
-
url: imageUrls[i],
|
|
327
|
-
position: [x, y, 0],
|
|
328
|
-
rotation: [0, 0, 0],
|
|
329
|
-
videoSrc: null,
|
|
330
|
-
});
|
|
331
|
-
}
|
|
332
|
-
return frames;
|
|
333
|
-
}
|
|
334
|
-
/**
|
|
335
|
-
* Creates a tunnel/perspective layout
|
|
336
|
-
*/
|
|
337
|
-
static tunnel(imageUrls, depth = 20, scaleFactor = 0.8) {
|
|
338
|
-
const frames = [];
|
|
339
|
-
const totalImages = imageUrls.length;
|
|
340
|
-
for (let i = 0; i < totalImages; i++) {
|
|
341
|
-
const progress = i / totalImages;
|
|
342
|
-
const z = -progress * depth;
|
|
343
|
-
const scale = Math.pow(scaleFactor, i);
|
|
344
|
-
frames.push({
|
|
345
|
-
url: imageUrls[i],
|
|
346
|
-
position: [0, 0, z],
|
|
347
|
-
rotation: [0, 0, 0],
|
|
348
|
-
videoSrc: null,
|
|
349
|
-
});
|
|
350
|
-
}
|
|
351
|
-
return frames;
|
|
352
|
-
}
|
|
353
|
-
/**
|
|
354
|
-
* Creates a tunnel-pyramid layout combining depth perspective with pyramid stacking
|
|
355
|
-
* Starts with 1 frame at front, expands as it recedes. All at y=0
|
|
356
|
-
*/
|
|
357
|
-
static tunnelPyramid(imageUrls, depth = 20, layers = 6, spacing = 2.5) {
|
|
358
|
-
const frames = [];
|
|
359
|
-
let imageIndex = 0;
|
|
360
|
-
let currentLayer = 1;
|
|
361
|
-
// Continue adding layers until all images are placed
|
|
362
|
-
while (imageIndex < imageUrls.length) {
|
|
363
|
-
const itemsInLayer = currentLayer;
|
|
364
|
-
const layerProgress = (currentLayer - 1) / (layers - 1 || 1);
|
|
365
|
-
const z = -layerProgress * depth;
|
|
366
|
-
const layerWidth = (itemsInLayer - 1) * spacing;
|
|
367
|
-
const offsetX = -layerWidth / 2;
|
|
368
|
-
for (let i = 0; i < itemsInLayer && imageIndex < imageUrls.length; i++) {
|
|
369
|
-
frames.push({
|
|
370
|
-
url: imageUrls[imageIndex],
|
|
371
|
-
position: [offsetX + i * spacing, 0, z], // Y always 0
|
|
372
|
-
rotation: [0, 0, 0],
|
|
373
|
-
videoSrc: null,
|
|
374
|
-
});
|
|
375
|
-
imageIndex++;
|
|
376
|
-
}
|
|
377
|
-
currentLayer++;
|
|
378
|
-
}
|
|
379
|
-
return frames;
|
|
380
|
-
}
|
|
381
|
-
/**
|
|
382
|
-
* Creates a tunnel-wave layout combining depth perspective with wave pattern
|
|
383
|
-
* Wave pattern in X axis, all at y=0, receding in Z
|
|
384
|
-
*/
|
|
385
|
-
static tunnelWave(imageUrls, depth = 20, amplitude = 2, frequency = 2, spacing = 1.5) {
|
|
386
|
-
const frames = [];
|
|
387
|
-
const totalImages = imageUrls.length;
|
|
388
|
-
for (let i = 0; i < totalImages; i++) {
|
|
389
|
-
const progress = i / totalImages;
|
|
390
|
-
const x = Math.sin(progress * frequency * Math.PI * 2) * amplitude;
|
|
391
|
-
const z = -progress * depth;
|
|
392
|
-
frames.push({
|
|
393
|
-
url: imageUrls[i],
|
|
394
|
-
position: [x, 0, z], // Y always 0, wave in X
|
|
395
|
-
rotation: [0, 0, 0],
|
|
396
|
-
videoSrc: null,
|
|
397
|
-
});
|
|
398
|
-
}
|
|
399
|
-
return frames;
|
|
400
|
-
}
|
|
401
|
-
/**
|
|
402
|
-
* Creates a random/organic layout
|
|
403
|
-
*/
|
|
404
|
-
static random(imageUrls, bounds = 8) {
|
|
405
|
-
const frames = [];
|
|
406
|
-
const totalImages = imageUrls.length;
|
|
407
|
-
for (let i = 0; i < totalImages; i++) {
|
|
408
|
-
const x = (Math.random() - 0.5) * bounds * 2;
|
|
409
|
-
const y = (Math.random() - 0.5) * bounds;
|
|
410
|
-
const z = (Math.random() - 0.5) * bounds * 2;
|
|
411
|
-
const rotY = Math.random() * Math.PI * 2;
|
|
412
|
-
frames.push({
|
|
413
|
-
url: imageUrls[i],
|
|
414
|
-
position: [x, y, z],
|
|
415
|
-
rotation: [0, rotY, 0],
|
|
416
|
-
videoSrc: null,
|
|
417
|
-
});
|
|
418
|
-
}
|
|
419
|
-
return frames;
|
|
420
|
-
}
|
|
421
|
-
/**
|
|
422
|
-
* Main method to generate gallery layout based on settings
|
|
423
|
-
*/
|
|
424
7
|
static generateGallery(imageUrls, settings) {
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
return this.horizontal(imageUrls, settings.spacing || 2.5, settings.startX || 0);
|
|
433
|
-
case 'vertical':
|
|
434
|
-
return this.vertical(imageUrls, settings.spacing || 2.0, settings.startY || 0);
|
|
435
|
-
case 'circular':
|
|
436
|
-
return this.circular(imageUrls, settings.radius || 5);
|
|
437
|
-
case 'spiral':
|
|
438
|
-
return this.spiral(imageUrls, settings.radius || 5, settings.rotations || 2, settings.heightIncrement || 0.5);
|
|
439
|
-
case 'arc':
|
|
440
|
-
return this.arc(imageUrls, settings.radius || 5, settings.arcAngle || Math.PI);
|
|
441
|
-
case 'pyramid':
|
|
442
|
-
return this.pyramid(imageUrls, settings.layers || 3, settings.spacing || 2.5);
|
|
443
|
-
case 'helix':
|
|
444
|
-
return this.helix(imageUrls, settings.radius || 3, settings.rotations || 3, settings.heightIncrement || 0.4);
|
|
445
|
-
case 'box':
|
|
446
|
-
return this.box(imageUrls, settings.size || 5);
|
|
447
|
-
case 'sphere':
|
|
448
|
-
return this.sphere(imageUrls, settings.radius || 5);
|
|
449
|
-
case 'fan':
|
|
450
|
-
return this.fan(imageUrls, settings.radius || 5, settings.arcAngle || Math.PI * 1.5);
|
|
451
|
-
case 'wave':
|
|
452
|
-
return this.wave(imageUrls, settings.amplitude || 2, settings.frequency || 2, settings.spacing || 1.5);
|
|
453
|
-
case 'tunnel':
|
|
454
|
-
return this.tunnel(imageUrls, settings.depth || 20, settings.scaleFactor || 0.8);
|
|
455
|
-
case 'tunnelPyramid':
|
|
456
|
-
return this.tunnelPyramid(imageUrls, settings.depth || 20, settings.layers || 3, settings.spacing || 2.5);
|
|
457
|
-
case 'tunnelWave':
|
|
458
|
-
return this.tunnelWave(imageUrls, settings.depth || 20, settings.amplitude || 2, settings.frequency || 2, settings.spacing || 1.5);
|
|
459
|
-
case 'random':
|
|
460
|
-
return this.random(imageUrls, settings.bounds || 8);
|
|
461
|
-
default:
|
|
462
|
-
return this.scattered(imageUrls);
|
|
463
|
-
}
|
|
8
|
+
return LayoutCreator.generate(imageUrls.length, settings).map((slot, i) => ({
|
|
9
|
+
url: imageUrls[i],
|
|
10
|
+
position: slot.position,
|
|
11
|
+
rotation: slot.rotation ?? [0, 0, 0],
|
|
12
|
+
...(slot.scale ? { scale: slot.scale } : {}),
|
|
13
|
+
videoSrc: null,
|
|
14
|
+
}));
|
|
464
15
|
}
|
|
465
16
|
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import type { GalleryLayout, LayoutParams, LayoutSlot, ObjectLayoutSettings } from "../types/gallery";
|
|
2
|
+
import type { CreatedObjectSettings } from "../types/scene3d";
|
|
3
|
+
export declare class LayoutCreator {
|
|
4
|
+
/**
|
|
5
|
+
* Creates a scattered gallery layout with concentric circles
|
|
6
|
+
* Center image + rings of images around it, with side images rotated inward
|
|
7
|
+
*/
|
|
8
|
+
static scattered(count: number, spacing?: number): LayoutSlot[];
|
|
9
|
+
/**
|
|
10
|
+
* Creates a grid/tile layout with frames arranged in rows and columns
|
|
11
|
+
*/
|
|
12
|
+
static tiles(count: number, columns?: number, spacing?: number): LayoutSlot[];
|
|
13
|
+
/**
|
|
14
|
+
* Creates a horizontal row layout
|
|
15
|
+
*/
|
|
16
|
+
static horizontal(count: number, spacing?: number, startX?: number): LayoutSlot[];
|
|
17
|
+
/**
|
|
18
|
+
* Creates a vertical column layout
|
|
19
|
+
*/
|
|
20
|
+
static vertical(count: number, spacing?: number, startY?: number): LayoutSlot[];
|
|
21
|
+
/**
|
|
22
|
+
* Creates a circular layout with images arranged in a ring
|
|
23
|
+
*/
|
|
24
|
+
static circular(count: number, radius?: number): LayoutSlot[];
|
|
25
|
+
/**
|
|
26
|
+
* Creates a spiral layout ascending or expanding outward
|
|
27
|
+
*/
|
|
28
|
+
static spiral(count: number, radius?: number, rotations?: number, heightIncrement?: number): LayoutSlot[];
|
|
29
|
+
/**
|
|
30
|
+
* Creates an arc/curved layout
|
|
31
|
+
*/
|
|
32
|
+
static arc(count: number, radius?: number, arcAngle?: number): LayoutSlot[];
|
|
33
|
+
/**
|
|
34
|
+
* Creates a pyramid stack layout
|
|
35
|
+
*/
|
|
36
|
+
static pyramid(count: number, layers?: number, spacing?: number): LayoutSlot[];
|
|
37
|
+
/**
|
|
38
|
+
* Creates a helix (DNA-style spiral)
|
|
39
|
+
*/
|
|
40
|
+
static helix(count: number, radius?: number, rotations?: number, heightIncrement?: number): LayoutSlot[];
|
|
41
|
+
/**
|
|
42
|
+
* Creates a 3D box layout with images on all 6 faces
|
|
43
|
+
*/
|
|
44
|
+
static box(count: number, size?: number): LayoutSlot[];
|
|
45
|
+
/**
|
|
46
|
+
* Creates a sphere layout with images distributed evenly on a sphere surface
|
|
47
|
+
* Uses Fibonacci sphere algorithm for even distribution
|
|
48
|
+
*/
|
|
49
|
+
static sphere(count: number, radius?: number): LayoutSlot[];
|
|
50
|
+
/**
|
|
51
|
+
* Creates a fan/radial layout
|
|
52
|
+
*/
|
|
53
|
+
static fan(count: number, radius?: number, arcAngle?: number): LayoutSlot[];
|
|
54
|
+
/**
|
|
55
|
+
* Creates a wave pattern layout
|
|
56
|
+
*/
|
|
57
|
+
static wave(count: number, amplitude?: number, frequency?: number, spacing?: number): LayoutSlot[];
|
|
58
|
+
/**
|
|
59
|
+
* Creates a tunnel/perspective layout
|
|
60
|
+
*/
|
|
61
|
+
static tunnel(count: number, depth?: number, scaleFactor?: number): LayoutSlot[];
|
|
62
|
+
/**
|
|
63
|
+
* Creates a tunnel-pyramid layout combining depth perspective with pyramid stacking
|
|
64
|
+
* Starts with 1 frame at front, expands as it recedes. All at y=0
|
|
65
|
+
*/
|
|
66
|
+
static tunnelPyramid(count: number, depth?: number, layers?: number, spacing?: number): LayoutSlot[];
|
|
67
|
+
/**
|
|
68
|
+
* Creates a tunnel-wave layout combining depth perspective with wave pattern
|
|
69
|
+
* Wave pattern in X axis, all at y=0, receding in Z
|
|
70
|
+
*/
|
|
71
|
+
static tunnelWave(count: number, depth?: number, amplitude?: number, frequency?: number, spacing?: number): LayoutSlot[];
|
|
72
|
+
/**
|
|
73
|
+
* Creates a random/organic layout
|
|
74
|
+
*/
|
|
75
|
+
static random(count: number, bounds?: number, seed?: number): LayoutSlot[];
|
|
76
|
+
/**
|
|
77
|
+
* Slots for `count` copies in the given layout. Reads only the fields the layout uses
|
|
78
|
+
* (see GALLERY_LAYOUT_CONFIGS); anything missing falls back to that layout's default.
|
|
79
|
+
*/
|
|
80
|
+
static generate(count: number, settings: LayoutParams & {
|
|
81
|
+
layout?: GalleryLayout;
|
|
82
|
+
seed?: number;
|
|
83
|
+
}): LayoutSlot[];
|
|
84
|
+
}
|
|
85
|
+
export declare const generateLayoutSlots: (count: number, settings: LayoutParams & {
|
|
86
|
+
layout?: GalleryLayout;
|
|
87
|
+
seed?: number;
|
|
88
|
+
}) => LayoutSlot[];
|
|
89
|
+
/** Object types whose renderer output ObjectNode can repeat across layout slots. */
|
|
90
|
+
export declare const LAYOUT_CAPABLE_TYPES: ReadonlySet<string>;
|
|
91
|
+
export declare const DEFAULT_OBJECT_LAYOUT: ObjectLayoutSettings;
|
|
92
|
+
/**
|
|
93
|
+
* Saved objects predate this field. Absent means no layout at all — never the default —
|
|
94
|
+
* and a partial block is merged over the defaults so an added knob fills in.
|
|
95
|
+
*/
|
|
96
|
+
export declare const normalizeObjectLayout: (layout: Partial<ObjectLayoutSettings> | null | undefined) => ObjectLayoutSettings | null;
|
|
97
|
+
/**
|
|
98
|
+
* Per-type ceilings on layout copies. Every copy is a full render of the object, so these
|
|
99
|
+
* track what each copy costs:
|
|
100
|
+
* - light: each one adds per-fragment cost to every lit material, and changing the light
|
|
101
|
+
* count recompiles every material (three.js keys programs on light counts).
|
|
102
|
+
* - effect: each copy is a whole particle / shader effect.
|
|
103
|
+
* - mesh in a points mode: each copy resamples its points (volumeFill raycasts in render).
|
|
104
|
+
* - model: geometry is shared with the cache master, but every part of every copy is its
|
|
105
|
+
* own draw call with its own material clone.
|
|
106
|
+
*/
|
|
107
|
+
export declare const LAYOUT_COUNT_LIMITS: {
|
|
108
|
+
readonly mesh: 100;
|
|
109
|
+
readonly meshPoints: 12;
|
|
110
|
+
readonly model: 30;
|
|
111
|
+
readonly effect: 24;
|
|
112
|
+
readonly light: 8;
|
|
113
|
+
};
|
|
114
|
+
/** Light kinds a layout makes sense for: N directional copies are one brighter light, and
|
|
115
|
+
* an ambient light has no position. */
|
|
116
|
+
export declare const LAYOUT_LIGHT_KINDS: ReadonlySet<string>;
|
|
117
|
+
export interface ObjectLayoutSupport {
|
|
118
|
+
allowed: boolean;
|
|
119
|
+
/** Why not, in words the panel can show. */
|
|
120
|
+
reason?: string;
|
|
121
|
+
maxCount: number;
|
|
122
|
+
}
|
|
123
|
+
/** Whether this object can be repeated, and how many times. One answer for the viewer and the panel. */
|
|
124
|
+
export declare const getObjectLayoutSupport: (obj: Pick<CreatedObjectSettings, "type" | "config" | "modeConfig">) => ObjectLayoutSupport;
|
|
125
|
+
//# sourceMappingURL=LayoutCreator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LayoutCreator.d.ts","sourceRoot":"","sources":["../../src/utils/LayoutCreator.ts"],"names":[],"mappings":"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"}
|