git-hash-art 0.1.0 → 0.3.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.
@@ -0,0 +1,1430 @@
1
+ import $4wRzV$colorscheme from "color-scheme";
2
+
3
+ /**
4
+ * Browser entry point for git-hash-art.
5
+ *
6
+ * This module has zero Node.js dependencies — it works with a standard
7
+ * HTMLCanvasElement or OffscreenCanvas and the native Canvas 2D API.
8
+ */ /**
9
+ * Pure rendering logic — environment-agnostic.
10
+ *
11
+ * Uses only the standard CanvasRenderingContext2D API so it works
12
+ * identically in Node (@napi-rs/canvas) and browsers.
13
+ *
14
+ * Generation pipeline:
15
+ * 1. Background — radial gradient from hash-derived dark palette
16
+ * 2. Composition mode — hash selects: radial, flow-field, spiral, grid-subdivision, or clustered
17
+ * 3. Color field — smooth positional color blending across the canvas
18
+ * 4. Shape layers — weighted selection, focal-point placement, transparency, glow, gradients, jitter
19
+ * 5. Recursive nesting — some shapes contain smaller shapes inside
20
+ * 6. Flow-line pass — bezier curves following a hash-derived vector field
21
+ * 7. Noise texture overlay — subtle grain for organic feel
22
+ * 8. Organic connecting curves — beziers between nearby shapes
23
+ */
24
+ // declare module 'color-scheme';
25
+
26
+
27
+ function $616009579e3d72c5$export$39a95c82b20fdf81(gitHash) {
28
+ return parseInt(gitHash.slice(0, 8), 16);
29
+ }
30
+ function $616009579e3d72c5$export$eaf9227667332084(seed) {
31
+ let s = seed | 0;
32
+ return ()=>{
33
+ s = s + 0x6d2b79f5 | 0;
34
+ let t = Math.imul(s ^ s >>> 15, 1 | s);
35
+ t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
36
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
37
+ };
38
+ }
39
+ function $616009579e3d72c5$export$e9cc707de01b7042(hash, offset = 0) {
40
+ let h = 0;
41
+ for(let i = 0; i < hash.length; i++)h = Math.imul(31, h) + hash.charCodeAt(i) | 0;
42
+ return h + offset | 0;
43
+ }
44
+ function $616009579e3d72c5$export$6b193cae730c547a(hash, index, min, max) {
45
+ const rng = $616009579e3d72c5$export$eaf9227667332084($616009579e3d72c5$export$e9cc707de01b7042(hash, index));
46
+ return min + rng() * (max - min);
47
+ }
48
+ const $616009579e3d72c5$export$bb9e4790bc99ae59 = {
49
+ GOLDEN_RATIO: 1.618034,
50
+ SQUARE_ROOT_2: Math.sqrt(2),
51
+ SQUARE_ROOT_3: Math.sqrt(3),
52
+ SQUARE_ROOT_5: Math.sqrt(5),
53
+ PI: Math.PI,
54
+ PHI: (1 + Math.sqrt(5)) / 2
55
+ };
56
+ class $616009579e3d72c5$export$da2372f11bc66b3f {
57
+ static getProportionalSize(baseSize, proportion) {
58
+ return baseSize * proportion;
59
+ }
60
+ static centerPattern(ctx, width, height) {
61
+ ctx.translate(width / 2, height / 2);
62
+ }
63
+ // Combines sacred geometry patterns with proper proportions
64
+ static layerPatterns(ctx, patterns, config) {
65
+ const { baseSize: baseSize, baseOpacity: baseOpacity = 0.6, opacityReduction: opacityReduction = 0.1, rotationOffset: rotationOffset = 0, proportionType: proportionType = "GOLDEN_RATIO" } = config;
66
+ patterns.forEach((pattern, index)=>{
67
+ const size = this.getProportionalSize(baseSize, Math.pow($616009579e3d72c5$export$bb9e4790bc99ae59[proportionType], index));
68
+ const opacity = Math.max(0.1, baseOpacity - index * opacityReduction);
69
+ const rotation = rotationOffset * index;
70
+ ctx.save();
71
+ ctx.globalAlpha = opacity;
72
+ ctx.rotate(rotation * Math.PI / 180);
73
+ shapes[pattern.type](ctx, size, pattern.config);
74
+ ctx.restore();
75
+ });
76
+ }
77
+ }
78
+
79
+
80
+ function $b5a262d09b87e373$export$f116a24fd288e742(gitHash) {
81
+ const seed = (0, $616009579e3d72c5$export$39a95c82b20fdf81)(gitHash);
82
+ const scheme = new (0, $4wRzV$colorscheme)();
83
+ scheme.from_hue(seed % 360).scheme("analogic").variation("soft");
84
+ let colors = scheme.colors().map((hex)=>`#${hex}`);
85
+ const contrastingHue = (seed + 180) % 360;
86
+ const contrastingScheme = new (0, $4wRzV$colorscheme)();
87
+ contrastingScheme.from_hue(contrastingHue).scheme("mono").variation("soft");
88
+ colors.push(`#${contrastingScheme.colors()[0]}`);
89
+ return colors;
90
+ }
91
+ class $b5a262d09b87e373$export$ab958c550f521376 {
92
+ constructor(gitHash){
93
+ this.seed = (0, $616009579e3d72c5$export$39a95c82b20fdf81)(gitHash);
94
+ this.baseScheme = this.generateBaseScheme();
95
+ this.complementaryScheme = this.generateComplementaryScheme();
96
+ this.triadicScheme = this.generateTriadicScheme();
97
+ this.metallic = this.generateMetallicColors();
98
+ }
99
+ generateBaseScheme() {
100
+ const scheme = new (0, $4wRzV$colorscheme)();
101
+ return scheme.from_hue(this.seed % 360).scheme("analogic").variation("soft").colors().map((hex)=>`#${hex}`);
102
+ }
103
+ generateComplementaryScheme() {
104
+ const complementaryHue = (this.seed + 180) % 360;
105
+ const scheme = new (0, $4wRzV$colorscheme)();
106
+ return scheme.from_hue(complementaryHue).scheme("mono").variation("soft").colors().map((hex)=>`#${hex}`);
107
+ }
108
+ generateTriadicScheme() {
109
+ const triadicHue = (this.seed + 120) % 360;
110
+ const scheme = new (0, $4wRzV$colorscheme)();
111
+ return scheme.from_hue(triadicHue).scheme("triade").variation("soft").colors().map((hex)=>`#${hex}`);
112
+ }
113
+ generateMetallicColors() {
114
+ return {
115
+ gold: "#FFD700",
116
+ silver: "#C0C0C0",
117
+ copper: "#B87333",
118
+ bronze: "#CD7F32"
119
+ };
120
+ }
121
+ /**
122
+ * Returns a flat array of hash-derived colors suitable for art generation.
123
+ * Combines base analogic, complementary, and triadic schemes for variety
124
+ * while maintaining color harmony.
125
+ */ getColors() {
126
+ // Deduplicate and return a rich palette
127
+ const all = [
128
+ ...this.baseScheme.slice(0, 4),
129
+ ...this.complementaryScheme.slice(0, 2),
130
+ ...this.triadicScheme.slice(0, 2)
131
+ ];
132
+ return [
133
+ ...new Set(all)
134
+ ];
135
+ }
136
+ /**
137
+ * Returns two background colors derived from the hash — darker variants
138
+ * of the base scheme for gradient backgrounds.
139
+ */ getBackgroundColors() {
140
+ return [
141
+ this.darken(this.baseScheme[0], 0.65),
142
+ this.darken(this.baseScheme[1], 0.55)
143
+ ];
144
+ }
145
+ /**
146
+ * Simple hex color darkening by a factor (0 = black, 1 = unchanged).
147
+ */ darken(hex, factor) {
148
+ const c = hex.replace("#", "");
149
+ const r = Math.round(parseInt(c.substring(0, 2), 16) * factor);
150
+ const g = Math.round(parseInt(c.substring(2, 4), 16) * factor);
151
+ const b = Math.round(parseInt(c.substring(4, 6), 16) * factor);
152
+ return `#${r.toString(16).padStart(2, "0")}${g.toString(16).padStart(2, "0")}${b.toString(16).padStart(2, "0")}`;
153
+ }
154
+ }
155
+ // ── Standalone color utilities ──────────────────────────────────────
156
+ /** Parse a hex color (#RRGGBB) into [r, g, b] 0-255. */ function $b5a262d09b87e373$var$hexToRgb(hex) {
157
+ const c = hex.replace("#", "");
158
+ return [
159
+ parseInt(c.substring(0, 2), 16),
160
+ parseInt(c.substring(2, 4), 16),
161
+ parseInt(c.substring(4, 6), 16)
162
+ ];
163
+ }
164
+ /** Format [r, g, b] back to #RRGGBB. */ function $b5a262d09b87e373$var$rgbToHex(r, g, b) {
165
+ const clamp = (v)=>Math.max(0, Math.min(255, Math.round(v)));
166
+ return `#${clamp(r).toString(16).padStart(2, "0")}${clamp(g).toString(16).padStart(2, "0")}${clamp(b).toString(16).padStart(2, "0")}`;
167
+ }
168
+ function $b5a262d09b87e373$export$f2121afcad3d553f(hex, alpha) {
169
+ const [r, g, b] = $b5a262d09b87e373$var$hexToRgb(hex);
170
+ return `rgba(${r},${g},${b},${alpha.toFixed(3)})`;
171
+ }
172
+ function $b5a262d09b87e373$export$59539d800dbe6858(hex, rng, amount = 0.1) {
173
+ const [r, g, b] = $b5a262d09b87e373$var$hexToRgb(hex);
174
+ const jit = ()=>(rng() - 0.5) * 2 * amount * 255;
175
+ return $b5a262d09b87e373$var$rgbToHex(r + jit(), g + jit(), b + jit());
176
+ }
177
+
178
+
179
+
180
+ const $44f5b87a40c9680b$export$8daab6f91f7ff730 = (ctx, size)=>{
181
+ ctx.beginPath();
182
+ ctx.arc(0, 0, size / 2, 0, Math.PI * 2);
183
+ };
184
+ const $44f5b87a40c9680b$export$9340b6a85ea854b9 = (ctx, size)=>{
185
+ ctx.beginPath();
186
+ ctx.rect(-size / 2, -size / 2, size, size);
187
+ };
188
+ const $44f5b87a40c9680b$export$e6b70c7883316010 = (ctx, size)=>{
189
+ ctx.beginPath();
190
+ ctx.moveTo(0, -size / 2);
191
+ ctx.lineTo(-size / 2, size / 2);
192
+ ctx.lineTo(size / 2, size / 2);
193
+ ctx.closePath();
194
+ };
195
+ const $44f5b87a40c9680b$export$252d2895fb67397b = (ctx, size)=>{
196
+ ctx.beginPath();
197
+ for(let i = 0; i < 6; i++){
198
+ const angle = Math.PI * 2 / 6 * i;
199
+ const x = size / 2 * Math.cos(angle);
200
+ const y = size / 2 * Math.sin(angle);
201
+ if (i === 0) ctx.moveTo(x, y);
202
+ else ctx.lineTo(x, y);
203
+ }
204
+ ctx.closePath();
205
+ };
206
+ const $44f5b87a40c9680b$export$ca57b923902a3c0 = (ctx, size)=>{
207
+ ctx.beginPath();
208
+ for(let i = 0; i < 10; i++){
209
+ const angle = Math.PI / 5 + Math.PI / 5 * i * 3;
210
+ const radius = i % 2 === 0 ? size / 2 : size / 4;
211
+ const x = radius * Math.cos(angle);
212
+ const y = radius * Math.sin(angle);
213
+ if (i === 0) ctx.moveTo(x, y);
214
+ else ctx.lineTo(x, y);
215
+ }
216
+ ctx.closePath();
217
+ };
218
+ const $44f5b87a40c9680b$export$cf8f2f05ac4f561f = (ctx, size)=>{
219
+ ctx.beginPath();
220
+ for(let i = 0; i < 10; i++){
221
+ const angle = Math.PI / 30 + Math.PI / 30 * i * 8;
222
+ const radius = i % 2 === 0 ? size / 2 : size / 8;
223
+ const x = radius * Math.cos(angle);
224
+ const y = radius * Math.sin(angle);
225
+ if (i === 0) ctx.moveTo(x, y);
226
+ else ctx.lineTo(x, y);
227
+ }
228
+ ctx.closePath();
229
+ };
230
+ const $44f5b87a40c9680b$export$1ed0c0bc398b6246 = (ctx, size)=>{
231
+ ctx.beginPath();
232
+ ctx.moveTo(0, size / 4);
233
+ ctx.quadraticCurveTo(size / 2, size / 4, 0, -size / 4);
234
+ ctx.quadraticCurveTo(-size / 2, size / 4, 0, size / 4);
235
+ };
236
+ const $44f5b87a40c9680b$export$57ec12daf141ce6 = (ctx, size)=>{
237
+ ctx.beginPath();
238
+ ctx.moveTo(0, -size / 2);
239
+ ctx.lineTo(size / 2, 0);
240
+ ctx.lineTo(0, size / 2);
241
+ ctx.lineTo(-size / 2, 0);
242
+ ctx.closePath();
243
+ };
244
+ const $44f5b87a40c9680b$export$39f25a4fc5491539 = (ctx, size)=>{
245
+ ctx.beginPath();
246
+ ctx.moveTo(-size / 2, -size / 2);
247
+ ctx.lineTo(size / 2, -size / 2);
248
+ ctx.lineTo(size / 2, size / 2);
249
+ ctx.lineTo(-size / 2, size / 2);
250
+ ctx.closePath();
251
+ };
252
+ const $44f5b87a40c9680b$export$492753207a5258e1 = {
253
+ circle: $44f5b87a40c9680b$export$8daab6f91f7ff730,
254
+ square: $44f5b87a40c9680b$export$9340b6a85ea854b9,
255
+ triangle: $44f5b87a40c9680b$export$e6b70c7883316010,
256
+ hexagon: $44f5b87a40c9680b$export$252d2895fb67397b,
257
+ star: $44f5b87a40c9680b$export$ca57b923902a3c0,
258
+ "jacked-star": $44f5b87a40c9680b$export$cf8f2f05ac4f561f,
259
+ heart: $44f5b87a40c9680b$export$1ed0c0bc398b6246,
260
+ diamond: $44f5b87a40c9680b$export$57ec12daf141ce6,
261
+ cube: $44f5b87a40c9680b$export$39f25a4fc5491539
262
+ };
263
+
264
+
265
+ // Define interfaces for our configurations
266
+ const $e0ea14d2e2f06463$export$9265403940be6b4 = {
267
+ // Standard sizes with different hashes
268
+ react: {
269
+ hash: "46192e59d42f741c761cbea79462a8b3815dd905",
270
+ width: 1024,
271
+ height: 1024
272
+ },
273
+ angular: {
274
+ hash: "f31a6c3e94420f43c0cd323a5a6a99376ee59ff8",
275
+ width: 1024,
276
+ height: 1024,
277
+ gridSize: 6
278
+ },
279
+ // Wide format variations
280
+ banner: {
281
+ hash: "d847ffd4269b22c54d6e85ad3c1892a298e961fb",
282
+ width: 1920,
283
+ height: 480,
284
+ gridSize: 8,
285
+ shapesPerLayer: 40
286
+ },
287
+ ultrawide: {
288
+ hash: "a3e126e537ed2cd11ddf3a96c37066e97c7afee6",
289
+ width: 3440,
290
+ height: 1440,
291
+ gridSize: 12,
292
+ shapesPerLayer: 60
293
+ },
294
+ // Social media sizes
295
+ "instagram-square": {
296
+ hash: "ff00ff00ff00ff00ff00ff00ff00ff00ff00ff0",
297
+ width: 1080,
298
+ height: 1080
299
+ },
300
+ "instagram-story": {
301
+ hash: "abc123def456abc123def456abc123def456abc1",
302
+ width: 1080,
303
+ height: 1920,
304
+ gridSize: 6,
305
+ layers: 6
306
+ },
307
+ "twitter-header": {
308
+ hash: "7777777777777777777777777777777777777777",
309
+ width: 1500,
310
+ height: 500,
311
+ gridSize: 8,
312
+ shapesPerLayer: 35
313
+ },
314
+ "linkedin-banner": {
315
+ hash: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
316
+ width: 1584,
317
+ height: 396,
318
+ gridSize: 8,
319
+ shapesPerLayer: 35
320
+ },
321
+ // Mobile sizes
322
+ "phone-wallpaper": {
323
+ hash: "ffffffffffffffffffffffffffffffffaaaaaaaa",
324
+ width: 1170,
325
+ height: 2532,
326
+ gridSize: 5,
327
+ layers: 6
328
+ },
329
+ "tablet-wallpaper": {
330
+ hash: "123456789abcdef0123456789abcdef012345678",
331
+ width: 2048,
332
+ height: 2732,
333
+ gridSize: 7,
334
+ layers: 6
335
+ },
336
+ // Special configurations
337
+ minimal: {
338
+ hash: "000000000000000000000000000000000fffffff",
339
+ width: 1024,
340
+ height: 1024,
341
+ layers: 3,
342
+ baseOpacity: 0.8,
343
+ shapesPerLayer: 15
344
+ },
345
+ complex: {
346
+ hash: "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef",
347
+ width: 2048,
348
+ height: 2048,
349
+ gridSize: 8,
350
+ layers: 7,
351
+ shapesPerLayer: 50,
352
+ minShapeSize: 30,
353
+ maxShapeSize: 250
354
+ }
355
+ };
356
+ const $e0ea14d2e2f06463$export$3572305709b6b48c = {
357
+ strokeStyle: "#000000",
358
+ fillStyle: "transparent",
359
+ lineWidth: 1,
360
+ rotation: 0,
361
+ iterations: 1,
362
+ animate: false
363
+ };
364
+ const $e0ea14d2e2f06463$export$a4ca1369b6d2c19e = {
365
+ BASIC: "basic",
366
+ DETAILED: "detailed",
367
+ ANIMATED: "animated"
368
+ };
369
+ const $e0ea14d2e2f06463$export$bb9e4790bc99ae59 = {
370
+ GOLDEN_RATIO: 1.618034,
371
+ SQUARE_ROOT_2: Math.sqrt(2),
372
+ SQUARE_ROOT_3: Math.sqrt(3),
373
+ SQUARE_ROOT_5: Math.sqrt(5),
374
+ PI: Math.PI,
375
+ PHI: (1 + Math.sqrt(5)) / 2
376
+ };
377
+ const $e0ea14d2e2f06463$export$c36defeab44ba3b3 = {
378
+ flowerOfLifeMandala: (size)=>[
379
+ // { type: "flowerOfLife", config: { size } },
380
+ {
381
+ type: "merkaba",
382
+ config: {
383
+ size: size * 0.8
384
+ }
385
+ },
386
+ {
387
+ type: "sriYantra",
388
+ config: {
389
+ size: size * 0.5
390
+ }
391
+ }
392
+ ],
393
+ platonicProgression: (size)=>[
394
+ {
395
+ type: "platonicSolid",
396
+ config: {
397
+ size: size,
398
+ type: "tetrahedron"
399
+ }
400
+ },
401
+ {
402
+ type: "platonicSolid",
403
+ config: {
404
+ size: size * 0.8,
405
+ type: "cube"
406
+ }
407
+ },
408
+ {
409
+ type: "platonicSolid",
410
+ config: {
411
+ size: size * 0.6,
412
+ type: "octahedron"
413
+ }
414
+ }
415
+ ],
416
+ cosmicTree: (size)=>[
417
+ // { type: "treeOfLife", config: { size } },
418
+ {
419
+ type: "fibonacciSpiral",
420
+ config: {
421
+ size: size * 0.9
422
+ }
423
+ },
424
+ {
425
+ type: "metatronsCube",
426
+ config: {
427
+ size: size * 0.7
428
+ }
429
+ }
430
+ ]
431
+ };
432
+
433
+
434
+ const $ce2c52df8af02e62$export$e6bfdeff8bfc94f9 = (degrees)=>degrees * Math.PI / 180;
435
+ const $ce2c52df8af02e62$export$e46c5570db033611 = (ctx, size, config)=>{
436
+ ctx.save();
437
+ ctx.translate(0, 0);
438
+ if (config.rotation) ctx.rotate($ce2c52df8af02e62$export$e6bfdeff8bfc94f9(config.rotation));
439
+ ctx.lineWidth = config.lineWidth;
440
+ ctx.strokeStyle = config.strokeStyle;
441
+ ctx.fillStyle = config.fillStyle;
442
+ };
443
+ const $ce2c52df8af02e62$export$68ae68d395d27fd1 = (ctx)=>{
444
+ ctx.restore();
445
+ };
446
+ const $ce2c52df8af02e62$export$70ba51ca253810ef = (type)=>({
447
+ enabled: false,
448
+ duration: 1000,
449
+ easing: "linear",
450
+ type: type
451
+ });
452
+ const $ce2c52df8af02e62$export$5627764dc1e1d74a = (cx, cy, radius, segments)=>{
453
+ const points = [];
454
+ for(let i = 0; i < segments; i++){
455
+ const angle = i / segments * Math.PI * 2;
456
+ points.push({
457
+ x: cx + Math.cos(angle) * radius,
458
+ y: cy + Math.sin(angle) * radius
459
+ });
460
+ }
461
+ return points;
462
+ };
463
+
464
+
465
+ const $f0f1a7293548e501$export$90cc629a1c6121c7 = {
466
+ platonic: {
467
+ tetrahedron: {
468
+ vertices: 4,
469
+ faces: 4
470
+ },
471
+ cube: {
472
+ vertices: 8,
473
+ faces: 6
474
+ },
475
+ octahedron: {
476
+ vertices: 6,
477
+ faces: 8
478
+ },
479
+ dodecahedron: {
480
+ vertices: 20,
481
+ faces: 12
482
+ },
483
+ icosahedron: {
484
+ vertices: 12,
485
+ faces: 20
486
+ }
487
+ },
488
+ fibonacci: {
489
+ iterations: 13,
490
+ growthFactor: 1.618034
491
+ },
492
+ goldenRatio: {
493
+ iterations: 8,
494
+ ratio: 1.618034
495
+ }
496
+ };
497
+ const $f0f1a7293548e501$export$4721fcae39954914 = (ctx, size, config = {})=>{
498
+ const finalConfig = {
499
+ ...(0, $e0ea14d2e2f06463$export$3572305709b6b48c),
500
+ ...config
501
+ };
502
+ (0, $ce2c52df8af02e62$export$e46c5570db033611)(ctx, size, finalConfig);
503
+ const solidType = config.type;
504
+ const solidConfig = $f0f1a7293548e501$export$90cc629a1c6121c7.platonic[solidType] ?? $f0f1a7293548e501$export$90cc629a1c6121c7.platonic.icosahedron;
505
+ const { vertices: vertices } = solidConfig;
506
+ const radius = size / 2;
507
+ // Calculate vertices based on platonic solid type
508
+ const points = (0, $ce2c52df8af02e62$export$5627764dc1e1d74a)(0, 0, radius, vertices);
509
+ ctx.beginPath();
510
+ // Draw edges between vertices
511
+ points.forEach((p1, i)=>{
512
+ points.slice(i + 1).forEach((p2)=>{
513
+ ctx.moveTo(p1.x, p1.y);
514
+ ctx.lineTo(p2.x, p2.y);
515
+ });
516
+ });
517
+ if (finalConfig.fillStyle !== "transparent") ctx.fill();
518
+ ctx.stroke();
519
+ (0, $ce2c52df8af02e62$export$68ae68d395d27fd1)(ctx);
520
+ };
521
+ const $f0f1a7293548e501$export$4091fa94ab006097 = (ctx, size, config = {})=>{
522
+ const finalConfig = {
523
+ ...(0, $e0ea14d2e2f06463$export$3572305709b6b48c),
524
+ ...$f0f1a7293548e501$export$90cc629a1c6121c7.fibonacci,
525
+ ...config
526
+ };
527
+ (0, $ce2c52df8af02e62$export$e46c5570db033611)(ctx, size, finalConfig);
528
+ let current = 1;
529
+ let previous = 1;
530
+ let scale = size / Math.pow(finalConfig.growthFactor, finalConfig.iterations);
531
+ ctx.beginPath();
532
+ for(let i = 0; i < finalConfig.iterations; i++){
533
+ const radius = scale * current;
534
+ const centerX = radius / 2;
535
+ const centerY = radius / 2;
536
+ ctx.arc(centerX, centerY, radius, Math.PI, Math.PI * 1.5);
537
+ // Calculate next Fibonacci number
538
+ const next = current + previous;
539
+ previous = current;
540
+ current = next;
541
+ // Transform for next iteration
542
+ ctx.translate(radius, 0);
543
+ ctx.rotate(Math.PI / 2);
544
+ }
545
+ ctx.stroke();
546
+ (0, $ce2c52df8af02e62$export$68ae68d395d27fd1)(ctx);
547
+ };
548
+ const $f0f1a7293548e501$export$c9043b89bcb14ed9 = (ctx, size, config = {})=>{
549
+ const finalConfig = {
550
+ ...(0, $e0ea14d2e2f06463$export$3572305709b6b48c),
551
+ ...config
552
+ };
553
+ (0, $ce2c52df8af02e62$export$e46c5570db033611)(ctx, size, finalConfig);
554
+ const gridSize = 8;
555
+ const unit = size / gridSize;
556
+ ctx.beginPath();
557
+ // Create base grid
558
+ for(let i = 0; i <= gridSize; i++)for(let j = 0; j <= gridSize; j++){
559
+ const x = (i - gridSize / 2) * unit;
560
+ const y = (j - gridSize / 2) * unit;
561
+ // Draw star pattern at each intersection
562
+ const radius = unit / 2;
563
+ for(let k = 0; k < 8; k++){
564
+ const angle = Math.PI / 4 * k;
565
+ const x1 = x + radius * Math.cos(angle);
566
+ const y1 = y + radius * Math.sin(angle);
567
+ const x2 = x + radius * Math.cos(angle + Math.PI / 4);
568
+ const y2 = y + radius * Math.sin(angle + Math.PI / 4);
569
+ ctx.moveTo(x1, y1);
570
+ ctx.lineTo(x2, y2);
571
+ }
572
+ }
573
+ ctx.stroke();
574
+ (0, $ce2c52df8af02e62$export$68ae68d395d27fd1)(ctx);
575
+ };
576
+ const $f0f1a7293548e501$export$16ea6f9310920305 = (ctx, size, config = {})=>{
577
+ const finalConfig = {
578
+ ...(0, $e0ea14d2e2f06463$export$3572305709b6b48c),
579
+ ...config
580
+ };
581
+ (0, $ce2c52df8af02e62$export$e46c5570db033611)(ctx, size, finalConfig);
582
+ const gridSize = 4;
583
+ const unit = size / gridSize;
584
+ const drawKnotSegment = (x, y, type)=>{
585
+ ctx.beginPath();
586
+ switch(type){
587
+ case "over":
588
+ ctx.moveTo(x, y);
589
+ ctx.bezierCurveTo(x + unit / 2, y, x + unit / 2, y + unit, x + unit, y + unit);
590
+ break;
591
+ case "under":
592
+ ctx.moveTo(x, y + unit);
593
+ ctx.bezierCurveTo(x + unit / 2, y + unit, x + unit / 2, y, x + unit, y);
594
+ break;
595
+ }
596
+ ctx.stroke();
597
+ };
598
+ // Create knot pattern
599
+ for(let i = 0; i < gridSize; i++)for(let j = 0; j < gridSize; j++){
600
+ const x = (i - gridSize / 2) * unit;
601
+ const y = (j - gridSize / 2) * unit;
602
+ drawKnotSegment(x, y, (i + j) % 2 === 0 ? "over" : "under");
603
+ }
604
+ (0, $ce2c52df8af02e62$export$68ae68d395d27fd1)(ctx);
605
+ };
606
+ const $f0f1a7293548e501$export$661ad3ddfb8912ed = (ctx, size, config = {})=>{
607
+ const finalConfig = {
608
+ ...(0, $e0ea14d2e2f06463$export$3572305709b6b48c),
609
+ ...config
610
+ };
611
+ (0, $ce2c52df8af02e62$export$e46c5570db033611)(ctx, size, finalConfig);
612
+ const radius = size / 2;
613
+ // Draw two intersecting tetrahedra
614
+ ctx.beginPath();
615
+ // First tetrahedron
616
+ const points1 = (0, $ce2c52df8af02e62$export$5627764dc1e1d74a)(0, 0, radius, 3);
617
+ points1.forEach((p1, i)=>{
618
+ points1.slice(i + 1).forEach((p2)=>{
619
+ ctx.moveTo(p1.x, p1.y);
620
+ ctx.lineTo(p2.x, p2.y);
621
+ });
622
+ });
623
+ // Second tetrahedron (rotated)
624
+ ctx.rotate(Math.PI / 6);
625
+ const points2 = (0, $ce2c52df8af02e62$export$5627764dc1e1d74a)(0, 0, radius, 3);
626
+ points2.forEach((p1, i)=>{
627
+ points2.slice(i + 1).forEach((p2)=>{
628
+ ctx.moveTo(p1.x, p1.y);
629
+ ctx.lineTo(p2.x, p2.y);
630
+ });
631
+ });
632
+ if (finalConfig.fillStyle !== "transparent") ctx.fill();
633
+ ctx.stroke();
634
+ (0, $ce2c52df8af02e62$export$68ae68d395d27fd1)(ctx);
635
+ };
636
+ const $f0f1a7293548e501$export$97222779ddee1bfe = (ctx, size, config = {})=>{
637
+ const finalConfig = {
638
+ ...(0, $e0ea14d2e2f06463$export$3572305709b6b48c),
639
+ ...config
640
+ };
641
+ (0, $ce2c52df8af02e62$export$e46c5570db033611)(ctx, size, finalConfig);
642
+ const numCircles = 8;
643
+ const numPoints = 16;
644
+ const radius = size / 2;
645
+ ctx.beginPath();
646
+ for(let i = 1; i <= numCircles; i++){
647
+ const circleRadius = radius / numCircles * i;
648
+ ctx.moveTo(circleRadius, 0);
649
+ ctx.arc(0, 0, circleRadius, 0, Math.PI * 2);
650
+ for(let j = 0; j < numPoints; j++){
651
+ const angle = Math.PI * 2 * j / numPoints;
652
+ const x = circleRadius * Math.cos(angle);
653
+ const y = circleRadius * Math.sin(angle);
654
+ ctx.moveTo(0, 0);
655
+ ctx.lineTo(x, y);
656
+ }
657
+ }
658
+ ctx.stroke();
659
+ (0, $ce2c52df8af02e62$export$68ae68d395d27fd1)(ctx);
660
+ };
661
+ const $f0f1a7293548e501$export$4fab07e71ae4aeeb = (ctx, size, config = {})=>{
662
+ const finalConfig = {
663
+ ...(0, $e0ea14d2e2f06463$export$3572305709b6b48c),
664
+ ...config,
665
+ iterations: 5
666
+ };
667
+ (0, $ce2c52df8af02e62$export$e46c5570db033611)(ctx, size, finalConfig);
668
+ const drawBranch = (x, y, length, angle, depth)=>{
669
+ if (depth === 0) return;
670
+ const endX = x + length * Math.cos(angle);
671
+ const endY = y + length * Math.sin(angle);
672
+ ctx.beginPath();
673
+ ctx.moveTo(x, y);
674
+ ctx.lineTo(endX, endY);
675
+ ctx.stroke();
676
+ drawBranch(endX, endY, length * 0.7, angle - Math.PI / 6, depth - 1);
677
+ drawBranch(endX, endY, length * 0.7, angle + Math.PI / 6, depth - 1);
678
+ };
679
+ drawBranch(0, size / 2, size / 4, -Math.PI / 2, finalConfig.iterations);
680
+ (0, $ce2c52df8af02e62$export$68ae68d395d27fd1)(ctx);
681
+ };
682
+ const $f0f1a7293548e501$export$dbe318a13ce51887 = {
683
+ platonicSolid: $f0f1a7293548e501$export$4721fcae39954914,
684
+ fibonacciSpiral: $f0f1a7293548e501$export$4091fa94ab006097,
685
+ islamicPattern: $f0f1a7293548e501$export$c9043b89bcb14ed9,
686
+ celticKnot: $f0f1a7293548e501$export$16ea6f9310920305,
687
+ merkaba: $f0f1a7293548e501$export$661ad3ddfb8912ed,
688
+ mandala: $f0f1a7293548e501$export$97222779ddee1bfe,
689
+ fractal: $f0f1a7293548e501$export$4fab07e71ae4aeeb
690
+ };
691
+
692
+
693
+
694
+ const $77711f013715e6da$export$3bac12a72d9de58c = (ctx, size)=>{
695
+ const radius = size / 6;
696
+ const centers = [
697
+ {
698
+ x: 0,
699
+ y: 0
700
+ },
701
+ {
702
+ x: radius * Math.sqrt(3),
703
+ y: 0
704
+ },
705
+ {
706
+ x: radius * Math.sqrt(3) / 2,
707
+ y: 1.5 * radius
708
+ },
709
+ {
710
+ x: -radius * Math.sqrt(3) / 2,
711
+ y: 1.5 * radius
712
+ },
713
+ {
714
+ x: -radius * Math.sqrt(3),
715
+ y: 0
716
+ },
717
+ {
718
+ x: -radius * Math.sqrt(3) / 2,
719
+ y: -1.5 * radius
720
+ },
721
+ {
722
+ x: radius * Math.sqrt(3) / 2,
723
+ y: -1.5 * radius
724
+ }
725
+ ];
726
+ ctx.beginPath();
727
+ centers.forEach((center)=>{
728
+ ctx.moveTo(center.x + radius, center.y);
729
+ ctx.arc(center.x, center.y, radius, 0, Math.PI * 2);
730
+ });
731
+ };
732
+ const $77711f013715e6da$export$639bf214428ceec2 = (ctx, size)=>{
733
+ const radius = size / 12;
734
+ const spacing = radius * 2.5;
735
+ // Sephirot positions (traditional layout)
736
+ const positions = [
737
+ {
738
+ x: 0,
739
+ y: -spacing * 2
740
+ },
741
+ {
742
+ x: -spacing,
743
+ y: -spacing
744
+ },
745
+ {
746
+ x: spacing,
747
+ y: -spacing
748
+ },
749
+ {
750
+ x: -spacing,
751
+ y: 0
752
+ },
753
+ {
754
+ x: spacing,
755
+ y: 0
756
+ },
757
+ {
758
+ x: 0,
759
+ y: 0
760
+ },
761
+ {
762
+ x: -spacing,
763
+ y: spacing
764
+ },
765
+ {
766
+ x: spacing,
767
+ y: spacing
768
+ },
769
+ {
770
+ x: 0,
771
+ y: spacing * 2
772
+ },
773
+ {
774
+ x: 0,
775
+ y: spacing * 3
776
+ }
777
+ ];
778
+ // Draw circles
779
+ ctx.beginPath();
780
+ positions.forEach((pos)=>{
781
+ ctx.moveTo(pos.x + radius, pos.y);
782
+ ctx.arc(pos.x, pos.y, radius, 0, Math.PI * 2);
783
+ });
784
+ // Draw connecting lines
785
+ ctx.moveTo(positions[0].x, positions[0].y);
786
+ positions.forEach((pos, i)=>{
787
+ if (i > 0) positions.slice(i + 1).forEach((nextPos)=>{
788
+ ctx.moveTo(pos.x, pos.y);
789
+ ctx.lineTo(nextPos.x, nextPos.y);
790
+ });
791
+ });
792
+ };
793
+ const $77711f013715e6da$export$de26673ceb3e04eb = (ctx, size)=>{
794
+ const radius = size / 3;
795
+ // Create 13 points - one center and 12 vertices of an icosahedron
796
+ // const phi = (1 + Math.sqrt(5)) / 2; // Golden ratio
797
+ const vertices = [
798
+ {
799
+ x: 0,
800
+ y: 0
801
+ },
802
+ ...(0, $ce2c52df8af02e62$export$5627764dc1e1d74a)(0, 0, radius, 6),
803
+ ...(0, $ce2c52df8af02e62$export$5627764dc1e1d74a)(0, 0, radius * 1.5, 6)
804
+ ];
805
+ ctx.beginPath();
806
+ // Draw all connecting lines
807
+ vertices.forEach((v1, i)=>{
808
+ vertices.slice(i + 1).forEach((v2)=>{
809
+ ctx.moveTo(v1.x, v1.y);
810
+ ctx.lineTo(v2.x, v2.y);
811
+ });
812
+ });
813
+ };
814
+ const $77711f013715e6da$export$f743702d1590aefb = (ctx, size)=>{
815
+ const radius = size / 2;
816
+ ctx.beginPath();
817
+ // Draw outer triangles
818
+ for(let i = 0; i < 9; i++){
819
+ const angle = i / 9 * Math.PI * 2;
820
+ const x1 = Math.cos(angle) * radius;
821
+ const y1 = Math.sin(angle) * radius;
822
+ const x2 = Math.cos(angle + Math.PI / 9) * radius;
823
+ const y2 = Math.sin(angle + Math.PI / 9) * radius;
824
+ ctx.moveTo(0, 0);
825
+ ctx.lineTo(x1, y1);
826
+ ctx.lineTo(x2, y2);
827
+ ctx.lineTo(0, 0);
828
+ }
829
+ // Draw inner triangles
830
+ const innerRadius = radius * 0.6;
831
+ for(let i = 0; i < 9; i++){
832
+ const angle = i / 9 * Math.PI * 2 + Math.PI / 18;
833
+ const x1 = Math.cos(angle) * innerRadius;
834
+ const y1 = Math.sin(angle) * innerRadius;
835
+ const x2 = Math.cos(angle + Math.PI / 9) * innerRadius;
836
+ const y2 = Math.sin(angle + Math.PI / 9) * innerRadius;
837
+ ctx.moveTo(0, 0);
838
+ ctx.lineTo(x1, y1);
839
+ ctx.lineTo(x2, y2);
840
+ ctx.lineTo(0, 0);
841
+ }
842
+ };
843
+ const $77711f013715e6da$export$980631444e370ab0 = (ctx, size)=>{
844
+ const radius = size / 6;
845
+ const centers = [
846
+ {
847
+ x: 0,
848
+ y: 0
849
+ },
850
+ {
851
+ x: radius * Math.sqrt(3),
852
+ y: 0
853
+ },
854
+ {
855
+ x: radius * Math.sqrt(3) / 2,
856
+ y: 1.5 * radius
857
+ },
858
+ {
859
+ x: -radius * Math.sqrt(3) / 2,
860
+ y: 1.5 * radius
861
+ },
862
+ {
863
+ x: -radius * Math.sqrt(3),
864
+ y: 0
865
+ },
866
+ {
867
+ x: -radius * Math.sqrt(3) / 2,
868
+ y: -1.5 * radius
869
+ },
870
+ {
871
+ x: radius * Math.sqrt(3) / 2,
872
+ y: -1.5 * radius
873
+ }
874
+ ];
875
+ ctx.beginPath();
876
+ centers.forEach((center)=>{
877
+ ctx.moveTo(center.x + radius, center.y);
878
+ ctx.arc(center.x, center.y, radius, 0, Math.PI * 2);
879
+ });
880
+ };
881
+ const $77711f013715e6da$export$eeae7765f05012e2 = (ctx, size)=>{
882
+ const radius = size / 4;
883
+ ctx.beginPath();
884
+ ctx.arc(-radius / 2, 0, radius, 0, Math.PI * 2);
885
+ ctx.arc(radius / 2, 0, radius, 0, Math.PI * 2);
886
+ };
887
+ const $77711f013715e6da$export$3355220a8108efc3 = (ctx, size)=>{
888
+ const outerRadius = size / 2;
889
+ const innerRadius = size / 4;
890
+ const steps = 36;
891
+ ctx.beginPath();
892
+ for(let i = 0; i < steps; i++){
893
+ const angle1 = i / steps * Math.PI * 2;
894
+ // const angle2 = ((i + 1) / steps) * Math.PI * 2;
895
+ for(let j = 0; j < steps; j++){
896
+ const phi1 = j / steps * Math.PI * 2;
897
+ const phi2 = (j + 1) / steps * Math.PI * 2;
898
+ const x1 = (outerRadius + innerRadius * Math.cos(phi1)) * Math.cos(angle1);
899
+ const y1 = (outerRadius + innerRadius * Math.cos(phi1)) * Math.sin(angle1);
900
+ const x2 = (outerRadius + innerRadius * Math.cos(phi2)) * Math.cos(angle1);
901
+ const y2 = (outerRadius + innerRadius * Math.cos(phi2)) * Math.sin(angle1);
902
+ ctx.moveTo(x1, y1);
903
+ ctx.lineTo(x2, y2);
904
+ }
905
+ }
906
+ };
907
+ const $77711f013715e6da$export$31e39e0544d9b1e8 = (ctx, size)=>{
908
+ const radius = size / 8;
909
+ const centers = [
910
+ {
911
+ x: 0,
912
+ y: 0
913
+ },
914
+ {
915
+ x: radius * 2,
916
+ y: 0
917
+ },
918
+ {
919
+ x: radius,
920
+ y: radius * Math.sqrt(3)
921
+ },
922
+ {
923
+ x: -radius,
924
+ y: radius * Math.sqrt(3)
925
+ },
926
+ {
927
+ x: -radius * 2,
928
+ y: 0
929
+ },
930
+ {
931
+ x: -radius,
932
+ y: -radius * Math.sqrt(3)
933
+ },
934
+ {
935
+ x: radius,
936
+ y: -radius * Math.sqrt(3)
937
+ }
938
+ ];
939
+ ctx.beginPath();
940
+ centers.forEach((center)=>{
941
+ ctx.moveTo(center.x + radius, center.y);
942
+ ctx.arc(center.x, center.y, radius, 0, Math.PI * 2);
943
+ });
944
+ };
945
+ const $77711f013715e6da$export$c2fc138f94dd4b2a = {
946
+ flowerOfLife: $77711f013715e6da$export$3bac12a72d9de58c,
947
+ treeOfLife: $77711f013715e6da$export$639bf214428ceec2,
948
+ metatronsCube: $77711f013715e6da$export$de26673ceb3e04eb,
949
+ sriYantra: $77711f013715e6da$export$f743702d1590aefb,
950
+ seedOfLife: $77711f013715e6da$export$980631444e370ab0,
951
+ vesicaPiscis: $77711f013715e6da$export$eeae7765f05012e2,
952
+ torus: $77711f013715e6da$export$3355220a8108efc3,
953
+ eggOfLife: $77711f013715e6da$export$31e39e0544d9b1e8
954
+ };
955
+
956
+
957
+ const $e41b41d8dcf837ad$export$4ff7fc6f1af248b5 = {
958
+ ...(0, $44f5b87a40c9680b$export$492753207a5258e1),
959
+ ...(0, $f0f1a7293548e501$export$dbe318a13ce51887),
960
+ ...(0, $77711f013715e6da$export$c2fc138f94dd4b2a)
961
+ };
962
+
963
+
964
+ function $e0f99502ff383dd8$export$71b514a25c47df50(ctx, shape, x, y, config) {
965
+ const { fillColor: fillColor, strokeColor: strokeColor, strokeWidth: strokeWidth, size: size, rotation: rotation } = config;
966
+ ctx.save();
967
+ ctx.translate(x, y);
968
+ ctx.rotate(rotation * Math.PI / 180);
969
+ ctx.fillStyle = fillColor;
970
+ ctx.strokeStyle = strokeColor;
971
+ ctx.lineWidth = strokeWidth;
972
+ const drawFunction = (0, $e41b41d8dcf837ad$export$4ff7fc6f1af248b5)[shape];
973
+ if (drawFunction) {
974
+ drawFunction(ctx, size);
975
+ ctx.fill();
976
+ ctx.stroke();
977
+ }
978
+ ctx.restore();
979
+ }
980
+ function $e0f99502ff383dd8$export$bb35a6995ddbf32d(ctx, shape, x, y, config) {
981
+ const { fillColor: fillColor, strokeColor: strokeColor, strokeWidth: strokeWidth, size: size, rotation: rotation, patterns: patterns = [], proportionType: proportionType = "GOLDEN_RATIO", baseOpacity: baseOpacity = 0.6, opacityReduction: opacityReduction = 0.1, glowRadius: glowRadius = 0, glowColor: glowColor, gradientFillEnd: gradientFillEnd } = config;
982
+ ctx.save();
983
+ ctx.translate(x, y);
984
+ ctx.rotate(rotation * Math.PI / 180);
985
+ // Glow / shadow effect
986
+ if (glowRadius > 0) {
987
+ ctx.shadowBlur = glowRadius;
988
+ ctx.shadowColor = glowColor || fillColor;
989
+ ctx.shadowOffsetX = 0;
990
+ ctx.shadowOffsetY = 0;
991
+ }
992
+ // Gradient fill or flat fill
993
+ if (gradientFillEnd) {
994
+ const grad = ctx.createRadialGradient(0, 0, 0, 0, 0, size / 2);
995
+ grad.addColorStop(0, fillColor);
996
+ grad.addColorStop(1, gradientFillEnd);
997
+ ctx.fillStyle = grad;
998
+ } else ctx.fillStyle = fillColor;
999
+ ctx.strokeStyle = strokeColor;
1000
+ ctx.lineWidth = strokeWidth;
1001
+ const drawFunction = (0, $e41b41d8dcf837ad$export$4ff7fc6f1af248b5)[shape];
1002
+ if (drawFunction) {
1003
+ drawFunction(ctx, size);
1004
+ ctx.fill();
1005
+ ctx.stroke();
1006
+ }
1007
+ // Reset shadow so patterns aren't double-glowed
1008
+ if (glowRadius > 0) ctx.shadowBlur = 0;
1009
+ // Layer additional patterns if specified
1010
+ if (patterns.length > 0) (0, $616009579e3d72c5$export$da2372f11bc66b3f).layerPatterns(ctx, patterns, {
1011
+ baseSize: size,
1012
+ baseOpacity: baseOpacity,
1013
+ opacityReduction: opacityReduction,
1014
+ proportionType: proportionType
1015
+ });
1016
+ ctx.restore();
1017
+ }
1018
+
1019
+
1020
+
1021
+
1022
+ /**
1023
+ * Configuration options for image generation.
1024
+ */ const $81c1b644006d48ec$export$c2f8e0cc249a8d8f = {
1025
+ width: 2048,
1026
+ height: 2048,
1027
+ gridSize: 5,
1028
+ layers: 4,
1029
+ minShapeSize: 30,
1030
+ maxShapeSize: 400,
1031
+ baseOpacity: 0.7,
1032
+ opacityReduction: 0.12,
1033
+ shapesPerLayer: 0
1034
+ };
1035
+
1036
+
1037
+ // ── Shape categories for weighted selection ─────────────────────────
1038
+ const $1f63dc64b5593c73$var$BASIC_SHAPES = [
1039
+ "circle",
1040
+ "square",
1041
+ "triangle",
1042
+ "hexagon",
1043
+ "diamond",
1044
+ "cube"
1045
+ ];
1046
+ const $1f63dc64b5593c73$var$COMPLEX_SHAPES = [
1047
+ "star",
1048
+ "jacked-star",
1049
+ "heart",
1050
+ "platonicSolid",
1051
+ "fibonacciSpiral",
1052
+ "islamicPattern",
1053
+ "celticKnot",
1054
+ "merkaba",
1055
+ "fractal"
1056
+ ];
1057
+ const $1f63dc64b5593c73$var$SACRED_SHAPES = [
1058
+ "mandala",
1059
+ "flowerOfLife",
1060
+ "treeOfLife",
1061
+ "metatronsCube",
1062
+ "sriYantra",
1063
+ "seedOfLife",
1064
+ "vesicaPiscis",
1065
+ "torus",
1066
+ "eggOfLife"
1067
+ ];
1068
+ const $1f63dc64b5593c73$var$COMPOSITION_MODES = [
1069
+ "radial",
1070
+ "flow-field",
1071
+ "spiral",
1072
+ "grid-subdivision",
1073
+ "clustered"
1074
+ ];
1075
+ // ── Helper: pick shape with layer-aware weighting ───────────────────
1076
+ function $1f63dc64b5593c73$var$pickShape(rng, layerRatio, shapeNames) {
1077
+ const basicW = 1 - layerRatio * 0.6;
1078
+ const complexW = 0.3 + layerRatio * 0.3;
1079
+ const sacredW = 0.1 + layerRatio * 0.4;
1080
+ const total = basicW + complexW + sacredW;
1081
+ const roll = rng() * total;
1082
+ let pool;
1083
+ if (roll < basicW) pool = $1f63dc64b5593c73$var$BASIC_SHAPES;
1084
+ else if (roll < basicW + complexW) pool = $1f63dc64b5593c73$var$COMPLEX_SHAPES;
1085
+ else pool = $1f63dc64b5593c73$var$SACRED_SHAPES;
1086
+ const available = pool.filter((s)=>shapeNames.includes(s));
1087
+ if (available.length === 0) return shapeNames[Math.floor(rng() * shapeNames.length)];
1088
+ return available[Math.floor(rng() * available.length)];
1089
+ }
1090
+ // ── Helper: simple 2D value noise (hash-seeded) ─────────────────────
1091
+ function $1f63dc64b5593c73$var$valueNoise(x, y, scale, rng) {
1092
+ // Cheap pseudo-noise: combine sin waves at different frequencies
1093
+ const nx = x / scale;
1094
+ const ny = y / scale;
1095
+ return (Math.sin(nx * 1.7 + ny * 2.3 + rng() * 0.001) * 0.5 + Math.sin(nx * 3.1 - ny * 1.9 + rng() * 0.001) * 0.3 + Math.sin(nx * 5.3 + ny * 4.7 + rng() * 0.001) * 0.2) * 0.5 + 0.5;
1096
+ }
1097
+ // ── Helper: get position based on composition mode ──────────────────
1098
+ function $1f63dc64b5593c73$var$getCompositionPosition(mode, rng, width, height, shapeIndex, totalShapes, cx, cy) {
1099
+ switch(mode){
1100
+ case "radial":
1101
+ {
1102
+ const angle = rng() * Math.PI * 2;
1103
+ const maxR = Math.min(width, height) * 0.45;
1104
+ const r = Math.pow(rng(), 0.7) * maxR;
1105
+ return {
1106
+ x: cx + Math.cos(angle) * r,
1107
+ y: cy + Math.sin(angle) * r
1108
+ };
1109
+ }
1110
+ case "spiral":
1111
+ {
1112
+ const t = shapeIndex / totalShapes;
1113
+ const turns = 3 + rng() * 2;
1114
+ const angle = t * Math.PI * 2 * turns;
1115
+ const maxR = Math.min(width, height) * 0.42;
1116
+ const r = t * maxR + (rng() - 0.5) * maxR * 0.15;
1117
+ return {
1118
+ x: cx + Math.cos(angle) * r,
1119
+ y: cy + Math.sin(angle) * r
1120
+ };
1121
+ }
1122
+ case "grid-subdivision":
1123
+ {
1124
+ const cells = 3 + Math.floor(rng() * 3);
1125
+ const cellW = width / cells;
1126
+ const cellH = height / cells;
1127
+ const gx = Math.floor(rng() * cells);
1128
+ const gy = Math.floor(rng() * cells);
1129
+ return {
1130
+ x: gx * cellW + rng() * cellW,
1131
+ y: gy * cellH + rng() * cellH
1132
+ };
1133
+ }
1134
+ case "clustered":
1135
+ {
1136
+ // Pick one of 3-5 cluster centers, then scatter around it
1137
+ const numClusters = 3 + Math.floor(rng() * 3);
1138
+ const ci = Math.floor(rng() * numClusters);
1139
+ // Deterministic cluster center from index
1140
+ const clusterRng = (0, $616009579e3d72c5$export$eaf9227667332084)((0, $616009579e3d72c5$export$e9cc707de01b7042)(String(ci), 999));
1141
+ const clx = width * (0.15 + clusterRng() * 0.7);
1142
+ const cly = height * (0.15 + clusterRng() * 0.7);
1143
+ const spread = Math.min(width, height) * 0.18;
1144
+ return {
1145
+ x: clx + (rng() - 0.5) * spread * 2,
1146
+ y: cly + (rng() - 0.5) * spread * 2
1147
+ };
1148
+ }
1149
+ case "flow-field":
1150
+ default:
1151
+ // Random position, will be adjusted by flow field direction later
1152
+ return {
1153
+ x: rng() * width,
1154
+ y: rng() * height
1155
+ };
1156
+ }
1157
+ }
1158
+ // ── Helper: positional color blending ───────────────────────────────
1159
+ function $1f63dc64b5593c73$var$getPositionalColor(x, y, width, height, colors, rng) {
1160
+ // Blend between palette colors based on position
1161
+ const nx = x / width;
1162
+ const ny = y / height;
1163
+ // Use position to bias which palette color is chosen
1164
+ const posIndex = (nx * 0.6 + ny * 0.4) * (colors.length - 1);
1165
+ const baseIdx = Math.floor(posIndex) % colors.length;
1166
+ // Then jitter it slightly
1167
+ return (0, $b5a262d09b87e373$export$59539d800dbe6858)(colors[baseIdx], rng, 0.08);
1168
+ }
1169
+ function $1f63dc64b5593c73$export$29a844702096332e(ctx, gitHash, config = {}) {
1170
+ const finalConfig = {
1171
+ ...(0, $81c1b644006d48ec$export$c2f8e0cc249a8d8f),
1172
+ ...config
1173
+ };
1174
+ const { width: width, height: height, gridSize: gridSize, layers: layers, minShapeSize: minShapeSize, maxShapeSize: maxShapeSize, baseOpacity: baseOpacity, opacityReduction: opacityReduction } = finalConfig;
1175
+ finalConfig.shapesPerLayer = finalConfig.shapesPerLayer || Math.floor(gridSize * gridSize * 1.5);
1176
+ const colorScheme = new (0, $b5a262d09b87e373$export$ab958c550f521376)(gitHash);
1177
+ const colors = colorScheme.getColors();
1178
+ const [bgStart, bgEnd] = colorScheme.getBackgroundColors();
1179
+ const shapeNames = Object.keys((0, $e41b41d8dcf837ad$export$4ff7fc6f1af248b5));
1180
+ const scaleFactor = Math.min(width, height) / 1024;
1181
+ const adjustedMinSize = minShapeSize * scaleFactor;
1182
+ const adjustedMaxSize = maxShapeSize * scaleFactor;
1183
+ const rng = (0, $616009579e3d72c5$export$eaf9227667332084)((0, $616009579e3d72c5$export$e9cc707de01b7042)(gitHash));
1184
+ const cx = width / 2;
1185
+ const cy = height / 2;
1186
+ // ── 1. Background ──────────────────────────────────────────────
1187
+ const bgRadius = Math.hypot(cx, cy);
1188
+ const bgGrad = ctx.createRadialGradient(cx, cy, 0, cx, cy, bgRadius);
1189
+ bgGrad.addColorStop(0, bgStart);
1190
+ bgGrad.addColorStop(1, bgEnd);
1191
+ ctx.fillStyle = bgGrad;
1192
+ ctx.fillRect(0, 0, width, height);
1193
+ // ── 2. Composition mode ────────────────────────────────────────
1194
+ const compositionMode = $1f63dc64b5593c73$var$COMPOSITION_MODES[Math.floor(rng() * $1f63dc64b5593c73$var$COMPOSITION_MODES.length)];
1195
+ // ── 3. Focal points ────────────────────────────────────────────
1196
+ const numFocal = 1 + Math.floor(rng() * 2);
1197
+ const focalPoints = [];
1198
+ for(let f = 0; f < numFocal; f++)focalPoints.push({
1199
+ x: width * (0.2 + rng() * 0.6),
1200
+ y: height * (0.2 + rng() * 0.6),
1201
+ strength: 0.3 + rng() * 0.4
1202
+ });
1203
+ function applyFocalBias(rx, ry) {
1204
+ let nearest = focalPoints[0];
1205
+ let minDist = Infinity;
1206
+ for (const fp of focalPoints){
1207
+ const d = Math.hypot(rx - fp.x, ry - fp.y);
1208
+ if (d < minDist) {
1209
+ minDist = d;
1210
+ nearest = fp;
1211
+ }
1212
+ }
1213
+ const pull = nearest.strength * rng() * 0.5;
1214
+ return [
1215
+ rx + (nearest.x - rx) * pull,
1216
+ ry + (nearest.y - ry) * pull
1217
+ ];
1218
+ }
1219
+ // ── 4. Flow field seed values (for flow-field mode & line pass) ─
1220
+ const fieldAngleBase = rng() * Math.PI * 2;
1221
+ const fieldFreq = 0.5 + rng() * 2;
1222
+ function flowAngle(x, y) {
1223
+ return fieldAngleBase + Math.sin(x / width * fieldFreq * Math.PI * 2) * Math.PI * 0.5 + Math.cos(y / height * fieldFreq * Math.PI * 2) * Math.PI * 0.5;
1224
+ }
1225
+ // ── 5. Shape layers ────────────────────────────────────────────
1226
+ const shapePositions = [];
1227
+ for(let layer = 0; layer < layers; layer++){
1228
+ const layerRatio = layers > 1 ? layer / (layers - 1) : 0;
1229
+ const numShapes = finalConfig.shapesPerLayer + Math.floor(rng() * finalConfig.shapesPerLayer * 0.3);
1230
+ const layerOpacity = Math.max(0.15, baseOpacity - layer * opacityReduction);
1231
+ const layerSizeScale = 1 - layer * 0.15;
1232
+ for(let i = 0; i < numShapes; i++){
1233
+ // Position from composition mode, then focal bias
1234
+ const rawPos = $1f63dc64b5593c73$var$getCompositionPosition(compositionMode, rng, width, height, i, numShapes, cx, cy);
1235
+ const [x, y] = applyFocalBias(rawPos.x, rawPos.y);
1236
+ // Weighted shape selection
1237
+ const shape = $1f63dc64b5593c73$var$pickShape(rng, layerRatio, shapeNames);
1238
+ // Power distribution for size
1239
+ const sizeT = Math.pow(rng(), 1.8);
1240
+ const size = (adjustedMinSize + sizeT * (adjustedMaxSize - adjustedMinSize)) * layerSizeScale;
1241
+ // Flow-field rotation in flow-field mode, random otherwise
1242
+ const rotation = compositionMode === "flow-field" ? flowAngle(x, y) * 180 / Math.PI + (rng() - 0.5) * 30 : rng() * 360;
1243
+ // Positional color blending + jitter
1244
+ const fillBase = $1f63dc64b5593c73$var$getPositionalColor(x, y, width, height, colors, rng);
1245
+ const strokeBase = colors[Math.floor(rng() * colors.length)];
1246
+ const fillColor = (0, $b5a262d09b87e373$export$59539d800dbe6858)(fillBase, rng, 0.06);
1247
+ const strokeColor = (0, $b5a262d09b87e373$export$59539d800dbe6858)(strokeBase, rng, 0.05);
1248
+ // Semi-transparent fill
1249
+ const fillAlpha = 0.2 + rng() * 0.5;
1250
+ const transparentFill = (0, $b5a262d09b87e373$export$f2121afcad3d553f)(fillColor, fillAlpha);
1251
+ const strokeWidth = (0.5 + rng() * 2.0) * scaleFactor;
1252
+ ctx.globalAlpha = layerOpacity * (0.5 + rng() * 0.5);
1253
+ // Glow on sacred shapes more often
1254
+ const isSacred = $1f63dc64b5593c73$var$SACRED_SHAPES.includes(shape);
1255
+ const glowChance = isSacred ? 0.45 : 0.2;
1256
+ const hasGlow = rng() < glowChance;
1257
+ const glowRadius = hasGlow ? (8 + rng() * 20) * scaleFactor : 0;
1258
+ // Gradient fill on ~30%
1259
+ const hasGradient = rng() < 0.3;
1260
+ const gradientEnd = hasGradient ? (0, $b5a262d09b87e373$export$59539d800dbe6858)(colors[Math.floor(rng() * colors.length)], rng, 0.1) : undefined;
1261
+ (0, $e0f99502ff383dd8$export$bb35a6995ddbf32d)(ctx, shape, x, y, {
1262
+ fillColor: transparentFill,
1263
+ strokeColor: strokeColor,
1264
+ strokeWidth: strokeWidth,
1265
+ size: size,
1266
+ rotation: rotation,
1267
+ proportionType: "GOLDEN_RATIO",
1268
+ glowRadius: glowRadius,
1269
+ glowColor: hasGlow ? (0, $b5a262d09b87e373$export$f2121afcad3d553f)(fillColor, 0.6) : undefined,
1270
+ gradientFillEnd: gradientEnd
1271
+ });
1272
+ shapePositions.push({
1273
+ x: x,
1274
+ y: y,
1275
+ size: size
1276
+ });
1277
+ // ── 5b. Recursive nesting: ~15% of larger shapes get inner shapes ──
1278
+ if (size > adjustedMaxSize * 0.4 && rng() < 0.15) {
1279
+ const innerCount = 1 + Math.floor(rng() * 3);
1280
+ for(let n = 0; n < innerCount; n++){
1281
+ const innerShape = $1f63dc64b5593c73$var$pickShape(rng, Math.min(1, layerRatio + 0.3), shapeNames);
1282
+ const innerSize = size * (0.15 + rng() * 0.25);
1283
+ const innerOffX = (rng() - 0.5) * size * 0.4;
1284
+ const innerOffY = (rng() - 0.5) * size * 0.4;
1285
+ const innerRot = rng() * 360;
1286
+ const innerFill = (0, $b5a262d09b87e373$export$f2121afcad3d553f)((0, $b5a262d09b87e373$export$59539d800dbe6858)(colors[Math.floor(rng() * colors.length)], rng, 0.1), 0.3 + rng() * 0.4);
1287
+ ctx.globalAlpha = layerOpacity * 0.7;
1288
+ (0, $e0f99502ff383dd8$export$bb35a6995ddbf32d)(ctx, innerShape, x + innerOffX, y + innerOffY, {
1289
+ fillColor: innerFill,
1290
+ strokeColor: (0, $b5a262d09b87e373$export$f2121afcad3d553f)(strokeColor, 0.5),
1291
+ strokeWidth: strokeWidth * 0.6,
1292
+ size: innerSize,
1293
+ rotation: innerRot,
1294
+ proportionType: "GOLDEN_RATIO"
1295
+ });
1296
+ }
1297
+ }
1298
+ }
1299
+ }
1300
+ // ── 6. Flow-line pass ──────────────────────────────────────────
1301
+ // Draw flowing curves that follow the hash-derived vector field
1302
+ const numFlowLines = 6 + Math.floor(rng() * 10);
1303
+ for(let i = 0; i < numFlowLines; i++){
1304
+ let fx = rng() * width;
1305
+ let fy = rng() * height;
1306
+ const steps = 30 + Math.floor(rng() * 40);
1307
+ const stepLen = (3 + rng() * 5) * scaleFactor;
1308
+ ctx.globalAlpha = 0.06 + rng() * 0.1;
1309
+ ctx.strokeStyle = (0, $b5a262d09b87e373$export$f2121afcad3d553f)(colors[Math.floor(rng() * colors.length)], 0.4);
1310
+ ctx.lineWidth = (0.5 + rng() * 1.5) * scaleFactor;
1311
+ ctx.beginPath();
1312
+ ctx.moveTo(fx, fy);
1313
+ for(let s = 0; s < steps; s++){
1314
+ const angle = flowAngle(fx, fy) + (rng() - 0.5) * 0.3;
1315
+ fx += Math.cos(angle) * stepLen;
1316
+ fy += Math.sin(angle) * stepLen;
1317
+ // Stay in bounds
1318
+ if (fx < 0 || fx > width || fy < 0 || fy > height) break;
1319
+ ctx.lineTo(fx, fy);
1320
+ }
1321
+ ctx.stroke();
1322
+ }
1323
+ // ── 7. Noise texture overlay ───────────────────────────────────
1324
+ // Subtle grain rendered as tiny semi-transparent dots
1325
+ const noiseRng = (0, $616009579e3d72c5$export$eaf9227667332084)((0, $616009579e3d72c5$export$e9cc707de01b7042)(gitHash, 777));
1326
+ const noiseDensity = Math.floor(width * height / 800);
1327
+ for(let i = 0; i < noiseDensity; i++){
1328
+ const nx = noiseRng() * width;
1329
+ const ny = noiseRng() * height;
1330
+ const brightness = noiseRng() > 0.5 ? 255 : 0;
1331
+ const alpha = 0.01 + noiseRng() * 0.03;
1332
+ ctx.globalAlpha = alpha;
1333
+ ctx.fillStyle = `rgba(${brightness},${brightness},${brightness},1)`;
1334
+ ctx.fillRect(nx, ny, 1 * scaleFactor, 1 * scaleFactor);
1335
+ }
1336
+ // ── 8. Organic connecting curves ───────────────────────────────
1337
+ if (shapePositions.length > 1) {
1338
+ const numCurves = Math.floor(8 * (width * height) / 1048576);
1339
+ ctx.lineWidth = 0.8 * scaleFactor;
1340
+ for(let i = 0; i < numCurves; i++){
1341
+ const idxA = Math.floor(rng() * shapePositions.length);
1342
+ const offset = 1 + Math.floor(rng() * Math.min(5, shapePositions.length - 1));
1343
+ const idxB = (idxA + offset) % shapePositions.length;
1344
+ const a = shapePositions[idxA];
1345
+ const b = shapePositions[idxB];
1346
+ const mx = (a.x + b.x) / 2;
1347
+ const my = (a.y + b.y) / 2;
1348
+ const dx = b.x - a.x;
1349
+ const dy = b.y - a.y;
1350
+ const dist = Math.hypot(dx, dy);
1351
+ const bulge = (rng() - 0.5) * dist * 0.4;
1352
+ const cpx = mx + -dy / (dist || 1) * bulge;
1353
+ const cpy = my + dx / (dist || 1) * bulge;
1354
+ ctx.globalAlpha = 0.06 + rng() * 0.1;
1355
+ ctx.strokeStyle = (0, $b5a262d09b87e373$export$f2121afcad3d553f)(colors[Math.floor(rng() * colors.length)], 0.3);
1356
+ ctx.beginPath();
1357
+ ctx.moveTo(a.x, a.y);
1358
+ ctx.quadraticCurveTo(cpx, cpy, b.x, b.y);
1359
+ ctx.stroke();
1360
+ }
1361
+ }
1362
+ ctx.globalAlpha = 1;
1363
+ }
1364
+
1365
+
1366
+
1367
+
1368
+ /**
1369
+ * Render hash-derived art directly onto an HTMLCanvasElement.
1370
+ *
1371
+ * The canvas should already have the desired width/height set.
1372
+ * Config width/height will be inferred from the canvas if not provided.
1373
+ *
1374
+ * @param canvas - An HTMLCanvasElement (or OffscreenCanvas)
1375
+ * @param gitHash - Hex hash string used as the deterministic seed
1376
+ * @param config - Partial generation config (merged with defaults)
1377
+ */ function $59c8fbfd7fd6ed6a$export$9379f53d1f28e74d(canvas, gitHash, config = {}) {
1378
+ const ctx = canvas.getContext("2d");
1379
+ if (!ctx) throw new Error("Failed to get 2D rendering context from canvas");
1380
+ const finalConfig = {
1381
+ width: canvas.width,
1382
+ height: canvas.height,
1383
+ ...config
1384
+ };
1385
+ (0, $1f63dc64b5593c73$export$29a844702096332e)(ctx, gitHash, finalConfig);
1386
+ }
1387
+ /**
1388
+ * Render hash-derived art and return it as a Blob (browser-native).
1389
+ *
1390
+ * @param gitHash - Hex hash string used as the deterministic seed
1391
+ * @param config - Partial generation config (merged with defaults)
1392
+ * @returns A Promise that resolves to a PNG Blob
1393
+ */ async function $59c8fbfd7fd6ed6a$export$ff855b1b91b18a57(gitHash, config = {}) {
1394
+ const finalConfig = {
1395
+ ...(0, $81c1b644006d48ec$export$c2f8e0cc249a8d8f),
1396
+ ...config
1397
+ };
1398
+ const { width: width, height: height } = finalConfig;
1399
+ const canvas = new OffscreenCanvas(width, height);
1400
+ const ctx = canvas.getContext("2d");
1401
+ if (!ctx) throw new Error("Failed to get 2D rendering context from OffscreenCanvas");
1402
+ (0, $1f63dc64b5593c73$export$29a844702096332e)(ctx, gitHash, finalConfig);
1403
+ return canvas.convertToBlob({
1404
+ type: "image/png"
1405
+ });
1406
+ }
1407
+ /**
1408
+ * Render hash-derived art and return it as a data URL string.
1409
+ *
1410
+ * @param gitHash - Hex hash string used as the deterministic seed
1411
+ * @param config - Partial generation config (merged with defaults)
1412
+ * @returns A data:image/png;base64,… string
1413
+ */ function $59c8fbfd7fd6ed6a$export$ef85a3c26609a653(gitHash, config = {}) {
1414
+ const finalConfig = {
1415
+ ...(0, $81c1b644006d48ec$export$c2f8e0cc249a8d8f),
1416
+ ...config
1417
+ };
1418
+ const { width: width, height: height } = finalConfig;
1419
+ const canvas = document.createElement("canvas");
1420
+ canvas.width = width;
1421
+ canvas.height = height;
1422
+ const ctx = canvas.getContext("2d");
1423
+ if (!ctx) throw new Error("Failed to get 2D rendering context");
1424
+ (0, $1f63dc64b5593c73$export$29a844702096332e)(ctx, gitHash, finalConfig);
1425
+ return canvas.toDataURL("image/png");
1426
+ }
1427
+
1428
+
1429
+ export {$59c8fbfd7fd6ed6a$export$9379f53d1f28e74d as renderToCanvas, $59c8fbfd7fd6ed6a$export$ff855b1b91b18a57 as generateImageBlob, $59c8fbfd7fd6ed6a$export$ef85a3c26609a653 as generateDataURL, $1f63dc64b5593c73$export$29a844702096332e as renderHashArt, $e0ea14d2e2f06463$export$9265403940be6b4 as PRESETS, $81c1b644006d48ec$export$c2f8e0cc249a8d8f as DEFAULT_CONFIG};
1430
+ //# sourceMappingURL=browser.js.map