Keeg42069 commited on
Commit
6ed37a0
·
verified ·
1 Parent(s): e7f7e82

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +772 -19
index.html CHANGED
@@ -1,19 +1,772 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Generative Shape Explorer 3000</title>
7
+ <!-- FontAwesome for Icons -->
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+
10
+ <style>
11
+ :root {
12
+ --bg-color: #0f172a;
13
+ --card-bg: #1e293b;
14
+ --primary: #3b82f6;
15
+ --primary-hover: #2563eb;
16
+ --accent: #8b5cf6;
17
+ --text-main: #f8fafc;
18
+ --text-muted: #94a3b8;
19
+ --border: #334155;
20
+ --shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
21
+ --header-height: 70px;
22
+ }
23
+
24
+ * {
25
+ box-sizing: border-box;
26
+ margin: 0;
27
+ padding: 0;
28
+ }
29
+
30
+ body {
31
+ font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
32
+ background-color: var(--bg-color);
33
+ color: var(--text-main);
34
+ min-height: 100vh;
35
+ display: flex;
36
+ flex-direction: column;
37
+ overflow-x: hidden;
38
+ }
39
+
40
+ /* Header */
41
+ header {
42
+ position: sticky;
43
+ top: 0;
44
+ z-index: 100;
45
+ background: rgba(15, 23, 42, 0.85);
46
+ backdrop-filter: blur(12px);
47
+ border-bottom: 1px solid var(--border);
48
+ height: var(--header-height);
49
+ display: flex;
50
+ align-items: center;
51
+ justify-content: space-between;
52
+ padding: 0 2rem;
53
+ box-shadow: var(--shadow);
54
+ }
55
+
56
+ .brand {
57
+ display: flex;
58
+ align-items: center;
59
+ gap: 10px;
60
+ font-size: 1.25rem;
61
+ font-weight: 700;
62
+ color: var(--text-main);
63
+ }
64
+
65
+ .brand i {
66
+ color: var(--primary);
67
+ }
68
+
69
+ .anycoder-link {
70
+ font-size: 0.9rem;
71
+ color: var(--text-muted);
72
+ text-decoration: none;
73
+ transition: color 0.2s;
74
+ display: flex;
75
+ align-items: center;
76
+ gap: 6px;
77
+ background: rgba(255, 255, 255, 0.05);
78
+ padding: 6px 12px;
79
+ border-radius: 20px;
80
+ border: 1px solid var(--border);
81
+ }
82
+
83
+ .anycoder-link:hover {
84
+ color: var(--primary);
85
+ border-color: var(--primary);
86
+ background: rgba(59, 130, 246, 0.1);
87
+ }
88
+
89
+ /* Controls Section */
90
+ .controls {
91
+ padding: 2rem;
92
+ display: flex;
93
+ flex-wrap: wrap;
94
+ gap: 1rem;
95
+ align-items: center;
96
+ justify-content: center;
97
+ background: linear-gradient(180deg, rgba(15, 23, 42, 1) 0%, rgba(15, 23, 42, 0) 100%);
98
+ }
99
+
100
+ .btn {
101
+ background-color: var(--card-bg);
102
+ color: var(--text-main);
103
+ border: 1px solid var(--border);
104
+ padding: 0.75rem 1.5rem;
105
+ border-radius: 8px;
106
+ cursor: pointer;
107
+ font-weight: 600;
108
+ transition: all 0.2s ease;
109
+ display: flex;
110
+ align-items: center;
111
+ gap: 8px;
112
+ font-size: 0.95rem;
113
+ }
114
+
115
+ .btn:hover {
116
+ background-color: var(--border);
117
+ transform: translateY(-1px);
118
+ }
119
+
120
+ .btn-primary {
121
+ background-color: var(--primary);
122
+ border-color: var(--primary);
123
+ color: white;
124
+ box-shadow: 0 0 15px rgba(59, 130, 246, 0.4);
125
+ }
126
+
127
+ .btn-primary:hover {
128
+ background-color: var(--primary-hover);
129
+ box-shadow: 0 0 20px rgba(59, 130, 246, 0.6);
130
+ }
131
+
132
+ .btn-danger {
133
+ color: #ef4444;
134
+ border-color: rgba(239, 68, 68, 0.3);
135
+ }
136
+
137
+ .btn-danger:hover {
138
+ background-color: rgba(239, 68, 68, 0.1);
139
+ }
140
+
141
+ /* Gallery Grid */
142
+ main {
143
+ flex: 1;
144
+ padding: 1rem 2rem 4rem 2rem;
145
+ max-width: 1600px;
146
+ margin: 0 auto;
147
+ width: 100%;
148
+ }
149
+
150
+ .gallery-stats {
151
+ margin-bottom: 1rem;
152
+ color: var(--text-muted);
153
+ font-size: 0.9rem;
154
+ display: flex;
155
+ justify-content: space-between;
156
+ align-items: center;
157
+ }
158
+
159
+ .grid-container {
160
+ display: grid;
161
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
162
+ gap: 1.5rem;
163
+ width: 100%;
164
+ }
165
+
166
+ .shape-card {
167
+ background-color: var(--card-bg);
168
+ border-radius: 12px;
169
+ overflow: hidden;
170
+ border: 1px solid var(--border);
171
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
172
+ position: relative;
173
+ aspect-ratio: 1;
174
+ display: flex;
175
+ align-items: center;
176
+ justify-content: center;
177
+ }
178
+
179
+ .shape-card:hover {
180
+ transform: translateY(-5px) scale(1.02);
181
+ box-shadow: 0 10px 20px rgba(0, 0, 0, 0.3);
182
+ border-color: var(--primary);
183
+ z-index: 2;
184
+ }
185
+
186
+ .shape-card img {
187
+ width: 100%;
188
+ height: 100%;
189
+ object-fit: contain;
190
+ background: radial-gradient(circle at center, #263345 0%, #1e293b 100%);
191
+ }
192
+
193
+ .shape-meta {
194
+ position: absolute;
195
+ bottom: 0;
196
+ left: 0;
197
+ right: 0;
198
+ background: rgba(15, 23, 42, 0.9);
199
+ padding: 8px;
200
+ font-size: 0.75rem;
201
+ color: var(--text-muted);
202
+ transform: translateY(100%);
203
+ transition: transform 0.2s ease;
204
+ display: flex;
205
+ justify-content: space-between;
206
+ }
207
+
208
+ .shape-card:hover .shape-meta {
209
+ transform: translateY(0);
210
+ }
211
+
212
+ /* Loading Overlay */
213
+ .loader-overlay {
214
+ position: fixed;
215
+ top: 0;
216
+ left: 0;
217
+ width: 100%;
218
+ height: 100%;
219
+ background: rgba(15, 23, 42, 0.8);
220
+ backdrop-filter: blur(5px);
221
+ z-index: 200;
222
+ display: flex;
223
+ flex-direction: column;
224
+ align-items: center;
225
+ justify-content: center;
226
+ opacity: 0;
227
+ pointer-events: none;
228
+ transition: opacity 0.3s ease;
229
+ }
230
+
231
+ .loader-overlay.active {
232
+ opacity: 1;
233
+ pointer-events: all;
234
+ }
235
+
236
+ .spinner {
237
+ width: 50px;
238
+ height: 50px;
239
+ border: 4px solid var(--border);
240
+ border-top: 4px solid var(--primary);
241
+ border-radius: 50%;
242
+ animation: spin 1s linear infinite;
243
+ margin-bottom: 1rem;
244
+ }
245
+
246
+ @keyframes spin {
247
+ 0% { transform: rotate(0deg); }
248
+ 100% { transform: rotate(360deg); }
249
+ }
250
+
251
+ .loading-text {
252
+ font-size: 1.1rem;
253
+ font-weight: 600;
254
+ }
255
+
256
+ /* Toast Notification */
257
+ .toast {
258
+ position: fixed;
259
+ bottom: 2rem;
260
+ right: 2rem;
261
+ background: var(--card-bg);
262
+ border: 1px solid var(--primary);
263
+ padding: 1rem 1.5rem;
264
+ border-radius: 8px;
265
+ color: var(--text-main);
266
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.3);
267
+ transform: translateY(100px);
268
+ opacity: 0;
269
+ transition: all 0.4s cubic-bezier(0.68, -0.55, 0.27, 1.55);
270
+ z-index: 300;
271
+ display: flex;
272
+ align-items: center;
273
+ gap: 12px;
274
+ }
275
+
276
+ .toast.show {
277
+ transform: translateY(0);
278
+ opacity: 1;
279
+ }
280
+
281
+ .toast i {
282
+ color: #10b981;
283
+ }
284
+
285
+ /* Footer */
286
+ footer {
287
+ text-align: center;
288
+ padding: 2rem;
289
+ color: var(--text-muted);
290
+ font-size: 0.85rem;
291
+ border-top: 1px solid var(--border);
292
+ margin-top: auto;
293
+ }
294
+
295
+ /* Responsive */
296
+ @media (max-width: 768px) {
297
+ header {
298
+ padding: 0 1rem;
299
+ }
300
+ .brand span {
301
+ display: none;
302
+ }
303
+ .controls {
304
+ padding: 1rem;
305
+ flex-direction: column;
306
+ align-items: stretch;
307
+ }
308
+ .grid-container {
309
+ grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
310
+ gap: 1rem;
311
+ }
312
+ }
313
+ </style>
314
+ </head>
315
+ <body>
316
+
317
+ <!-- Header -->
318
+ <header>
319
+ <div class="brand">
320
+ <i class="fa-solid fa-shapes"></i>
321
+ <span>ShapeGen 3000</span>
322
+ </div>
323
+ <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="anycoder-link">
324
+ <i class="fa-solid fa-code"></i> Built with anycoder
325
+ </a>
326
+ </header>
327
+
328
+ <!-- Controls -->
329
+ <section class="controls">
330
+ <button class="btn btn-primary" id="generateBtn">
331
+ <i class="fa-solid fa-wand-magic-sparkles"></i> Generate 300 Images
332
+ </button>
333
+ <button class="btn btn-danger" id="clearBtn">
334
+ <i class="fa-solid fa-trash-can"></i> Clear Gallery
335
+ </button>
336
+ </section>
337
+
338
+ <!-- Main Content -->
339
+ <main>
340
+ <div class="gallery-stats">
341
+ <span id="statusText">Ready to generate.</span>
342
+ <span id="countText">Items: 0</span>
343
+ </div>
344
+ <div class="grid-container" id="gallery">
345
+ <!-- Images will be injected here -->
346
+ </div>
347
+ </main>
348
+
349
+ <!-- Footer -->
350
+ <footer>
351
+ <p>Procedural Generation using HTML5 Canvas & JavaScript</p>
352
+ </footer>
353
+
354
+ <!-- Loading Overlay -->
355
+ <div class="loader-overlay" id="loader">
356
+ <div class="spinner"></div>
357
+ <div class="loading-text" id="loadingText">Initializing...</div>
358
+ </div>
359
+
360
+ <!-- Toast Notification -->
361
+ <div class="toast" id="toast">
362
+ <i class="fa-solid fa-check-circle"></i>
363
+ <span id="toastMessage">Operation successful</span>
364
+ </div>
365
+
366
+ <script>
367
+ /**
368
+ * Shape Generator Logic
369
+ * Handles math for 2D and simulated 3D shapes
370
+ */
371
+ const ShapeGenerator = {
372
+ canvas: document.createElement('canvas'),
373
+ ctx: null,
374
+ width: 400,
375
+ height: 400,
376
+
377
+ init() {
378
+ this.canvas.width = this.width;
379
+ this.canvas.height = this.height;
380
+ this.ctx = this.canvas.getContext('2d');
381
+ },
382
+
383
+ // Helper: Random Integer
384
+ randomInt(min, max) {
385
+ return Math.floor(Math.random() * (max - min + 1)) + min;
386
+ },
387
+
388
+ // Helper: Random Color (HSL for vibrancy)
389
+ randomColor() {
390
+ const h = this.randomInt(0, 360);
391
+ const s = this.randomInt(60, 100);
392
+ const l = this.randomInt(40, 70);
393
+ return `hsl(${h}, ${s}%, ${l}%)`;
394
+ },
395
+
396
+ // Helper: 3D Rotation Matrix
397
+ rotate3D(x, y, z, angleX, angleY) {
398
+ // Rotate around Y
399
+ let cosY = Math.cos(angleY);
400
+ let sinY = Math.sin(angleY);
401
+ let x1 = x * cosY - z * sinY;
402
+ let z1 = z * cosY + x * sinY;
403
+
404
+ // Rotate around X
405
+ let cosX = Math.cos(angleX);
406
+ let sinX = Math.sin(angleX);
407
+ let y2 = y * cosX - z1 * sinX;
408
+ let z2 = z1 * cosX + y * sinX;
409
+
410
+ return { x: x1, y: y2, z: z2 };
411
+ },
412
+
413
+ // Helper: Project 3D point to 2D canvas
414
+ project(x, y, z) {
415
+ const scale = 400; // Perspective scale
416
+ const distance = 4; // Camera distance
417
+ const factor = scale / (distance - z);
418
+ return {
419
+ x: x * factor + this.width / 2,
420
+ y: y * factor + this.height / 2
421
+ };
422
+ },
423
+
424
+ clear() {
425
+ this.ctx.clearRect(0, 0, this.width, this.height);
426
+ // Add a subtle background gradient
427
+ const grad = this.ctx.createRadialGradient(200, 200, 50, 200, 200, 300);
428
+ grad.addColorStop(0, '#263345');
429
+ grad.addColorStop(1, '#0f172a');
430
+ this.ctx.fillStyle = grad;
431
+ this.ctx.fillRect(0, 0, this.width, this.height);
432
+ },
433
+
434
+ generateImage(id) {
435
+ this.clear();
436
+ const type = Math.random() > 0.5 ? '3d' : '2d';
437
+ const color = this.randomColor();
438
+ const accentColor = this.randomColor();
439
+
440
+ // Randomize parameters
441
+ const params = {
442
+ angle: Math.random() * Math.PI * 2, // Rotation angle
443
+ depth: Math.random() * 0.5 + 0.5, // Depth scale
444
+ size: Math.random() * 0.5 + 0.5, // Overall size
445
+ lineWidth: Math.random() * 3 + 1,
446
+ filled: Math.random() > 0.3
447
+ };
448
+
449
+ let shapeName = "";
450
+
451
+ if (type === '2d') {
452
+ // 2D Shapes with simulated depth (extrusion)
453
+ const shapes = ['square', 'triangle', 'pentagon', 'hexagon', 'circle', 'star'];
454
+ const shape = shapes[Math.floor(Math.random() * shapes.length)];
455
+ shapeName = shape.toUpperCase();
456
+ this.draw2DShape(shape, color, params);
457
+ } else {
458
+ // 3D Shapes
459
+ const shapes = ['cube', 'pyramid', 'octahedron', 'torus'];
460
+ const shape = shapes[Math.floor(Math.random() * shapes.length)];
461
+ shapeName = shape.toUpperCase();
462
+ this.draw3DShape(shape, color, params);
463
+ }
464
+
465
+ return {
466
+ src: this.canvas.toDataURL('image/png'),
467
+ meta: `${shapeName} | Angle: ${Math.round(params.angle * 57)}° | Depth: ${params.depth.toFixed(2)}`
468
+ };
469
+ },
470
+
471
+ draw2DShape(shape, color, params) {
472
+ const cx = this.width / 2;
473
+ const cy = this.height / 2;
474
+ const size = 100 * params.size;
475
+
476
+ this.ctx.save();
477
+ this.ctx.translate(cx, cy);
478
+ this.ctx.rotate(params.angle);
479
+
480
+ this.ctx.strokeStyle = color;
481
+ this.ctx.fillStyle = color.replace(')', ', 0.2)'); // Transparent fill
482
+ this.ctx.lineWidth = params.lineWidth;
483
+ this.ctx.lineJoin = 'round';
484
+
485
+ // Simulate depth by drawing multiple layers (Extrusion effect)
486
+ const layers = Math.floor(params.depth * 10);
487
+ const offset = 4;
488
+
489
+ this.ctx.beginPath();
490
+ for(let i = 0; i < layers; i++) {
491
+ this.drawPath(shape, size - (i * offset / 2));
492
+ if (params.filled && i === 0) this.ctx.fill();
493
+ if (i < layers - 1) {
494
+ this.ctx.globalAlpha = 1 - (i / layers);
495
+ this.ctx.stroke();
496
+ this.ctx.beginPath();
497
+ }
498
+ }
499
+ this.ctx.globalAlpha = 1;
500
+ this.ctx.stroke(); // Final stroke
501
+
502
+ // Add a "center point" or highlight
503
+ this.ctx.fillStyle = '#fff';
504
+ this.ctx.beginPath();
505
+ this.ctx.arc(0, 0, 3, 0, Math.PI * 2);
506
+ this.ctx.fill();
507
+
508
+ this.ctx.restore();
509
+ },
510
+
511
+ drawPath(shape, size) {
512
+ if (shape === 'circle') {
513
+ this.ctx.arc(0, 0, size, 0, Math.PI * 2);
514
+ } else if (shape === 'square') {
515
+ this.ctx.rect(-size, -size, size * 2, size * 2);
516
+ } else if (shape === 'triangle') {
517
+ this.ctx.moveTo(0, -size);
518
+ this.ctx.lineTo(size, size);
519
+ this.ctx.lineTo(-size, size);
520
+ this.ctx.closePath();
521
+ } else if (shape === 'pentagon' || shape === 'hexagon') {
522
+ const sides = shape === 'pentagon' ? 5 : 6;
523
+ for (let i = 0; i <= sides; i++) {
524
+ const angle = (i * 2 * Math.PI) / sides - Math.PI / 2;
525
+ const x = size * Math.cos(angle);
526
+ const y = size * Math.sin(angle);
527
+ if (i === 0) this.ctx.moveTo(x, y);
528
+ else this.ctx.lineTo(x, y);
529
+ }
530
+ } else if (shape === 'star') {
531
+ const points = 5;
532
+ const outer = size;
533
+ const inner = size / 2;
534
+ for (let i = 0; i < points * 2; i++) {
535
+ const r = (i % 2 === 0) ? outer : inner;
536
+ const angle = (i * Math.PI) / points - Math.PI / 2;
537
+ const x = r * Math.cos(angle);
538
+ const y = r * Math.sin(angle);
539
+ if (i === 0) this.ctx.moveTo(x, y);
540
+ else this.ctx.lineTo(x, y);
541
+ }
542
+ this.ctx.closePath();
543
+ }
544
+ },
545
+
546
+ draw3DShape(shape, color, params) {
547
+ const vertices = [];
548
+ const edges = [];
549
+ const size = 120 * params.size;
550
+
551
+ // Define Wireframes
552
+ if (shape === 'cube') {
553
+ // 8 Vertices
554
+ for(let x=-1; x<=1; x+=2) {
555
+ for(let y=-1; y<=1; y+=2) {
556
+ for(let z=-1; z<=1; z+=2) {
557
+ vertices.push({x: x*size, y: y*size, z: z*size});
558
+ }
559
+ }
560
+ }
561
+ // 12 Edges
562
+ for(let i=0; i<8; i++) {
563
+ for(let j=i+1; j<8; j++) {
564
+ // Connect if differ by exactly 1 coordinate
565
+ const diff = Math.abs(vertices[i].x - vertices[j].x) +
566
+ Math.abs(vertices[i].y - vertices[j].y) +
567
+ Math.abs(vertices[i].z - vertices[j].z);
568
+ if(diff === size*2) edges.push([i, j]);
569
+ }
570
+ }
571
+ } else if (shape === 'pyramid') {
572
+ // 5 Vertices (Square base, point top)
573
+ const h = size * 1.5;
574
+ vertices.push({x:0, y:-h, z:0}); // Tip
575
+ vertices.push({x:-size, y:size, z:-size});
576
+ vertices.push({x:size, y:size, z:-size});
577
+ vertices.push({x:size, y:size, z:size});
578
+ vertices.push({x:-size, y:size, z:size});
579
+ edges.push([0,1], [0,2], [0,3], [0,4], [1,2], [2,3], [3,4], [4,1]);
580
+ } else if (shape === 'octahedron') {
581
+ // 6 Vertices
582
+ vertices.push({x:size, y:0, z:0});
583
+ vertices.push({x:-size, y:0, z:0});
584
+ vertices.push({x:0, y:size, z:0});
585
+ vertices.push({x:0, y:-size, z:0});
586
+ vertices.push({x:0, y:0, z:size});
587
+ vertices.push({x:0, y:0, z:-size});
588
+ // Connect all non-opposites
589
+ for(let i=0; i<6; i++){
590
+ for(let j=i+1; j<6; j++){
591
+ if (i+j !== 1 && i+j !== 5 && i+j !== 9) { // Simple logic to skip exact opposites
592
+ // Better logic: distance check
593
+ const v1 = vertices[i];
594
+ const v2 = vertices[j];
595
+ const dist = Math.sqrt((v1.x-v2.x)**2 + (v1.y-v2.y)**2 + (v1.z-v2.z)**2);
596
+ if (Math.abs(dist - size*1.414) < 1) edges.push([i,j]);
597
+ }
598
+ }
599
+ }
600
+ } else if (shape === 'torus') {
601
+ // Simulated Torus (Loops)
602
+ const rings = 3;
603
+ const segments = 16;
604
+ const R = size;
605
+ const r = size * 0.4;
606
+
607
+ // Draw 3 circles rotated around Y
608
+ for(let ring=0; ring<rings; ring++) {
609
+ const ringAngle = (ring / rings) * Math.PI * 2;
610
+ this.ctx.beginPath();
611
+ for(let i=0; i<=segments; i++) {
612
+ const theta = (i/segments) * Math.PI * 2;
613
+ // Torus parametric equation
614
+ let x = (R + r * Math.cos(theta)) * Math.cos(ringAngle);
615
+ let y = r * Math.sin(theta);
616
+ let z = (R + r * Math.cos(theta)) * Math.sin(ringAngle);
617
+
618
+ // Apply rotation
619
+ const rot = this.rotate3D(x, y, z, params.angle, params.angle * 0.5);
620
+ const proj = this.project(rot.x, rot.y, rot.z);
621
+ if(i===0) this.ctx.moveTo(proj.x, proj.y);
622
+ else this.ctx.lineTo(proj.x, proj.y);
623
+ }
624
+ this.ctx.strokeStyle = color;
625
+ this.ctx.lineWidth = params.lineWidth;
626
+ this.ctx.stroke();
627
+ }
628
+ return; // Torus handles its own drawing
629
+ }
630
+
631
+ // Transform and Draw Vertices/Edges
632
+ this.ctx.strokeStyle = color;
633
+ this.ctx.fillStyle = color;
634
+ this.ctx.lineWidth = params.lineWidth;
635
+ this.ctx.lineCap = 'round';
636
+
637
+ // Draw Edges
638
+ this.ctx.beginPath();
639
+ edges.forEach(edge => {
640
+ const v1 = this.rotate3D(vertices[edge[0]].x, vertices[edge[0]].y, vertices[edge[0]].z, params.angle, params.angle * 0.7);
641
+ const p1 = this.project(v1.x, v1.y, v1.z);
642
+
643
+ const v2 = this.rotate3D(vertices[edge[1]].x, vertices[edge[1]].y, vertices[edge[1]].z, params.angle, params.angle * 0.7);
644
+ const p2 = this.project(v2.x, v2.y, v2.z);
645
+
646
+ this.ctx.moveTo(p1.x, p1.y);
647
+ this.ctx.lineTo(p2.x, p2.y);
648
+ });
649
+ this.ctx.stroke();
650
+
651
+ // Draw Vertices (Dots) for depth perception
652
+ vertices.forEach(v => {
653
+ const rot = this.rotate3D(v.x, v.y, v.z, params.angle, params.angle * 0.7);
654
+ const proj = this.project(rot.x, rot.y, rot.z);
655
+
656
+ // Size based on Z depth (simple fog effect)
657
+ const scale = (rot.z + 300) / 400;
658
+ this.ctx.beginPath();
659
+ this.ctx.arc(proj.x, proj.y, 4 * scale, 0, Math.PI*2);
660
+ this.ctx.fill();
661
+ });
662
+ }
663
+ };
664
+
665
+ /**
666
+ * Application Logic
667
+ */
668
+ const App = {
669
+ totalToGenerate: 300,
670
+ gallery: document.getElementById('gallery'),
671
+ loader: document.getElementById('loader'),
672
+ loadingText: document.getElementById('loadingText'),
673
+ countText: document.getElementById('countText'),
674
+ statusText: document.getElementById('statusText'),
675
+
676
+ init() {
677
+ ShapeGenerator.init();
678
+
679
+ document.getElementById('generateBtn').addEventListener('click', () => this.runGeneration());
680
+ document.getElementById('clearBtn').addEventListener('click', () => this.clearGallery());
681
+ },
682
+
683
+ async runGeneration() {
684
+ this.showLoader(true);
685
+ this.gallery.innerHTML = ''; // Clear previous
686
+
687
+ // Process in batches to keep UI responsive
688
+ const batchSize = 10;
689
+ let generated = 0;
690
+
691
+ while (generated < this.totalToGenerate) {
692
+ const batch = [];
693
+ for (let i = 0; i < batchSize && generated < this.totalToGenerate; i++) {
694
+ batch.push(generated);
695
+ generated++;
696
+ }
697
+
698
+ // Process batch
699
+ await new Promise(resolve => setTimeout(resolve, 0)); // Yield to main thread
700
+
701
+ batch.forEach(id => {
702
+ const imgData = ShapeGenerator.generateImage(id);
703
+ this.addImageToGrid(imgData, id);
704
+ });
705
+
706
+ this.updateLoader(Math.floor((generated / this.totalToGenerate) * 100), generated);
707
+ }
708
+
709
+ this.showLoader(false);
710
+ this.showToast(`Successfully generated ${this.totalToGenerate} unique shapes!`);
711
+ this.statusText.textContent = "Generation complete.";
712
+ },
713
+
714
+ addImageToGrid(data, index) {
715
+ const card = document.createElement('div');
716
+ card.className = 'shape-card';
717
+
718
+ const img = document.createElement('img');
719
+ img.src = data.src;
720
+ img.alt = `Generated shape ${index}`;
721
+
722
+ const meta = document.createElement('div');
723
+ meta.className = 'shape-meta';
724
+ meta.innerHTML = `<span>#${index + 1}</span> <span>${data.meta.split('|')[0]}</span>`;
725
+
726
+ card.appendChild(img);
727
+ card.appendChild(meta);
728
+
729
+ this.gallery.appendChild(card);
730
+ this.countText.textContent = `Items: ${this.gallery.children.length}`;
731
+ },
732
+
733
+ clearGallery() {
734
+ this.gallery.innerHTML = '';
735
+ this.countText.textContent = 'Items: 0';
736
+ this.statusText.textContent = "Gallery cleared.";
737
+ this.showToast("Gallery cleared.");
738
+ },
739
+
740
+ showLoader(show) {
741
+ if (show) {
742
+ this.loader.classList.add('active');
743
+ this.loadingText.textContent = "Generating 300 images...";
744
+ } else {
745
+ this.loader.classList.remove('active');
746
+ }
747
+ },
748
+
749
+ updateLoader(percent, count) {
750
+ this.loadingText.textContent = `Generating... ${percent}% (${count}/${this.totalToGenerate})`;
751
+ },
752
+
753
+ showToast(message) {
754
+ const toast = document.getElementById('toast');
755
+ const msgSpan = document.getElementById('toastMessage');
756
+ msgSpan.textContent = message;
757
+ toast.classList.add('show');
758
+
759
+ setTimeout(() => {
760
+ toast.classList.remove('show');
761
+ }, 3000);
762
+ }
763
+ };
764
+
765
+ // Initialize App
766
+ document.addEventListener('DOMContentLoaded', () => {
767
+ App.init();
768
+ });
769
+
770
+ </script>
771
+ </body>
772
+ </html>