git-hash-art 0.7.0 → 0.9.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.
- package/ALGORITHM.md +323 -270
- package/CHANGELOG.md +18 -0
- package/bin/cli.js +17 -14
- package/bin/generateExamples.js +6 -14
- package/bin/generateVersionComparison.js +353 -0
- package/dist/browser.js +2398 -225
- package/dist/browser.js.map +1 -1
- package/dist/main.js +2398 -225
- package/dist/main.js.map +1 -1
- package/dist/module.js +2398 -225
- package/dist/module.js.map +1 -1
- package/package.json +2 -1
- package/src/lib/archetypes.ts +119 -0
- package/src/lib/canvas/colors.ts +110 -2
- package/src/lib/canvas/draw.ts +359 -9
- package/src/lib/canvas/shapes/affinity.ts +624 -0
- package/src/lib/canvas/shapes/procedural.ts +395 -32
- package/src/lib/render.ts +531 -155
package/dist/main.js
CHANGED
|
@@ -27,17 +27,22 @@ $parcel$export(module.exports, "DEFAULT_CONFIG", () => $93cf69256c93baa9$export$
|
|
|
27
27
|
* identically in Node (@napi-rs/canvas) and browsers.
|
|
28
28
|
*
|
|
29
29
|
* Generation pipeline:
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
30
|
+
* 0. Archetype selection + shape palette + color hierarchy
|
|
31
|
+
* 1. Background — style from archetype, gradient mesh for depth
|
|
32
|
+
* 1b. Layered background — archetype-coherent shapes
|
|
33
|
+
* 2. Composition mode + symmetry
|
|
34
|
+
* 3. Focal points + void zones + hero avoidance field
|
|
35
|
+
* 4. Flow field
|
|
36
|
+
* 4b. Hero shape
|
|
37
|
+
* 5. Shape layers — palette-driven selection, affinity-aware styles,
|
|
38
|
+
* size echo, tangent placement, atmospheric depth
|
|
37
39
|
* 5b. Recursive nesting
|
|
38
|
-
* 6. Flow
|
|
39
|
-
*
|
|
40
|
-
*
|
|
40
|
+
* 6. Flow lines — variable color, branching, pressure simulation
|
|
41
|
+
* 6b. Symmetry mirroring
|
|
42
|
+
* 7. Noise texture
|
|
43
|
+
* 8. Vignette
|
|
44
|
+
* 9. Organic connecting curves
|
|
45
|
+
* 10. Post-processing — color grading, chromatic aberration, bloom
|
|
41
46
|
*/
|
|
42
47
|
// declare module 'color-scheme';
|
|
43
48
|
|
|
@@ -411,6 +416,67 @@ function $d016ad53434219a1$export$f2121afcad3d553f(hex, alpha) {
|
|
|
411
416
|
const [r, g, b] = $d016ad53434219a1$var$hexToRgb(hex);
|
|
412
417
|
return `rgba(${r},${g},${b},${alpha.toFixed(3)})`;
|
|
413
418
|
}
|
|
419
|
+
function $d016ad53434219a1$export$fabac4600b87056(colors, rng) {
|
|
420
|
+
if (colors.length < 3) return {
|
|
421
|
+
dominant: colors[0] || "#888888",
|
|
422
|
+
secondary: colors[1] || colors[0] || "#888888",
|
|
423
|
+
accent: colors[colors.length - 1] || "#888888",
|
|
424
|
+
all: colors
|
|
425
|
+
};
|
|
426
|
+
// Pick dominant as the color closest to the palette's average hue
|
|
427
|
+
const hsls = colors.map((c)=>$d016ad53434219a1$var$hexToHsl(c));
|
|
428
|
+
const avgHue = hsls.reduce((s, h)=>s + h[0], 0) / hsls.length;
|
|
429
|
+
let dominantIdx = 0;
|
|
430
|
+
let minDist = 360;
|
|
431
|
+
for(let i = 0; i < hsls.length; i++){
|
|
432
|
+
const d = Math.min(Math.abs(hsls[i][0] - avgHue), 360 - Math.abs(hsls[i][0] - avgHue));
|
|
433
|
+
if (d < minDist) {
|
|
434
|
+
minDist = d;
|
|
435
|
+
dominantIdx = i;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
// Accent is the color most distant from dominant in hue
|
|
439
|
+
let accentIdx = 0;
|
|
440
|
+
let maxDist = 0;
|
|
441
|
+
for(let i = 0; i < hsls.length; i++){
|
|
442
|
+
if (i === dominantIdx) continue;
|
|
443
|
+
const d = Math.min(Math.abs(hsls[i][0] - hsls[dominantIdx][0]), 360 - Math.abs(hsls[i][0] - hsls[dominantIdx][0]));
|
|
444
|
+
if (d > maxDist) {
|
|
445
|
+
maxDist = d;
|
|
446
|
+
accentIdx = i;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
// Secondary is the remaining color with highest saturation
|
|
450
|
+
let secondaryIdx = 0;
|
|
451
|
+
let maxSat = -1;
|
|
452
|
+
for(let i = 0; i < hsls.length; i++){
|
|
453
|
+
if (i === dominantIdx || i === accentIdx) continue;
|
|
454
|
+
if (hsls[i][1] > maxSat) {
|
|
455
|
+
maxSat = hsls[i][1];
|
|
456
|
+
secondaryIdx = i;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
if (secondaryIdx === dominantIdx) secondaryIdx = accentIdx === 0 ? 1 : 0;
|
|
460
|
+
return {
|
|
461
|
+
dominant: colors[dominantIdx],
|
|
462
|
+
secondary: colors[secondaryIdx],
|
|
463
|
+
accent: colors[accentIdx],
|
|
464
|
+
all: colors
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
function $d016ad53434219a1$export$b49f62f0a99da0e8(hierarchy, rng) {
|
|
468
|
+
const roll = rng();
|
|
469
|
+
if (roll < 0.60) return hierarchy.dominant;
|
|
470
|
+
if (roll < 0.85) return hierarchy.secondary;
|
|
471
|
+
return hierarchy.accent;
|
|
472
|
+
}
|
|
473
|
+
function $d016ad53434219a1$export$18a34c25ea7e724b(hex, rng, hueAmount = 8, slAmount = 0.06) {
|
|
474
|
+
const [h, s, l] = $d016ad53434219a1$var$hexToHsl(hex);
|
|
475
|
+
const newH = (h + (rng() - 0.5) * hueAmount * 2 + 360) % 360;
|
|
476
|
+
const newS = Math.max(0, Math.min(1, s + (rng() - 0.5) * slAmount * 2));
|
|
477
|
+
const newL = Math.max(0, Math.min(1, l + (rng() - 0.5) * slAmount * 2));
|
|
478
|
+
return $d016ad53434219a1$var$hslToHex(newH, newS, newL);
|
|
479
|
+
}
|
|
414
480
|
function $d016ad53434219a1$export$59539d800dbe6858(hex, rng, amount = 0.1) {
|
|
415
481
|
const [r, g, b] = $d016ad53434219a1$var$hexToRgb(hex);
|
|
416
482
|
const jit = ()=>(rng() - 0.5) * 2 * amount * 255;
|
|
@@ -450,6 +516,31 @@ function $d016ad53434219a1$export$90ad0e6170cf6af5(fgHex, bgLuminance, minContra
|
|
|
450
516
|
return $d016ad53434219a1$var$hslToHex(h, targetS, targetL);
|
|
451
517
|
}
|
|
452
518
|
}
|
|
519
|
+
function $d016ad53434219a1$export$4a3734b8c4b5c0e(hex, gradeHue, intensity) {
|
|
520
|
+
const [h, s, l] = $d016ad53434219a1$var$hexToHsl(hex);
|
|
521
|
+
// Blend hue toward the grade hue
|
|
522
|
+
const hueDiff = (gradeHue - h + 540) % 360 - 180;
|
|
523
|
+
const newH = (h + hueDiff * intensity * 0.3 + 360) % 360;
|
|
524
|
+
// Slightly unify saturation
|
|
525
|
+
const newS = Math.max(0, Math.min(1, s + (0.5 - s) * intensity * 0.15));
|
|
526
|
+
return $d016ad53434219a1$var$hslToHex(newH, newS, l);
|
|
527
|
+
}
|
|
528
|
+
function $d016ad53434219a1$export$6d1620b367f86f7a(rng) {
|
|
529
|
+
// Warm golden, cool blue, rosy, teal, amber
|
|
530
|
+
const GRADE_HUES = [
|
|
531
|
+
40,
|
|
532
|
+
220,
|
|
533
|
+
340,
|
|
534
|
+
175,
|
|
535
|
+
30
|
|
536
|
+
];
|
|
537
|
+
const hue = GRADE_HUES[Math.floor(rng() * GRADE_HUES.length)] + (rng() - 0.5) * 20;
|
|
538
|
+
const intensity = 0.15 + rng() * 0.25;
|
|
539
|
+
return {
|
|
540
|
+
hue: (hue + 360) % 360,
|
|
541
|
+
intensity: intensity
|
|
542
|
+
};
|
|
543
|
+
}
|
|
453
544
|
|
|
454
545
|
|
|
455
546
|
|
|
@@ -1240,35 +1331,32 @@ const $dd5df256f00f6199$export$c2fc138f94dd4b2a = {
|
|
|
1240
1331
|
*/ const $6222456bc073291c$export$580f80cfb9de73bc = (ctx, size, config)=>{
|
|
1241
1332
|
const rng = config?.rng ?? Math.random;
|
|
1242
1333
|
const r = size / 2;
|
|
1243
|
-
const numPoints = 5 + Math.floor(rng() * 5);
|
|
1334
|
+
const numPoints = 5 + Math.floor(rng() * 5);
|
|
1244
1335
|
const points = [];
|
|
1245
1336
|
for(let i = 0; i < numPoints; i++){
|
|
1246
1337
|
const angle = i / numPoints * Math.PI * 2;
|
|
1247
|
-
const jitter = 0.5 + rng() * 0.5;
|
|
1338
|
+
const jitter = 0.5 + rng() * 0.5;
|
|
1248
1339
|
points.push({
|
|
1249
1340
|
x: Math.cos(angle) * r * jitter,
|
|
1250
1341
|
y: Math.sin(angle) * r * jitter
|
|
1251
1342
|
});
|
|
1252
1343
|
}
|
|
1253
1344
|
ctx.beginPath();
|
|
1254
|
-
// Start at midpoint between last and first point
|
|
1255
1345
|
const last = points[points.length - 1];
|
|
1256
1346
|
const first = points[0];
|
|
1257
1347
|
ctx.moveTo((last.x + first.x) / 2, (last.y + first.y) / 2);
|
|
1258
1348
|
for(let i = 0; i < numPoints; i++){
|
|
1259
1349
|
const curr = points[i];
|
|
1260
1350
|
const next = points[(i + 1) % numPoints];
|
|
1261
|
-
|
|
1262
|
-
const midY = (curr.y + next.y) / 2;
|
|
1263
|
-
ctx.quadraticCurveTo(curr.x, curr.y, midX, midY);
|
|
1351
|
+
ctx.quadraticCurveTo(curr.x, curr.y, (curr.x + next.x) / 2, (curr.y + next.y) / 2);
|
|
1264
1352
|
}
|
|
1265
1353
|
ctx.closePath();
|
|
1266
1354
|
};
|
|
1267
1355
|
const $6222456bc073291c$export$7a6094023f0902a6 = (ctx, size, config)=>{
|
|
1268
1356
|
const rng = config?.rng ?? Math.random;
|
|
1269
1357
|
const r = size / 2;
|
|
1270
|
-
const sides = 3 + Math.floor(rng() * 10);
|
|
1271
|
-
const jitterAmount = 0.1 + rng() * 0.4;
|
|
1358
|
+
const sides = 3 + Math.floor(rng() * 10);
|
|
1359
|
+
const jitterAmount = 0.1 + rng() * 0.4;
|
|
1272
1360
|
ctx.beginPath();
|
|
1273
1361
|
for(let i = 0; i < sides; i++){
|
|
1274
1362
|
const angle = i / sides * Math.PI * 2 - Math.PI / 2;
|
|
@@ -1283,10 +1371,9 @@ const $6222456bc073291c$export$7a6094023f0902a6 = (ctx, size, config)=>{
|
|
|
1283
1371
|
const $6222456bc073291c$export$ef56b4a8316e47d5 = (ctx, size, config)=>{
|
|
1284
1372
|
const rng = config?.rng ?? Math.random;
|
|
1285
1373
|
const r = size / 2;
|
|
1286
|
-
|
|
1287
|
-
const
|
|
1288
|
-
const
|
|
1289
|
-
const phase = rng() * Math.PI; // phase offset
|
|
1374
|
+
const freqA = 1 + Math.floor(rng() * 5);
|
|
1375
|
+
const freqB = 1 + Math.floor(rng() * 5);
|
|
1376
|
+
const phase = rng() * Math.PI;
|
|
1290
1377
|
const steps = 120;
|
|
1291
1378
|
ctx.beginPath();
|
|
1292
1379
|
for(let i = 0; i <= steps; i++){
|
|
@@ -1301,7 +1388,6 @@ const $6222456bc073291c$export$ef56b4a8316e47d5 = (ctx, size, config)=>{
|
|
|
1301
1388
|
const $6222456bc073291c$export$1db9219b4f34658c = (ctx, size, config)=>{
|
|
1302
1389
|
const rng = config?.rng ?? Math.random;
|
|
1303
1390
|
const r = size / 2;
|
|
1304
|
-
// Exponent range: 0.3 (spiky astroid) to 5 (rounded rectangle)
|
|
1305
1391
|
const n = 0.3 + rng() * 4.7;
|
|
1306
1392
|
const steps = 120;
|
|
1307
1393
|
ctx.beginPath();
|
|
@@ -1309,7 +1395,6 @@ const $6222456bc073291c$export$1db9219b4f34658c = (ctx, size, config)=>{
|
|
|
1309
1395
|
const t = i / steps * Math.PI * 2;
|
|
1310
1396
|
const cosT = Math.cos(t);
|
|
1311
1397
|
const sinT = Math.sin(t);
|
|
1312
|
-
// Superellipse parametric form
|
|
1313
1398
|
const x = Math.sign(cosT) * Math.pow(Math.abs(cosT), 2 / n) * r;
|
|
1314
1399
|
const y = Math.sign(sinT) * Math.pow(Math.abs(sinT), 2 / n) * r;
|
|
1315
1400
|
if (i === 0) ctx.moveTo(x, y);
|
|
@@ -1320,11 +1405,9 @@ const $6222456bc073291c$export$1db9219b4f34658c = (ctx, size, config)=>{
|
|
|
1320
1405
|
const $6222456bc073291c$export$b027c64d22b01985 = (ctx, size, config)=>{
|
|
1321
1406
|
const rng = config?.rng ?? Math.random;
|
|
1322
1407
|
const scale = size / 2;
|
|
1323
|
-
// R = outer radius, r = inner radius, d = pen distance from inner center
|
|
1324
1408
|
const R = 1;
|
|
1325
|
-
const r = 0.2 + rng() * 0.6;
|
|
1326
|
-
const d = 0.3 + rng() * 0.7;
|
|
1327
|
-
// Number of full rotations needed to close the curve
|
|
1409
|
+
const r = 0.2 + rng() * 0.6;
|
|
1410
|
+
const d = 0.3 + rng() * 0.7;
|
|
1328
1411
|
const gcd = (a, b)=>{
|
|
1329
1412
|
const ai = Math.round(a * 1000);
|
|
1330
1413
|
const bi = Math.round(b * 1000);
|
|
@@ -1332,7 +1415,7 @@ const $6222456bc073291c$export$b027c64d22b01985 = (ctx, size, config)=>{
|
|
|
1332
1415
|
return g(ai, bi) / 1000;
|
|
1333
1416
|
};
|
|
1334
1417
|
const period = r / gcd(R, r);
|
|
1335
|
-
const maxT = Math.min(period, 10) * Math.PI * 2;
|
|
1418
|
+
const maxT = Math.min(period, 10) * Math.PI * 2;
|
|
1336
1419
|
const steps = Math.min(600, Math.floor(maxT * 20));
|
|
1337
1420
|
ctx.beginPath();
|
|
1338
1421
|
for(let i = 0; i <= steps; i++){
|
|
@@ -1347,9 +1430,9 @@ const $6222456bc073291c$export$b027c64d22b01985 = (ctx, size, config)=>{
|
|
|
1347
1430
|
const $6222456bc073291c$export$7608ccd03bfb705d = (ctx, size, config)=>{
|
|
1348
1431
|
const rng = config?.rng ?? Math.random;
|
|
1349
1432
|
const r = size / 2;
|
|
1350
|
-
const rings = 2 + Math.floor(rng() * 4);
|
|
1351
|
-
const freq = 3 + Math.floor(rng() * 12);
|
|
1352
|
-
const amp = 0.05 + rng() * 0.15;
|
|
1433
|
+
const rings = 2 + Math.floor(rng() * 4);
|
|
1434
|
+
const freq = 3 + Math.floor(rng() * 12);
|
|
1435
|
+
const amp = 0.05 + rng() * 0.15;
|
|
1353
1436
|
ctx.beginPath();
|
|
1354
1437
|
for(let ring = 0; ring < rings; ring++){
|
|
1355
1438
|
const baseR = r * (0.3 + ring / rings * 0.7);
|
|
@@ -1367,7 +1450,7 @@ const $6222456bc073291c$export$7608ccd03bfb705d = (ctx, size, config)=>{
|
|
|
1367
1450
|
const $6222456bc073291c$export$11a377e7498bb523 = (ctx, size, config)=>{
|
|
1368
1451
|
const rng = config?.rng ?? Math.random;
|
|
1369
1452
|
const r = size / 2;
|
|
1370
|
-
const k = 2 + Math.floor(rng() * 6);
|
|
1453
|
+
const k = 2 + Math.floor(rng() * 6);
|
|
1371
1454
|
const steps = 200;
|
|
1372
1455
|
ctx.beginPath();
|
|
1373
1456
|
for(let i = 0; i <= steps; i++){
|
|
@@ -1380,6 +1463,308 @@ const $6222456bc073291c$export$11a377e7498bb523 = (ctx, size, config)=>{
|
|
|
1380
1463
|
}
|
|
1381
1464
|
ctx.closePath();
|
|
1382
1465
|
};
|
|
1466
|
+
const $6222456bc073291c$export$76b6526575ea179b = (ctx, size, config)=>{
|
|
1467
|
+
const rng = config?.rng ?? Math.random;
|
|
1468
|
+
const r = size / 2;
|
|
1469
|
+
const shardCount = 4 + Math.floor(rng() * 5); // 4-8 shards
|
|
1470
|
+
ctx.beginPath();
|
|
1471
|
+
for(let s = 0; s < shardCount; s++){
|
|
1472
|
+
const baseAngle = s / shardCount * Math.PI * 2 + (rng() - 0.5) * 0.3;
|
|
1473
|
+
const dist = r * (0.15 + rng() * 0.35);
|
|
1474
|
+
const cx = Math.cos(baseAngle) * dist;
|
|
1475
|
+
const cy = Math.sin(baseAngle) * dist;
|
|
1476
|
+
const shardSize = r * (0.2 + rng() * 0.4);
|
|
1477
|
+
const verts = 3 + Math.floor(rng() * 3); // 3-5 vertices per shard
|
|
1478
|
+
const shardAngleOffset = rng() * Math.PI * 2;
|
|
1479
|
+
for(let v = 0; v < verts; v++){
|
|
1480
|
+
const angle = shardAngleOffset + v / verts * Math.PI * 2;
|
|
1481
|
+
// Elongate shards along their radial direction
|
|
1482
|
+
const stretch = v % 2 === 0 ? 1.0 : 0.3 + rng() * 0.4;
|
|
1483
|
+
const px = cx + Math.cos(angle) * shardSize * stretch;
|
|
1484
|
+
const py = cy + Math.sin(angle) * shardSize * stretch;
|
|
1485
|
+
if (v === 0) ctx.moveTo(px, py);
|
|
1486
|
+
else ctx.lineTo(px, py);
|
|
1487
|
+
}
|
|
1488
|
+
ctx.closePath();
|
|
1489
|
+
}
|
|
1490
|
+
};
|
|
1491
|
+
const $6222456bc073291c$export$ed9ff98da5b05073 = (ctx, size, config)=>{
|
|
1492
|
+
const rng = config?.rng ?? Math.random;
|
|
1493
|
+
const r = size / 2;
|
|
1494
|
+
const edgeCount = 5 + Math.floor(rng() * 4); // 5-8 edges
|
|
1495
|
+
const points = [];
|
|
1496
|
+
// Generate edge midpoints at varying distances
|
|
1497
|
+
for(let i = 0; i < edgeCount; i++){
|
|
1498
|
+
const angle = i / edgeCount * Math.PI * 2 + (rng() - 0.5) * 0.4;
|
|
1499
|
+
const dist = r * (0.6 + rng() * 0.4);
|
|
1500
|
+
points.push({
|
|
1501
|
+
angle: angle,
|
|
1502
|
+
x: Math.cos(angle) * dist,
|
|
1503
|
+
y: Math.sin(angle) * dist
|
|
1504
|
+
});
|
|
1505
|
+
}
|
|
1506
|
+
// Sort by angle for proper winding
|
|
1507
|
+
points.sort((a, b)=>a.angle - b.angle);
|
|
1508
|
+
ctx.beginPath();
|
|
1509
|
+
ctx.moveTo(points[0].x, points[0].y);
|
|
1510
|
+
for(let i = 1; i < points.length; i++)ctx.lineTo(points[i].x, points[i].y);
|
|
1511
|
+
ctx.closePath();
|
|
1512
|
+
};
|
|
1513
|
+
const $6222456bc073291c$export$e0452d9a794fe7e5 = (ctx, size, config)=>{
|
|
1514
|
+
const rng = config?.rng ?? Math.random;
|
|
1515
|
+
const r = size / 2;
|
|
1516
|
+
const biteSize = 0.6 + rng() * 0.3; // 60-90% of radius
|
|
1517
|
+
const biteOffset = r * (0.3 + rng() * 0.4);
|
|
1518
|
+
const biteAngle = rng() * Math.PI * 2;
|
|
1519
|
+
// Outer circle
|
|
1520
|
+
ctx.beginPath();
|
|
1521
|
+
ctx.arc(0, 0, r, 0, Math.PI * 2);
|
|
1522
|
+
// Subtract inner circle using even-odd rule
|
|
1523
|
+
const bx = Math.cos(biteAngle) * biteOffset;
|
|
1524
|
+
const by = Math.sin(biteAngle) * biteOffset;
|
|
1525
|
+
// Draw inner circle counter-clockwise for subtraction
|
|
1526
|
+
ctx.moveTo(bx + r * biteSize, by);
|
|
1527
|
+
ctx.arc(bx, by, r * biteSize, 0, Math.PI * 2, true);
|
|
1528
|
+
};
|
|
1529
|
+
const $6222456bc073291c$export$38bfe5eb52137e01 = (ctx, size, config)=>{
|
|
1530
|
+
const rng = config?.rng ?? Math.random;
|
|
1531
|
+
const r = size / 2;
|
|
1532
|
+
const segments = 12 + Math.floor(rng() * 8);
|
|
1533
|
+
const startAngle = rng() * Math.PI * 2;
|
|
1534
|
+
const curvature = (rng() - 0.5) * 0.4;
|
|
1535
|
+
// Build spine points
|
|
1536
|
+
const spine = [];
|
|
1537
|
+
let angle = startAngle;
|
|
1538
|
+
let px = 0, py = 0;
|
|
1539
|
+
for(let i = 0; i <= segments; i++){
|
|
1540
|
+
spine.push({
|
|
1541
|
+
x: px,
|
|
1542
|
+
y: py
|
|
1543
|
+
});
|
|
1544
|
+
const stepLen = r / segments * (1.5 + rng() * 0.5);
|
|
1545
|
+
angle += curvature + (rng() - 0.5) * 0.6;
|
|
1546
|
+
px += Math.cos(angle) * stepLen;
|
|
1547
|
+
py += Math.sin(angle) * stepLen;
|
|
1548
|
+
}
|
|
1549
|
+
// Build tapered outline by offsetting perpendicular to spine
|
|
1550
|
+
ctx.beginPath();
|
|
1551
|
+
const leftSide = [];
|
|
1552
|
+
const rightSide = [];
|
|
1553
|
+
for(let i = 0; i < spine.length; i++){
|
|
1554
|
+
const t = i / (spine.length - 1);
|
|
1555
|
+
const width = r * 0.12 * (1 - t * 0.9); // taper from thick to thin
|
|
1556
|
+
const next = spine[Math.min(i + 1, spine.length - 1)];
|
|
1557
|
+
const dx = next.x - spine[i].x;
|
|
1558
|
+
const dy = next.y - spine[i].y;
|
|
1559
|
+
const len = Math.hypot(dx, dy) || 1;
|
|
1560
|
+
const nx = -dy / len;
|
|
1561
|
+
const ny = dx / len;
|
|
1562
|
+
leftSide.push({
|
|
1563
|
+
x: spine[i].x + nx * width,
|
|
1564
|
+
y: spine[i].y + ny * width
|
|
1565
|
+
});
|
|
1566
|
+
rightSide.push({
|
|
1567
|
+
x: spine[i].x - nx * width,
|
|
1568
|
+
y: spine[i].y - ny * width
|
|
1569
|
+
});
|
|
1570
|
+
}
|
|
1571
|
+
ctx.moveTo(leftSide[0].x, leftSide[0].y);
|
|
1572
|
+
for(let i = 1; i < leftSide.length; i++)ctx.lineTo(leftSide[i].x, leftSide[i].y);
|
|
1573
|
+
for(let i = rightSide.length - 1; i >= 0; i--)ctx.lineTo(rightSide[i].x, rightSide[i].y);
|
|
1574
|
+
ctx.closePath();
|
|
1575
|
+
};
|
|
1576
|
+
const $6222456bc073291c$export$105caa8cfd63c422 = (ctx, size, config)=>{
|
|
1577
|
+
const rng = config?.rng ?? Math.random;
|
|
1578
|
+
const r = size / 2;
|
|
1579
|
+
const lobeCount = 4 + Math.floor(rng() * 4); // 4-7 lobes
|
|
1580
|
+
const spineAngle = rng() * Math.PI * 2;
|
|
1581
|
+
const spineLen = r * 0.6;
|
|
1582
|
+
ctx.beginPath();
|
|
1583
|
+
for(let i = 0; i < lobeCount; i++){
|
|
1584
|
+
const t = i / (lobeCount - 1) - 0.5; // -0.5 to 0.5
|
|
1585
|
+
const sx = Math.cos(spineAngle) * spineLen * t;
|
|
1586
|
+
const sy = Math.sin(spineAngle) * spineLen * t;
|
|
1587
|
+
// Offset perpendicular for cloud shape
|
|
1588
|
+
const perpAngle = spineAngle + Math.PI / 2;
|
|
1589
|
+
const perpOff = (rng() - 0.3) * r * 0.3;
|
|
1590
|
+
const cx = sx + Math.cos(perpAngle) * perpOff;
|
|
1591
|
+
const cy = sy + Math.sin(perpAngle) * perpOff;
|
|
1592
|
+
const lobeR = r * (0.25 + rng() * 0.2);
|
|
1593
|
+
ctx.moveTo(cx + lobeR, cy);
|
|
1594
|
+
ctx.arc(cx, cy, lobeR, 0, Math.PI * 2);
|
|
1595
|
+
}
|
|
1596
|
+
};
|
|
1597
|
+
const $6222456bc073291c$export$e181e5bd3c539569 = (ctx, size, config)=>{
|
|
1598
|
+
const rng = config?.rng ?? Math.random;
|
|
1599
|
+
const r = size / 2;
|
|
1600
|
+
const spikeCount = 8 + Math.floor(rng() * 8); // 8-15 spikes
|
|
1601
|
+
const points = [];
|
|
1602
|
+
for(let i = 0; i < spikeCount; i++){
|
|
1603
|
+
const angle = i / spikeCount * Math.PI * 2;
|
|
1604
|
+
const isSpike = i % 2 === 0;
|
|
1605
|
+
const dist = isSpike ? r * (0.5 + rng() * 0.5 // spikes reach 50-100% of radius
|
|
1606
|
+
) : r * (0.15 + rng() * 0.2); // valleys at 15-35%
|
|
1607
|
+
points.push({
|
|
1608
|
+
x: Math.cos(angle) * dist,
|
|
1609
|
+
y: Math.sin(angle) * dist
|
|
1610
|
+
});
|
|
1611
|
+
}
|
|
1612
|
+
ctx.beginPath();
|
|
1613
|
+
ctx.moveTo(points[0].x, points[0].y);
|
|
1614
|
+
for(let i = 0; i < points.length; i++){
|
|
1615
|
+
const curr = points[i];
|
|
1616
|
+
const next = points[(i + 1) % points.length];
|
|
1617
|
+
const cpx = (curr.x + next.x) / 2 + (rng() - 0.5) * r * 0.15;
|
|
1618
|
+
const cpy = (curr.y + next.y) / 2 + (rng() - 0.5) * r * 0.15;
|
|
1619
|
+
ctx.quadraticCurveTo(cpx, cpy, next.x, next.y);
|
|
1620
|
+
}
|
|
1621
|
+
ctx.closePath();
|
|
1622
|
+
};
|
|
1623
|
+
const $6222456bc073291c$export$155b4780b4c6bb7b = (ctx, size, config)=>{
|
|
1624
|
+
const rng = config?.rng ?? Math.random;
|
|
1625
|
+
const r = size / 2;
|
|
1626
|
+
const subdivisions = 1 + Math.floor(rng() * 3); // 1-3
|
|
1627
|
+
// Start with icosahedron vertices projected to 2D
|
|
1628
|
+
const baseVerts = 6 + subdivisions * 4;
|
|
1629
|
+
const points = [];
|
|
1630
|
+
for(let i = 0; i < baseVerts; i++){
|
|
1631
|
+
const angle = i / baseVerts * Math.PI * 2;
|
|
1632
|
+
const ring = i % 2 === 0 ? 1.0 : 0.5 + rng() * 0.3;
|
|
1633
|
+
points.push({
|
|
1634
|
+
x: Math.cos(angle) * r * ring,
|
|
1635
|
+
y: Math.sin(angle) * r * ring
|
|
1636
|
+
});
|
|
1637
|
+
}
|
|
1638
|
+
ctx.beginPath();
|
|
1639
|
+
// Draw triangulated mesh — connect each point to neighbors and center
|
|
1640
|
+
for(let i = 0; i < points.length; i++){
|
|
1641
|
+
const next = points[(i + 1) % points.length];
|
|
1642
|
+
ctx.moveTo(points[i].x, points[i].y);
|
|
1643
|
+
ctx.lineTo(next.x, next.y);
|
|
1644
|
+
// Connect to center
|
|
1645
|
+
ctx.moveTo(points[i].x, points[i].y);
|
|
1646
|
+
ctx.lineTo(0, 0);
|
|
1647
|
+
// Cross-connect to create triangulation
|
|
1648
|
+
if (i % 2 === 0 && i + 2 < points.length) {
|
|
1649
|
+
ctx.moveTo(points[i].x, points[i].y);
|
|
1650
|
+
ctx.lineTo(points[i + 2].x, points[i + 2].y);
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
// Outer ring
|
|
1654
|
+
ctx.moveTo(points[0].x, points[0].y);
|
|
1655
|
+
for(let i = 1; i < points.length; i++)ctx.lineTo(points[i].x, points[i].y);
|
|
1656
|
+
ctx.closePath();
|
|
1657
|
+
};
|
|
1658
|
+
const $6222456bc073291c$export$9a7e648f11155172 = (ctx, size, config)=>{
|
|
1659
|
+
const rng = config?.rng ?? Math.random;
|
|
1660
|
+
const r = size / 2;
|
|
1661
|
+
const phi = (1 + Math.sqrt(5)) / 2; // golden ratio
|
|
1662
|
+
const isKite = rng() < 0.5;
|
|
1663
|
+
ctx.beginPath();
|
|
1664
|
+
if (isKite) {
|
|
1665
|
+
// Kite: two golden triangles joined at base
|
|
1666
|
+
const topY = -r;
|
|
1667
|
+
const bottomY = r * (1 / phi);
|
|
1668
|
+
const midY = r * (1 / phi - 1) * 0.3;
|
|
1669
|
+
const wingX = r * 0.6;
|
|
1670
|
+
ctx.moveTo(0, topY);
|
|
1671
|
+
ctx.lineTo(wingX, midY);
|
|
1672
|
+
ctx.lineTo(0, bottomY);
|
|
1673
|
+
ctx.lineTo(-wingX, midY);
|
|
1674
|
+
} else {
|
|
1675
|
+
// Dart: concave quadrilateral
|
|
1676
|
+
const topY = -r;
|
|
1677
|
+
const bottomY = r * 0.3;
|
|
1678
|
+
const midY = -r * 0.1;
|
|
1679
|
+
const wingX = r * 0.5;
|
|
1680
|
+
ctx.moveTo(0, topY);
|
|
1681
|
+
ctx.lineTo(wingX, midY);
|
|
1682
|
+
ctx.lineTo(0, bottomY);
|
|
1683
|
+
ctx.lineTo(-wingX, midY);
|
|
1684
|
+
}
|
|
1685
|
+
ctx.closePath();
|
|
1686
|
+
};
|
|
1687
|
+
const $6222456bc073291c$export$1fc0aedbabd73399 = (ctx, size, config)=>{
|
|
1688
|
+
const r = size / 2;
|
|
1689
|
+
// Vertices of equilateral triangle
|
|
1690
|
+
const verts = [];
|
|
1691
|
+
for(let i = 0; i < 3; i++){
|
|
1692
|
+
const angle = i / 3 * Math.PI * 2 - Math.PI / 2;
|
|
1693
|
+
verts.push({
|
|
1694
|
+
x: Math.cos(angle) * r * 0.7,
|
|
1695
|
+
y: Math.sin(angle) * r * 0.7
|
|
1696
|
+
});
|
|
1697
|
+
}
|
|
1698
|
+
// Side length = distance between vertices
|
|
1699
|
+
const sideLen = Math.hypot(verts[1].x - verts[0].x, verts[1].y - verts[0].y);
|
|
1700
|
+
ctx.beginPath();
|
|
1701
|
+
for(let i = 0; i < 3; i++){
|
|
1702
|
+
const from = verts[(i + 1) % 3];
|
|
1703
|
+
const to = verts[(i + 2) % 3];
|
|
1704
|
+
const center = verts[i];
|
|
1705
|
+
const startAngle = Math.atan2(from.y - center.y, from.x - center.x);
|
|
1706
|
+
const endAngle = Math.atan2(to.y - center.y, to.x - center.x);
|
|
1707
|
+
if (i === 0) ctx.moveTo(from.x, from.y);
|
|
1708
|
+
ctx.arc(center.x, center.y, sideLen, startAngle, endAngle);
|
|
1709
|
+
}
|
|
1710
|
+
ctx.closePath();
|
|
1711
|
+
};
|
|
1712
|
+
const $6222456bc073291c$export$ef7b5e0c19a21fd1 = (ctx, size, config)=>{
|
|
1713
|
+
const rng = config?.rng ?? Math.random;
|
|
1714
|
+
const r = size / 2;
|
|
1715
|
+
const dotCount = 15 + Math.floor(rng() * 25); // 15-39 dots
|
|
1716
|
+
const clusterTightness = 0.3 + rng() * 0.5;
|
|
1717
|
+
ctx.beginPath();
|
|
1718
|
+
for(let i = 0; i < dotCount; i++){
|
|
1719
|
+
// Gaussian-ish distribution via Box-Muller approximation
|
|
1720
|
+
const u1 = Math.max(0.001, rng());
|
|
1721
|
+
const u2 = rng();
|
|
1722
|
+
const mag = Math.sqrt(-2 * Math.log(u1)) * clusterTightness;
|
|
1723
|
+
const angle = u2 * Math.PI * 2;
|
|
1724
|
+
const dx = Math.cos(angle) * mag * r;
|
|
1725
|
+
const dy = Math.sin(angle) * mag * r;
|
|
1726
|
+
const dotR = r * (0.02 + rng() * 0.04);
|
|
1727
|
+
ctx.moveTo(dx + dotR, dy);
|
|
1728
|
+
ctx.arc(dx, dy, dotR, 0, Math.PI * 2);
|
|
1729
|
+
}
|
|
1730
|
+
};
|
|
1731
|
+
const $6222456bc073291c$export$f15df8ab60dfcc9a = (ctx, size, config)=>{
|
|
1732
|
+
const rng = config?.rng ?? Math.random;
|
|
1733
|
+
const r = size / 2;
|
|
1734
|
+
const angle1 = rng() * Math.PI;
|
|
1735
|
+
const angle2 = angle1 + Math.PI / 2 + (rng() - 0.5) * 0.3;
|
|
1736
|
+
const spacing = r * (0.08 + rng() * 0.08);
|
|
1737
|
+
const hasCross = rng() < 0.6;
|
|
1738
|
+
// Draw bounding shape (ellipse)
|
|
1739
|
+
const rx = r * (0.7 + rng() * 0.3);
|
|
1740
|
+
const ry = r * (0.5 + rng() * 0.3);
|
|
1741
|
+
// Outer boundary
|
|
1742
|
+
ctx.beginPath();
|
|
1743
|
+
ctx.ellipse(0, 0, rx, ry, 0, 0, Math.PI * 2);
|
|
1744
|
+
// Hatch lines clipped to the ellipse
|
|
1745
|
+
const cos1 = Math.cos(angle1);
|
|
1746
|
+
const sin1 = Math.sin(angle1);
|
|
1747
|
+
for(let d = -r; d <= r; d += spacing){
|
|
1748
|
+
const lx1 = d * cos1 - r * sin1;
|
|
1749
|
+
const ly1 = d * sin1 + r * cos1;
|
|
1750
|
+
const lx2 = d * cos1 + r * sin1;
|
|
1751
|
+
const ly2 = d * sin1 - r * cos1;
|
|
1752
|
+
ctx.moveTo(lx1, ly1);
|
|
1753
|
+
ctx.lineTo(lx2, ly2);
|
|
1754
|
+
}
|
|
1755
|
+
if (hasCross) {
|
|
1756
|
+
const cos2 = Math.cos(angle2);
|
|
1757
|
+
const sin2 = Math.sin(angle2);
|
|
1758
|
+
for(let d = -r; d <= r; d += spacing * 1.3){
|
|
1759
|
+
const lx1 = d * cos2 - r * sin2;
|
|
1760
|
+
const ly1 = d * sin2 + r * cos2;
|
|
1761
|
+
const lx2 = d * cos2 + r * sin2;
|
|
1762
|
+
const ly2 = d * sin2 - r * cos2;
|
|
1763
|
+
ctx.moveTo(lx1, ly1);
|
|
1764
|
+
ctx.lineTo(lx2, ly2);
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
};
|
|
1383
1768
|
const $6222456bc073291c$export$40cfb4c637f2fbb5 = {
|
|
1384
1769
|
blob: $6222456bc073291c$export$580f80cfb9de73bc,
|
|
1385
1770
|
ngon: $6222456bc073291c$export$7a6094023f0902a6,
|
|
@@ -1387,7 +1772,18 @@ const $6222456bc073291c$export$40cfb4c637f2fbb5 = {
|
|
|
1387
1772
|
superellipse: $6222456bc073291c$export$1db9219b4f34658c,
|
|
1388
1773
|
spirograph: $6222456bc073291c$export$b027c64d22b01985,
|
|
1389
1774
|
waveRing: $6222456bc073291c$export$7608ccd03bfb705d,
|
|
1390
|
-
rose: $6222456bc073291c$export$11a377e7498bb523
|
|
1775
|
+
rose: $6222456bc073291c$export$11a377e7498bb523,
|
|
1776
|
+
shardField: $6222456bc073291c$export$76b6526575ea179b,
|
|
1777
|
+
voronoiCell: $6222456bc073291c$export$ed9ff98da5b05073,
|
|
1778
|
+
crescent: $6222456bc073291c$export$e0452d9a794fe7e5,
|
|
1779
|
+
tendril: $6222456bc073291c$export$38bfe5eb52137e01,
|
|
1780
|
+
cloudForm: $6222456bc073291c$export$105caa8cfd63c422,
|
|
1781
|
+
inkSplat: $6222456bc073291c$export$e181e5bd3c539569,
|
|
1782
|
+
geodesicDome: $6222456bc073291c$export$155b4780b4c6bb7b,
|
|
1783
|
+
penroseTile: $6222456bc073291c$export$9a7e648f11155172,
|
|
1784
|
+
reuleauxTriangle: $6222456bc073291c$export$1fc0aedbabd73399,
|
|
1785
|
+
dotCluster: $6222456bc073291c$export$ef7b5e0c19a21fd1,
|
|
1786
|
+
crosshatchPatch: $6222456bc073291c$export$f15df8ab60dfcc9a
|
|
1391
1787
|
};
|
|
1392
1788
|
|
|
1393
1789
|
|
|
@@ -1422,7 +1818,13 @@ const $c3de8257a8baa3b0$var$RENDER_STYLES = [
|
|
|
1422
1818
|
"dashed",
|
|
1423
1819
|
"watercolor",
|
|
1424
1820
|
"hatched",
|
|
1425
|
-
"incomplete"
|
|
1821
|
+
"incomplete",
|
|
1822
|
+
"stipple",
|
|
1823
|
+
"stencil",
|
|
1824
|
+
"noise-grain",
|
|
1825
|
+
"wood-grain",
|
|
1826
|
+
"marble-vein",
|
|
1827
|
+
"fabric-weave"
|
|
1426
1828
|
];
|
|
1427
1829
|
function $c3de8257a8baa3b0$export$9fd4e64b2acd410e(rng) {
|
|
1428
1830
|
return $c3de8257a8baa3b0$var$RENDER_STYLES[Math.floor(rng() * $c3de8257a8baa3b0$var$RENDER_STYLES.length)];
|
|
@@ -1480,23 +1882,50 @@ function $c3de8257a8baa3b0$export$71b514a25c47df50(ctx, shape, x, y, config) {
|
|
|
1480
1882
|
break;
|
|
1481
1883
|
case "watercolor":
|
|
1482
1884
|
{
|
|
1483
|
-
//
|
|
1484
|
-
const passes =
|
|
1885
|
+
// Improved watercolor: edge darkening + radial bleed + layered washes
|
|
1886
|
+
const passes = 4 + (rng ? Math.floor(rng() * 2) : 0);
|
|
1485
1887
|
const savedAlpha = ctx.globalAlpha;
|
|
1486
|
-
|
|
1888
|
+
// Pass 1: Base wash — large, soft fill at low opacity
|
|
1889
|
+
ctx.globalAlpha = savedAlpha * 0.15;
|
|
1890
|
+
ctx.save();
|
|
1891
|
+
const baseScale = 1.08 + (rng ? rng() * 0.04 : 0);
|
|
1892
|
+
ctx.scale(baseScale, baseScale);
|
|
1893
|
+
ctx.fill();
|
|
1894
|
+
ctx.restore();
|
|
1895
|
+
// Pass 2: Multiple offset washes with radial displacement
|
|
1896
|
+
ctx.globalAlpha = savedAlpha * (0.25 / passes * 2);
|
|
1487
1897
|
for(let p = 0; p < passes; p++){
|
|
1488
|
-
|
|
1489
|
-
const
|
|
1898
|
+
// Radial outward displacement (not uniform) for organic bleed
|
|
1899
|
+
const angle = rng ? rng() * Math.PI * 2 : p * Math.PI / 2;
|
|
1900
|
+
const dist = rng ? rng() * size * 0.05 : size * 0.02;
|
|
1901
|
+
const jx = Math.cos(angle) * dist;
|
|
1902
|
+
const jy = Math.sin(angle) * dist;
|
|
1490
1903
|
ctx.save();
|
|
1491
1904
|
ctx.translate(jx, jy);
|
|
1492
1905
|
ctx.fill();
|
|
1493
1906
|
ctx.restore();
|
|
1494
1907
|
}
|
|
1908
|
+
// Pass 3: Edge darkening — draw a slightly smaller shape with lighter fill
|
|
1909
|
+
// to simulate pigment pooling at boundaries
|
|
1910
|
+
ctx.globalAlpha = savedAlpha * 0.35;
|
|
1911
|
+
ctx.save();
|
|
1912
|
+
const innerScale = 0.85 + (rng ? rng() * 0.08 : 0);
|
|
1913
|
+
ctx.scale(innerScale, innerScale);
|
|
1914
|
+
// Lighten the fill for the inner area
|
|
1915
|
+
const origFill = ctx.fillStyle;
|
|
1916
|
+
if (typeof fillColor === "string") ctx.fillStyle = fillColor.replace(/[\d.]+\)$/, (m)=>{
|
|
1917
|
+
const v = parseFloat(m);
|
|
1918
|
+
return Math.min(1, v * 1.4).toFixed(2) + ")";
|
|
1919
|
+
});
|
|
1920
|
+
ctx.fill();
|
|
1921
|
+
ctx.fillStyle = origFill;
|
|
1922
|
+
ctx.restore();
|
|
1495
1923
|
ctx.globalAlpha = savedAlpha;
|
|
1496
|
-
//
|
|
1497
|
-
ctx.globalAlpha *= 0.
|
|
1924
|
+
// Soft stroke on top — thinner than normal for delicacy
|
|
1925
|
+
ctx.globalAlpha *= 0.25;
|
|
1926
|
+
ctx.lineWidth = strokeWidth * 0.6;
|
|
1498
1927
|
ctx.stroke();
|
|
1499
|
-
ctx.globalAlpha /= 0.
|
|
1928
|
+
ctx.globalAlpha /= 0.25;
|
|
1500
1929
|
break;
|
|
1501
1930
|
}
|
|
1502
1931
|
case "hatched":
|
|
@@ -1569,6 +1998,213 @@ function $c3de8257a8baa3b0$export$71b514a25c47df50(ctx, shape, x, y, config) {
|
|
|
1569
1998
|
ctx.lineDashOffset = 0;
|
|
1570
1999
|
break;
|
|
1571
2000
|
}
|
|
2001
|
+
case "stipple":
|
|
2002
|
+
{
|
|
2003
|
+
// Dot-fill texture — clip to shape, then scatter dots
|
|
2004
|
+
const savedAlphaS = ctx.globalAlpha;
|
|
2005
|
+
ctx.globalAlpha = savedAlphaS * 0.15;
|
|
2006
|
+
ctx.fill(); // ghost fill
|
|
2007
|
+
ctx.globalAlpha = savedAlphaS;
|
|
2008
|
+
ctx.save();
|
|
2009
|
+
ctx.clip();
|
|
2010
|
+
const dotSpacing = Math.max(2, size * 0.03);
|
|
2011
|
+
const extent = size * 0.55;
|
|
2012
|
+
ctx.globalAlpha = savedAlphaS * 0.7;
|
|
2013
|
+
for(let dx = -extent; dx <= extent; dx += dotSpacing)for(let dy = -extent; dy <= extent; dy += dotSpacing){
|
|
2014
|
+
// Jitter each dot position for organic feel
|
|
2015
|
+
const jx = rng ? (rng() - 0.5) * dotSpacing * 0.6 : 0;
|
|
2016
|
+
const jy = rng ? (rng() - 0.5) * dotSpacing * 0.6 : 0;
|
|
2017
|
+
const dotR = rng ? dotSpacing * (0.15 + rng() * 0.2) : dotSpacing * 0.2;
|
|
2018
|
+
ctx.beginPath();
|
|
2019
|
+
ctx.arc(dx + jx, dy + jy, dotR, 0, Math.PI * 2);
|
|
2020
|
+
ctx.fill();
|
|
2021
|
+
}
|
|
2022
|
+
ctx.restore();
|
|
2023
|
+
ctx.globalAlpha = savedAlphaS;
|
|
2024
|
+
// Outline
|
|
2025
|
+
ctx.globalAlpha *= 0.4;
|
|
2026
|
+
ctx.stroke();
|
|
2027
|
+
ctx.globalAlpha /= 0.4;
|
|
2028
|
+
break;
|
|
2029
|
+
}
|
|
2030
|
+
case "stencil":
|
|
2031
|
+
{
|
|
2032
|
+
// Negative-space cutout — fill a rectangle, then erase the shape
|
|
2033
|
+
const savedAlphaSt = ctx.globalAlpha;
|
|
2034
|
+
// Fill a bounding area with the stroke color
|
|
2035
|
+
ctx.globalAlpha = savedAlphaSt * 0.5;
|
|
2036
|
+
ctx.fillStyle = strokeColor;
|
|
2037
|
+
ctx.fillRect(-size * 0.6, -size * 0.6, size * 1.2, size * 1.2);
|
|
2038
|
+
// Cut out the shape using destination-out
|
|
2039
|
+
ctx.globalCompositeOperation = "destination-out";
|
|
2040
|
+
ctx.globalAlpha = 1;
|
|
2041
|
+
ctx.fill();
|
|
2042
|
+
ctx.globalCompositeOperation = "source-over";
|
|
2043
|
+
ctx.globalAlpha = savedAlphaSt;
|
|
2044
|
+
// Subtle outline of the cutout
|
|
2045
|
+
ctx.globalAlpha *= 0.3;
|
|
2046
|
+
ctx.stroke();
|
|
2047
|
+
ctx.globalAlpha /= 0.3;
|
|
2048
|
+
break;
|
|
2049
|
+
}
|
|
2050
|
+
case "noise-grain":
|
|
2051
|
+
{
|
|
2052
|
+
// Procedural noise grain texture clipped to shape boundary
|
|
2053
|
+
const savedAlphaN = ctx.globalAlpha;
|
|
2054
|
+
ctx.globalAlpha = savedAlphaN * 0.25;
|
|
2055
|
+
ctx.fill(); // base tint
|
|
2056
|
+
ctx.globalAlpha = savedAlphaN;
|
|
2057
|
+
ctx.save();
|
|
2058
|
+
ctx.clip();
|
|
2059
|
+
const grainSpacing = Math.max(1.5, size * 0.015);
|
|
2060
|
+
const extentN = size * 0.55;
|
|
2061
|
+
ctx.globalAlpha = savedAlphaN * 0.6;
|
|
2062
|
+
for(let gx = -extentN; gx <= extentN; gx += grainSpacing)for(let gy = -extentN; gy <= extentN; gy += grainSpacing){
|
|
2063
|
+
if (!rng) break;
|
|
2064
|
+
const jx = (rng() - 0.5) * grainSpacing * 1.2;
|
|
2065
|
+
const jy = (rng() - 0.5) * grainSpacing * 1.2;
|
|
2066
|
+
const brightness = rng() > 0.5 ? 255 : 0;
|
|
2067
|
+
const dotAlpha = 0.15 + rng() * 0.35;
|
|
2068
|
+
ctx.globalAlpha = savedAlphaN * dotAlpha;
|
|
2069
|
+
ctx.fillStyle = `rgba(${brightness},${brightness},${brightness},1)`;
|
|
2070
|
+
const dotSize = grainSpacing * (0.3 + rng() * 0.5);
|
|
2071
|
+
ctx.fillRect(gx + jx, gy + jy, dotSize, dotSize);
|
|
2072
|
+
}
|
|
2073
|
+
ctx.restore();
|
|
2074
|
+
ctx.fillStyle = fillColor;
|
|
2075
|
+
ctx.globalAlpha = savedAlphaN;
|
|
2076
|
+
ctx.globalAlpha *= 0.4;
|
|
2077
|
+
ctx.stroke();
|
|
2078
|
+
ctx.globalAlpha /= 0.4;
|
|
2079
|
+
break;
|
|
2080
|
+
}
|
|
2081
|
+
case "wood-grain":
|
|
2082
|
+
{
|
|
2083
|
+
// Parallel wavy lines simulating wood grain, clipped to shape
|
|
2084
|
+
const savedAlphaW = ctx.globalAlpha;
|
|
2085
|
+
ctx.globalAlpha = savedAlphaW * 0.2;
|
|
2086
|
+
ctx.fill(); // base tint
|
|
2087
|
+
ctx.globalAlpha = savedAlphaW;
|
|
2088
|
+
ctx.save();
|
|
2089
|
+
ctx.clip();
|
|
2090
|
+
const grainLineSpacing = Math.max(2, size * 0.035);
|
|
2091
|
+
const extentW = size * 0.55;
|
|
2092
|
+
const waveFreq = rng ? 3 + rng() * 5 : 5;
|
|
2093
|
+
const waveAmp = rng ? size * (0.01 + rng() * 0.03) : size * 0.02;
|
|
2094
|
+
const grainAngle = rng ? rng() * Math.PI : Math.PI * 0.25;
|
|
2095
|
+
ctx.lineWidth = Math.max(0.5, strokeWidth * 0.3);
|
|
2096
|
+
ctx.globalAlpha = savedAlphaW * 0.5;
|
|
2097
|
+
const cosG = Math.cos(grainAngle);
|
|
2098
|
+
const sinG = Math.sin(grainAngle);
|
|
2099
|
+
for(let d = -extentW; d <= extentW; d += grainLineSpacing){
|
|
2100
|
+
ctx.beginPath();
|
|
2101
|
+
for(let t = -extentW; t <= extentW; t += 2){
|
|
2102
|
+
const wave = Math.sin(t / extentW * waveFreq * Math.PI) * waveAmp;
|
|
2103
|
+
const px = t * cosG - (d + wave) * sinG;
|
|
2104
|
+
const py = t * sinG + (d + wave) * cosG;
|
|
2105
|
+
if (t === -extentW) ctx.moveTo(px, py);
|
|
2106
|
+
else ctx.lineTo(px, py);
|
|
2107
|
+
}
|
|
2108
|
+
ctx.stroke();
|
|
2109
|
+
}
|
|
2110
|
+
ctx.restore();
|
|
2111
|
+
ctx.globalAlpha = savedAlphaW;
|
|
2112
|
+
ctx.globalAlpha *= 0.35;
|
|
2113
|
+
ctx.stroke();
|
|
2114
|
+
ctx.globalAlpha /= 0.35;
|
|
2115
|
+
break;
|
|
2116
|
+
}
|
|
2117
|
+
case "marble-vein":
|
|
2118
|
+
{
|
|
2119
|
+
// Branching vein lines on a soft fill, clipped to shape
|
|
2120
|
+
const savedAlphaM = ctx.globalAlpha;
|
|
2121
|
+
ctx.globalAlpha = savedAlphaM * 0.35;
|
|
2122
|
+
ctx.fill(); // soft base
|
|
2123
|
+
ctx.globalAlpha = savedAlphaM;
|
|
2124
|
+
ctx.save();
|
|
2125
|
+
ctx.clip();
|
|
2126
|
+
const veinCount = rng ? 2 + Math.floor(rng() * 3) : 3;
|
|
2127
|
+
const extentM = size * 0.45;
|
|
2128
|
+
ctx.lineWidth = Math.max(0.5, strokeWidth * 0.5);
|
|
2129
|
+
ctx.globalAlpha = savedAlphaM * 0.4;
|
|
2130
|
+
for(let v = 0; v < veinCount; v++){
|
|
2131
|
+
const startX = rng ? (rng() - 0.5) * extentM * 2 : 0;
|
|
2132
|
+
const startY = rng ? -extentM + rng() * extentM * 0.5 : -extentM;
|
|
2133
|
+
let vx = startX;
|
|
2134
|
+
let vy = startY;
|
|
2135
|
+
const steps = 15 + (rng ? Math.floor(rng() * 15) : 10);
|
|
2136
|
+
const stepLen = size * 0.04;
|
|
2137
|
+
ctx.beginPath();
|
|
2138
|
+
ctx.moveTo(vx, vy);
|
|
2139
|
+
for(let s = 0; s < steps; s++){
|
|
2140
|
+
const drift = rng ? (rng() - 0.5) * stepLen * 1.5 : 0;
|
|
2141
|
+
vx += drift;
|
|
2142
|
+
vy += stepLen;
|
|
2143
|
+
ctx.lineTo(vx, vy);
|
|
2144
|
+
// Branch ~20% of the time
|
|
2145
|
+
if (rng && rng() < 0.2 && s > 2 && s < steps - 3) {
|
|
2146
|
+
const branchDir = rng() < 0.5 ? -1 : 1;
|
|
2147
|
+
let bx = vx;
|
|
2148
|
+
let by = vy;
|
|
2149
|
+
const bSteps = 3 + Math.floor(rng() * 5);
|
|
2150
|
+
ctx.moveTo(bx, by);
|
|
2151
|
+
for(let bs = 0; bs < bSteps; bs++){
|
|
2152
|
+
bx += branchDir * stepLen * (0.5 + rng() * 0.5);
|
|
2153
|
+
by += stepLen * 0.6;
|
|
2154
|
+
ctx.lineTo(bx, by);
|
|
2155
|
+
}
|
|
2156
|
+
ctx.moveTo(vx, vy); // return to main vein
|
|
2157
|
+
}
|
|
2158
|
+
}
|
|
2159
|
+
ctx.stroke();
|
|
2160
|
+
}
|
|
2161
|
+
ctx.restore();
|
|
2162
|
+
ctx.globalAlpha = savedAlphaM;
|
|
2163
|
+
ctx.globalAlpha *= 0.3;
|
|
2164
|
+
ctx.stroke();
|
|
2165
|
+
ctx.globalAlpha /= 0.3;
|
|
2166
|
+
break;
|
|
2167
|
+
}
|
|
2168
|
+
case "fabric-weave":
|
|
2169
|
+
{
|
|
2170
|
+
// Interlocking horizontal/vertical threads clipped to shape
|
|
2171
|
+
const savedAlphaF = ctx.globalAlpha;
|
|
2172
|
+
ctx.globalAlpha = savedAlphaF * 0.15;
|
|
2173
|
+
ctx.fill(); // ghost base
|
|
2174
|
+
ctx.globalAlpha = savedAlphaF;
|
|
2175
|
+
ctx.save();
|
|
2176
|
+
ctx.clip();
|
|
2177
|
+
const threadSpacing = Math.max(2, size * 0.04);
|
|
2178
|
+
const extentF = size * 0.55;
|
|
2179
|
+
ctx.lineWidth = Math.max(0.8, threadSpacing * 0.5);
|
|
2180
|
+
ctx.globalAlpha = savedAlphaF * 0.55;
|
|
2181
|
+
// Horizontal threads
|
|
2182
|
+
for(let y = -extentF; y <= extentF; y += threadSpacing * 2){
|
|
2183
|
+
ctx.beginPath();
|
|
2184
|
+
ctx.moveTo(-extentF, y);
|
|
2185
|
+
ctx.lineTo(extentF, y);
|
|
2186
|
+
ctx.stroke();
|
|
2187
|
+
}
|
|
2188
|
+
// Vertical threads (offset by half spacing for weave effect)
|
|
2189
|
+
ctx.globalAlpha = savedAlphaF * 0.45;
|
|
2190
|
+
ctx.strokeStyle = fillColor;
|
|
2191
|
+
for(let x = -extentF; x <= extentF; x += threadSpacing * 2){
|
|
2192
|
+
ctx.beginPath();
|
|
2193
|
+
for(let y = -extentF; y <= extentF; y += threadSpacing * 2){
|
|
2194
|
+
// Over-under: draw segment, skip segment
|
|
2195
|
+
ctx.moveTo(x, y);
|
|
2196
|
+
ctx.lineTo(x, y + threadSpacing);
|
|
2197
|
+
}
|
|
2198
|
+
ctx.stroke();
|
|
2199
|
+
}
|
|
2200
|
+
ctx.strokeStyle = strokeColor;
|
|
2201
|
+
ctx.restore();
|
|
2202
|
+
ctx.globalAlpha = savedAlphaF;
|
|
2203
|
+
ctx.globalAlpha *= 0.3;
|
|
2204
|
+
ctx.stroke();
|
|
2205
|
+
ctx.globalAlpha /= 0.3;
|
|
2206
|
+
break;
|
|
2207
|
+
}
|
|
1572
2208
|
case "fill-and-stroke":
|
|
1573
2209
|
default:
|
|
1574
2210
|
ctx.fill();
|
|
@@ -1615,77 +2251,1076 @@ function $c3de8257a8baa3b0$export$bb35a6995ddbf32d(ctx, shape, x, y, config) {
|
|
|
1615
2251
|
});
|
|
1616
2252
|
ctx.restore();
|
|
1617
2253
|
}
|
|
2254
|
+
function $c3de8257a8baa3b0$export$8bd8bbd1a8e53689(ctx, shape, x, y, config) {
|
|
2255
|
+
const { mirrorAxis: mirrorAxis = "horizontal", mirrorGap: mirrorGap = 0 } = config;
|
|
2256
|
+
// Draw the primary shape
|
|
2257
|
+
$c3de8257a8baa3b0$export$bb35a6995ddbf32d(ctx, shape, x, y, config);
|
|
2258
|
+
// Draw the mirrored copy
|
|
2259
|
+
ctx.save();
|
|
2260
|
+
const savedAlpha = ctx.globalAlpha;
|
|
2261
|
+
ctx.globalAlpha = savedAlpha * 0.7; // mirror is slightly softer
|
|
2262
|
+
switch(mirrorAxis){
|
|
2263
|
+
case "horizontal":
|
|
2264
|
+
// Reflect across vertical axis at shape position
|
|
2265
|
+
$c3de8257a8baa3b0$export$bb35a6995ddbf32d(ctx, shape, x, y + mirrorGap, {
|
|
2266
|
+
...config,
|
|
2267
|
+
rotation: -(config.rotation || 0),
|
|
2268
|
+
size: config.size * 0.95
|
|
2269
|
+
});
|
|
2270
|
+
break;
|
|
2271
|
+
case "vertical":
|
|
2272
|
+
$c3de8257a8baa3b0$export$bb35a6995ddbf32d(ctx, shape, x + mirrorGap, y, {
|
|
2273
|
+
...config,
|
|
2274
|
+
rotation: 180 - (config.rotation || 0),
|
|
2275
|
+
size: config.size * 0.95
|
|
2276
|
+
});
|
|
2277
|
+
break;
|
|
2278
|
+
case "diagonal":
|
|
2279
|
+
// Reflect across 45° axis
|
|
2280
|
+
$c3de8257a8baa3b0$export$bb35a6995ddbf32d(ctx, shape, x + mirrorGap * 0.7, y + mirrorGap * 0.7, {
|
|
2281
|
+
...config,
|
|
2282
|
+
rotation: 90 - (config.rotation || 0),
|
|
2283
|
+
size: config.size * 0.9
|
|
2284
|
+
});
|
|
2285
|
+
break;
|
|
2286
|
+
case "radial-4":
|
|
2287
|
+
// Four-way radial mirror
|
|
2288
|
+
for(let i = 1; i < 4; i++){
|
|
2289
|
+
const angle = i / 4 * Math.PI * 2;
|
|
2290
|
+
const mx = x + Math.cos(angle) * mirrorGap;
|
|
2291
|
+
const my = y + Math.sin(angle) * mirrorGap;
|
|
2292
|
+
ctx.globalAlpha = savedAlpha * (0.7 - i * 0.1);
|
|
2293
|
+
$c3de8257a8baa3b0$export$bb35a6995ddbf32d(ctx, shape, mx, my, {
|
|
2294
|
+
...config,
|
|
2295
|
+
rotation: (config.rotation || 0) + i * 90,
|
|
2296
|
+
size: config.size * (0.95 - i * 0.05)
|
|
2297
|
+
});
|
|
2298
|
+
}
|
|
2299
|
+
break;
|
|
2300
|
+
}
|
|
2301
|
+
ctx.globalAlpha = savedAlpha;
|
|
2302
|
+
ctx.restore();
|
|
2303
|
+
}
|
|
2304
|
+
function $c3de8257a8baa3b0$export$879206e23912d1a9(rng) {
|
|
2305
|
+
const roll = rng();
|
|
2306
|
+
if (roll < 0.60) return null;
|
|
2307
|
+
if (roll < 0.75) return "horizontal";
|
|
2308
|
+
if (roll < 0.87) return "vertical";
|
|
2309
|
+
if (roll < 0.95) return "diagonal";
|
|
2310
|
+
return "radial-4";
|
|
2311
|
+
}
|
|
1618
2312
|
|
|
1619
2313
|
|
|
1620
2314
|
|
|
1621
|
-
|
|
1622
|
-
/**
|
|
1623
|
-
* Configuration options for image generation.
|
|
1624
|
-
*/ const $93cf69256c93baa9$export$c2f8e0cc249a8d8f = {
|
|
1625
|
-
width: 2048,
|
|
1626
|
-
height: 2048,
|
|
1627
|
-
gridSize: 5,
|
|
1628
|
-
layers: 4,
|
|
1629
|
-
minShapeSize: 30,
|
|
1630
|
-
maxShapeSize: 400,
|
|
1631
|
-
baseOpacity: 0.7,
|
|
1632
|
-
opacityReduction: 0.12,
|
|
1633
|
-
shapesPerLayer: 0
|
|
1634
|
-
};
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
2315
|
/**
|
|
1638
|
-
*
|
|
1639
|
-
*
|
|
2316
|
+
* Shape affinity system — controls which shapes look good together,
|
|
2317
|
+
* quality tiers for different rendering contexts, and size preferences.
|
|
1640
2318
|
*
|
|
1641
|
-
*
|
|
1642
|
-
* that
|
|
1643
|
-
*/ // ──
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
"
|
|
1657
|
-
"
|
|
1658
|
-
"
|
|
2319
|
+
* This replaces the naive "pick any shape" approach with intentional
|
|
2320
|
+
* curation that produces more cohesive compositions.
|
|
2321
|
+
*/ // ── Quality tiers ───────────────────────────────────────────────────
|
|
2322
|
+
// Not all shapes render equally well at all sizes or in all contexts.
|
|
2323
|
+
// Tier 1 shapes are visually strong at any size; Tier 3 shapes need
|
|
2324
|
+
// specific conditions to look good.
|
|
2325
|
+
const $e73976f898150d4d$export$4343b39fe47bd82c = {
|
|
2326
|
+
// ── Basic shapes ──────────────────────────────────────────────
|
|
2327
|
+
circle: {
|
|
2328
|
+
tier: 1,
|
|
2329
|
+
minSizeFraction: 0.05,
|
|
2330
|
+
maxSizeFraction: 1.0,
|
|
2331
|
+
affinities: [
|
|
2332
|
+
"circle",
|
|
2333
|
+
"blob",
|
|
2334
|
+
"hexagon",
|
|
2335
|
+
"flowerOfLife",
|
|
2336
|
+
"seedOfLife"
|
|
1659
2337
|
],
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
2338
|
+
category: "basic",
|
|
2339
|
+
heroCandidate: false,
|
|
2340
|
+
bestStyles: [
|
|
2341
|
+
"fill-only",
|
|
2342
|
+
"watercolor",
|
|
2343
|
+
"fill-and-stroke"
|
|
2344
|
+
]
|
|
1665
2345
|
},
|
|
1666
|
-
{
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
2346
|
+
square: {
|
|
2347
|
+
tier: 2,
|
|
2348
|
+
minSizeFraction: 0.08,
|
|
2349
|
+
maxSizeFraction: 0.7,
|
|
2350
|
+
affinities: [
|
|
2351
|
+
"square",
|
|
2352
|
+
"diamond",
|
|
2353
|
+
"superellipse",
|
|
2354
|
+
"islamicPattern"
|
|
2355
|
+
],
|
|
2356
|
+
category: "basic",
|
|
2357
|
+
heroCandidate: false,
|
|
2358
|
+
bestStyles: [
|
|
1677
2359
|
"fill-and-stroke",
|
|
1678
2360
|
"stroke-only",
|
|
1679
|
-
"
|
|
2361
|
+
"hatched"
|
|
2362
|
+
]
|
|
2363
|
+
},
|
|
2364
|
+
triangle: {
|
|
2365
|
+
tier: 1,
|
|
2366
|
+
minSizeFraction: 0.06,
|
|
2367
|
+
maxSizeFraction: 0.9,
|
|
2368
|
+
affinities: [
|
|
2369
|
+
"triangle",
|
|
2370
|
+
"diamond",
|
|
2371
|
+
"hexagon",
|
|
2372
|
+
"merkaba",
|
|
2373
|
+
"sriYantra"
|
|
1680
2374
|
],
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
2375
|
+
category: "basic",
|
|
2376
|
+
heroCandidate: false,
|
|
2377
|
+
bestStyles: [
|
|
2378
|
+
"fill-and-stroke",
|
|
2379
|
+
"fill-only",
|
|
2380
|
+
"watercolor"
|
|
2381
|
+
]
|
|
1686
2382
|
},
|
|
1687
|
-
{
|
|
1688
|
-
|
|
2383
|
+
hexagon: {
|
|
2384
|
+
tier: 1,
|
|
2385
|
+
minSizeFraction: 0.05,
|
|
2386
|
+
maxSizeFraction: 1.0,
|
|
2387
|
+
affinities: [
|
|
2388
|
+
"hexagon",
|
|
2389
|
+
"circle",
|
|
2390
|
+
"flowerOfLife",
|
|
2391
|
+
"metatronsCube",
|
|
2392
|
+
"triangle"
|
|
2393
|
+
],
|
|
2394
|
+
category: "basic",
|
|
2395
|
+
heroCandidate: false,
|
|
2396
|
+
bestStyles: [
|
|
2397
|
+
"fill-only",
|
|
2398
|
+
"fill-and-stroke",
|
|
2399
|
+
"watercolor"
|
|
2400
|
+
]
|
|
2401
|
+
},
|
|
2402
|
+
star: {
|
|
2403
|
+
tier: 2,
|
|
2404
|
+
minSizeFraction: 0.08,
|
|
2405
|
+
maxSizeFraction: 0.6,
|
|
2406
|
+
affinities: [
|
|
2407
|
+
"star",
|
|
2408
|
+
"circle",
|
|
2409
|
+
"mandala",
|
|
2410
|
+
"spirograph"
|
|
2411
|
+
],
|
|
2412
|
+
category: "basic",
|
|
2413
|
+
heroCandidate: false,
|
|
2414
|
+
bestStyles: [
|
|
2415
|
+
"fill-and-stroke",
|
|
2416
|
+
"stroke-only",
|
|
2417
|
+
"dashed"
|
|
2418
|
+
]
|
|
2419
|
+
},
|
|
2420
|
+
"jacked-star": {
|
|
2421
|
+
tier: 3,
|
|
2422
|
+
minSizeFraction: 0.1,
|
|
2423
|
+
maxSizeFraction: 0.4,
|
|
2424
|
+
affinities: [
|
|
2425
|
+
"star",
|
|
2426
|
+
"circle"
|
|
2427
|
+
],
|
|
2428
|
+
category: "basic",
|
|
2429
|
+
heroCandidate: false,
|
|
2430
|
+
bestStyles: [
|
|
2431
|
+
"stroke-only",
|
|
2432
|
+
"dashed"
|
|
2433
|
+
]
|
|
2434
|
+
},
|
|
2435
|
+
heart: {
|
|
2436
|
+
tier: 3,
|
|
2437
|
+
minSizeFraction: 0.1,
|
|
2438
|
+
maxSizeFraction: 0.5,
|
|
2439
|
+
affinities: [
|
|
2440
|
+
"circle",
|
|
2441
|
+
"blob"
|
|
2442
|
+
],
|
|
2443
|
+
category: "basic",
|
|
2444
|
+
heroCandidate: false,
|
|
2445
|
+
bestStyles: [
|
|
2446
|
+
"fill-only",
|
|
2447
|
+
"watercolor"
|
|
2448
|
+
]
|
|
2449
|
+
},
|
|
2450
|
+
diamond: {
|
|
2451
|
+
tier: 2,
|
|
2452
|
+
minSizeFraction: 0.06,
|
|
2453
|
+
maxSizeFraction: 0.8,
|
|
2454
|
+
affinities: [
|
|
2455
|
+
"diamond",
|
|
2456
|
+
"triangle",
|
|
2457
|
+
"square",
|
|
2458
|
+
"merkaba"
|
|
2459
|
+
],
|
|
2460
|
+
category: "basic",
|
|
2461
|
+
heroCandidate: false,
|
|
2462
|
+
bestStyles: [
|
|
2463
|
+
"fill-and-stroke",
|
|
2464
|
+
"fill-only",
|
|
2465
|
+
"double-stroke"
|
|
2466
|
+
]
|
|
2467
|
+
},
|
|
2468
|
+
cube: {
|
|
2469
|
+
tier: 3,
|
|
2470
|
+
minSizeFraction: 0.08,
|
|
2471
|
+
maxSizeFraction: 0.5,
|
|
2472
|
+
affinities: [
|
|
2473
|
+
"square",
|
|
2474
|
+
"diamond"
|
|
2475
|
+
],
|
|
2476
|
+
category: "basic",
|
|
2477
|
+
heroCandidate: false,
|
|
2478
|
+
bestStyles: [
|
|
2479
|
+
"stroke-only",
|
|
2480
|
+
"fill-and-stroke"
|
|
2481
|
+
]
|
|
2482
|
+
},
|
|
2483
|
+
// ── Complex shapes ────────────────────────────────────────────
|
|
2484
|
+
platonicSolid: {
|
|
2485
|
+
tier: 2,
|
|
2486
|
+
minSizeFraction: 0.15,
|
|
2487
|
+
maxSizeFraction: 0.8,
|
|
2488
|
+
affinities: [
|
|
2489
|
+
"metatronsCube",
|
|
2490
|
+
"merkaba",
|
|
2491
|
+
"hexagon",
|
|
2492
|
+
"triangle"
|
|
2493
|
+
],
|
|
2494
|
+
category: "complex",
|
|
2495
|
+
heroCandidate: true,
|
|
2496
|
+
bestStyles: [
|
|
2497
|
+
"stroke-only",
|
|
2498
|
+
"double-stroke",
|
|
2499
|
+
"dashed"
|
|
2500
|
+
]
|
|
2501
|
+
},
|
|
2502
|
+
fibonacciSpiral: {
|
|
2503
|
+
tier: 1,
|
|
2504
|
+
minSizeFraction: 0.2,
|
|
2505
|
+
maxSizeFraction: 1.0,
|
|
2506
|
+
affinities: [
|
|
2507
|
+
"circle",
|
|
2508
|
+
"rose",
|
|
2509
|
+
"spirograph",
|
|
2510
|
+
"flowerOfLife"
|
|
2511
|
+
],
|
|
2512
|
+
category: "complex",
|
|
2513
|
+
heroCandidate: true,
|
|
2514
|
+
bestStyles: [
|
|
2515
|
+
"stroke-only",
|
|
2516
|
+
"incomplete",
|
|
2517
|
+
"watercolor"
|
|
2518
|
+
]
|
|
2519
|
+
},
|
|
2520
|
+
islamicPattern: {
|
|
2521
|
+
tier: 2,
|
|
2522
|
+
minSizeFraction: 0.25,
|
|
2523
|
+
maxSizeFraction: 0.9,
|
|
2524
|
+
affinities: [
|
|
2525
|
+
"square",
|
|
2526
|
+
"hexagon",
|
|
2527
|
+
"star",
|
|
2528
|
+
"mandala"
|
|
2529
|
+
],
|
|
2530
|
+
category: "complex",
|
|
2531
|
+
heroCandidate: true,
|
|
2532
|
+
bestStyles: [
|
|
2533
|
+
"stroke-only",
|
|
2534
|
+
"dashed",
|
|
2535
|
+
"hatched"
|
|
2536
|
+
]
|
|
2537
|
+
},
|
|
2538
|
+
celticKnot: {
|
|
2539
|
+
tier: 2,
|
|
2540
|
+
minSizeFraction: 0.2,
|
|
2541
|
+
maxSizeFraction: 0.7,
|
|
2542
|
+
affinities: [
|
|
2543
|
+
"circle",
|
|
2544
|
+
"lissajous",
|
|
2545
|
+
"spirograph"
|
|
2546
|
+
],
|
|
2547
|
+
category: "complex",
|
|
2548
|
+
heroCandidate: true,
|
|
2549
|
+
bestStyles: [
|
|
2550
|
+
"stroke-only",
|
|
2551
|
+
"double-stroke"
|
|
2552
|
+
]
|
|
2553
|
+
},
|
|
2554
|
+
merkaba: {
|
|
2555
|
+
tier: 1,
|
|
2556
|
+
minSizeFraction: 0.15,
|
|
2557
|
+
maxSizeFraction: 1.0,
|
|
2558
|
+
affinities: [
|
|
2559
|
+
"triangle",
|
|
2560
|
+
"diamond",
|
|
2561
|
+
"sriYantra",
|
|
2562
|
+
"metatronsCube"
|
|
2563
|
+
],
|
|
2564
|
+
category: "complex",
|
|
2565
|
+
heroCandidate: true,
|
|
2566
|
+
bestStyles: [
|
|
2567
|
+
"stroke-only",
|
|
2568
|
+
"fill-and-stroke",
|
|
2569
|
+
"double-stroke"
|
|
2570
|
+
]
|
|
2571
|
+
},
|
|
2572
|
+
mandala: {
|
|
2573
|
+
tier: 1,
|
|
2574
|
+
minSizeFraction: 0.2,
|
|
2575
|
+
maxSizeFraction: 1.0,
|
|
2576
|
+
affinities: [
|
|
2577
|
+
"circle",
|
|
2578
|
+
"flowerOfLife",
|
|
2579
|
+
"spirograph",
|
|
2580
|
+
"rose"
|
|
2581
|
+
],
|
|
2582
|
+
category: "complex",
|
|
2583
|
+
heroCandidate: true,
|
|
2584
|
+
bestStyles: [
|
|
2585
|
+
"stroke-only",
|
|
2586
|
+
"dashed",
|
|
2587
|
+
"incomplete"
|
|
2588
|
+
]
|
|
2589
|
+
},
|
|
2590
|
+
fractal: {
|
|
2591
|
+
tier: 2,
|
|
2592
|
+
minSizeFraction: 0.2,
|
|
2593
|
+
maxSizeFraction: 0.8,
|
|
2594
|
+
affinities: [
|
|
2595
|
+
"blob",
|
|
2596
|
+
"lissajous",
|
|
2597
|
+
"circle"
|
|
2598
|
+
],
|
|
2599
|
+
category: "complex",
|
|
2600
|
+
heroCandidate: true,
|
|
2601
|
+
bestStyles: [
|
|
2602
|
+
"stroke-only",
|
|
2603
|
+
"incomplete"
|
|
2604
|
+
]
|
|
2605
|
+
},
|
|
2606
|
+
// ── Sacred shapes ─────────────────────────────────────────────
|
|
2607
|
+
flowerOfLife: {
|
|
2608
|
+
tier: 1,
|
|
2609
|
+
minSizeFraction: 0.2,
|
|
2610
|
+
maxSizeFraction: 1.0,
|
|
2611
|
+
affinities: [
|
|
2612
|
+
"circle",
|
|
2613
|
+
"hexagon",
|
|
2614
|
+
"seedOfLife",
|
|
2615
|
+
"eggOfLife",
|
|
2616
|
+
"metatronsCube"
|
|
2617
|
+
],
|
|
2618
|
+
category: "sacred",
|
|
2619
|
+
heroCandidate: true,
|
|
2620
|
+
bestStyles: [
|
|
2621
|
+
"stroke-only",
|
|
2622
|
+
"watercolor",
|
|
2623
|
+
"incomplete"
|
|
2624
|
+
]
|
|
2625
|
+
},
|
|
2626
|
+
treeOfLife: {
|
|
2627
|
+
tier: 2,
|
|
2628
|
+
minSizeFraction: 0.25,
|
|
2629
|
+
maxSizeFraction: 0.9,
|
|
2630
|
+
affinities: [
|
|
2631
|
+
"circle",
|
|
2632
|
+
"flowerOfLife",
|
|
2633
|
+
"metatronsCube"
|
|
2634
|
+
],
|
|
2635
|
+
category: "sacred",
|
|
2636
|
+
heroCandidate: true,
|
|
2637
|
+
bestStyles: [
|
|
2638
|
+
"stroke-only",
|
|
2639
|
+
"double-stroke"
|
|
2640
|
+
]
|
|
2641
|
+
},
|
|
2642
|
+
metatronsCube: {
|
|
2643
|
+
tier: 1,
|
|
2644
|
+
minSizeFraction: 0.2,
|
|
2645
|
+
maxSizeFraction: 1.0,
|
|
2646
|
+
affinities: [
|
|
2647
|
+
"hexagon",
|
|
2648
|
+
"flowerOfLife",
|
|
2649
|
+
"platonicSolid",
|
|
2650
|
+
"merkaba"
|
|
2651
|
+
],
|
|
2652
|
+
category: "sacred",
|
|
2653
|
+
heroCandidate: true,
|
|
2654
|
+
bestStyles: [
|
|
2655
|
+
"stroke-only",
|
|
2656
|
+
"dashed",
|
|
2657
|
+
"incomplete"
|
|
2658
|
+
]
|
|
2659
|
+
},
|
|
2660
|
+
sriYantra: {
|
|
2661
|
+
tier: 1,
|
|
2662
|
+
minSizeFraction: 0.2,
|
|
2663
|
+
maxSizeFraction: 1.0,
|
|
2664
|
+
affinities: [
|
|
2665
|
+
"triangle",
|
|
2666
|
+
"merkaba",
|
|
2667
|
+
"mandala",
|
|
2668
|
+
"diamond"
|
|
2669
|
+
],
|
|
2670
|
+
category: "sacred",
|
|
2671
|
+
heroCandidate: true,
|
|
2672
|
+
bestStyles: [
|
|
2673
|
+
"stroke-only",
|
|
2674
|
+
"fill-and-stroke",
|
|
2675
|
+
"double-stroke"
|
|
2676
|
+
]
|
|
2677
|
+
},
|
|
2678
|
+
seedOfLife: {
|
|
2679
|
+
tier: 1,
|
|
2680
|
+
minSizeFraction: 0.15,
|
|
2681
|
+
maxSizeFraction: 0.9,
|
|
2682
|
+
affinities: [
|
|
2683
|
+
"circle",
|
|
2684
|
+
"flowerOfLife",
|
|
2685
|
+
"eggOfLife",
|
|
2686
|
+
"hexagon"
|
|
2687
|
+
],
|
|
2688
|
+
category: "sacred",
|
|
2689
|
+
heroCandidate: true,
|
|
2690
|
+
bestStyles: [
|
|
2691
|
+
"stroke-only",
|
|
2692
|
+
"watercolor",
|
|
2693
|
+
"fill-only"
|
|
2694
|
+
]
|
|
2695
|
+
},
|
|
2696
|
+
vesicaPiscis: {
|
|
2697
|
+
tier: 2,
|
|
2698
|
+
minSizeFraction: 0.15,
|
|
2699
|
+
maxSizeFraction: 0.7,
|
|
2700
|
+
affinities: [
|
|
2701
|
+
"circle",
|
|
2702
|
+
"seedOfLife",
|
|
2703
|
+
"flowerOfLife"
|
|
2704
|
+
],
|
|
2705
|
+
category: "sacred",
|
|
2706
|
+
heroCandidate: false,
|
|
2707
|
+
bestStyles: [
|
|
2708
|
+
"stroke-only",
|
|
2709
|
+
"watercolor"
|
|
2710
|
+
]
|
|
2711
|
+
},
|
|
2712
|
+
torus: {
|
|
2713
|
+
tier: 3,
|
|
2714
|
+
minSizeFraction: 0.2,
|
|
2715
|
+
maxSizeFraction: 0.6,
|
|
2716
|
+
affinities: [
|
|
2717
|
+
"circle",
|
|
2718
|
+
"spirograph",
|
|
2719
|
+
"waveRing"
|
|
2720
|
+
],
|
|
2721
|
+
category: "sacred",
|
|
2722
|
+
heroCandidate: false,
|
|
2723
|
+
bestStyles: [
|
|
2724
|
+
"stroke-only",
|
|
2725
|
+
"dashed"
|
|
2726
|
+
]
|
|
2727
|
+
},
|
|
2728
|
+
eggOfLife: {
|
|
2729
|
+
tier: 2,
|
|
2730
|
+
minSizeFraction: 0.15,
|
|
2731
|
+
maxSizeFraction: 0.8,
|
|
2732
|
+
affinities: [
|
|
2733
|
+
"circle",
|
|
2734
|
+
"seedOfLife",
|
|
2735
|
+
"flowerOfLife"
|
|
2736
|
+
],
|
|
2737
|
+
category: "sacred",
|
|
2738
|
+
heroCandidate: true,
|
|
2739
|
+
bestStyles: [
|
|
2740
|
+
"stroke-only",
|
|
2741
|
+
"watercolor"
|
|
2742
|
+
]
|
|
2743
|
+
},
|
|
2744
|
+
// ── Procedural shapes ─────────────────────────────────────────
|
|
2745
|
+
blob: {
|
|
2746
|
+
tier: 1,
|
|
2747
|
+
minSizeFraction: 0.05,
|
|
2748
|
+
maxSizeFraction: 1.0,
|
|
2749
|
+
affinities: [
|
|
2750
|
+
"blob",
|
|
2751
|
+
"circle",
|
|
2752
|
+
"superellipse",
|
|
2753
|
+
"waveRing"
|
|
2754
|
+
],
|
|
2755
|
+
category: "procedural",
|
|
2756
|
+
heroCandidate: false,
|
|
2757
|
+
bestStyles: [
|
|
2758
|
+
"fill-only",
|
|
2759
|
+
"watercolor",
|
|
2760
|
+
"fill-and-stroke"
|
|
2761
|
+
]
|
|
2762
|
+
},
|
|
2763
|
+
ngon: {
|
|
2764
|
+
tier: 2,
|
|
2765
|
+
minSizeFraction: 0.06,
|
|
2766
|
+
maxSizeFraction: 0.8,
|
|
2767
|
+
affinities: [
|
|
2768
|
+
"hexagon",
|
|
2769
|
+
"triangle",
|
|
2770
|
+
"diamond",
|
|
2771
|
+
"superellipse"
|
|
2772
|
+
],
|
|
2773
|
+
category: "procedural",
|
|
2774
|
+
heroCandidate: false,
|
|
2775
|
+
bestStyles: [
|
|
2776
|
+
"fill-and-stroke",
|
|
2777
|
+
"fill-only",
|
|
2778
|
+
"hatched"
|
|
2779
|
+
]
|
|
2780
|
+
},
|
|
2781
|
+
lissajous: {
|
|
2782
|
+
tier: 2,
|
|
2783
|
+
minSizeFraction: 0.15,
|
|
2784
|
+
maxSizeFraction: 0.8,
|
|
2785
|
+
affinities: [
|
|
2786
|
+
"spirograph",
|
|
2787
|
+
"rose",
|
|
2788
|
+
"celticKnot",
|
|
2789
|
+
"fibonacciSpiral"
|
|
2790
|
+
],
|
|
2791
|
+
category: "procedural",
|
|
2792
|
+
heroCandidate: false,
|
|
2793
|
+
bestStyles: [
|
|
2794
|
+
"stroke-only",
|
|
2795
|
+
"incomplete",
|
|
2796
|
+
"dashed"
|
|
2797
|
+
]
|
|
2798
|
+
},
|
|
2799
|
+
superellipse: {
|
|
2800
|
+
tier: 1,
|
|
2801
|
+
minSizeFraction: 0.05,
|
|
2802
|
+
maxSizeFraction: 1.0,
|
|
2803
|
+
affinities: [
|
|
2804
|
+
"circle",
|
|
2805
|
+
"square",
|
|
2806
|
+
"blob",
|
|
2807
|
+
"hexagon"
|
|
2808
|
+
],
|
|
2809
|
+
category: "procedural",
|
|
2810
|
+
heroCandidate: false,
|
|
2811
|
+
bestStyles: [
|
|
2812
|
+
"fill-only",
|
|
2813
|
+
"watercolor",
|
|
2814
|
+
"fill-and-stroke",
|
|
2815
|
+
"wood-grain"
|
|
2816
|
+
]
|
|
2817
|
+
},
|
|
2818
|
+
spirograph: {
|
|
2819
|
+
tier: 1,
|
|
2820
|
+
minSizeFraction: 0.15,
|
|
2821
|
+
maxSizeFraction: 0.9,
|
|
2822
|
+
affinities: [
|
|
2823
|
+
"rose",
|
|
2824
|
+
"lissajous",
|
|
2825
|
+
"mandala",
|
|
2826
|
+
"flowerOfLife"
|
|
2827
|
+
],
|
|
2828
|
+
category: "procedural",
|
|
2829
|
+
heroCandidate: true,
|
|
2830
|
+
bestStyles: [
|
|
2831
|
+
"stroke-only",
|
|
2832
|
+
"incomplete",
|
|
2833
|
+
"dashed"
|
|
2834
|
+
]
|
|
2835
|
+
},
|
|
2836
|
+
waveRing: {
|
|
2837
|
+
tier: 2,
|
|
2838
|
+
minSizeFraction: 0.1,
|
|
2839
|
+
maxSizeFraction: 0.8,
|
|
2840
|
+
affinities: [
|
|
2841
|
+
"circle",
|
|
2842
|
+
"blob",
|
|
2843
|
+
"torus",
|
|
2844
|
+
"spirograph"
|
|
2845
|
+
],
|
|
2846
|
+
category: "procedural",
|
|
2847
|
+
heroCandidate: false,
|
|
2848
|
+
bestStyles: [
|
|
2849
|
+
"stroke-only",
|
|
2850
|
+
"dashed",
|
|
2851
|
+
"incomplete"
|
|
2852
|
+
]
|
|
2853
|
+
},
|
|
2854
|
+
rose: {
|
|
2855
|
+
tier: 1,
|
|
2856
|
+
minSizeFraction: 0.1,
|
|
2857
|
+
maxSizeFraction: 0.9,
|
|
2858
|
+
affinities: [
|
|
2859
|
+
"spirograph",
|
|
2860
|
+
"mandala",
|
|
2861
|
+
"flowerOfLife",
|
|
2862
|
+
"circle"
|
|
2863
|
+
],
|
|
2864
|
+
category: "procedural",
|
|
2865
|
+
heroCandidate: true,
|
|
2866
|
+
bestStyles: [
|
|
2867
|
+
"stroke-only",
|
|
2868
|
+
"fill-only",
|
|
2869
|
+
"watercolor"
|
|
2870
|
+
]
|
|
2871
|
+
},
|
|
2872
|
+
// ── New procedural shapes ─────────────────────────────────────
|
|
2873
|
+
shardField: {
|
|
2874
|
+
tier: 2,
|
|
2875
|
+
minSizeFraction: 0.1,
|
|
2876
|
+
maxSizeFraction: 0.7,
|
|
2877
|
+
affinities: [
|
|
2878
|
+
"voronoiCell",
|
|
2879
|
+
"diamond",
|
|
2880
|
+
"triangle",
|
|
2881
|
+
"penroseTile"
|
|
2882
|
+
],
|
|
2883
|
+
category: "procedural",
|
|
2884
|
+
heroCandidate: false,
|
|
2885
|
+
bestStyles: [
|
|
2886
|
+
"fill-and-stroke",
|
|
2887
|
+
"stroke-only",
|
|
2888
|
+
"fill-only"
|
|
2889
|
+
]
|
|
2890
|
+
},
|
|
2891
|
+
voronoiCell: {
|
|
2892
|
+
tier: 1,
|
|
2893
|
+
minSizeFraction: 0.08,
|
|
2894
|
+
maxSizeFraction: 0.9,
|
|
2895
|
+
affinities: [
|
|
2896
|
+
"shardField",
|
|
2897
|
+
"ngon",
|
|
2898
|
+
"superellipse",
|
|
2899
|
+
"blob"
|
|
2900
|
+
],
|
|
2901
|
+
category: "procedural",
|
|
2902
|
+
heroCandidate: false,
|
|
2903
|
+
bestStyles: [
|
|
2904
|
+
"fill-and-stroke",
|
|
2905
|
+
"fill-only",
|
|
2906
|
+
"watercolor",
|
|
2907
|
+
"marble-vein"
|
|
2908
|
+
]
|
|
2909
|
+
},
|
|
2910
|
+
crescent: {
|
|
2911
|
+
tier: 1,
|
|
2912
|
+
minSizeFraction: 0.1,
|
|
2913
|
+
maxSizeFraction: 1.0,
|
|
2914
|
+
affinities: [
|
|
2915
|
+
"circle",
|
|
2916
|
+
"blob",
|
|
2917
|
+
"cloudForm",
|
|
2918
|
+
"vesicaPiscis"
|
|
2919
|
+
],
|
|
2920
|
+
category: "procedural",
|
|
2921
|
+
heroCandidate: true,
|
|
2922
|
+
bestStyles: [
|
|
2923
|
+
"fill-only",
|
|
2924
|
+
"watercolor",
|
|
2925
|
+
"fill-and-stroke"
|
|
2926
|
+
]
|
|
2927
|
+
},
|
|
2928
|
+
tendril: {
|
|
2929
|
+
tier: 2,
|
|
2930
|
+
minSizeFraction: 0.1,
|
|
2931
|
+
maxSizeFraction: 0.8,
|
|
2932
|
+
affinities: [
|
|
2933
|
+
"blob",
|
|
2934
|
+
"inkSplat",
|
|
2935
|
+
"lissajous",
|
|
2936
|
+
"fibonacciSpiral"
|
|
2937
|
+
],
|
|
2938
|
+
category: "procedural",
|
|
2939
|
+
heroCandidate: false,
|
|
2940
|
+
bestStyles: [
|
|
2941
|
+
"fill-only",
|
|
2942
|
+
"watercolor",
|
|
2943
|
+
"fill-and-stroke"
|
|
2944
|
+
]
|
|
2945
|
+
},
|
|
2946
|
+
cloudForm: {
|
|
2947
|
+
tier: 1,
|
|
2948
|
+
minSizeFraction: 0.15,
|
|
2949
|
+
maxSizeFraction: 1.0,
|
|
2950
|
+
affinities: [
|
|
2951
|
+
"blob",
|
|
2952
|
+
"circle",
|
|
2953
|
+
"crescent",
|
|
2954
|
+
"superellipse"
|
|
2955
|
+
],
|
|
2956
|
+
category: "procedural",
|
|
2957
|
+
heroCandidate: false,
|
|
2958
|
+
bestStyles: [
|
|
2959
|
+
"fill-only",
|
|
2960
|
+
"watercolor"
|
|
2961
|
+
]
|
|
2962
|
+
},
|
|
2963
|
+
inkSplat: {
|
|
2964
|
+
tier: 2,
|
|
2965
|
+
minSizeFraction: 0.1,
|
|
2966
|
+
maxSizeFraction: 0.8,
|
|
2967
|
+
affinities: [
|
|
2968
|
+
"blob",
|
|
2969
|
+
"tendril",
|
|
2970
|
+
"shardField",
|
|
2971
|
+
"star"
|
|
2972
|
+
],
|
|
2973
|
+
category: "procedural",
|
|
2974
|
+
heroCandidate: false,
|
|
2975
|
+
bestStyles: [
|
|
2976
|
+
"fill-only",
|
|
2977
|
+
"watercolor",
|
|
2978
|
+
"fill-and-stroke"
|
|
2979
|
+
]
|
|
2980
|
+
},
|
|
2981
|
+
geodesicDome: {
|
|
2982
|
+
tier: 2,
|
|
2983
|
+
minSizeFraction: 0.2,
|
|
2984
|
+
maxSizeFraction: 0.9,
|
|
2985
|
+
affinities: [
|
|
2986
|
+
"metatronsCube",
|
|
2987
|
+
"platonicSolid",
|
|
2988
|
+
"hexagon",
|
|
2989
|
+
"triangle"
|
|
2990
|
+
],
|
|
2991
|
+
category: "procedural",
|
|
2992
|
+
heroCandidate: true,
|
|
2993
|
+
bestStyles: [
|
|
2994
|
+
"stroke-only",
|
|
2995
|
+
"dashed",
|
|
2996
|
+
"double-stroke"
|
|
2997
|
+
]
|
|
2998
|
+
},
|
|
2999
|
+
penroseTile: {
|
|
3000
|
+
tier: 2,
|
|
3001
|
+
minSizeFraction: 0.06,
|
|
3002
|
+
maxSizeFraction: 0.6,
|
|
3003
|
+
affinities: [
|
|
3004
|
+
"diamond",
|
|
3005
|
+
"triangle",
|
|
3006
|
+
"shardField",
|
|
3007
|
+
"voronoiCell"
|
|
3008
|
+
],
|
|
3009
|
+
category: "procedural",
|
|
3010
|
+
heroCandidate: false,
|
|
3011
|
+
bestStyles: [
|
|
3012
|
+
"fill-and-stroke",
|
|
3013
|
+
"fill-only",
|
|
3014
|
+
"double-stroke"
|
|
3015
|
+
]
|
|
3016
|
+
},
|
|
3017
|
+
reuleauxTriangle: {
|
|
3018
|
+
tier: 1,
|
|
3019
|
+
minSizeFraction: 0.08,
|
|
3020
|
+
maxSizeFraction: 1.0,
|
|
3021
|
+
affinities: [
|
|
3022
|
+
"triangle",
|
|
3023
|
+
"circle",
|
|
3024
|
+
"superellipse",
|
|
3025
|
+
"vesicaPiscis"
|
|
3026
|
+
],
|
|
3027
|
+
category: "procedural",
|
|
3028
|
+
heroCandidate: true,
|
|
3029
|
+
bestStyles: [
|
|
3030
|
+
"fill-and-stroke",
|
|
3031
|
+
"fill-only",
|
|
3032
|
+
"watercolor"
|
|
3033
|
+
]
|
|
3034
|
+
},
|
|
3035
|
+
dotCluster: {
|
|
3036
|
+
tier: 3,
|
|
3037
|
+
minSizeFraction: 0.05,
|
|
3038
|
+
maxSizeFraction: 0.5,
|
|
3039
|
+
affinities: [
|
|
3040
|
+
"cloudForm",
|
|
3041
|
+
"inkSplat",
|
|
3042
|
+
"blob"
|
|
3043
|
+
],
|
|
3044
|
+
category: "procedural",
|
|
3045
|
+
heroCandidate: false,
|
|
3046
|
+
bestStyles: [
|
|
3047
|
+
"fill-only",
|
|
3048
|
+
"stipple"
|
|
3049
|
+
]
|
|
3050
|
+
},
|
|
3051
|
+
crosshatchPatch: {
|
|
3052
|
+
tier: 3,
|
|
3053
|
+
minSizeFraction: 0.1,
|
|
3054
|
+
maxSizeFraction: 0.6,
|
|
3055
|
+
affinities: [
|
|
3056
|
+
"voronoiCell",
|
|
3057
|
+
"ngon",
|
|
3058
|
+
"superellipse"
|
|
3059
|
+
],
|
|
3060
|
+
category: "procedural",
|
|
3061
|
+
heroCandidate: false,
|
|
3062
|
+
bestStyles: [
|
|
3063
|
+
"stroke-only",
|
|
3064
|
+
"hatched",
|
|
3065
|
+
"fabric-weave"
|
|
3066
|
+
]
|
|
3067
|
+
}
|
|
3068
|
+
};
|
|
3069
|
+
function $e73976f898150d4d$export$4a95df8944b5033b(rng, shapeNames, archetypeName) {
|
|
3070
|
+
const available = shapeNames.filter((s)=>$e73976f898150d4d$export$4343b39fe47bd82c[s]);
|
|
3071
|
+
// Pick a seed shape — tier 1 shapes that are hero candidates
|
|
3072
|
+
const heroPool = available.filter((s)=>$e73976f898150d4d$export$4343b39fe47bd82c[s].tier === 1 && $e73976f898150d4d$export$4343b39fe47bd82c[s].heroCandidate);
|
|
3073
|
+
const seedShape = heroPool.length > 0 ? heroPool[Math.floor(rng() * heroPool.length)] : available[Math.floor(rng() * available.length)];
|
|
3074
|
+
const seedProfile = $e73976f898150d4d$export$4343b39fe47bd82c[seedShape];
|
|
3075
|
+
// Primary: seed shape + its direct affinities (tier 1-2 only)
|
|
3076
|
+
const primaryCandidates = [
|
|
3077
|
+
seedShape,
|
|
3078
|
+
...seedProfile.affinities
|
|
3079
|
+
].filter((s)=>available.includes(s)).filter((s)=>$e73976f898150d4d$export$4343b39fe47bd82c[s].tier <= 2);
|
|
3080
|
+
const primary = [
|
|
3081
|
+
...new Set(primaryCandidates)
|
|
3082
|
+
].slice(0, 5);
|
|
3083
|
+
// Supporting: affinities of affinities, plus same-category shapes
|
|
3084
|
+
const supportingSet = new Set();
|
|
3085
|
+
for (const p of primary){
|
|
3086
|
+
const profile = $e73976f898150d4d$export$4343b39fe47bd82c[p];
|
|
3087
|
+
if (!profile) continue;
|
|
3088
|
+
for (const aff of profile.affinities)if (available.includes(aff) && !primary.includes(aff)) supportingSet.add(aff);
|
|
3089
|
+
}
|
|
3090
|
+
// Add same-category tier 1-2 shapes
|
|
3091
|
+
for (const s of available){
|
|
3092
|
+
const p = $e73976f898150d4d$export$4343b39fe47bd82c[s];
|
|
3093
|
+
if (p.category === seedProfile.category && p.tier <= 2 && !primary.includes(s)) supportingSet.add(s);
|
|
3094
|
+
}
|
|
3095
|
+
const supporting = [
|
|
3096
|
+
...supportingSet
|
|
3097
|
+
].slice(0, 6);
|
|
3098
|
+
// Accents: tier 1 shapes from other categories for contrast
|
|
3099
|
+
const usedCategories = new Set([
|
|
3100
|
+
...primary,
|
|
3101
|
+
...supporting
|
|
3102
|
+
].map((s)=>$e73976f898150d4d$export$4343b39fe47bd82c[s]?.category));
|
|
3103
|
+
const accentCandidates = available.filter((s)=>!primary.includes(s) && !supporting.includes(s) && $e73976f898150d4d$export$4343b39fe47bd82c[s].tier <= 2 && !usedCategories.has($e73976f898150d4d$export$4343b39fe47bd82c[s].category));
|
|
3104
|
+
// Shuffle and take a few
|
|
3105
|
+
const accents = [];
|
|
3106
|
+
const shuffled = [
|
|
3107
|
+
...accentCandidates
|
|
3108
|
+
];
|
|
3109
|
+
for(let i = shuffled.length - 1; i > 0; i--){
|
|
3110
|
+
const j = Math.floor(rng() * (i + 1));
|
|
3111
|
+
[shuffled[i], shuffled[j]] = [
|
|
3112
|
+
shuffled[j],
|
|
3113
|
+
shuffled[i]
|
|
3114
|
+
];
|
|
3115
|
+
}
|
|
3116
|
+
accents.push(...shuffled.slice(0, 3));
|
|
3117
|
+
// For certain archetypes, bias the palette
|
|
3118
|
+
if (archetypeName === "geometric-precision") // Remove blobs and organic shapes from primary
|
|
3119
|
+
return {
|
|
3120
|
+
primary: primary.filter((s)=>$e73976f898150d4d$export$4343b39fe47bd82c[s]?.category !== "procedural" || s === "ngon"),
|
|
3121
|
+
supporting: supporting.filter((s)=>s !== "blob"),
|
|
3122
|
+
accents: accents
|
|
3123
|
+
};
|
|
3124
|
+
if (archetypeName === "organic-flow") {
|
|
3125
|
+
// Boost procedural/organic shapes
|
|
3126
|
+
const organicBoost = available.filter((s)=>[
|
|
3127
|
+
"blob",
|
|
3128
|
+
"superellipse",
|
|
3129
|
+
"waveRing",
|
|
3130
|
+
"rose"
|
|
3131
|
+
].includes(s) && !primary.includes(s));
|
|
3132
|
+
return {
|
|
3133
|
+
primary: [
|
|
3134
|
+
...primary,
|
|
3135
|
+
...organicBoost.slice(0, 2)
|
|
3136
|
+
],
|
|
3137
|
+
supporting: supporting,
|
|
3138
|
+
accents: accents
|
|
3139
|
+
};
|
|
3140
|
+
}
|
|
3141
|
+
if (archetypeName === "shattered-glass") {
|
|
3142
|
+
// Favor angular, fragmented shapes
|
|
3143
|
+
const shardBoost = available.filter((s)=>[
|
|
3144
|
+
"shardField",
|
|
3145
|
+
"voronoiCell",
|
|
3146
|
+
"penroseTile",
|
|
3147
|
+
"diamond",
|
|
3148
|
+
"triangle",
|
|
3149
|
+
"ngon"
|
|
3150
|
+
].includes(s) && !primary.includes(s));
|
|
3151
|
+
return {
|
|
3152
|
+
primary: [
|
|
3153
|
+
...primary.filter((s)=>s !== "blob" && s !== "cloudForm"),
|
|
3154
|
+
...shardBoost.slice(0, 3)
|
|
3155
|
+
],
|
|
3156
|
+
supporting: supporting.filter((s)=>s !== "blob" && s !== "cloudForm"),
|
|
3157
|
+
accents: accents
|
|
3158
|
+
};
|
|
3159
|
+
}
|
|
3160
|
+
if (archetypeName === "botanical") {
|
|
3161
|
+
// Favor organic, flowing shapes
|
|
3162
|
+
const botanicalBoost = available.filter((s)=>[
|
|
3163
|
+
"tendril",
|
|
3164
|
+
"cloudForm",
|
|
3165
|
+
"blob",
|
|
3166
|
+
"crescent",
|
|
3167
|
+
"rose",
|
|
3168
|
+
"inkSplat"
|
|
3169
|
+
].includes(s) && !primary.includes(s));
|
|
3170
|
+
return {
|
|
3171
|
+
primary: [
|
|
3172
|
+
...primary,
|
|
3173
|
+
...botanicalBoost.slice(0, 3)
|
|
3174
|
+
],
|
|
3175
|
+
supporting: supporting,
|
|
3176
|
+
accents: accents
|
|
3177
|
+
};
|
|
3178
|
+
}
|
|
3179
|
+
if (archetypeName === "stipple-portrait") {
|
|
3180
|
+
// Favor small, dot-friendly shapes
|
|
3181
|
+
const stippleBoost = available.filter((s)=>[
|
|
3182
|
+
"dotCluster",
|
|
3183
|
+
"circle",
|
|
3184
|
+
"crosshatchPatch",
|
|
3185
|
+
"voronoiCell",
|
|
3186
|
+
"blob"
|
|
3187
|
+
].includes(s) && !primary.includes(s));
|
|
3188
|
+
return {
|
|
3189
|
+
primary: [
|
|
3190
|
+
...primary,
|
|
3191
|
+
...stippleBoost.slice(0, 3)
|
|
3192
|
+
],
|
|
3193
|
+
supporting: supporting,
|
|
3194
|
+
accents: accents
|
|
3195
|
+
};
|
|
3196
|
+
}
|
|
3197
|
+
if (archetypeName === "celestial") {
|
|
3198
|
+
// Favor sacred geometry and cosmic shapes
|
|
3199
|
+
const celestialBoost = available.filter((s)=>[
|
|
3200
|
+
"crescent",
|
|
3201
|
+
"geodesicDome",
|
|
3202
|
+
"mandala",
|
|
3203
|
+
"flowerOfLife",
|
|
3204
|
+
"spirograph",
|
|
3205
|
+
"fibonacciSpiral"
|
|
3206
|
+
].includes(s) && !primary.includes(s));
|
|
3207
|
+
return {
|
|
3208
|
+
primary: [
|
|
3209
|
+
...primary,
|
|
3210
|
+
...celestialBoost.slice(0, 3)
|
|
3211
|
+
],
|
|
3212
|
+
supporting: supporting,
|
|
3213
|
+
accents: accents
|
|
3214
|
+
};
|
|
3215
|
+
}
|
|
3216
|
+
return {
|
|
3217
|
+
primary: primary,
|
|
3218
|
+
supporting: supporting,
|
|
3219
|
+
accents: accents
|
|
3220
|
+
};
|
|
3221
|
+
}
|
|
3222
|
+
function $e73976f898150d4d$export$3c37d9a045754d0e(palette, rng, sizeFraction) {
|
|
3223
|
+
// Filter each tier by size constraints
|
|
3224
|
+
const validPrimary = palette.primary.filter((s)=>{
|
|
3225
|
+
const p = $e73976f898150d4d$export$4343b39fe47bd82c[s];
|
|
3226
|
+
return p && sizeFraction >= p.minSizeFraction && sizeFraction <= p.maxSizeFraction;
|
|
3227
|
+
});
|
|
3228
|
+
const validSupporting = palette.supporting.filter((s)=>{
|
|
3229
|
+
const p = $e73976f898150d4d$export$4343b39fe47bd82c[s];
|
|
3230
|
+
return p && sizeFraction >= p.minSizeFraction && sizeFraction <= p.maxSizeFraction;
|
|
3231
|
+
});
|
|
3232
|
+
const validAccents = palette.accents.filter((s)=>{
|
|
3233
|
+
const p = $e73976f898150d4d$export$4343b39fe47bd82c[s];
|
|
3234
|
+
return p && sizeFraction >= p.minSizeFraction && sizeFraction <= p.maxSizeFraction;
|
|
3235
|
+
});
|
|
3236
|
+
const roll = rng();
|
|
3237
|
+
if (roll < 0.60 && validPrimary.length > 0) return validPrimary[Math.floor(rng() * validPrimary.length)];
|
|
3238
|
+
if (roll < 0.90 && validSupporting.length > 0) return validSupporting[Math.floor(rng() * validSupporting.length)];
|
|
3239
|
+
if (validAccents.length > 0) return validAccents[Math.floor(rng() * validAccents.length)];
|
|
3240
|
+
// Fallback: any valid primary or supporting
|
|
3241
|
+
const fallback = [
|
|
3242
|
+
...validPrimary,
|
|
3243
|
+
...validSupporting
|
|
3244
|
+
];
|
|
3245
|
+
if (fallback.length > 0) return fallback[Math.floor(rng() * fallback.length)];
|
|
3246
|
+
// Ultimate fallback
|
|
3247
|
+
return palette.primary[0] || "circle";
|
|
3248
|
+
}
|
|
3249
|
+
function $e73976f898150d4d$export$ab873bb6fb56c1a8(shapeName, layerStyle, rng) {
|
|
3250
|
+
const profile = $e73976f898150d4d$export$4343b39fe47bd82c[shapeName];
|
|
3251
|
+
if (!profile || rng() > 0.7) return layerStyle;
|
|
3252
|
+
return profile.bestStyles[Math.floor(rng() * profile.bestStyles.length)];
|
|
3253
|
+
}
|
|
3254
|
+
|
|
3255
|
+
|
|
3256
|
+
|
|
3257
|
+
/**
|
|
3258
|
+
* Configuration options for image generation.
|
|
3259
|
+
*/ const $93cf69256c93baa9$export$c2f8e0cc249a8d8f = {
|
|
3260
|
+
width: 2048,
|
|
3261
|
+
height: 2048,
|
|
3262
|
+
gridSize: 5,
|
|
3263
|
+
layers: 4,
|
|
3264
|
+
minShapeSize: 30,
|
|
3265
|
+
maxShapeSize: 400,
|
|
3266
|
+
baseOpacity: 0.7,
|
|
3267
|
+
opacityReduction: 0.12,
|
|
3268
|
+
shapesPerLayer: 0
|
|
3269
|
+
};
|
|
3270
|
+
|
|
3271
|
+
|
|
3272
|
+
/**
|
|
3273
|
+
* Visual archetypes — fundamentally different rendering personalities
|
|
3274
|
+
* selected deterministically from the hash.
|
|
3275
|
+
*
|
|
3276
|
+
* Each archetype overrides key rendering parameters to produce images
|
|
3277
|
+
* that look like they came from different generators entirely.
|
|
3278
|
+
*/ // ── Archetype definitions ───────────────────────────────────────────
|
|
3279
|
+
const $f89bc858f7202849$var$ARCHETYPES = [
|
|
3280
|
+
{
|
|
3281
|
+
name: "dense-chaotic",
|
|
3282
|
+
gridSize: 9,
|
|
3283
|
+
layers: 5,
|
|
3284
|
+
baseOpacity: 0.5,
|
|
3285
|
+
opacityReduction: 0.06,
|
|
3286
|
+
minShapeSize: 10,
|
|
3287
|
+
maxShapeSize: 200,
|
|
3288
|
+
backgroundStyle: "radial-dark",
|
|
3289
|
+
paletteMode: "harmonious",
|
|
3290
|
+
preferredStyles: [
|
|
3291
|
+
"fill-and-stroke",
|
|
3292
|
+
"watercolor",
|
|
3293
|
+
"fill-only"
|
|
3294
|
+
],
|
|
3295
|
+
flowLineMultiplier: 2.5,
|
|
3296
|
+
heroShape: false,
|
|
3297
|
+
glowMultiplier: 0.5,
|
|
3298
|
+
sizePower: 1.2,
|
|
3299
|
+
invertForeground: false
|
|
3300
|
+
},
|
|
3301
|
+
{
|
|
3302
|
+
name: "minimal-spacious",
|
|
3303
|
+
gridSize: 2,
|
|
3304
|
+
layers: 2,
|
|
3305
|
+
baseOpacity: 0.85,
|
|
3306
|
+
opacityReduction: 0.05,
|
|
3307
|
+
minShapeSize: 150,
|
|
3308
|
+
maxShapeSize: 600,
|
|
3309
|
+
backgroundStyle: "solid-light",
|
|
3310
|
+
paletteMode: "duotone",
|
|
3311
|
+
preferredStyles: [
|
|
3312
|
+
"fill-and-stroke",
|
|
3313
|
+
"stroke-only",
|
|
3314
|
+
"incomplete"
|
|
3315
|
+
],
|
|
3316
|
+
flowLineMultiplier: 0.3,
|
|
3317
|
+
heroShape: true,
|
|
3318
|
+
glowMultiplier: 0,
|
|
3319
|
+
sizePower: 0.8,
|
|
3320
|
+
invertForeground: false
|
|
3321
|
+
},
|
|
3322
|
+
{
|
|
3323
|
+
name: "organic-flow",
|
|
1689
3324
|
gridSize: 4,
|
|
1690
3325
|
layers: 3,
|
|
1691
3326
|
baseOpacity: 0.4,
|
|
@@ -1832,6 +3467,69 @@ const $f89bc858f7202849$var$ARCHETYPES = [
|
|
|
1832
3467
|
sizePower: 2.5,
|
|
1833
3468
|
invertForeground: false
|
|
1834
3469
|
},
|
|
3470
|
+
{
|
|
3471
|
+
name: "watercolor-wash",
|
|
3472
|
+
gridSize: 3,
|
|
3473
|
+
layers: 3,
|
|
3474
|
+
baseOpacity: 0.25,
|
|
3475
|
+
opacityReduction: 0.03,
|
|
3476
|
+
minShapeSize: 200,
|
|
3477
|
+
maxShapeSize: 700,
|
|
3478
|
+
backgroundStyle: "radial-light",
|
|
3479
|
+
paletteMode: "harmonious",
|
|
3480
|
+
preferredStyles: [
|
|
3481
|
+
"watercolor",
|
|
3482
|
+
"fill-only",
|
|
3483
|
+
"incomplete"
|
|
3484
|
+
],
|
|
3485
|
+
flowLineMultiplier: 0.5,
|
|
3486
|
+
heroShape: false,
|
|
3487
|
+
glowMultiplier: 0.3,
|
|
3488
|
+
sizePower: 0.6,
|
|
3489
|
+
invertForeground: false
|
|
3490
|
+
},
|
|
3491
|
+
{
|
|
3492
|
+
name: "op-art",
|
|
3493
|
+
gridSize: 8,
|
|
3494
|
+
layers: 2,
|
|
3495
|
+
baseOpacity: 0.95,
|
|
3496
|
+
opacityReduction: 0.05,
|
|
3497
|
+
minShapeSize: 20,
|
|
3498
|
+
maxShapeSize: 200,
|
|
3499
|
+
backgroundStyle: "solid-light",
|
|
3500
|
+
paletteMode: "high-contrast",
|
|
3501
|
+
preferredStyles: [
|
|
3502
|
+
"fill-and-stroke",
|
|
3503
|
+
"stroke-only",
|
|
3504
|
+
"dashed"
|
|
3505
|
+
],
|
|
3506
|
+
flowLineMultiplier: 0,
|
|
3507
|
+
heroShape: false,
|
|
3508
|
+
glowMultiplier: 0,
|
|
3509
|
+
sizePower: 0.4,
|
|
3510
|
+
invertForeground: false
|
|
3511
|
+
},
|
|
3512
|
+
{
|
|
3513
|
+
name: "collage",
|
|
3514
|
+
gridSize: 4,
|
|
3515
|
+
layers: 3,
|
|
3516
|
+
baseOpacity: 0.9,
|
|
3517
|
+
opacityReduction: 0.08,
|
|
3518
|
+
minShapeSize: 80,
|
|
3519
|
+
maxShapeSize: 500,
|
|
3520
|
+
backgroundStyle: "solid-light",
|
|
3521
|
+
paletteMode: "duotone",
|
|
3522
|
+
preferredStyles: [
|
|
3523
|
+
"fill-and-stroke",
|
|
3524
|
+
"fill-only",
|
|
3525
|
+
"double-stroke"
|
|
3526
|
+
],
|
|
3527
|
+
flowLineMultiplier: 0,
|
|
3528
|
+
heroShape: true,
|
|
3529
|
+
glowMultiplier: 0,
|
|
3530
|
+
sizePower: 0.7,
|
|
3531
|
+
invertForeground: false
|
|
3532
|
+
},
|
|
1835
3533
|
{
|
|
1836
3534
|
name: "classic",
|
|
1837
3535
|
gridSize: 5,
|
|
@@ -1852,6 +3550,91 @@ const $f89bc858f7202849$var$ARCHETYPES = [
|
|
|
1852
3550
|
glowMultiplier: 1,
|
|
1853
3551
|
sizePower: 1.8,
|
|
1854
3552
|
invertForeground: false
|
|
3553
|
+
},
|
|
3554
|
+
{
|
|
3555
|
+
name: "shattered-glass",
|
|
3556
|
+
gridSize: 8,
|
|
3557
|
+
layers: 3,
|
|
3558
|
+
baseOpacity: 0.85,
|
|
3559
|
+
opacityReduction: 0.1,
|
|
3560
|
+
minShapeSize: 15,
|
|
3561
|
+
maxShapeSize: 250,
|
|
3562
|
+
backgroundStyle: "solid-dark",
|
|
3563
|
+
paletteMode: "high-contrast",
|
|
3564
|
+
preferredStyles: [
|
|
3565
|
+
"fill-and-stroke",
|
|
3566
|
+
"stroke-only",
|
|
3567
|
+
"fill-only"
|
|
3568
|
+
],
|
|
3569
|
+
flowLineMultiplier: 0,
|
|
3570
|
+
heroShape: false,
|
|
3571
|
+
glowMultiplier: 0.3,
|
|
3572
|
+
sizePower: 1.0,
|
|
3573
|
+
invertForeground: false
|
|
3574
|
+
},
|
|
3575
|
+
{
|
|
3576
|
+
name: "botanical",
|
|
3577
|
+
gridSize: 4,
|
|
3578
|
+
layers: 4,
|
|
3579
|
+
baseOpacity: 0.5,
|
|
3580
|
+
opacityReduction: 0.06,
|
|
3581
|
+
minShapeSize: 30,
|
|
3582
|
+
maxShapeSize: 400,
|
|
3583
|
+
backgroundStyle: "radial-light",
|
|
3584
|
+
paletteMode: "earth",
|
|
3585
|
+
preferredStyles: [
|
|
3586
|
+
"watercolor",
|
|
3587
|
+
"fill-only",
|
|
3588
|
+
"incomplete"
|
|
3589
|
+
],
|
|
3590
|
+
flowLineMultiplier: 3,
|
|
3591
|
+
heroShape: true,
|
|
3592
|
+
glowMultiplier: 0.2,
|
|
3593
|
+
sizePower: 1.6,
|
|
3594
|
+
invertForeground: false
|
|
3595
|
+
},
|
|
3596
|
+
{
|
|
3597
|
+
name: "stipple-portrait",
|
|
3598
|
+
gridSize: 9,
|
|
3599
|
+
layers: 2,
|
|
3600
|
+
baseOpacity: 0.8,
|
|
3601
|
+
opacityReduction: 0.05,
|
|
3602
|
+
minShapeSize: 5,
|
|
3603
|
+
maxShapeSize: 120,
|
|
3604
|
+
backgroundStyle: "solid-light",
|
|
3605
|
+
paletteMode: "monochrome",
|
|
3606
|
+
preferredStyles: [
|
|
3607
|
+
"stipple",
|
|
3608
|
+
"fill-only",
|
|
3609
|
+
"hatched"
|
|
3610
|
+
],
|
|
3611
|
+
flowLineMultiplier: 0,
|
|
3612
|
+
heroShape: false,
|
|
3613
|
+
glowMultiplier: 0,
|
|
3614
|
+
sizePower: 2.8,
|
|
3615
|
+
invertForeground: false
|
|
3616
|
+
},
|
|
3617
|
+
{
|
|
3618
|
+
name: "celestial",
|
|
3619
|
+
gridSize: 7,
|
|
3620
|
+
layers: 5,
|
|
3621
|
+
baseOpacity: 0.45,
|
|
3622
|
+
opacityReduction: 0.04,
|
|
3623
|
+
minShapeSize: 8,
|
|
3624
|
+
maxShapeSize: 450,
|
|
3625
|
+
backgroundStyle: "radial-dark",
|
|
3626
|
+
paletteMode: "neon",
|
|
3627
|
+
preferredStyles: [
|
|
3628
|
+
"fill-only",
|
|
3629
|
+
"watercolor",
|
|
3630
|
+
"stroke-only",
|
|
3631
|
+
"incomplete"
|
|
3632
|
+
],
|
|
3633
|
+
flowLineMultiplier: 2,
|
|
3634
|
+
heroShape: true,
|
|
3635
|
+
glowMultiplier: 2.5,
|
|
3636
|
+
sizePower: 2.2,
|
|
3637
|
+
invertForeground: false
|
|
1855
3638
|
}
|
|
1856
3639
|
];
|
|
1857
3640
|
function $f89bc858f7202849$export$f1142fd7da4d6590(rng) {
|
|
@@ -1859,26 +3642,7 @@ function $f89bc858f7202849$export$f1142fd7da4d6590(rng) {
|
|
|
1859
3642
|
}
|
|
1860
3643
|
|
|
1861
3644
|
|
|
1862
|
-
// ── Shape categories for weighted selection
|
|
1863
|
-
const $4f72c5a314eddf25$var$BASIC_SHAPES = [
|
|
1864
|
-
"circle",
|
|
1865
|
-
"square",
|
|
1866
|
-
"triangle",
|
|
1867
|
-
"hexagon",
|
|
1868
|
-
"diamond",
|
|
1869
|
-
"cube"
|
|
1870
|
-
];
|
|
1871
|
-
const $4f72c5a314eddf25$var$COMPLEX_SHAPES = [
|
|
1872
|
-
"star",
|
|
1873
|
-
"jacked-star",
|
|
1874
|
-
"heart",
|
|
1875
|
-
"platonicSolid",
|
|
1876
|
-
"fibonacciSpiral",
|
|
1877
|
-
"islamicPattern",
|
|
1878
|
-
"celticKnot",
|
|
1879
|
-
"merkaba",
|
|
1880
|
-
"fractal"
|
|
1881
|
-
];
|
|
3645
|
+
// ── Shape categories for weighted selection (legacy fallback) ───────
|
|
1882
3646
|
const $4f72c5a314eddf25$var$SACRED_SHAPES = [
|
|
1883
3647
|
"mandala",
|
|
1884
3648
|
"flowerOfLife",
|
|
@@ -1890,15 +3654,6 @@ const $4f72c5a314eddf25$var$SACRED_SHAPES = [
|
|
|
1890
3654
|
"torus",
|
|
1891
3655
|
"eggOfLife"
|
|
1892
3656
|
];
|
|
1893
|
-
const $4f72c5a314eddf25$var$PROCEDURAL_SHAPES = [
|
|
1894
|
-
"blob",
|
|
1895
|
-
"ngon",
|
|
1896
|
-
"lissajous",
|
|
1897
|
-
"superellipse",
|
|
1898
|
-
"spirograph",
|
|
1899
|
-
"waveRing",
|
|
1900
|
-
"rose"
|
|
1901
|
-
];
|
|
1902
3657
|
const $4f72c5a314eddf25$var$COMPOSITION_MODES = [
|
|
1903
3658
|
"radial",
|
|
1904
3659
|
"flow-field",
|
|
@@ -1906,23 +3661,6 @@ const $4f72c5a314eddf25$var$COMPOSITION_MODES = [
|
|
|
1906
3661
|
"grid-subdivision",
|
|
1907
3662
|
"clustered"
|
|
1908
3663
|
];
|
|
1909
|
-
// ── Helper: pick shape with layer-aware weighting ───────────────────
|
|
1910
|
-
function $4f72c5a314eddf25$var$pickShape(rng, layerRatio, shapeNames) {
|
|
1911
|
-
const basicW = 1 - layerRatio * 0.6;
|
|
1912
|
-
const complexW = 0.3 + layerRatio * 0.3;
|
|
1913
|
-
const sacredW = 0.1 + layerRatio * 0.4;
|
|
1914
|
-
const proceduralW = 0.25 + layerRatio * 0.2; // always present, grows with depth
|
|
1915
|
-
const total = basicW + complexW + sacredW + proceduralW;
|
|
1916
|
-
const roll = rng() * total;
|
|
1917
|
-
let pool;
|
|
1918
|
-
if (roll < basicW) pool = $4f72c5a314eddf25$var$BASIC_SHAPES;
|
|
1919
|
-
else if (roll < basicW + complexW) pool = $4f72c5a314eddf25$var$COMPLEX_SHAPES;
|
|
1920
|
-
else if (roll < basicW + complexW + sacredW) pool = $4f72c5a314eddf25$var$SACRED_SHAPES;
|
|
1921
|
-
else pool = $4f72c5a314eddf25$var$PROCEDURAL_SHAPES;
|
|
1922
|
-
const available = pool.filter((s)=>shapeNames.includes(s));
|
|
1923
|
-
if (available.length === 0) return shapeNames[Math.floor(rng() * shapeNames.length)];
|
|
1924
|
-
return available[Math.floor(rng() * available.length)];
|
|
1925
|
-
}
|
|
1926
3664
|
// ── Helper: get position based on composition mode ──────────────────
|
|
1927
3665
|
function $4f72c5a314eddf25$var$getCompositionPosition(mode, rng, width, height, shapeIndex, totalShapes, cx, cy) {
|
|
1928
3666
|
switch(mode){
|
|
@@ -1981,22 +3719,28 @@ function $4f72c5a314eddf25$var$getCompositionPosition(mode, rng, width, height,
|
|
|
1981
3719
|
};
|
|
1982
3720
|
}
|
|
1983
3721
|
}
|
|
1984
|
-
// ── Helper: positional color
|
|
1985
|
-
function $4f72c5a314eddf25$var$getPositionalColor(x, y, width, height,
|
|
1986
|
-
|
|
1987
|
-
const
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
return (0, $d016ad53434219a1$export$
|
|
3722
|
+
// ── Helper: positional color from hierarchy ─────────────────────────
|
|
3723
|
+
function $4f72c5a314eddf25$var$getPositionalColor(x, y, width, height, hierarchy, rng) {
|
|
3724
|
+
// Blend position into color selection — shapes near center lean dominant
|
|
3725
|
+
const distFromCenter = Math.hypot(x - width / 2, y - height / 2) / Math.hypot(width / 2, height / 2);
|
|
3726
|
+
// Center = more dominant, edges = more accent
|
|
3727
|
+
if (distFromCenter < 0.35) return (0, $d016ad53434219a1$export$18a34c25ea7e724b)(hierarchy.dominant, rng, 10, 0.08);
|
|
3728
|
+
else if (distFromCenter < 0.7) return (0, $d016ad53434219a1$export$18a34c25ea7e724b)((0, $d016ad53434219a1$export$b49f62f0a99da0e8)(hierarchy, rng), rng, 8, 0.06);
|
|
3729
|
+
else {
|
|
3730
|
+
// Edges: bias toward secondary/accent
|
|
3731
|
+
const roll = rng();
|
|
3732
|
+
const color = roll < 0.4 ? hierarchy.secondary : roll < 0.75 ? hierarchy.accent : hierarchy.dominant;
|
|
3733
|
+
return (0, $d016ad53434219a1$export$18a34c25ea7e724b)(color, rng, 12, 0.08);
|
|
3734
|
+
}
|
|
1991
3735
|
}
|
|
1992
|
-
// ── Helper: check if a position is inside a void zone
|
|
3736
|
+
// ── Helper: check if a position is inside a void zone ───────────────
|
|
1993
3737
|
function $4f72c5a314eddf25$var$isInVoidZone(x, y, voidZones) {
|
|
1994
3738
|
for (const zone of voidZones){
|
|
1995
3739
|
if (Math.hypot(x - zone.x, y - zone.y) < zone.radius) return true;
|
|
1996
3740
|
}
|
|
1997
3741
|
return false;
|
|
1998
3742
|
}
|
|
1999
|
-
// ── Helper: density check
|
|
3743
|
+
// ── Helper: density check ───────────────────────────────────────────
|
|
2000
3744
|
function $4f72c5a314eddf25$var$localDensity(x, y, positions, radius) {
|
|
2001
3745
|
let count = 0;
|
|
2002
3746
|
for (const p of positions)if (Math.hypot(x - p.x, y - p.y) < radius) count++;
|
|
@@ -2070,6 +3814,135 @@ function $4f72c5a314eddf25$var$drawBackground(ctx, style, bgStart, bgEnd, width,
|
|
|
2070
3814
|
}
|
|
2071
3815
|
}
|
|
2072
3816
|
}
|
|
3817
|
+
const $4f72c5a314eddf25$var$CONSTELLATIONS = [
|
|
3818
|
+
{
|
|
3819
|
+
name: "flanked-triangle",
|
|
3820
|
+
build: (rng, baseSize)=>{
|
|
3821
|
+
const gap = baseSize * (0.6 + rng() * 0.3);
|
|
3822
|
+
return [
|
|
3823
|
+
{
|
|
3824
|
+
dx: 0,
|
|
3825
|
+
dy: 0,
|
|
3826
|
+
shape: "triangle",
|
|
3827
|
+
size: baseSize,
|
|
3828
|
+
rotation: rng() * 360
|
|
3829
|
+
},
|
|
3830
|
+
{
|
|
3831
|
+
dx: -gap,
|
|
3832
|
+
dy: gap * 0.3,
|
|
3833
|
+
shape: "circle",
|
|
3834
|
+
size: baseSize * 0.35,
|
|
3835
|
+
rotation: 0
|
|
3836
|
+
},
|
|
3837
|
+
{
|
|
3838
|
+
dx: gap,
|
|
3839
|
+
dy: gap * 0.3,
|
|
3840
|
+
shape: "circle",
|
|
3841
|
+
size: baseSize * 0.35,
|
|
3842
|
+
rotation: 0
|
|
3843
|
+
}
|
|
3844
|
+
];
|
|
3845
|
+
}
|
|
3846
|
+
},
|
|
3847
|
+
{
|
|
3848
|
+
name: "hexagon-ring",
|
|
3849
|
+
build: (rng, baseSize)=>{
|
|
3850
|
+
const members = [];
|
|
3851
|
+
const count = 5 + Math.floor(rng() * 2);
|
|
3852
|
+
const ringR = baseSize * 0.6;
|
|
3853
|
+
for(let i = 0; i < count; i++){
|
|
3854
|
+
const angle = i / count * Math.PI * 2;
|
|
3855
|
+
members.push({
|
|
3856
|
+
dx: Math.cos(angle) * ringR,
|
|
3857
|
+
dy: Math.sin(angle) * ringR,
|
|
3858
|
+
shape: "hexagon",
|
|
3859
|
+
size: baseSize * (0.25 + rng() * 0.1),
|
|
3860
|
+
rotation: angle * 180 / Math.PI
|
|
3861
|
+
});
|
|
3862
|
+
}
|
|
3863
|
+
return members;
|
|
3864
|
+
}
|
|
3865
|
+
},
|
|
3866
|
+
{
|
|
3867
|
+
name: "spiral-dots",
|
|
3868
|
+
build: (rng, baseSize)=>{
|
|
3869
|
+
const members = [];
|
|
3870
|
+
const count = 7 + Math.floor(rng() * 5);
|
|
3871
|
+
const turns = 1.5 + rng();
|
|
3872
|
+
for(let i = 0; i < count; i++){
|
|
3873
|
+
const t = i / count;
|
|
3874
|
+
const angle = t * Math.PI * 2 * turns;
|
|
3875
|
+
const r = t * baseSize * 0.7;
|
|
3876
|
+
members.push({
|
|
3877
|
+
dx: Math.cos(angle) * r,
|
|
3878
|
+
dy: Math.sin(angle) * r,
|
|
3879
|
+
shape: "circle",
|
|
3880
|
+
size: baseSize * (0.08 + (1 - t) * 0.12),
|
|
3881
|
+
rotation: 0
|
|
3882
|
+
});
|
|
3883
|
+
}
|
|
3884
|
+
return members;
|
|
3885
|
+
}
|
|
3886
|
+
},
|
|
3887
|
+
{
|
|
3888
|
+
name: "diamond-cluster",
|
|
3889
|
+
build: (rng, baseSize)=>{
|
|
3890
|
+
const gap = baseSize * 0.45;
|
|
3891
|
+
return [
|
|
3892
|
+
{
|
|
3893
|
+
dx: 0,
|
|
3894
|
+
dy: -gap,
|
|
3895
|
+
shape: "diamond",
|
|
3896
|
+
size: baseSize * 0.4,
|
|
3897
|
+
rotation: 0
|
|
3898
|
+
},
|
|
3899
|
+
{
|
|
3900
|
+
dx: gap,
|
|
3901
|
+
dy: 0,
|
|
3902
|
+
shape: "diamond",
|
|
3903
|
+
size: baseSize * 0.35,
|
|
3904
|
+
rotation: 15
|
|
3905
|
+
},
|
|
3906
|
+
{
|
|
3907
|
+
dx: 0,
|
|
3908
|
+
dy: gap,
|
|
3909
|
+
shape: "diamond",
|
|
3910
|
+
size: baseSize * 0.3,
|
|
3911
|
+
rotation: 30
|
|
3912
|
+
},
|
|
3913
|
+
{
|
|
3914
|
+
dx: -gap,
|
|
3915
|
+
dy: 0,
|
|
3916
|
+
shape: "diamond",
|
|
3917
|
+
size: baseSize * 0.35,
|
|
3918
|
+
rotation: -15
|
|
3919
|
+
}
|
|
3920
|
+
];
|
|
3921
|
+
}
|
|
3922
|
+
},
|
|
3923
|
+
{
|
|
3924
|
+
name: "crescent-pair",
|
|
3925
|
+
build: (rng, baseSize)=>{
|
|
3926
|
+
const gap = baseSize * 0.5;
|
|
3927
|
+
return [
|
|
3928
|
+
{
|
|
3929
|
+
dx: -gap * 0.4,
|
|
3930
|
+
dy: 0,
|
|
3931
|
+
shape: "crescent",
|
|
3932
|
+
size: baseSize * 0.5,
|
|
3933
|
+
rotation: rng() * 30
|
|
3934
|
+
},
|
|
3935
|
+
{
|
|
3936
|
+
dx: gap * 0.4,
|
|
3937
|
+
dy: 0,
|
|
3938
|
+
shape: "crescent",
|
|
3939
|
+
size: baseSize * 0.45,
|
|
3940
|
+
rotation: 180 + rng() * 30
|
|
3941
|
+
}
|
|
3942
|
+
];
|
|
3943
|
+
}
|
|
3944
|
+
}
|
|
3945
|
+
];
|
|
2073
3946
|
function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
2074
3947
|
const finalConfig = {
|
|
2075
3948
|
...(0, $93cf69256c93baa9$export$c2f8e0cc249a8d8f),
|
|
@@ -2092,7 +3965,15 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2092
3965
|
const [bgStart, bgEnd] = colorScheme.getBackgroundColorsByMode(archetype.paletteMode);
|
|
2093
3966
|
const tempMode = colorScheme.getTemperatureMode();
|
|
2094
3967
|
const fgTempTarget = tempMode === "warm-bg" ? "cool" : tempMode === "cool-bg" ? "warm" : null;
|
|
3968
|
+
// ── 0b. Color hierarchy — dominant/secondary/accent weighting ──
|
|
3969
|
+
const colorHierarchy = (0, $d016ad53434219a1$export$fabac4600b87056)(colors, rng);
|
|
3970
|
+
// ── 0c. Shape palette — curated shapes that work well together ──
|
|
2095
3971
|
const shapeNames = Object.keys((0, $9c828bde2acaae64$export$4ff7fc6f1af248b5));
|
|
3972
|
+
const shapePalette = (0, $e73976f898150d4d$export$4a95df8944b5033b)(rng, shapeNames, archetype.name);
|
|
3973
|
+
// ── 0d. Color grading — unified tone for the whole image ───────
|
|
3974
|
+
const colorGrade = (0, $d016ad53434219a1$export$6d1620b367f86f7a)(rng);
|
|
3975
|
+
// ── 0e. Light direction — consistent shadow angle ──────────────
|
|
3976
|
+
const lightAngle = rng() * Math.PI * 2;
|
|
2096
3977
|
const scaleFactor = Math.min(width, height) / 1024;
|
|
2097
3978
|
const adjustedMinSize = minShapeSize * scaleFactor;
|
|
2098
3979
|
const adjustedMaxSize = maxShapeSize * scaleFactor;
|
|
@@ -2101,27 +3982,45 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2101
3982
|
// ── 1. Background ──────────────────────────────────────────────
|
|
2102
3983
|
const bgRadius = Math.hypot(cx, cy);
|
|
2103
3984
|
$4f72c5a314eddf25$var$drawBackground(ctx, archetype.backgroundStyle, bgStart, bgEnd, width, height, cx, cy, bgRadius, rng, colors);
|
|
3985
|
+
// Gradient mesh overlay — 3-4 color control points for richer backgrounds
|
|
3986
|
+
const meshPoints = 3 + Math.floor(rng() * 2);
|
|
3987
|
+
ctx.globalCompositeOperation = "soft-light";
|
|
3988
|
+
for(let i = 0; i < meshPoints; i++){
|
|
3989
|
+
const mx = rng() * width;
|
|
3990
|
+
const my = rng() * height;
|
|
3991
|
+
const mRadius = Math.min(width, height) * (0.3 + rng() * 0.4);
|
|
3992
|
+
const mColor = (0, $d016ad53434219a1$export$b49f62f0a99da0e8)(colorHierarchy, rng);
|
|
3993
|
+
const grad = ctx.createRadialGradient(mx, my, 0, mx, my, mRadius);
|
|
3994
|
+
grad.addColorStop(0, (0, $d016ad53434219a1$export$f2121afcad3d553f)(mColor, 0.08 + rng() * 0.06));
|
|
3995
|
+
grad.addColorStop(1, "rgba(0,0,0,0)");
|
|
3996
|
+
ctx.globalAlpha = 1;
|
|
3997
|
+
ctx.fillStyle = grad;
|
|
3998
|
+
ctx.fillRect(0, 0, width, height);
|
|
3999
|
+
}
|
|
4000
|
+
ctx.globalCompositeOperation = "source-over";
|
|
2104
4001
|
// Compute average background luminance for contrast enforcement
|
|
2105
4002
|
const bgLum = ((0, $d016ad53434219a1$export$5c6e3c2b59b7fbbe)(bgStart) + (0, $d016ad53434219a1$export$5c6e3c2b59b7fbbe)(bgEnd)) / 2;
|
|
2106
|
-
// ── 1b. Layered background
|
|
2107
|
-
// Draw large, very faint shapes to give the background texture
|
|
4003
|
+
// ── 1b. Layered background — archetype-coherent shapes ─────────
|
|
2108
4004
|
const bgShapeCount = 3 + Math.floor(rng() * 4);
|
|
2109
4005
|
ctx.globalCompositeOperation = "soft-light";
|
|
2110
4006
|
for(let i = 0; i < bgShapeCount; i++){
|
|
2111
4007
|
const bx = rng() * width;
|
|
2112
4008
|
const by = rng() * height;
|
|
2113
4009
|
const bSize = width * 0.3 + rng() * width * 0.5;
|
|
2114
|
-
const bColor =
|
|
4010
|
+
const bColor = (0, $d016ad53434219a1$export$b49f62f0a99da0e8)(colorHierarchy, rng);
|
|
2115
4011
|
ctx.globalAlpha = 0.03 + rng() * 0.05;
|
|
2116
4012
|
ctx.fillStyle = (0, $d016ad53434219a1$export$f2121afcad3d553f)(bColor, 0.15);
|
|
2117
4013
|
ctx.beginPath();
|
|
2118
|
-
|
|
4014
|
+
// Use archetype-appropriate background shapes
|
|
4015
|
+
if (archetype.name === "geometric-precision" || archetype.name === "op-art") // Rectangular shapes for geometric archetypes
|
|
4016
|
+
ctx.rect(bx - bSize / 2, by - bSize / 2, bSize, bSize * (0.5 + rng() * 0.5));
|
|
4017
|
+
else ctx.arc(bx, by, bSize / 2, 0, Math.PI * 2);
|
|
2119
4018
|
ctx.fill();
|
|
2120
4019
|
}
|
|
2121
4020
|
// Subtle concentric rings from center
|
|
2122
4021
|
const ringCount = 2 + Math.floor(rng() * 3);
|
|
2123
4022
|
ctx.globalAlpha = 0.02 + rng() * 0.03;
|
|
2124
|
-
ctx.strokeStyle = (0, $d016ad53434219a1$export$f2121afcad3d553f)(
|
|
4023
|
+
ctx.strokeStyle = (0, $d016ad53434219a1$export$f2121afcad3d553f)(colorHierarchy.dominant, 0.1);
|
|
2125
4024
|
ctx.lineWidth = 1 * scaleFactor;
|
|
2126
4025
|
for(let i = 1; i <= ringCount; i++){
|
|
2127
4026
|
const r = Math.min(width, height) * 0.15 * i;
|
|
@@ -2130,12 +4029,70 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2130
4029
|
ctx.stroke();
|
|
2131
4030
|
}
|
|
2132
4031
|
ctx.globalCompositeOperation = "source-over";
|
|
4032
|
+
// ── 1c. Background pattern layer — subtle textured paper ───────
|
|
4033
|
+
const bgPatternRoll = rng();
|
|
4034
|
+
if (bgPatternRoll < 0.6) {
|
|
4035
|
+
ctx.save();
|
|
4036
|
+
ctx.globalCompositeOperation = "soft-light";
|
|
4037
|
+
const patternOpacity = 0.02 + rng() * 0.04;
|
|
4038
|
+
const patternColor = (0, $d016ad53434219a1$export$f2121afcad3d553f)(colorHierarchy.dominant, 0.15);
|
|
4039
|
+
if (bgPatternRoll < 0.2) {
|
|
4040
|
+
// Dot grid
|
|
4041
|
+
const dotSpacing = Math.max(8, Math.min(width, height) * (0.015 + rng() * 0.015));
|
|
4042
|
+
const dotR = dotSpacing * 0.08;
|
|
4043
|
+
ctx.globalAlpha = patternOpacity;
|
|
4044
|
+
ctx.fillStyle = patternColor;
|
|
4045
|
+
for(let px = 0; px < width; px += dotSpacing)for(let py = 0; py < height; py += dotSpacing){
|
|
4046
|
+
ctx.beginPath();
|
|
4047
|
+
ctx.arc(px, py, dotR, 0, Math.PI * 2);
|
|
4048
|
+
ctx.fill();
|
|
4049
|
+
}
|
|
4050
|
+
} else if (bgPatternRoll < 0.4) {
|
|
4051
|
+
// Diagonal lines
|
|
4052
|
+
const lineSpacing = Math.max(6, Math.min(width, height) * (0.02 + rng() * 0.02));
|
|
4053
|
+
ctx.globalAlpha = patternOpacity;
|
|
4054
|
+
ctx.strokeStyle = patternColor;
|
|
4055
|
+
ctx.lineWidth = 0.5 * scaleFactor;
|
|
4056
|
+
const diag = Math.hypot(width, height);
|
|
4057
|
+
for(let d = -diag; d < diag; d += lineSpacing){
|
|
4058
|
+
ctx.beginPath();
|
|
4059
|
+
ctx.moveTo(d, 0);
|
|
4060
|
+
ctx.lineTo(d + height, height);
|
|
4061
|
+
ctx.stroke();
|
|
4062
|
+
}
|
|
4063
|
+
} else {
|
|
4064
|
+
// Tessellation — hexagonal grid of tiny shapes
|
|
4065
|
+
const tessSize = Math.max(10, Math.min(width, height) * (0.025 + rng() * 0.02));
|
|
4066
|
+
const tessH = tessSize * Math.sqrt(3);
|
|
4067
|
+
ctx.globalAlpha = patternOpacity * 0.7;
|
|
4068
|
+
ctx.strokeStyle = patternColor;
|
|
4069
|
+
ctx.lineWidth = 0.4 * scaleFactor;
|
|
4070
|
+
for(let row = 0; row * tessH < height + tessH; row++){
|
|
4071
|
+
const offsetX = row % 2 * tessSize * 0.75;
|
|
4072
|
+
for(let col = 0; col * tessSize * 1.5 < width + tessSize * 1.5; col++){
|
|
4073
|
+
const hx = col * tessSize * 1.5 + offsetX;
|
|
4074
|
+
const hy = row * tessH;
|
|
4075
|
+
ctx.beginPath();
|
|
4076
|
+
for(let s = 0; s < 6; s++){
|
|
4077
|
+
const angle = Math.PI / 3 * s - Math.PI / 6;
|
|
4078
|
+
const vx = hx + Math.cos(angle) * tessSize * 0.5;
|
|
4079
|
+
const vy = hy + Math.sin(angle) * tessSize * 0.5;
|
|
4080
|
+
if (s === 0) ctx.moveTo(vx, vy);
|
|
4081
|
+
else ctx.lineTo(vx, vy);
|
|
4082
|
+
}
|
|
4083
|
+
ctx.closePath();
|
|
4084
|
+
ctx.stroke();
|
|
4085
|
+
}
|
|
4086
|
+
}
|
|
4087
|
+
}
|
|
4088
|
+
ctx.restore();
|
|
4089
|
+
}
|
|
4090
|
+
ctx.globalCompositeOperation = "source-over";
|
|
2133
4091
|
// ── 2. Composition mode ────────────────────────────────────────
|
|
2134
4092
|
const compositionMode = $4f72c5a314eddf25$var$COMPOSITION_MODES[Math.floor(rng() * $4f72c5a314eddf25$var$COMPOSITION_MODES.length)];
|
|
2135
4093
|
const symRoll = rng();
|
|
2136
4094
|
const symmetryMode = symRoll < 0.10 ? "bilateral-x" : symRoll < 0.20 ? "bilateral-y" : symRoll < 0.25 ? "quad" : "none";
|
|
2137
4095
|
// ── 3. Focal points + void zones ───────────────────────────────
|
|
2138
|
-
// Rule-of-thirds intersection points for intentional composition
|
|
2139
4096
|
const THIRDS_POINTS = [
|
|
2140
4097
|
{
|
|
2141
4098
|
x: 1 / 3,
|
|
@@ -2156,10 +4113,8 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2156
4113
|
];
|
|
2157
4114
|
const numFocal = 1 + Math.floor(rng() * 2);
|
|
2158
4115
|
const focalPoints = [];
|
|
2159
|
-
for(let f = 0; f < numFocal; f++)
|
|
2160
|
-
if (rng() < 0.7) {
|
|
4116
|
+
for(let f = 0; f < numFocal; f++)if (rng() < 0.7) {
|
|
2161
4117
|
const tp = THIRDS_POINTS[Math.floor(rng() * THIRDS_POINTS.length)];
|
|
2162
|
-
// Small jitter around the thirds point so it's not robotic
|
|
2163
4118
|
focalPoints.push({
|
|
2164
4119
|
x: width * (tp.x + (rng() - 0.5) * 0.08),
|
|
2165
4120
|
y: height * (tp.y + (rng() - 0.5) * 0.08),
|
|
@@ -2170,7 +4125,6 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2170
4125
|
y: height * (0.2 + rng() * 0.6),
|
|
2171
4126
|
strength: 0.3 + rng() * 0.4
|
|
2172
4127
|
});
|
|
2173
|
-
// Feature E: 1-2 void zones where shapes are sparse (negative space)
|
|
2174
4128
|
const numVoids = Math.floor(rng() * 2) + 1;
|
|
2175
4129
|
const voidZones = [];
|
|
2176
4130
|
for(let v = 0; v < numVoids; v++)voidZones.push({
|
|
@@ -2202,20 +4156,24 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2202
4156
|
}
|
|
2203
4157
|
// Track all placed shapes for density checks and connecting curves
|
|
2204
4158
|
const shapePositions = [];
|
|
4159
|
+
// Hero avoidance radius — shapes near the hero orient toward it
|
|
4160
|
+
let heroCenter = null;
|
|
2205
4161
|
// ── 4b. Hero shape — a dominant focal element ───────────────────
|
|
2206
4162
|
if (archetype.heroShape && rng() < 0.6) {
|
|
2207
4163
|
const heroFocal = focalPoints[0];
|
|
4164
|
+
// Use shape palette hero candidates
|
|
2208
4165
|
const heroPool = [
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
];
|
|
2214
|
-
const heroShape = heroPool.filter((s)=>shapeNames.includes(s))[Math.floor(rng() * heroPool.filter((s)=>shapeNames.includes(s)).length)] || shapeNames[Math.floor(rng() * shapeNames.length)];
|
|
4166
|
+
...shapePalette.primary,
|
|
4167
|
+
...shapePalette.supporting
|
|
4168
|
+
].filter((s)=>(0, $e73976f898150d4d$export$4343b39fe47bd82c)[s]?.heroCandidate && shapeNames.includes(s));
|
|
4169
|
+
const heroShape = heroPool.length > 0 ? heroPool[Math.floor(rng() * heroPool.length)] : shapeNames[Math.floor(rng() * shapeNames.length)];
|
|
2215
4170
|
const heroSize = adjustedMaxSize * (0.8 + rng() * 0.5);
|
|
2216
4171
|
const heroRotation = rng() * 360;
|
|
2217
|
-
const heroFill = (0, $d016ad53434219a1$export$f2121afcad3d553f)((0, $d016ad53434219a1$export$90ad0e6170cf6af5)((0, $d016ad53434219a1$export$
|
|
2218
|
-
const heroStroke = (0, $d016ad53434219a1$export$90ad0e6170cf6af5)((0, $d016ad53434219a1$export$
|
|
4172
|
+
const heroFill = (0, $d016ad53434219a1$export$f2121afcad3d553f)((0, $d016ad53434219a1$export$90ad0e6170cf6af5)((0, $d016ad53434219a1$export$18a34c25ea7e724b)(colorHierarchy.dominant, rng, 6, 0.05), bgLum), 0.15 + rng() * 0.2);
|
|
4173
|
+
const heroStroke = (0, $d016ad53434219a1$export$90ad0e6170cf6af5)((0, $d016ad53434219a1$export$18a34c25ea7e724b)(colorHierarchy.accent, rng, 6, 0.05), bgLum);
|
|
4174
|
+
// Get best style for this hero shape
|
|
4175
|
+
const heroProfile = (0, $e73976f898150d4d$export$4343b39fe47bd82c)[heroShape];
|
|
4176
|
+
const heroStyle = heroProfile ? heroProfile.bestStyles[Math.floor(rng() * heroProfile.bestStyles.length)] : rng() < 0.4 ? "watercolor" : "fill-and-stroke";
|
|
2219
4177
|
ctx.globalAlpha = 0.5 + rng() * 0.2;
|
|
2220
4178
|
(0, $c3de8257a8baa3b0$export$bb35a6995ddbf32d)(ctx, heroShape, heroFocal.x, heroFocal.y, {
|
|
2221
4179
|
fillColor: heroFill,
|
|
@@ -2226,14 +4184,20 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2226
4184
|
proportionType: "GOLDEN_RATIO",
|
|
2227
4185
|
glowRadius: (12 + rng() * 20) * scaleFactor,
|
|
2228
4186
|
glowColor: (0, $d016ad53434219a1$export$f2121afcad3d553f)(heroStroke, 0.4),
|
|
2229
|
-
gradientFillEnd: (0, $d016ad53434219a1$export$
|
|
2230
|
-
renderStyle:
|
|
4187
|
+
gradientFillEnd: (0, $d016ad53434219a1$export$18a34c25ea7e724b)(colorHierarchy.secondary, rng, 10, 0.1),
|
|
4188
|
+
renderStyle: heroStyle,
|
|
2231
4189
|
rng: rng
|
|
2232
4190
|
});
|
|
2233
|
-
|
|
4191
|
+
heroCenter = {
|
|
2234
4192
|
x: heroFocal.x,
|
|
2235
4193
|
y: heroFocal.y,
|
|
2236
4194
|
size: heroSize
|
|
4195
|
+
};
|
|
4196
|
+
shapePositions.push({
|
|
4197
|
+
x: heroFocal.x,
|
|
4198
|
+
y: heroFocal.y,
|
|
4199
|
+
size: heroSize,
|
|
4200
|
+
shape: heroShape
|
|
2237
4201
|
});
|
|
2238
4202
|
}
|
|
2239
4203
|
// ── 5. Shape layers ────────────────────────────────────────────
|
|
@@ -2244,46 +4208,64 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2244
4208
|
const numShapes = shapesPerLayer + Math.floor(rng() * shapesPerLayer * 0.3);
|
|
2245
4209
|
const layerOpacity = Math.max(0.15, baseOpacity - layer * opacityReduction);
|
|
2246
4210
|
const layerSizeScale = 1 - layer * 0.15;
|
|
2247
|
-
//
|
|
4211
|
+
// Per-layer blend mode
|
|
2248
4212
|
const layerBlend = (0, $c3de8257a8baa3b0$export$7bb7bff4e26fa06b)(rng);
|
|
2249
4213
|
ctx.globalCompositeOperation = layerBlend;
|
|
2250
|
-
//
|
|
4214
|
+
// Per-layer render style bias — prefer archetype styles
|
|
2251
4215
|
const layerRenderStyle = rng() < 0.6 ? archetype.preferredStyles[Math.floor(rng() * archetype.preferredStyles.length)] : (0, $c3de8257a8baa3b0$export$9fd4e64b2acd410e)(rng);
|
|
2252
|
-
//
|
|
2253
|
-
const atmosphericDesat = layerRatio * 0.3;
|
|
4216
|
+
// Atmospheric desaturation for later layers
|
|
4217
|
+
const atmosphericDesat = layerRatio * 0.3;
|
|
4218
|
+
// Depth-of-field simulation — later layers are "further away"
|
|
4219
|
+
// Reduce stroke widths and shift colors toward the background
|
|
4220
|
+
const dofFactor = 1 - layerRatio * 0.5; // 1.0 for front layer, 0.5 for back
|
|
4221
|
+
const dofStrokeScale = 0.4 + dofFactor * 0.6; // strokes thin out with depth
|
|
4222
|
+
const dofContrastReduction = layerRatio * 0.2; // colors fade toward bg
|
|
2254
4223
|
for(let i = 0; i < numShapes; i++){
|
|
2255
4224
|
// Position from composition mode, then focal bias
|
|
2256
4225
|
const rawPos = $4f72c5a314eddf25$var$getCompositionPosition(compositionMode, rng, width, height, i, numShapes, cx, cy);
|
|
2257
4226
|
const [x, y] = applyFocalBias(rawPos.x, rawPos.y);
|
|
2258
|
-
//
|
|
4227
|
+
// Skip shapes in void zones, reduce in dense areas
|
|
2259
4228
|
if ($4f72c5a314eddf25$var$isInVoidZone(x, y, voidZones)) {
|
|
2260
|
-
// 85% chance to skip — allows a few shapes to bleed in
|
|
2261
4229
|
if (rng() < 0.85) continue;
|
|
2262
4230
|
}
|
|
2263
4231
|
if ($4f72c5a314eddf25$var$localDensity(x, y, shapePositions, densityCheckRadius) > maxLocalDensity) {
|
|
2264
|
-
if (rng() < 0.6) continue;
|
|
4232
|
+
if (rng() < 0.6) continue;
|
|
2265
4233
|
}
|
|
2266
|
-
// Weighted shape selection
|
|
2267
|
-
const shape = $4f72c5a314eddf25$var$pickShape(rng, layerRatio, shapeNames);
|
|
2268
4234
|
// Power distribution for size — archetype controls the curve
|
|
2269
4235
|
const sizeT = Math.pow(rng(), archetype.sizePower);
|
|
2270
4236
|
const size = (adjustedMinSize + sizeT * (adjustedMaxSize - adjustedMinSize)) * layerSizeScale;
|
|
4237
|
+
// Size fraction for affinity-aware shape selection
|
|
4238
|
+
const sizeFraction = size / adjustedMaxSize;
|
|
4239
|
+
// Palette-driven shape selection (replaces naive pickShape)
|
|
4240
|
+
const shape = (0, $e73976f898150d4d$export$3c37d9a045754d0e)(shapePalette, rng, sizeFraction);
|
|
2271
4241
|
// Flow-field rotation in flow-field mode, random otherwise
|
|
2272
|
-
|
|
2273
|
-
//
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
4242
|
+
let rotation = compositionMode === "flow-field" ? flowAngle(x, y) * 180 / Math.PI + (rng() - 0.5) * 30 : rng() * 360;
|
|
4243
|
+
// Hero avoidance: shapes near the hero orient toward it
|
|
4244
|
+
if (heroCenter) {
|
|
4245
|
+
const distToHero = Math.hypot(x - heroCenter.x, y - heroCenter.y);
|
|
4246
|
+
const heroInfluence = heroCenter.size * 1.5;
|
|
4247
|
+
if (distToHero < heroInfluence && distToHero > 0) {
|
|
4248
|
+
const angleToHero = Math.atan2(heroCenter.y - y, heroCenter.x - x) * 180 / Math.PI;
|
|
4249
|
+
const blendFactor = 1 - distToHero / heroInfluence;
|
|
4250
|
+
rotation = rotation + (angleToHero - rotation) * blendFactor * 0.4;
|
|
4251
|
+
}
|
|
4252
|
+
}
|
|
4253
|
+
// Positional color from hierarchy + jitter
|
|
4254
|
+
let fillBase = $4f72c5a314eddf25$var$getPositionalColor(x, y, width, height, colorHierarchy, rng);
|
|
4255
|
+
const strokeBase = (0, $d016ad53434219a1$export$b49f62f0a99da0e8)(colorHierarchy, rng);
|
|
4256
|
+
// Desaturate colors on later layers for depth
|
|
2277
4257
|
if (atmosphericDesat > 0) fillBase = (0, $d016ad53434219a1$export$fb75607d98509d9)(fillBase, atmosphericDesat);
|
|
2278
4258
|
// Temperature contrast: shift foreground shapes opposite to background
|
|
2279
4259
|
if (fgTempTarget) fillBase = (0, $d016ad53434219a1$export$51ea55f869b7e0d3)(fillBase, fgTempTarget, 0.15 + layerRatio * 0.1);
|
|
2280
|
-
const fillColor = (0, $d016ad53434219a1$export$90ad0e6170cf6af5)((0, $d016ad53434219a1$export$
|
|
2281
|
-
const strokeColor = (0, $d016ad53434219a1$export$90ad0e6170cf6af5)((0, $d016ad53434219a1$export$
|
|
4260
|
+
const fillColor = (0, $d016ad53434219a1$export$90ad0e6170cf6af5)((0, $d016ad53434219a1$export$18a34c25ea7e724b)(fillBase, rng, 6, 0.05), bgLum);
|
|
4261
|
+
const strokeColor = (0, $d016ad53434219a1$export$90ad0e6170cf6af5)((0, $d016ad53434219a1$export$18a34c25ea7e724b)(strokeBase, rng, 5, 0.04), bgLum);
|
|
2282
4262
|
// Semi-transparent fill
|
|
2283
4263
|
const fillAlpha = 0.2 + rng() * 0.5;
|
|
2284
4264
|
const transparentFill = (0, $d016ad53434219a1$export$f2121afcad3d553f)(fillColor, fillAlpha);
|
|
2285
|
-
const strokeWidth = (0.5 + rng() * 2.0) * scaleFactor;
|
|
2286
|
-
|
|
4265
|
+
const strokeWidth = (0.5 + rng() * 2.0) * scaleFactor * dofStrokeScale;
|
|
4266
|
+
// Depth-of-field: reduce opacity slightly for distant layers
|
|
4267
|
+
const dofOpacityScale = 1 - dofContrastReduction;
|
|
4268
|
+
ctx.globalAlpha = layerOpacity * (0.5 + rng() * 0.5) * dofOpacityScale;
|
|
2287
4269
|
// Glow on sacred shapes more often — scaled by archetype
|
|
2288
4270
|
const isSacred = $4f72c5a314eddf25$var$SACRED_SHAPES.includes(shape);
|
|
2289
4271
|
const baseGlowChance = isSacred ? 0.45 : 0.2;
|
|
@@ -2292,58 +4274,177 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2292
4274
|
const glowRadius = hasGlow ? (8 + rng() * 20) * scaleFactor : 0;
|
|
2293
4275
|
// Gradient fill on ~30%
|
|
2294
4276
|
const hasGradient = rng() < 0.3;
|
|
2295
|
-
const gradientEnd = hasGradient ? (0, $d016ad53434219a1$export$
|
|
2296
|
-
//
|
|
2297
|
-
const shapeRenderStyle =
|
|
2298
|
-
//
|
|
4277
|
+
const gradientEnd = hasGradient ? (0, $d016ad53434219a1$export$18a34c25ea7e724b)((0, $d016ad53434219a1$export$b49f62f0a99da0e8)(colorHierarchy, rng), rng, 10, 0.1) : undefined;
|
|
4278
|
+
// Affinity-aware render style selection
|
|
4279
|
+
const shapeRenderStyle = (0, $e73976f898150d4d$export$ab873bb6fb56c1a8)(shape, layerRenderStyle, rng);
|
|
4280
|
+
// Organic edge jitter — applied via watercolor style on ~15% of shapes
|
|
2299
4281
|
const useOrganicEdges = rng() < 0.15 && shapeRenderStyle === "fill-and-stroke";
|
|
2300
4282
|
const finalRenderStyle = useOrganicEdges ? "watercolor" : shapeRenderStyle;
|
|
2301
|
-
|
|
4283
|
+
// Consistent light direction — subtle shadow offset
|
|
4284
|
+
const shadowDist = hasGlow ? 0 : size * 0.02;
|
|
4285
|
+
const shadowOffX = shadowDist * Math.cos(lightAngle);
|
|
4286
|
+
const shadowOffY = shadowDist * Math.sin(lightAngle);
|
|
4287
|
+
// ── 5a. Tangent placement — nudge toward nearest shape edge ──
|
|
4288
|
+
let finalX = x;
|
|
4289
|
+
let finalY = y;
|
|
4290
|
+
if (shapePositions.length > 0 && rng() < 0.25) {
|
|
4291
|
+
// Find nearest placed shape
|
|
4292
|
+
let nearestDist = Infinity;
|
|
4293
|
+
let nearestPos = null;
|
|
4294
|
+
for (const sp of shapePositions){
|
|
4295
|
+
const d = Math.hypot(x - sp.x, y - sp.y);
|
|
4296
|
+
if (d < nearestDist && d > 0) {
|
|
4297
|
+
nearestDist = d;
|
|
4298
|
+
nearestPos = sp;
|
|
4299
|
+
}
|
|
4300
|
+
}
|
|
4301
|
+
if (nearestPos) {
|
|
4302
|
+
// Target distance: edges kissing (sum of half-sizes)
|
|
4303
|
+
const targetDist = (size + nearestPos.size) * 0.5;
|
|
4304
|
+
if (nearestDist > targetDist * 0.5 && nearestDist < targetDist * 3) {
|
|
4305
|
+
const angle = Math.atan2(y - nearestPos.y, x - nearestPos.x);
|
|
4306
|
+
finalX = nearestPos.x + Math.cos(angle) * targetDist;
|
|
4307
|
+
finalY = nearestPos.y + Math.sin(angle) * targetDist;
|
|
4308
|
+
// Keep in bounds
|
|
4309
|
+
finalX = Math.max(0, Math.min(width, finalX));
|
|
4310
|
+
finalY = Math.max(0, Math.min(height, finalY));
|
|
4311
|
+
}
|
|
4312
|
+
}
|
|
4313
|
+
}
|
|
4314
|
+
// ── 5b. Shape mirroring — basic shapes get reflected copies ──
|
|
4315
|
+
const mirrorAxis = (0, $c3de8257a8baa3b0$export$879206e23912d1a9)(rng);
|
|
4316
|
+
const isBasicShape = [
|
|
4317
|
+
"circle",
|
|
4318
|
+
"triangle",
|
|
4319
|
+
"square",
|
|
4320
|
+
"hexagon",
|
|
4321
|
+
"star",
|
|
4322
|
+
"diamond",
|
|
4323
|
+
"crescent",
|
|
4324
|
+
"penroseTile",
|
|
4325
|
+
"reuleauxTriangle"
|
|
4326
|
+
].includes(shape);
|
|
4327
|
+
const shouldMirror = mirrorAxis !== null && isBasicShape && size > adjustedMaxSize * 0.2;
|
|
4328
|
+
const shapeConfig = {
|
|
2302
4329
|
fillColor: transparentFill,
|
|
2303
4330
|
strokeColor: strokeColor,
|
|
2304
4331
|
strokeWidth: strokeWidth,
|
|
2305
4332
|
size: size,
|
|
2306
4333
|
rotation: rotation,
|
|
2307
4334
|
proportionType: "GOLDEN_RATIO",
|
|
2308
|
-
glowRadius: glowRadius,
|
|
2309
|
-
glowColor: hasGlow ? (0, $d016ad53434219a1$export$f2121afcad3d553f)(fillColor, 0.6) : undefined,
|
|
4335
|
+
glowRadius: glowRadius || (shadowDist > 0 ? shadowDist * 2 : 0),
|
|
4336
|
+
glowColor: hasGlow ? (0, $d016ad53434219a1$export$f2121afcad3d553f)(fillColor, 0.6) : shadowDist > 0 ? "rgba(0,0,0,0.08)" : undefined,
|
|
2310
4337
|
gradientFillEnd: gradientEnd,
|
|
2311
4338
|
renderStyle: finalRenderStyle,
|
|
2312
4339
|
rng: rng
|
|
4340
|
+
};
|
|
4341
|
+
if (shouldMirror) (0, $c3de8257a8baa3b0$export$8bd8bbd1a8e53689)(ctx, shape, finalX, finalY, {
|
|
4342
|
+
...shapeConfig,
|
|
4343
|
+
mirrorAxis: mirrorAxis,
|
|
4344
|
+
mirrorGap: size * (0.1 + rng() * 0.3)
|
|
2313
4345
|
});
|
|
4346
|
+
else (0, $c3de8257a8baa3b0$export$bb35a6995ddbf32d)(ctx, shape, finalX, finalY, shapeConfig);
|
|
2314
4347
|
shapePositions.push({
|
|
2315
|
-
x:
|
|
2316
|
-
y:
|
|
2317
|
-
size: size
|
|
4348
|
+
x: finalX,
|
|
4349
|
+
y: finalY,
|
|
4350
|
+
size: size,
|
|
4351
|
+
shape: shape
|
|
2318
4352
|
});
|
|
2319
|
-
// ──
|
|
4353
|
+
// ── 5c. Size echo — large shapes spawn trailing smaller copies ──
|
|
4354
|
+
if (size > adjustedMaxSize * 0.5 && rng() < 0.2) {
|
|
4355
|
+
const echoCount = 2 + Math.floor(rng() * 2);
|
|
4356
|
+
const echoAngle = rng() * Math.PI * 2;
|
|
4357
|
+
for(let e = 0; e < echoCount; e++){
|
|
4358
|
+
const echoScale = 0.3 - e * 0.08;
|
|
4359
|
+
const echoDist = size * (0.6 + e * 0.4);
|
|
4360
|
+
const echoX = finalX + Math.cos(echoAngle) * echoDist;
|
|
4361
|
+
const echoY = finalY + Math.sin(echoAngle) * echoDist;
|
|
4362
|
+
const echoSize = size * Math.max(0.1, echoScale);
|
|
4363
|
+
if (echoX < 0 || echoX > width || echoY < 0 || echoY > height) continue;
|
|
4364
|
+
ctx.globalAlpha = layerOpacity * (0.4 - e * 0.1);
|
|
4365
|
+
(0, $c3de8257a8baa3b0$export$bb35a6995ddbf32d)(ctx, shape, echoX, echoY, {
|
|
4366
|
+
fillColor: (0, $d016ad53434219a1$export$f2121afcad3d553f)(fillColor, fillAlpha * 0.6),
|
|
4367
|
+
strokeColor: (0, $d016ad53434219a1$export$f2121afcad3d553f)(strokeColor, 0.4),
|
|
4368
|
+
strokeWidth: strokeWidth * 0.6,
|
|
4369
|
+
size: echoSize,
|
|
4370
|
+
rotation: rotation + (e + 1) * 15,
|
|
4371
|
+
proportionType: "GOLDEN_RATIO",
|
|
4372
|
+
renderStyle: finalRenderStyle,
|
|
4373
|
+
rng: rng
|
|
4374
|
+
});
|
|
4375
|
+
shapePositions.push({
|
|
4376
|
+
x: echoX,
|
|
4377
|
+
y: echoY,
|
|
4378
|
+
size: echoSize,
|
|
4379
|
+
shape: shape
|
|
4380
|
+
});
|
|
4381
|
+
}
|
|
4382
|
+
}
|
|
4383
|
+
// ── 5d. Recursive nesting ──────────────────────────────────
|
|
2320
4384
|
if (size > adjustedMaxSize * 0.4 && rng() < 0.15) {
|
|
2321
4385
|
const innerCount = 1 + Math.floor(rng() * 3);
|
|
2322
4386
|
for(let n = 0; n < innerCount; n++){
|
|
2323
|
-
|
|
4387
|
+
// Pick inner shape from palette affinities
|
|
4388
|
+
const innerSizeFraction = size * 0.25 / adjustedMaxSize;
|
|
4389
|
+
const innerShape = (0, $e73976f898150d4d$export$3c37d9a045754d0e)(shapePalette, rng, innerSizeFraction);
|
|
2324
4390
|
const innerSize = size * (0.15 + rng() * 0.25);
|
|
2325
4391
|
const innerOffX = (rng() - 0.5) * size * 0.4;
|
|
2326
4392
|
const innerOffY = (rng() - 0.5) * size * 0.4;
|
|
2327
4393
|
const innerRot = rng() * 360;
|
|
2328
|
-
const innerFill = (0, $d016ad53434219a1$export$f2121afcad3d553f)((0, $d016ad53434219a1$export$
|
|
4394
|
+
const innerFill = (0, $d016ad53434219a1$export$f2121afcad3d553f)((0, $d016ad53434219a1$export$18a34c25ea7e724b)((0, $d016ad53434219a1$export$b49f62f0a99da0e8)(colorHierarchy, rng), rng, 10, 0.1), 0.3 + rng() * 0.4);
|
|
2329
4395
|
ctx.globalAlpha = layerOpacity * 0.7;
|
|
2330
|
-
(0, $c3de8257a8baa3b0$export$bb35a6995ddbf32d)(ctx, innerShape,
|
|
4396
|
+
(0, $c3de8257a8baa3b0$export$bb35a6995ddbf32d)(ctx, innerShape, finalX + innerOffX, finalY + innerOffY, {
|
|
2331
4397
|
fillColor: innerFill,
|
|
2332
4398
|
strokeColor: (0, $d016ad53434219a1$export$f2121afcad3d553f)(strokeColor, 0.5),
|
|
2333
4399
|
strokeWidth: strokeWidth * 0.6,
|
|
2334
4400
|
size: innerSize,
|
|
2335
4401
|
rotation: innerRot,
|
|
2336
4402
|
proportionType: "GOLDEN_RATIO",
|
|
2337
|
-
renderStyle:
|
|
4403
|
+
renderStyle: (0, $e73976f898150d4d$export$ab873bb6fb56c1a8)(innerShape, layerRenderStyle, rng),
|
|
4404
|
+
rng: rng
|
|
4405
|
+
});
|
|
4406
|
+
}
|
|
4407
|
+
}
|
|
4408
|
+
// ── 5e. Shape constellations — pre-composed groups ─────────
|
|
4409
|
+
if (size > adjustedMaxSize * 0.35 && rng() < 0.12) {
|
|
4410
|
+
const constellation = $4f72c5a314eddf25$var$CONSTELLATIONS[Math.floor(rng() * $4f72c5a314eddf25$var$CONSTELLATIONS.length)];
|
|
4411
|
+
const members = constellation.build(rng, size);
|
|
4412
|
+
const groupRotation = rng() * Math.PI * 2;
|
|
4413
|
+
const cosR = Math.cos(groupRotation);
|
|
4414
|
+
const sinR = Math.sin(groupRotation);
|
|
4415
|
+
for (const member of members){
|
|
4416
|
+
// Rotate the group offset by the group rotation
|
|
4417
|
+
const mx = finalX + member.dx * cosR - member.dy * sinR;
|
|
4418
|
+
const my = finalY + member.dx * sinR + member.dy * cosR;
|
|
4419
|
+
if (mx < 0 || mx > width || my < 0 || my > height) continue;
|
|
4420
|
+
const memberFill = (0, $d016ad53434219a1$export$f2121afcad3d553f)((0, $d016ad53434219a1$export$18a34c25ea7e724b)((0, $d016ad53434219a1$export$b49f62f0a99da0e8)(colorHierarchy, rng), rng, 8, 0.06), fillAlpha * 0.8);
|
|
4421
|
+
const memberStroke = (0, $d016ad53434219a1$export$90ad0e6170cf6af5)((0, $d016ad53434219a1$export$18a34c25ea7e724b)(strokeBase, rng, 5, 0.04), bgLum);
|
|
4422
|
+
ctx.globalAlpha = layerOpacity * 0.6;
|
|
4423
|
+
// Use the member's shape if available, otherwise fall back to palette
|
|
4424
|
+
const memberShape = shapeNames.includes(member.shape) ? member.shape : (0, $e73976f898150d4d$export$3c37d9a045754d0e)(shapePalette, rng, member.size / adjustedMaxSize);
|
|
4425
|
+
(0, $c3de8257a8baa3b0$export$bb35a6995ddbf32d)(ctx, memberShape, mx, my, {
|
|
4426
|
+
fillColor: memberFill,
|
|
4427
|
+
strokeColor: memberStroke,
|
|
4428
|
+
strokeWidth: strokeWidth * 0.7,
|
|
4429
|
+
size: member.size,
|
|
4430
|
+
rotation: member.rotation + groupRotation * 180 / Math.PI,
|
|
4431
|
+
proportionType: "GOLDEN_RATIO",
|
|
4432
|
+
renderStyle: (0, $e73976f898150d4d$export$ab873bb6fb56c1a8)(memberShape, layerRenderStyle, rng),
|
|
2338
4433
|
rng: rng
|
|
2339
4434
|
});
|
|
4435
|
+
shapePositions.push({
|
|
4436
|
+
x: mx,
|
|
4437
|
+
y: my,
|
|
4438
|
+
size: member.size,
|
|
4439
|
+
shape: memberShape
|
|
4440
|
+
});
|
|
2340
4441
|
}
|
|
2341
4442
|
}
|
|
2342
4443
|
}
|
|
2343
4444
|
}
|
|
2344
4445
|
// Reset blend mode for post-processing passes
|
|
2345
4446
|
ctx.globalCompositeOperation = "source-over";
|
|
2346
|
-
// ── 6. Flow-line pass
|
|
4447
|
+
// ── 6. Flow-line pass — variable color, branching, pressure ────
|
|
2347
4448
|
const baseFlowLines = 6 + Math.floor(rng() * 10);
|
|
2348
4449
|
const numFlowLines = Math.round(baseFlowLines * archetype.flowLineMultiplier);
|
|
2349
4450
|
for(let i = 0; i < numFlowLines; i++){
|
|
@@ -2352,9 +4453,13 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2352
4453
|
const steps = 30 + Math.floor(rng() * 40);
|
|
2353
4454
|
const stepLen = (3 + rng() * 5) * scaleFactor;
|
|
2354
4455
|
const startWidth = (1 + rng() * 3) * scaleFactor;
|
|
2355
|
-
|
|
4456
|
+
// Variable color: interpolate between two hierarchy colors along the stroke
|
|
4457
|
+
const lineColorStart = (0, $d016ad53434219a1$export$90ad0e6170cf6af5)((0, $d016ad53434219a1$export$b49f62f0a99da0e8)(colorHierarchy, rng), bgLum);
|
|
4458
|
+
const lineColorEnd = (0, $d016ad53434219a1$export$90ad0e6170cf6af5)((0, $d016ad53434219a1$export$b49f62f0a99da0e8)(colorHierarchy, rng), bgLum);
|
|
2356
4459
|
const lineAlpha = 0.06 + rng() * 0.1;
|
|
2357
|
-
//
|
|
4460
|
+
// Pressure simulation: sinusoidal width variation
|
|
4461
|
+
const pressureFreq = 2 + rng() * 4;
|
|
4462
|
+
const pressurePhase = rng() * Math.PI * 2;
|
|
2358
4463
|
let prevX = fx;
|
|
2359
4464
|
let prevY = fy;
|
|
2360
4465
|
for(let s = 0; s < steps; s++){
|
|
@@ -2362,37 +4467,61 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2362
4467
|
fx += Math.cos(angle) * stepLen;
|
|
2363
4468
|
fy += Math.sin(angle) * stepLen;
|
|
2364
4469
|
if (fx < 0 || fx > width || fy < 0 || fy > height) break;
|
|
2365
|
-
|
|
2366
|
-
|
|
4470
|
+
const t = s / steps;
|
|
4471
|
+
// Taper + pressure
|
|
4472
|
+
const taper = 1 - t * 0.8;
|
|
4473
|
+
const pressure = 0.6 + 0.4 * Math.sin(t * pressureFreq * Math.PI + pressurePhase);
|
|
2367
4474
|
ctx.globalAlpha = lineAlpha * taper;
|
|
4475
|
+
// Interpolate color along stroke
|
|
4476
|
+
const lineColor = t < 0.5 ? (0, $d016ad53434219a1$export$f2121afcad3d553f)(lineColorStart, 0.4 + t * 0.2) : (0, $d016ad53434219a1$export$f2121afcad3d553f)(lineColorEnd, 0.4 + (1 - t) * 0.2);
|
|
2368
4477
|
ctx.strokeStyle = lineColor;
|
|
2369
|
-
ctx.lineWidth = startWidth * taper;
|
|
4478
|
+
ctx.lineWidth = startWidth * taper * pressure;
|
|
2370
4479
|
ctx.lineCap = "round";
|
|
2371
4480
|
ctx.beginPath();
|
|
2372
4481
|
ctx.moveTo(prevX, prevY);
|
|
2373
4482
|
ctx.lineTo(fx, fy);
|
|
2374
4483
|
ctx.stroke();
|
|
4484
|
+
// Branching: ~12% chance per step to spawn a thinner child stroke
|
|
4485
|
+
if (rng() < 0.12 && s > 5 && s < steps - 10) {
|
|
4486
|
+
const branchAngle = angle + (rng() < 0.5 ? 1 : -1) * (0.3 + rng() * 0.5);
|
|
4487
|
+
let bx = fx;
|
|
4488
|
+
let by = fy;
|
|
4489
|
+
let bPrevX = fx;
|
|
4490
|
+
let bPrevY = fy;
|
|
4491
|
+
const branchSteps = 5 + Math.floor(rng() * 10);
|
|
4492
|
+
const branchWidth = startWidth * taper * 0.4;
|
|
4493
|
+
for(let bs = 0; bs < branchSteps; bs++){
|
|
4494
|
+
const bAngle = branchAngle + (rng() - 0.5) * 0.2;
|
|
4495
|
+
bx += Math.cos(bAngle) * stepLen * 0.8;
|
|
4496
|
+
by += Math.sin(bAngle) * stepLen * 0.8;
|
|
4497
|
+
if (bx < 0 || bx > width || by < 0 || by > height) break;
|
|
4498
|
+
const bTaper = 1 - bs / branchSteps * 0.9;
|
|
4499
|
+
ctx.globalAlpha = lineAlpha * taper * bTaper * 0.6;
|
|
4500
|
+
ctx.lineWidth = branchWidth * bTaper;
|
|
4501
|
+
ctx.beginPath();
|
|
4502
|
+
ctx.moveTo(bPrevX, bPrevY);
|
|
4503
|
+
ctx.lineTo(bx, by);
|
|
4504
|
+
ctx.stroke();
|
|
4505
|
+
bPrevX = bx;
|
|
4506
|
+
bPrevY = by;
|
|
4507
|
+
}
|
|
4508
|
+
}
|
|
2375
4509
|
prevX = fx;
|
|
2376
4510
|
prevY = fy;
|
|
2377
4511
|
}
|
|
2378
4512
|
}
|
|
2379
4513
|
// ── 6b. Apply symmetry mirroring ─────────────────────────────────
|
|
2380
|
-
// Mirror the rendered content (shapes + flow lines) before post-processing.
|
|
2381
|
-
// Uses ctx.canvas which is available in both Node (@napi-rs/canvas) and browsers.
|
|
2382
4514
|
if (symmetryMode !== "none") {
|
|
2383
4515
|
const canvas = ctx.canvas;
|
|
2384
4516
|
ctx.save();
|
|
2385
4517
|
if (symmetryMode === "bilateral-x" || symmetryMode === "quad") {
|
|
2386
|
-
// Mirror left half onto right half
|
|
2387
4518
|
ctx.save();
|
|
2388
4519
|
ctx.translate(width, 0);
|
|
2389
4520
|
ctx.scale(-1, 1);
|
|
2390
|
-
// Draw the left half (0 to cx) onto the mirrored right side
|
|
2391
4521
|
ctx.drawImage(canvas, 0, 0, Math.ceil(cx), height, 0, 0, Math.ceil(cx), height);
|
|
2392
4522
|
ctx.restore();
|
|
2393
4523
|
}
|
|
2394
4524
|
if (symmetryMode === "bilateral-y" || symmetryMode === "quad") {
|
|
2395
|
-
// Mirror top half onto bottom half
|
|
2396
4525
|
ctx.save();
|
|
2397
4526
|
ctx.translate(0, height);
|
|
2398
4527
|
ctx.scale(1, -1);
|
|
@@ -2415,7 +4544,7 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2415
4544
|
}
|
|
2416
4545
|
// ── 8. Vignette — darken edges to draw the eye inward ───────────
|
|
2417
4546
|
ctx.globalAlpha = 1;
|
|
2418
|
-
const vignetteStrength = 0.25 + rng() * 0.2;
|
|
4547
|
+
const vignetteStrength = 0.25 + rng() * 0.2;
|
|
2419
4548
|
const vigGrad = ctx.createRadialGradient(cx, cy, Math.min(width, height) * 0.3, cx, cy, bgRadius);
|
|
2420
4549
|
vigGrad.addColorStop(0, "rgba(0,0,0,0)");
|
|
2421
4550
|
vigGrad.addColorStop(0.6, "rgba(0,0,0,0)");
|
|
@@ -2441,13 +4570,57 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2441
4570
|
const cpx = mx + -dy / (dist || 1) * bulge;
|
|
2442
4571
|
const cpy = my + dx / (dist || 1) * bulge;
|
|
2443
4572
|
ctx.globalAlpha = 0.06 + rng() * 0.1;
|
|
2444
|
-
ctx.strokeStyle = (0, $d016ad53434219a1$export$f2121afcad3d553f)((0, $d016ad53434219a1$export$90ad0e6170cf6af5)(
|
|
4573
|
+
ctx.strokeStyle = (0, $d016ad53434219a1$export$f2121afcad3d553f)((0, $d016ad53434219a1$export$90ad0e6170cf6af5)((0, $d016ad53434219a1$export$b49f62f0a99da0e8)(colorHierarchy, rng), bgLum), 0.3);
|
|
2445
4574
|
ctx.beginPath();
|
|
2446
4575
|
ctx.moveTo(a.x, a.y);
|
|
2447
4576
|
ctx.quadraticCurveTo(cpx, cpy, b.x, b.y);
|
|
2448
4577
|
ctx.stroke();
|
|
2449
4578
|
}
|
|
2450
4579
|
}
|
|
4580
|
+
// ── 10. Post-processing ────────────────────────────────────────
|
|
4581
|
+
// 10a. Color grading — unified tone across the whole image
|
|
4582
|
+
// Apply as a semi-transparent overlay in the grade hue
|
|
4583
|
+
ctx.globalAlpha = colorGrade.intensity * 0.25;
|
|
4584
|
+
ctx.globalCompositeOperation = "soft-light";
|
|
4585
|
+
const gradeHsl = `hsl(${Math.round(colorGrade.hue)}, 40%, 50%)`;
|
|
4586
|
+
ctx.fillStyle = gradeHsl;
|
|
4587
|
+
ctx.fillRect(0, 0, width, height);
|
|
4588
|
+
ctx.globalCompositeOperation = "source-over";
|
|
4589
|
+
// 10b. Chromatic aberration — subtle RGB channel offset at edges
|
|
4590
|
+
// Only apply for neon/cosmic/ethereal archetypes where it fits
|
|
4591
|
+
const chromaArchetypes = [
|
|
4592
|
+
"neon-glow",
|
|
4593
|
+
"cosmic",
|
|
4594
|
+
"ethereal"
|
|
4595
|
+
];
|
|
4596
|
+
if (chromaArchetypes.includes(archetype.name)) {
|
|
4597
|
+
const chromaOffset = Math.ceil(2 * scaleFactor);
|
|
4598
|
+
const canvas = ctx.canvas;
|
|
4599
|
+
// Shift red channel slightly
|
|
4600
|
+
ctx.globalAlpha = 0.03;
|
|
4601
|
+
ctx.globalCompositeOperation = "screen";
|
|
4602
|
+
ctx.drawImage(canvas, chromaOffset, 0, width, height, 0, 0, width, height);
|
|
4603
|
+
// Shift blue channel opposite
|
|
4604
|
+
ctx.drawImage(canvas, -chromaOffset, 0, width, height, 0, 0, width, height);
|
|
4605
|
+
ctx.globalCompositeOperation = "source-over";
|
|
4606
|
+
}
|
|
4607
|
+
// 10c. Bloom — soft glow on bright areas for neon/cosmic archetypes
|
|
4608
|
+
const bloomArchetypes = [
|
|
4609
|
+
"neon-glow",
|
|
4610
|
+
"cosmic"
|
|
4611
|
+
];
|
|
4612
|
+
if (bloomArchetypes.includes(archetype.name)) {
|
|
4613
|
+
const canvas = ctx.canvas;
|
|
4614
|
+
ctx.globalAlpha = 0.08;
|
|
4615
|
+
ctx.globalCompositeOperation = "screen";
|
|
4616
|
+
// Draw the image slightly scaled up and blurred via shadow
|
|
4617
|
+
ctx.save();
|
|
4618
|
+
ctx.shadowBlur = 30 * scaleFactor;
|
|
4619
|
+
ctx.shadowColor = "rgba(255,255,255,0.3)";
|
|
4620
|
+
ctx.drawImage(canvas, 0, 0, width, height);
|
|
4621
|
+
ctx.restore();
|
|
4622
|
+
ctx.globalCompositeOperation = "source-over";
|
|
4623
|
+
}
|
|
2451
4624
|
ctx.globalAlpha = 1;
|
|
2452
4625
|
}
|
|
2453
4626
|
|