git-hash-art 0.8.0 → 0.10.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 +425 -274
- package/CHANGELOG.md +18 -0
- package/bin/cli.js +17 -14
- package/bin/generateVersionComparison.js +353 -0
- package/dist/browser.js +1563 -123
- package/dist/browser.js.map +1 -1
- package/dist/main.js +1563 -123
- package/dist/main.js.map +1 -1
- package/dist/module.js +1563 -123
- package/dist/module.js.map +1 -1
- package/package.json +2 -1
- package/src/lib/archetypes.ts +115 -3
- package/src/lib/canvas/colors.ts +25 -0
- package/src/lib/canvas/draw.ts +348 -1
- package/src/lib/canvas/shapes/affinity.ts +149 -4
- package/src/lib/canvas/shapes/procedural.ts +395 -32
- package/src/lib/render.ts +426 -19
package/dist/module.js
CHANGED
|
@@ -527,6 +527,18 @@ function $9d614e7d77fc2947$export$6d1620b367f86f7a(rng) {
|
|
|
527
527
|
intensity: intensity
|
|
528
528
|
};
|
|
529
529
|
}
|
|
530
|
+
function $9d614e7d77fc2947$export$1793a1bfbe4f6ff5(hex, degrees) {
|
|
531
|
+
const [h, s, l] = $9d614e7d77fc2947$var$hexToHsl(hex);
|
|
532
|
+
return $9d614e7d77fc2947$var$hslToHex((h + degrees + 360) % 360, s, l);
|
|
533
|
+
}
|
|
534
|
+
function $9d614e7d77fc2947$export$703ba40a4347f77a(base, layerRatio, hueShiftPerLayer) {
|
|
535
|
+
const shift = layerRatio * hueShiftPerLayer;
|
|
536
|
+
return {
|
|
537
|
+
dominant: $9d614e7d77fc2947$export$1793a1bfbe4f6ff5(base.dominant, shift),
|
|
538
|
+
secondary: $9d614e7d77fc2947$export$1793a1bfbe4f6ff5(base.secondary, shift * 0.7),
|
|
539
|
+
accent: $9d614e7d77fc2947$export$1793a1bfbe4f6ff5(base.accent, shift * 0.5)
|
|
540
|
+
};
|
|
541
|
+
}
|
|
530
542
|
|
|
531
543
|
|
|
532
544
|
|
|
@@ -1317,35 +1329,32 @@ const $d63629e16208c310$export$c2fc138f94dd4b2a = {
|
|
|
1317
1329
|
*/ const $a7241acce45d5513$export$580f80cfb9de73bc = (ctx, size, config)=>{
|
|
1318
1330
|
const rng = config?.rng ?? Math.random;
|
|
1319
1331
|
const r = size / 2;
|
|
1320
|
-
const numPoints = 5 + Math.floor(rng() * 5);
|
|
1332
|
+
const numPoints = 5 + Math.floor(rng() * 5);
|
|
1321
1333
|
const points = [];
|
|
1322
1334
|
for(let i = 0; i < numPoints; i++){
|
|
1323
1335
|
const angle = i / numPoints * Math.PI * 2;
|
|
1324
|
-
const jitter = 0.5 + rng() * 0.5;
|
|
1336
|
+
const jitter = 0.5 + rng() * 0.5;
|
|
1325
1337
|
points.push({
|
|
1326
1338
|
x: Math.cos(angle) * r * jitter,
|
|
1327
1339
|
y: Math.sin(angle) * r * jitter
|
|
1328
1340
|
});
|
|
1329
1341
|
}
|
|
1330
1342
|
ctx.beginPath();
|
|
1331
|
-
// Start at midpoint between last and first point
|
|
1332
1343
|
const last = points[points.length - 1];
|
|
1333
1344
|
const first = points[0];
|
|
1334
1345
|
ctx.moveTo((last.x + first.x) / 2, (last.y + first.y) / 2);
|
|
1335
1346
|
for(let i = 0; i < numPoints; i++){
|
|
1336
1347
|
const curr = points[i];
|
|
1337
1348
|
const next = points[(i + 1) % numPoints];
|
|
1338
|
-
|
|
1339
|
-
const midY = (curr.y + next.y) / 2;
|
|
1340
|
-
ctx.quadraticCurveTo(curr.x, curr.y, midX, midY);
|
|
1349
|
+
ctx.quadraticCurveTo(curr.x, curr.y, (curr.x + next.x) / 2, (curr.y + next.y) / 2);
|
|
1341
1350
|
}
|
|
1342
1351
|
ctx.closePath();
|
|
1343
1352
|
};
|
|
1344
1353
|
const $a7241acce45d5513$export$7a6094023f0902a6 = (ctx, size, config)=>{
|
|
1345
1354
|
const rng = config?.rng ?? Math.random;
|
|
1346
1355
|
const r = size / 2;
|
|
1347
|
-
const sides = 3 + Math.floor(rng() * 10);
|
|
1348
|
-
const jitterAmount = 0.1 + rng() * 0.4;
|
|
1356
|
+
const sides = 3 + Math.floor(rng() * 10);
|
|
1357
|
+
const jitterAmount = 0.1 + rng() * 0.4;
|
|
1349
1358
|
ctx.beginPath();
|
|
1350
1359
|
for(let i = 0; i < sides; i++){
|
|
1351
1360
|
const angle = i / sides * Math.PI * 2 - Math.PI / 2;
|
|
@@ -1360,10 +1369,9 @@ const $a7241acce45d5513$export$7a6094023f0902a6 = (ctx, size, config)=>{
|
|
|
1360
1369
|
const $a7241acce45d5513$export$ef56b4a8316e47d5 = (ctx, size, config)=>{
|
|
1361
1370
|
const rng = config?.rng ?? Math.random;
|
|
1362
1371
|
const r = size / 2;
|
|
1363
|
-
|
|
1364
|
-
const
|
|
1365
|
-
const
|
|
1366
|
-
const phase = rng() * Math.PI; // phase offset
|
|
1372
|
+
const freqA = 1 + Math.floor(rng() * 5);
|
|
1373
|
+
const freqB = 1 + Math.floor(rng() * 5);
|
|
1374
|
+
const phase = rng() * Math.PI;
|
|
1367
1375
|
const steps = 120;
|
|
1368
1376
|
ctx.beginPath();
|
|
1369
1377
|
for(let i = 0; i <= steps; i++){
|
|
@@ -1378,7 +1386,6 @@ const $a7241acce45d5513$export$ef56b4a8316e47d5 = (ctx, size, config)=>{
|
|
|
1378
1386
|
const $a7241acce45d5513$export$1db9219b4f34658c = (ctx, size, config)=>{
|
|
1379
1387
|
const rng = config?.rng ?? Math.random;
|
|
1380
1388
|
const r = size / 2;
|
|
1381
|
-
// Exponent range: 0.3 (spiky astroid) to 5 (rounded rectangle)
|
|
1382
1389
|
const n = 0.3 + rng() * 4.7;
|
|
1383
1390
|
const steps = 120;
|
|
1384
1391
|
ctx.beginPath();
|
|
@@ -1386,7 +1393,6 @@ const $a7241acce45d5513$export$1db9219b4f34658c = (ctx, size, config)=>{
|
|
|
1386
1393
|
const t = i / steps * Math.PI * 2;
|
|
1387
1394
|
const cosT = Math.cos(t);
|
|
1388
1395
|
const sinT = Math.sin(t);
|
|
1389
|
-
// Superellipse parametric form
|
|
1390
1396
|
const x = Math.sign(cosT) * Math.pow(Math.abs(cosT), 2 / n) * r;
|
|
1391
1397
|
const y = Math.sign(sinT) * Math.pow(Math.abs(sinT), 2 / n) * r;
|
|
1392
1398
|
if (i === 0) ctx.moveTo(x, y);
|
|
@@ -1397,11 +1403,9 @@ const $a7241acce45d5513$export$1db9219b4f34658c = (ctx, size, config)=>{
|
|
|
1397
1403
|
const $a7241acce45d5513$export$b027c64d22b01985 = (ctx, size, config)=>{
|
|
1398
1404
|
const rng = config?.rng ?? Math.random;
|
|
1399
1405
|
const scale = size / 2;
|
|
1400
|
-
// R = outer radius, r = inner radius, d = pen distance from inner center
|
|
1401
1406
|
const R = 1;
|
|
1402
|
-
const r = 0.2 + rng() * 0.6;
|
|
1403
|
-
const d = 0.3 + rng() * 0.7;
|
|
1404
|
-
// Number of full rotations needed to close the curve
|
|
1407
|
+
const r = 0.2 + rng() * 0.6;
|
|
1408
|
+
const d = 0.3 + rng() * 0.7;
|
|
1405
1409
|
const gcd = (a, b)=>{
|
|
1406
1410
|
const ai = Math.round(a * 1000);
|
|
1407
1411
|
const bi = Math.round(b * 1000);
|
|
@@ -1409,7 +1413,7 @@ const $a7241acce45d5513$export$b027c64d22b01985 = (ctx, size, config)=>{
|
|
|
1409
1413
|
return g(ai, bi) / 1000;
|
|
1410
1414
|
};
|
|
1411
1415
|
const period = r / gcd(R, r);
|
|
1412
|
-
const maxT = Math.min(period, 10) * Math.PI * 2;
|
|
1416
|
+
const maxT = Math.min(period, 10) * Math.PI * 2;
|
|
1413
1417
|
const steps = Math.min(600, Math.floor(maxT * 20));
|
|
1414
1418
|
ctx.beginPath();
|
|
1415
1419
|
for(let i = 0; i <= steps; i++){
|
|
@@ -1424,9 +1428,9 @@ const $a7241acce45d5513$export$b027c64d22b01985 = (ctx, size, config)=>{
|
|
|
1424
1428
|
const $a7241acce45d5513$export$7608ccd03bfb705d = (ctx, size, config)=>{
|
|
1425
1429
|
const rng = config?.rng ?? Math.random;
|
|
1426
1430
|
const r = size / 2;
|
|
1427
|
-
const rings = 2 + Math.floor(rng() * 4);
|
|
1428
|
-
const freq = 3 + Math.floor(rng() * 12);
|
|
1429
|
-
const amp = 0.05 + rng() * 0.15;
|
|
1431
|
+
const rings = 2 + Math.floor(rng() * 4);
|
|
1432
|
+
const freq = 3 + Math.floor(rng() * 12);
|
|
1433
|
+
const amp = 0.05 + rng() * 0.15;
|
|
1430
1434
|
ctx.beginPath();
|
|
1431
1435
|
for(let ring = 0; ring < rings; ring++){
|
|
1432
1436
|
const baseR = r * (0.3 + ring / rings * 0.7);
|
|
@@ -1444,7 +1448,7 @@ const $a7241acce45d5513$export$7608ccd03bfb705d = (ctx, size, config)=>{
|
|
|
1444
1448
|
const $a7241acce45d5513$export$11a377e7498bb523 = (ctx, size, config)=>{
|
|
1445
1449
|
const rng = config?.rng ?? Math.random;
|
|
1446
1450
|
const r = size / 2;
|
|
1447
|
-
const k = 2 + Math.floor(rng() * 6);
|
|
1451
|
+
const k = 2 + Math.floor(rng() * 6);
|
|
1448
1452
|
const steps = 200;
|
|
1449
1453
|
ctx.beginPath();
|
|
1450
1454
|
for(let i = 0; i <= steps; i++){
|
|
@@ -1457,6 +1461,308 @@ const $a7241acce45d5513$export$11a377e7498bb523 = (ctx, size, config)=>{
|
|
|
1457
1461
|
}
|
|
1458
1462
|
ctx.closePath();
|
|
1459
1463
|
};
|
|
1464
|
+
const $a7241acce45d5513$export$76b6526575ea179b = (ctx, size, config)=>{
|
|
1465
|
+
const rng = config?.rng ?? Math.random;
|
|
1466
|
+
const r = size / 2;
|
|
1467
|
+
const shardCount = 4 + Math.floor(rng() * 5); // 4-8 shards
|
|
1468
|
+
ctx.beginPath();
|
|
1469
|
+
for(let s = 0; s < shardCount; s++){
|
|
1470
|
+
const baseAngle = s / shardCount * Math.PI * 2 + (rng() - 0.5) * 0.3;
|
|
1471
|
+
const dist = r * (0.15 + rng() * 0.35);
|
|
1472
|
+
const cx = Math.cos(baseAngle) * dist;
|
|
1473
|
+
const cy = Math.sin(baseAngle) * dist;
|
|
1474
|
+
const shardSize = r * (0.2 + rng() * 0.4);
|
|
1475
|
+
const verts = 3 + Math.floor(rng() * 3); // 3-5 vertices per shard
|
|
1476
|
+
const shardAngleOffset = rng() * Math.PI * 2;
|
|
1477
|
+
for(let v = 0; v < verts; v++){
|
|
1478
|
+
const angle = shardAngleOffset + v / verts * Math.PI * 2;
|
|
1479
|
+
// Elongate shards along their radial direction
|
|
1480
|
+
const stretch = v % 2 === 0 ? 1.0 : 0.3 + rng() * 0.4;
|
|
1481
|
+
const px = cx + Math.cos(angle) * shardSize * stretch;
|
|
1482
|
+
const py = cy + Math.sin(angle) * shardSize * stretch;
|
|
1483
|
+
if (v === 0) ctx.moveTo(px, py);
|
|
1484
|
+
else ctx.lineTo(px, py);
|
|
1485
|
+
}
|
|
1486
|
+
ctx.closePath();
|
|
1487
|
+
}
|
|
1488
|
+
};
|
|
1489
|
+
const $a7241acce45d5513$export$ed9ff98da5b05073 = (ctx, size, config)=>{
|
|
1490
|
+
const rng = config?.rng ?? Math.random;
|
|
1491
|
+
const r = size / 2;
|
|
1492
|
+
const edgeCount = 5 + Math.floor(rng() * 4); // 5-8 edges
|
|
1493
|
+
const points = [];
|
|
1494
|
+
// Generate edge midpoints at varying distances
|
|
1495
|
+
for(let i = 0; i < edgeCount; i++){
|
|
1496
|
+
const angle = i / edgeCount * Math.PI * 2 + (rng() - 0.5) * 0.4;
|
|
1497
|
+
const dist = r * (0.6 + rng() * 0.4);
|
|
1498
|
+
points.push({
|
|
1499
|
+
angle: angle,
|
|
1500
|
+
x: Math.cos(angle) * dist,
|
|
1501
|
+
y: Math.sin(angle) * dist
|
|
1502
|
+
});
|
|
1503
|
+
}
|
|
1504
|
+
// Sort by angle for proper winding
|
|
1505
|
+
points.sort((a, b)=>a.angle - b.angle);
|
|
1506
|
+
ctx.beginPath();
|
|
1507
|
+
ctx.moveTo(points[0].x, points[0].y);
|
|
1508
|
+
for(let i = 1; i < points.length; i++)ctx.lineTo(points[i].x, points[i].y);
|
|
1509
|
+
ctx.closePath();
|
|
1510
|
+
};
|
|
1511
|
+
const $a7241acce45d5513$export$e0452d9a794fe7e5 = (ctx, size, config)=>{
|
|
1512
|
+
const rng = config?.rng ?? Math.random;
|
|
1513
|
+
const r = size / 2;
|
|
1514
|
+
const biteSize = 0.6 + rng() * 0.3; // 60-90% of radius
|
|
1515
|
+
const biteOffset = r * (0.3 + rng() * 0.4);
|
|
1516
|
+
const biteAngle = rng() * Math.PI * 2;
|
|
1517
|
+
// Outer circle
|
|
1518
|
+
ctx.beginPath();
|
|
1519
|
+
ctx.arc(0, 0, r, 0, Math.PI * 2);
|
|
1520
|
+
// Subtract inner circle using even-odd rule
|
|
1521
|
+
const bx = Math.cos(biteAngle) * biteOffset;
|
|
1522
|
+
const by = Math.sin(biteAngle) * biteOffset;
|
|
1523
|
+
// Draw inner circle counter-clockwise for subtraction
|
|
1524
|
+
ctx.moveTo(bx + r * biteSize, by);
|
|
1525
|
+
ctx.arc(bx, by, r * biteSize, 0, Math.PI * 2, true);
|
|
1526
|
+
};
|
|
1527
|
+
const $a7241acce45d5513$export$38bfe5eb52137e01 = (ctx, size, config)=>{
|
|
1528
|
+
const rng = config?.rng ?? Math.random;
|
|
1529
|
+
const r = size / 2;
|
|
1530
|
+
const segments = 12 + Math.floor(rng() * 8);
|
|
1531
|
+
const startAngle = rng() * Math.PI * 2;
|
|
1532
|
+
const curvature = (rng() - 0.5) * 0.4;
|
|
1533
|
+
// Build spine points
|
|
1534
|
+
const spine = [];
|
|
1535
|
+
let angle = startAngle;
|
|
1536
|
+
let px = 0, py = 0;
|
|
1537
|
+
for(let i = 0; i <= segments; i++){
|
|
1538
|
+
spine.push({
|
|
1539
|
+
x: px,
|
|
1540
|
+
y: py
|
|
1541
|
+
});
|
|
1542
|
+
const stepLen = r / segments * (1.5 + rng() * 0.5);
|
|
1543
|
+
angle += curvature + (rng() - 0.5) * 0.6;
|
|
1544
|
+
px += Math.cos(angle) * stepLen;
|
|
1545
|
+
py += Math.sin(angle) * stepLen;
|
|
1546
|
+
}
|
|
1547
|
+
// Build tapered outline by offsetting perpendicular to spine
|
|
1548
|
+
ctx.beginPath();
|
|
1549
|
+
const leftSide = [];
|
|
1550
|
+
const rightSide = [];
|
|
1551
|
+
for(let i = 0; i < spine.length; i++){
|
|
1552
|
+
const t = i / (spine.length - 1);
|
|
1553
|
+
const width = r * 0.12 * (1 - t * 0.9); // taper from thick to thin
|
|
1554
|
+
const next = spine[Math.min(i + 1, spine.length - 1)];
|
|
1555
|
+
const dx = next.x - spine[i].x;
|
|
1556
|
+
const dy = next.y - spine[i].y;
|
|
1557
|
+
const len = Math.hypot(dx, dy) || 1;
|
|
1558
|
+
const nx = -dy / len;
|
|
1559
|
+
const ny = dx / len;
|
|
1560
|
+
leftSide.push({
|
|
1561
|
+
x: spine[i].x + nx * width,
|
|
1562
|
+
y: spine[i].y + ny * width
|
|
1563
|
+
});
|
|
1564
|
+
rightSide.push({
|
|
1565
|
+
x: spine[i].x - nx * width,
|
|
1566
|
+
y: spine[i].y - ny * width
|
|
1567
|
+
});
|
|
1568
|
+
}
|
|
1569
|
+
ctx.moveTo(leftSide[0].x, leftSide[0].y);
|
|
1570
|
+
for(let i = 1; i < leftSide.length; i++)ctx.lineTo(leftSide[i].x, leftSide[i].y);
|
|
1571
|
+
for(let i = rightSide.length - 1; i >= 0; i--)ctx.lineTo(rightSide[i].x, rightSide[i].y);
|
|
1572
|
+
ctx.closePath();
|
|
1573
|
+
};
|
|
1574
|
+
const $a7241acce45d5513$export$105caa8cfd63c422 = (ctx, size, config)=>{
|
|
1575
|
+
const rng = config?.rng ?? Math.random;
|
|
1576
|
+
const r = size / 2;
|
|
1577
|
+
const lobeCount = 4 + Math.floor(rng() * 4); // 4-7 lobes
|
|
1578
|
+
const spineAngle = rng() * Math.PI * 2;
|
|
1579
|
+
const spineLen = r * 0.6;
|
|
1580
|
+
ctx.beginPath();
|
|
1581
|
+
for(let i = 0; i < lobeCount; i++){
|
|
1582
|
+
const t = i / (lobeCount - 1) - 0.5; // -0.5 to 0.5
|
|
1583
|
+
const sx = Math.cos(spineAngle) * spineLen * t;
|
|
1584
|
+
const sy = Math.sin(spineAngle) * spineLen * t;
|
|
1585
|
+
// Offset perpendicular for cloud shape
|
|
1586
|
+
const perpAngle = spineAngle + Math.PI / 2;
|
|
1587
|
+
const perpOff = (rng() - 0.3) * r * 0.3;
|
|
1588
|
+
const cx = sx + Math.cos(perpAngle) * perpOff;
|
|
1589
|
+
const cy = sy + Math.sin(perpAngle) * perpOff;
|
|
1590
|
+
const lobeR = r * (0.25 + rng() * 0.2);
|
|
1591
|
+
ctx.moveTo(cx + lobeR, cy);
|
|
1592
|
+
ctx.arc(cx, cy, lobeR, 0, Math.PI * 2);
|
|
1593
|
+
}
|
|
1594
|
+
};
|
|
1595
|
+
const $a7241acce45d5513$export$e181e5bd3c539569 = (ctx, size, config)=>{
|
|
1596
|
+
const rng = config?.rng ?? Math.random;
|
|
1597
|
+
const r = size / 2;
|
|
1598
|
+
const spikeCount = 8 + Math.floor(rng() * 8); // 8-15 spikes
|
|
1599
|
+
const points = [];
|
|
1600
|
+
for(let i = 0; i < spikeCount; i++){
|
|
1601
|
+
const angle = i / spikeCount * Math.PI * 2;
|
|
1602
|
+
const isSpike = i % 2 === 0;
|
|
1603
|
+
const dist = isSpike ? r * (0.5 + rng() * 0.5 // spikes reach 50-100% of radius
|
|
1604
|
+
) : r * (0.15 + rng() * 0.2); // valleys at 15-35%
|
|
1605
|
+
points.push({
|
|
1606
|
+
x: Math.cos(angle) * dist,
|
|
1607
|
+
y: Math.sin(angle) * dist
|
|
1608
|
+
});
|
|
1609
|
+
}
|
|
1610
|
+
ctx.beginPath();
|
|
1611
|
+
ctx.moveTo(points[0].x, points[0].y);
|
|
1612
|
+
for(let i = 0; i < points.length; i++){
|
|
1613
|
+
const curr = points[i];
|
|
1614
|
+
const next = points[(i + 1) % points.length];
|
|
1615
|
+
const cpx = (curr.x + next.x) / 2 + (rng() - 0.5) * r * 0.15;
|
|
1616
|
+
const cpy = (curr.y + next.y) / 2 + (rng() - 0.5) * r * 0.15;
|
|
1617
|
+
ctx.quadraticCurveTo(cpx, cpy, next.x, next.y);
|
|
1618
|
+
}
|
|
1619
|
+
ctx.closePath();
|
|
1620
|
+
};
|
|
1621
|
+
const $a7241acce45d5513$export$155b4780b4c6bb7b = (ctx, size, config)=>{
|
|
1622
|
+
const rng = config?.rng ?? Math.random;
|
|
1623
|
+
const r = size / 2;
|
|
1624
|
+
const subdivisions = 1 + Math.floor(rng() * 3); // 1-3
|
|
1625
|
+
// Start with icosahedron vertices projected to 2D
|
|
1626
|
+
const baseVerts = 6 + subdivisions * 4;
|
|
1627
|
+
const points = [];
|
|
1628
|
+
for(let i = 0; i < baseVerts; i++){
|
|
1629
|
+
const angle = i / baseVerts * Math.PI * 2;
|
|
1630
|
+
const ring = i % 2 === 0 ? 1.0 : 0.5 + rng() * 0.3;
|
|
1631
|
+
points.push({
|
|
1632
|
+
x: Math.cos(angle) * r * ring,
|
|
1633
|
+
y: Math.sin(angle) * r * ring
|
|
1634
|
+
});
|
|
1635
|
+
}
|
|
1636
|
+
ctx.beginPath();
|
|
1637
|
+
// Draw triangulated mesh — connect each point to neighbors and center
|
|
1638
|
+
for(let i = 0; i < points.length; i++){
|
|
1639
|
+
const next = points[(i + 1) % points.length];
|
|
1640
|
+
ctx.moveTo(points[i].x, points[i].y);
|
|
1641
|
+
ctx.lineTo(next.x, next.y);
|
|
1642
|
+
// Connect to center
|
|
1643
|
+
ctx.moveTo(points[i].x, points[i].y);
|
|
1644
|
+
ctx.lineTo(0, 0);
|
|
1645
|
+
// Cross-connect to create triangulation
|
|
1646
|
+
if (i % 2 === 0 && i + 2 < points.length) {
|
|
1647
|
+
ctx.moveTo(points[i].x, points[i].y);
|
|
1648
|
+
ctx.lineTo(points[i + 2].x, points[i + 2].y);
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
// Outer ring
|
|
1652
|
+
ctx.moveTo(points[0].x, points[0].y);
|
|
1653
|
+
for(let i = 1; i < points.length; i++)ctx.lineTo(points[i].x, points[i].y);
|
|
1654
|
+
ctx.closePath();
|
|
1655
|
+
};
|
|
1656
|
+
const $a7241acce45d5513$export$9a7e648f11155172 = (ctx, size, config)=>{
|
|
1657
|
+
const rng = config?.rng ?? Math.random;
|
|
1658
|
+
const r = size / 2;
|
|
1659
|
+
const phi = (1 + Math.sqrt(5)) / 2; // golden ratio
|
|
1660
|
+
const isKite = rng() < 0.5;
|
|
1661
|
+
ctx.beginPath();
|
|
1662
|
+
if (isKite) {
|
|
1663
|
+
// Kite: two golden triangles joined at base
|
|
1664
|
+
const topY = -r;
|
|
1665
|
+
const bottomY = r * (1 / phi);
|
|
1666
|
+
const midY = r * (1 / phi - 1) * 0.3;
|
|
1667
|
+
const wingX = r * 0.6;
|
|
1668
|
+
ctx.moveTo(0, topY);
|
|
1669
|
+
ctx.lineTo(wingX, midY);
|
|
1670
|
+
ctx.lineTo(0, bottomY);
|
|
1671
|
+
ctx.lineTo(-wingX, midY);
|
|
1672
|
+
} else {
|
|
1673
|
+
// Dart: concave quadrilateral
|
|
1674
|
+
const topY = -r;
|
|
1675
|
+
const bottomY = r * 0.3;
|
|
1676
|
+
const midY = -r * 0.1;
|
|
1677
|
+
const wingX = r * 0.5;
|
|
1678
|
+
ctx.moveTo(0, topY);
|
|
1679
|
+
ctx.lineTo(wingX, midY);
|
|
1680
|
+
ctx.lineTo(0, bottomY);
|
|
1681
|
+
ctx.lineTo(-wingX, midY);
|
|
1682
|
+
}
|
|
1683
|
+
ctx.closePath();
|
|
1684
|
+
};
|
|
1685
|
+
const $a7241acce45d5513$export$1fc0aedbabd73399 = (ctx, size, config)=>{
|
|
1686
|
+
const r = size / 2;
|
|
1687
|
+
// Vertices of equilateral triangle
|
|
1688
|
+
const verts = [];
|
|
1689
|
+
for(let i = 0; i < 3; i++){
|
|
1690
|
+
const angle = i / 3 * Math.PI * 2 - Math.PI / 2;
|
|
1691
|
+
verts.push({
|
|
1692
|
+
x: Math.cos(angle) * r * 0.7,
|
|
1693
|
+
y: Math.sin(angle) * r * 0.7
|
|
1694
|
+
});
|
|
1695
|
+
}
|
|
1696
|
+
// Side length = distance between vertices
|
|
1697
|
+
const sideLen = Math.hypot(verts[1].x - verts[0].x, verts[1].y - verts[0].y);
|
|
1698
|
+
ctx.beginPath();
|
|
1699
|
+
for(let i = 0; i < 3; i++){
|
|
1700
|
+
const from = verts[(i + 1) % 3];
|
|
1701
|
+
const to = verts[(i + 2) % 3];
|
|
1702
|
+
const center = verts[i];
|
|
1703
|
+
const startAngle = Math.atan2(from.y - center.y, from.x - center.x);
|
|
1704
|
+
const endAngle = Math.atan2(to.y - center.y, to.x - center.x);
|
|
1705
|
+
if (i === 0) ctx.moveTo(from.x, from.y);
|
|
1706
|
+
ctx.arc(center.x, center.y, sideLen, startAngle, endAngle);
|
|
1707
|
+
}
|
|
1708
|
+
ctx.closePath();
|
|
1709
|
+
};
|
|
1710
|
+
const $a7241acce45d5513$export$ef7b5e0c19a21fd1 = (ctx, size, config)=>{
|
|
1711
|
+
const rng = config?.rng ?? Math.random;
|
|
1712
|
+
const r = size / 2;
|
|
1713
|
+
const dotCount = 15 + Math.floor(rng() * 25); // 15-39 dots
|
|
1714
|
+
const clusterTightness = 0.3 + rng() * 0.5;
|
|
1715
|
+
ctx.beginPath();
|
|
1716
|
+
for(let i = 0; i < dotCount; i++){
|
|
1717
|
+
// Gaussian-ish distribution via Box-Muller approximation
|
|
1718
|
+
const u1 = Math.max(0.001, rng());
|
|
1719
|
+
const u2 = rng();
|
|
1720
|
+
const mag = Math.sqrt(-2 * Math.log(u1)) * clusterTightness;
|
|
1721
|
+
const angle = u2 * Math.PI * 2;
|
|
1722
|
+
const dx = Math.cos(angle) * mag * r;
|
|
1723
|
+
const dy = Math.sin(angle) * mag * r;
|
|
1724
|
+
const dotR = r * (0.02 + rng() * 0.04);
|
|
1725
|
+
ctx.moveTo(dx + dotR, dy);
|
|
1726
|
+
ctx.arc(dx, dy, dotR, 0, Math.PI * 2);
|
|
1727
|
+
}
|
|
1728
|
+
};
|
|
1729
|
+
const $a7241acce45d5513$export$f15df8ab60dfcc9a = (ctx, size, config)=>{
|
|
1730
|
+
const rng = config?.rng ?? Math.random;
|
|
1731
|
+
const r = size / 2;
|
|
1732
|
+
const angle1 = rng() * Math.PI;
|
|
1733
|
+
const angle2 = angle1 + Math.PI / 2 + (rng() - 0.5) * 0.3;
|
|
1734
|
+
const spacing = r * (0.08 + rng() * 0.08);
|
|
1735
|
+
const hasCross = rng() < 0.6;
|
|
1736
|
+
// Draw bounding shape (ellipse)
|
|
1737
|
+
const rx = r * (0.7 + rng() * 0.3);
|
|
1738
|
+
const ry = r * (0.5 + rng() * 0.3);
|
|
1739
|
+
// Outer boundary
|
|
1740
|
+
ctx.beginPath();
|
|
1741
|
+
ctx.ellipse(0, 0, rx, ry, 0, 0, Math.PI * 2);
|
|
1742
|
+
// Hatch lines clipped to the ellipse
|
|
1743
|
+
const cos1 = Math.cos(angle1);
|
|
1744
|
+
const sin1 = Math.sin(angle1);
|
|
1745
|
+
for(let d = -r; d <= r; d += spacing){
|
|
1746
|
+
const lx1 = d * cos1 - r * sin1;
|
|
1747
|
+
const ly1 = d * sin1 + r * cos1;
|
|
1748
|
+
const lx2 = d * cos1 + r * sin1;
|
|
1749
|
+
const ly2 = d * sin1 - r * cos1;
|
|
1750
|
+
ctx.moveTo(lx1, ly1);
|
|
1751
|
+
ctx.lineTo(lx2, ly2);
|
|
1752
|
+
}
|
|
1753
|
+
if (hasCross) {
|
|
1754
|
+
const cos2 = Math.cos(angle2);
|
|
1755
|
+
const sin2 = Math.sin(angle2);
|
|
1756
|
+
for(let d = -r; d <= r; d += spacing * 1.3){
|
|
1757
|
+
const lx1 = d * cos2 - r * sin2;
|
|
1758
|
+
const ly1 = d * sin2 + r * cos2;
|
|
1759
|
+
const lx2 = d * cos2 + r * sin2;
|
|
1760
|
+
const ly2 = d * sin2 - r * cos2;
|
|
1761
|
+
ctx.moveTo(lx1, ly1);
|
|
1762
|
+
ctx.lineTo(lx2, ly2);
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
};
|
|
1460
1766
|
const $a7241acce45d5513$export$40cfb4c637f2fbb5 = {
|
|
1461
1767
|
blob: $a7241acce45d5513$export$580f80cfb9de73bc,
|
|
1462
1768
|
ngon: $a7241acce45d5513$export$7a6094023f0902a6,
|
|
@@ -1464,7 +1770,18 @@ const $a7241acce45d5513$export$40cfb4c637f2fbb5 = {
|
|
|
1464
1770
|
superellipse: $a7241acce45d5513$export$1db9219b4f34658c,
|
|
1465
1771
|
spirograph: $a7241acce45d5513$export$b027c64d22b01985,
|
|
1466
1772
|
waveRing: $a7241acce45d5513$export$7608ccd03bfb705d,
|
|
1467
|
-
rose: $a7241acce45d5513$export$11a377e7498bb523
|
|
1773
|
+
rose: $a7241acce45d5513$export$11a377e7498bb523,
|
|
1774
|
+
shardField: $a7241acce45d5513$export$76b6526575ea179b,
|
|
1775
|
+
voronoiCell: $a7241acce45d5513$export$ed9ff98da5b05073,
|
|
1776
|
+
crescent: $a7241acce45d5513$export$e0452d9a794fe7e5,
|
|
1777
|
+
tendril: $a7241acce45d5513$export$38bfe5eb52137e01,
|
|
1778
|
+
cloudForm: $a7241acce45d5513$export$105caa8cfd63c422,
|
|
1779
|
+
inkSplat: $a7241acce45d5513$export$e181e5bd3c539569,
|
|
1780
|
+
geodesicDome: $a7241acce45d5513$export$155b4780b4c6bb7b,
|
|
1781
|
+
penroseTile: $a7241acce45d5513$export$9a7e648f11155172,
|
|
1782
|
+
reuleauxTriangle: $a7241acce45d5513$export$1fc0aedbabd73399,
|
|
1783
|
+
dotCluster: $a7241acce45d5513$export$ef7b5e0c19a21fd1,
|
|
1784
|
+
crosshatchPatch: $a7241acce45d5513$export$f15df8ab60dfcc9a
|
|
1468
1785
|
};
|
|
1469
1786
|
|
|
1470
1787
|
|
|
@@ -1499,7 +1816,14 @@ const $9beb8f41637c29fd$var$RENDER_STYLES = [
|
|
|
1499
1816
|
"dashed",
|
|
1500
1817
|
"watercolor",
|
|
1501
1818
|
"hatched",
|
|
1502
|
-
"incomplete"
|
|
1819
|
+
"incomplete",
|
|
1820
|
+
"stipple",
|
|
1821
|
+
"stencil",
|
|
1822
|
+
"noise-grain",
|
|
1823
|
+
"wood-grain",
|
|
1824
|
+
"marble-vein",
|
|
1825
|
+
"fabric-weave",
|
|
1826
|
+
"hand-drawn"
|
|
1503
1827
|
];
|
|
1504
1828
|
function $9beb8f41637c29fd$export$9fd4e64b2acd410e(rng) {
|
|
1505
1829
|
return $9beb8f41637c29fd$var$RENDER_STYLES[Math.floor(rng() * $9beb8f41637c29fd$var$RENDER_STYLES.length)];
|
|
@@ -1673,6 +1997,240 @@ function $9beb8f41637c29fd$export$71b514a25c47df50(ctx, shape, x, y, config) {
|
|
|
1673
1997
|
ctx.lineDashOffset = 0;
|
|
1674
1998
|
break;
|
|
1675
1999
|
}
|
|
2000
|
+
case "stipple":
|
|
2001
|
+
{
|
|
2002
|
+
// Dot-fill texture — clip to shape, then scatter dots
|
|
2003
|
+
const savedAlphaS = ctx.globalAlpha;
|
|
2004
|
+
ctx.globalAlpha = savedAlphaS * 0.15;
|
|
2005
|
+
ctx.fill(); // ghost fill
|
|
2006
|
+
ctx.globalAlpha = savedAlphaS;
|
|
2007
|
+
ctx.save();
|
|
2008
|
+
ctx.clip();
|
|
2009
|
+
const dotSpacing = Math.max(2, size * 0.03);
|
|
2010
|
+
const extent = size * 0.55;
|
|
2011
|
+
ctx.globalAlpha = savedAlphaS * 0.7;
|
|
2012
|
+
for(let dx = -extent; dx <= extent; dx += dotSpacing)for(let dy = -extent; dy <= extent; dy += dotSpacing){
|
|
2013
|
+
// Jitter each dot position for organic feel
|
|
2014
|
+
const jx = rng ? (rng() - 0.5) * dotSpacing * 0.6 : 0;
|
|
2015
|
+
const jy = rng ? (rng() - 0.5) * dotSpacing * 0.6 : 0;
|
|
2016
|
+
const dotR = rng ? dotSpacing * (0.15 + rng() * 0.2) : dotSpacing * 0.2;
|
|
2017
|
+
ctx.beginPath();
|
|
2018
|
+
ctx.arc(dx + jx, dy + jy, dotR, 0, Math.PI * 2);
|
|
2019
|
+
ctx.fill();
|
|
2020
|
+
}
|
|
2021
|
+
ctx.restore();
|
|
2022
|
+
ctx.globalAlpha = savedAlphaS;
|
|
2023
|
+
// Outline
|
|
2024
|
+
ctx.globalAlpha *= 0.4;
|
|
2025
|
+
ctx.stroke();
|
|
2026
|
+
ctx.globalAlpha /= 0.4;
|
|
2027
|
+
break;
|
|
2028
|
+
}
|
|
2029
|
+
case "stencil":
|
|
2030
|
+
{
|
|
2031
|
+
// Negative-space cutout — fill a rectangle, then erase the shape
|
|
2032
|
+
const savedAlphaSt = ctx.globalAlpha;
|
|
2033
|
+
// Fill a bounding area with the stroke color
|
|
2034
|
+
ctx.globalAlpha = savedAlphaSt * 0.5;
|
|
2035
|
+
ctx.fillStyle = strokeColor;
|
|
2036
|
+
ctx.fillRect(-size * 0.6, -size * 0.6, size * 1.2, size * 1.2);
|
|
2037
|
+
// Cut out the shape using destination-out
|
|
2038
|
+
ctx.globalCompositeOperation = "destination-out";
|
|
2039
|
+
ctx.globalAlpha = 1;
|
|
2040
|
+
ctx.fill();
|
|
2041
|
+
ctx.globalCompositeOperation = "source-over";
|
|
2042
|
+
ctx.globalAlpha = savedAlphaSt;
|
|
2043
|
+
// Subtle outline of the cutout
|
|
2044
|
+
ctx.globalAlpha *= 0.3;
|
|
2045
|
+
ctx.stroke();
|
|
2046
|
+
ctx.globalAlpha /= 0.3;
|
|
2047
|
+
break;
|
|
2048
|
+
}
|
|
2049
|
+
case "noise-grain":
|
|
2050
|
+
{
|
|
2051
|
+
// Procedural noise grain texture clipped to shape boundary
|
|
2052
|
+
const savedAlphaN = ctx.globalAlpha;
|
|
2053
|
+
ctx.globalAlpha = savedAlphaN * 0.25;
|
|
2054
|
+
ctx.fill(); // base tint
|
|
2055
|
+
ctx.globalAlpha = savedAlphaN;
|
|
2056
|
+
ctx.save();
|
|
2057
|
+
ctx.clip();
|
|
2058
|
+
const grainSpacing = Math.max(1.5, size * 0.015);
|
|
2059
|
+
const extentN = size * 0.55;
|
|
2060
|
+
ctx.globalAlpha = savedAlphaN * 0.6;
|
|
2061
|
+
for(let gx = -extentN; gx <= extentN; gx += grainSpacing)for(let gy = -extentN; gy <= extentN; gy += grainSpacing){
|
|
2062
|
+
if (!rng) break;
|
|
2063
|
+
const jx = (rng() - 0.5) * grainSpacing * 1.2;
|
|
2064
|
+
const jy = (rng() - 0.5) * grainSpacing * 1.2;
|
|
2065
|
+
const brightness = rng() > 0.5 ? 255 : 0;
|
|
2066
|
+
const dotAlpha = 0.15 + rng() * 0.35;
|
|
2067
|
+
ctx.globalAlpha = savedAlphaN * dotAlpha;
|
|
2068
|
+
ctx.fillStyle = `rgba(${brightness},${brightness},${brightness},1)`;
|
|
2069
|
+
const dotSize = grainSpacing * (0.3 + rng() * 0.5);
|
|
2070
|
+
ctx.fillRect(gx + jx, gy + jy, dotSize, dotSize);
|
|
2071
|
+
}
|
|
2072
|
+
ctx.restore();
|
|
2073
|
+
ctx.fillStyle = fillColor;
|
|
2074
|
+
ctx.globalAlpha = savedAlphaN;
|
|
2075
|
+
ctx.globalAlpha *= 0.4;
|
|
2076
|
+
ctx.stroke();
|
|
2077
|
+
ctx.globalAlpha /= 0.4;
|
|
2078
|
+
break;
|
|
2079
|
+
}
|
|
2080
|
+
case "wood-grain":
|
|
2081
|
+
{
|
|
2082
|
+
// Parallel wavy lines simulating wood grain, clipped to shape
|
|
2083
|
+
const savedAlphaW = ctx.globalAlpha;
|
|
2084
|
+
ctx.globalAlpha = savedAlphaW * 0.2;
|
|
2085
|
+
ctx.fill(); // base tint
|
|
2086
|
+
ctx.globalAlpha = savedAlphaW;
|
|
2087
|
+
ctx.save();
|
|
2088
|
+
ctx.clip();
|
|
2089
|
+
const grainLineSpacing = Math.max(2, size * 0.035);
|
|
2090
|
+
const extentW = size * 0.55;
|
|
2091
|
+
const waveFreq = rng ? 3 + rng() * 5 : 5;
|
|
2092
|
+
const waveAmp = rng ? size * (0.01 + rng() * 0.03) : size * 0.02;
|
|
2093
|
+
const grainAngle = rng ? rng() * Math.PI : Math.PI * 0.25;
|
|
2094
|
+
ctx.lineWidth = Math.max(0.5, strokeWidth * 0.3);
|
|
2095
|
+
ctx.globalAlpha = savedAlphaW * 0.5;
|
|
2096
|
+
const cosG = Math.cos(grainAngle);
|
|
2097
|
+
const sinG = Math.sin(grainAngle);
|
|
2098
|
+
for(let d = -extentW; d <= extentW; d += grainLineSpacing){
|
|
2099
|
+
ctx.beginPath();
|
|
2100
|
+
for(let t = -extentW; t <= extentW; t += 2){
|
|
2101
|
+
const wave = Math.sin(t / extentW * waveFreq * Math.PI) * waveAmp;
|
|
2102
|
+
const px = t * cosG - (d + wave) * sinG;
|
|
2103
|
+
const py = t * sinG + (d + wave) * cosG;
|
|
2104
|
+
if (t === -extentW) ctx.moveTo(px, py);
|
|
2105
|
+
else ctx.lineTo(px, py);
|
|
2106
|
+
}
|
|
2107
|
+
ctx.stroke();
|
|
2108
|
+
}
|
|
2109
|
+
ctx.restore();
|
|
2110
|
+
ctx.globalAlpha = savedAlphaW;
|
|
2111
|
+
ctx.globalAlpha *= 0.35;
|
|
2112
|
+
ctx.stroke();
|
|
2113
|
+
ctx.globalAlpha /= 0.35;
|
|
2114
|
+
break;
|
|
2115
|
+
}
|
|
2116
|
+
case "marble-vein":
|
|
2117
|
+
{
|
|
2118
|
+
// Branching vein lines on a soft fill, clipped to shape
|
|
2119
|
+
const savedAlphaM = ctx.globalAlpha;
|
|
2120
|
+
ctx.globalAlpha = savedAlphaM * 0.35;
|
|
2121
|
+
ctx.fill(); // soft base
|
|
2122
|
+
ctx.globalAlpha = savedAlphaM;
|
|
2123
|
+
ctx.save();
|
|
2124
|
+
ctx.clip();
|
|
2125
|
+
const veinCount = rng ? 2 + Math.floor(rng() * 3) : 3;
|
|
2126
|
+
const extentM = size * 0.45;
|
|
2127
|
+
ctx.lineWidth = Math.max(0.5, strokeWidth * 0.5);
|
|
2128
|
+
ctx.globalAlpha = savedAlphaM * 0.4;
|
|
2129
|
+
for(let v = 0; v < veinCount; v++){
|
|
2130
|
+
const startX = rng ? (rng() - 0.5) * extentM * 2 : 0;
|
|
2131
|
+
const startY = rng ? -extentM + rng() * extentM * 0.5 : -extentM;
|
|
2132
|
+
let vx = startX;
|
|
2133
|
+
let vy = startY;
|
|
2134
|
+
const steps = 15 + (rng ? Math.floor(rng() * 15) : 10);
|
|
2135
|
+
const stepLen = size * 0.04;
|
|
2136
|
+
ctx.beginPath();
|
|
2137
|
+
ctx.moveTo(vx, vy);
|
|
2138
|
+
for(let s = 0; s < steps; s++){
|
|
2139
|
+
const drift = rng ? (rng() - 0.5) * stepLen * 1.5 : 0;
|
|
2140
|
+
vx += drift;
|
|
2141
|
+
vy += stepLen;
|
|
2142
|
+
ctx.lineTo(vx, vy);
|
|
2143
|
+
// Branch ~20% of the time
|
|
2144
|
+
if (rng && rng() < 0.2 && s > 2 && s < steps - 3) {
|
|
2145
|
+
const branchDir = rng() < 0.5 ? -1 : 1;
|
|
2146
|
+
let bx = vx;
|
|
2147
|
+
let by = vy;
|
|
2148
|
+
const bSteps = 3 + Math.floor(rng() * 5);
|
|
2149
|
+
ctx.moveTo(bx, by);
|
|
2150
|
+
for(let bs = 0; bs < bSteps; bs++){
|
|
2151
|
+
bx += branchDir * stepLen * (0.5 + rng() * 0.5);
|
|
2152
|
+
by += stepLen * 0.6;
|
|
2153
|
+
ctx.lineTo(bx, by);
|
|
2154
|
+
}
|
|
2155
|
+
ctx.moveTo(vx, vy); // return to main vein
|
|
2156
|
+
}
|
|
2157
|
+
}
|
|
2158
|
+
ctx.stroke();
|
|
2159
|
+
}
|
|
2160
|
+
ctx.restore();
|
|
2161
|
+
ctx.globalAlpha = savedAlphaM;
|
|
2162
|
+
ctx.globalAlpha *= 0.3;
|
|
2163
|
+
ctx.stroke();
|
|
2164
|
+
ctx.globalAlpha /= 0.3;
|
|
2165
|
+
break;
|
|
2166
|
+
}
|
|
2167
|
+
case "fabric-weave":
|
|
2168
|
+
{
|
|
2169
|
+
// Interlocking horizontal/vertical threads clipped to shape
|
|
2170
|
+
const savedAlphaF = ctx.globalAlpha;
|
|
2171
|
+
ctx.globalAlpha = savedAlphaF * 0.15;
|
|
2172
|
+
ctx.fill(); // ghost base
|
|
2173
|
+
ctx.globalAlpha = savedAlphaF;
|
|
2174
|
+
ctx.save();
|
|
2175
|
+
ctx.clip();
|
|
2176
|
+
const threadSpacing = Math.max(2, size * 0.04);
|
|
2177
|
+
const extentF = size * 0.55;
|
|
2178
|
+
ctx.lineWidth = Math.max(0.8, threadSpacing * 0.5);
|
|
2179
|
+
ctx.globalAlpha = savedAlphaF * 0.55;
|
|
2180
|
+
// Horizontal threads
|
|
2181
|
+
for(let y = -extentF; y <= extentF; y += threadSpacing * 2){
|
|
2182
|
+
ctx.beginPath();
|
|
2183
|
+
ctx.moveTo(-extentF, y);
|
|
2184
|
+
ctx.lineTo(extentF, y);
|
|
2185
|
+
ctx.stroke();
|
|
2186
|
+
}
|
|
2187
|
+
// Vertical threads (offset by half spacing for weave effect)
|
|
2188
|
+
ctx.globalAlpha = savedAlphaF * 0.45;
|
|
2189
|
+
ctx.strokeStyle = fillColor;
|
|
2190
|
+
for(let x = -extentF; x <= extentF; x += threadSpacing * 2){
|
|
2191
|
+
ctx.beginPath();
|
|
2192
|
+
for(let y = -extentF; y <= extentF; y += threadSpacing * 2){
|
|
2193
|
+
// Over-under: draw segment, skip segment
|
|
2194
|
+
ctx.moveTo(x, y);
|
|
2195
|
+
ctx.lineTo(x, y + threadSpacing);
|
|
2196
|
+
}
|
|
2197
|
+
ctx.stroke();
|
|
2198
|
+
}
|
|
2199
|
+
ctx.strokeStyle = strokeColor;
|
|
2200
|
+
ctx.restore();
|
|
2201
|
+
ctx.globalAlpha = savedAlphaF;
|
|
2202
|
+
ctx.globalAlpha *= 0.3;
|
|
2203
|
+
ctx.stroke();
|
|
2204
|
+
ctx.globalAlpha /= 0.3;
|
|
2205
|
+
break;
|
|
2206
|
+
}
|
|
2207
|
+
case "hand-drawn":
|
|
2208
|
+
{
|
|
2209
|
+
// Wobbly hand-drawn edge treatment — fill normally, then redraw
|
|
2210
|
+
// the outline with perturbed control points for a sketchy feel
|
|
2211
|
+
const savedAlphaHD = ctx.globalAlpha;
|
|
2212
|
+
ctx.globalAlpha = savedAlphaHD * 0.85;
|
|
2213
|
+
ctx.fill();
|
|
2214
|
+
ctx.globalAlpha = savedAlphaHD;
|
|
2215
|
+
// Draw 2-3 slightly offset wobbly strokes for a sketchy look
|
|
2216
|
+
const wobblePasses = 2 + (rng ? Math.floor(rng() * 2) : 0);
|
|
2217
|
+
ctx.lineWidth = strokeWidth * 0.8;
|
|
2218
|
+
for(let wp = 0; wp < wobblePasses; wp++){
|
|
2219
|
+
ctx.globalAlpha = savedAlphaHD * (0.4 - wp * 0.1);
|
|
2220
|
+
ctx.save();
|
|
2221
|
+
// Slight random offset per pass
|
|
2222
|
+
const wobbleX = rng ? (rng() - 0.5) * size * 0.02 : 0;
|
|
2223
|
+
const wobbleY = rng ? (rng() - 0.5) * size * 0.02 : 0;
|
|
2224
|
+
ctx.translate(wobbleX, wobbleY);
|
|
2225
|
+
// Slightly different scale per pass for edge variation
|
|
2226
|
+
const wobbleScale = 1 + (rng ? (rng() - 0.5) * 0.03 : 0);
|
|
2227
|
+
ctx.scale(wobbleScale, wobbleScale);
|
|
2228
|
+
ctx.stroke();
|
|
2229
|
+
ctx.restore();
|
|
2230
|
+
}
|
|
2231
|
+
ctx.globalAlpha = savedAlphaHD;
|
|
2232
|
+
break;
|
|
2233
|
+
}
|
|
1676
2234
|
case "fill-and-stroke":
|
|
1677
2235
|
default:
|
|
1678
2236
|
ctx.fill();
|
|
@@ -1719,6 +2277,64 @@ function $9beb8f41637c29fd$export$bb35a6995ddbf32d(ctx, shape, x, y, config) {
|
|
|
1719
2277
|
});
|
|
1720
2278
|
ctx.restore();
|
|
1721
2279
|
}
|
|
2280
|
+
function $9beb8f41637c29fd$export$8bd8bbd1a8e53689(ctx, shape, x, y, config) {
|
|
2281
|
+
const { mirrorAxis: mirrorAxis = "horizontal", mirrorGap: mirrorGap = 0 } = config;
|
|
2282
|
+
// Draw the primary shape
|
|
2283
|
+
$9beb8f41637c29fd$export$bb35a6995ddbf32d(ctx, shape, x, y, config);
|
|
2284
|
+
// Draw the mirrored copy
|
|
2285
|
+
ctx.save();
|
|
2286
|
+
const savedAlpha = ctx.globalAlpha;
|
|
2287
|
+
ctx.globalAlpha = savedAlpha * 0.7; // mirror is slightly softer
|
|
2288
|
+
switch(mirrorAxis){
|
|
2289
|
+
case "horizontal":
|
|
2290
|
+
// Reflect across vertical axis at shape position
|
|
2291
|
+
$9beb8f41637c29fd$export$bb35a6995ddbf32d(ctx, shape, x, y + mirrorGap, {
|
|
2292
|
+
...config,
|
|
2293
|
+
rotation: -(config.rotation || 0),
|
|
2294
|
+
size: config.size * 0.95
|
|
2295
|
+
});
|
|
2296
|
+
break;
|
|
2297
|
+
case "vertical":
|
|
2298
|
+
$9beb8f41637c29fd$export$bb35a6995ddbf32d(ctx, shape, x + mirrorGap, y, {
|
|
2299
|
+
...config,
|
|
2300
|
+
rotation: 180 - (config.rotation || 0),
|
|
2301
|
+
size: config.size * 0.95
|
|
2302
|
+
});
|
|
2303
|
+
break;
|
|
2304
|
+
case "diagonal":
|
|
2305
|
+
// Reflect across 45° axis
|
|
2306
|
+
$9beb8f41637c29fd$export$bb35a6995ddbf32d(ctx, shape, x + mirrorGap * 0.7, y + mirrorGap * 0.7, {
|
|
2307
|
+
...config,
|
|
2308
|
+
rotation: 90 - (config.rotation || 0),
|
|
2309
|
+
size: config.size * 0.9
|
|
2310
|
+
});
|
|
2311
|
+
break;
|
|
2312
|
+
case "radial-4":
|
|
2313
|
+
// Four-way radial mirror
|
|
2314
|
+
for(let i = 1; i < 4; i++){
|
|
2315
|
+
const angle = i / 4 * Math.PI * 2;
|
|
2316
|
+
const mx = x + Math.cos(angle) * mirrorGap;
|
|
2317
|
+
const my = y + Math.sin(angle) * mirrorGap;
|
|
2318
|
+
ctx.globalAlpha = savedAlpha * (0.7 - i * 0.1);
|
|
2319
|
+
$9beb8f41637c29fd$export$bb35a6995ddbf32d(ctx, shape, mx, my, {
|
|
2320
|
+
...config,
|
|
2321
|
+
rotation: (config.rotation || 0) + i * 90,
|
|
2322
|
+
size: config.size * (0.95 - i * 0.05)
|
|
2323
|
+
});
|
|
2324
|
+
}
|
|
2325
|
+
break;
|
|
2326
|
+
}
|
|
2327
|
+
ctx.globalAlpha = savedAlpha;
|
|
2328
|
+
ctx.restore();
|
|
2329
|
+
}
|
|
2330
|
+
function $9beb8f41637c29fd$export$879206e23912d1a9(rng) {
|
|
2331
|
+
const roll = rng();
|
|
2332
|
+
if (roll < 0.60) return null;
|
|
2333
|
+
if (roll < 0.75) return "horizontal";
|
|
2334
|
+
if (roll < 0.87) return "vertical";
|
|
2335
|
+
if (roll < 0.95) return "diagonal";
|
|
2336
|
+
return "radial-4";
|
|
2337
|
+
}
|
|
1722
2338
|
|
|
1723
2339
|
|
|
1724
2340
|
|
|
@@ -1750,7 +2366,8 @@ const $24064302523652b1$export$4343b39fe47bd82c = {
|
|
|
1750
2366
|
bestStyles: [
|
|
1751
2367
|
"fill-only",
|
|
1752
2368
|
"watercolor",
|
|
1753
|
-
"fill-and-stroke"
|
|
2369
|
+
"fill-and-stroke",
|
|
2370
|
+
"hand-drawn"
|
|
1754
2371
|
]
|
|
1755
2372
|
},
|
|
1756
2373
|
square: {
|
|
@@ -1787,7 +2404,8 @@ const $24064302523652b1$export$4343b39fe47bd82c = {
|
|
|
1787
2404
|
bestStyles: [
|
|
1788
2405
|
"fill-and-stroke",
|
|
1789
2406
|
"fill-only",
|
|
1790
|
-
"watercolor"
|
|
2407
|
+
"watercolor",
|
|
2408
|
+
"hand-drawn"
|
|
1791
2409
|
]
|
|
1792
2410
|
},
|
|
1793
2411
|
hexagon: {
|
|
@@ -2167,7 +2785,8 @@ const $24064302523652b1$export$4343b39fe47bd82c = {
|
|
|
2167
2785
|
bestStyles: [
|
|
2168
2786
|
"fill-only",
|
|
2169
2787
|
"watercolor",
|
|
2170
|
-
"fill-and-stroke"
|
|
2788
|
+
"fill-and-stroke",
|
|
2789
|
+
"hand-drawn"
|
|
2171
2790
|
]
|
|
2172
2791
|
},
|
|
2173
2792
|
ngon: {
|
|
@@ -2221,7 +2840,8 @@ const $24064302523652b1$export$4343b39fe47bd82c = {
|
|
|
2221
2840
|
bestStyles: [
|
|
2222
2841
|
"fill-only",
|
|
2223
2842
|
"watercolor",
|
|
2224
|
-
"fill-and-stroke"
|
|
2843
|
+
"fill-and-stroke",
|
|
2844
|
+
"wood-grain"
|
|
2225
2845
|
]
|
|
2226
2846
|
},
|
|
2227
2847
|
spirograph: {
|
|
@@ -2277,84 +2897,355 @@ const $24064302523652b1$export$4343b39fe47bd82c = {
|
|
|
2277
2897
|
"fill-only",
|
|
2278
2898
|
"watercolor"
|
|
2279
2899
|
]
|
|
2280
|
-
}
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
}
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2900
|
+
},
|
|
2901
|
+
// ── New procedural shapes ─────────────────────────────────────
|
|
2902
|
+
shardField: {
|
|
2903
|
+
tier: 2,
|
|
2904
|
+
minSizeFraction: 0.1,
|
|
2905
|
+
maxSizeFraction: 0.7,
|
|
2906
|
+
affinities: [
|
|
2907
|
+
"voronoiCell",
|
|
2908
|
+
"diamond",
|
|
2909
|
+
"triangle",
|
|
2910
|
+
"penroseTile"
|
|
2911
|
+
],
|
|
2912
|
+
category: "procedural",
|
|
2913
|
+
heroCandidate: false,
|
|
2914
|
+
bestStyles: [
|
|
2915
|
+
"fill-and-stroke",
|
|
2916
|
+
"stroke-only",
|
|
2917
|
+
"fill-only"
|
|
2918
|
+
]
|
|
2919
|
+
},
|
|
2920
|
+
voronoiCell: {
|
|
2921
|
+
tier: 1,
|
|
2922
|
+
minSizeFraction: 0.08,
|
|
2923
|
+
maxSizeFraction: 0.9,
|
|
2924
|
+
affinities: [
|
|
2925
|
+
"shardField",
|
|
2926
|
+
"ngon",
|
|
2927
|
+
"superellipse",
|
|
2928
|
+
"blob"
|
|
2929
|
+
],
|
|
2930
|
+
category: "procedural",
|
|
2931
|
+
heroCandidate: false,
|
|
2932
|
+
bestStyles: [
|
|
2933
|
+
"fill-and-stroke",
|
|
2934
|
+
"fill-only",
|
|
2935
|
+
"watercolor",
|
|
2936
|
+
"marble-vein"
|
|
2937
|
+
]
|
|
2938
|
+
},
|
|
2939
|
+
crescent: {
|
|
2940
|
+
tier: 1,
|
|
2941
|
+
minSizeFraction: 0.1,
|
|
2942
|
+
maxSizeFraction: 1.0,
|
|
2943
|
+
affinities: [
|
|
2944
|
+
"circle",
|
|
2945
|
+
"blob",
|
|
2946
|
+
"cloudForm",
|
|
2947
|
+
"vesicaPiscis"
|
|
2948
|
+
],
|
|
2949
|
+
category: "procedural",
|
|
2950
|
+
heroCandidate: true,
|
|
2951
|
+
bestStyles: [
|
|
2952
|
+
"fill-only",
|
|
2953
|
+
"watercolor",
|
|
2954
|
+
"fill-and-stroke"
|
|
2955
|
+
]
|
|
2956
|
+
},
|
|
2957
|
+
tendril: {
|
|
2958
|
+
tier: 2,
|
|
2959
|
+
minSizeFraction: 0.1,
|
|
2960
|
+
maxSizeFraction: 0.8,
|
|
2961
|
+
affinities: [
|
|
2962
|
+
"blob",
|
|
2963
|
+
"inkSplat",
|
|
2964
|
+
"lissajous",
|
|
2965
|
+
"fibonacciSpiral"
|
|
2966
|
+
],
|
|
2967
|
+
category: "procedural",
|
|
2968
|
+
heroCandidate: false,
|
|
2969
|
+
bestStyles: [
|
|
2970
|
+
"fill-only",
|
|
2971
|
+
"watercolor",
|
|
2972
|
+
"fill-and-stroke"
|
|
2973
|
+
]
|
|
2974
|
+
},
|
|
2975
|
+
cloudForm: {
|
|
2976
|
+
tier: 1,
|
|
2977
|
+
minSizeFraction: 0.15,
|
|
2978
|
+
maxSizeFraction: 1.0,
|
|
2979
|
+
affinities: [
|
|
2980
|
+
"blob",
|
|
2981
|
+
"circle",
|
|
2982
|
+
"crescent",
|
|
2983
|
+
"superellipse"
|
|
2984
|
+
],
|
|
2985
|
+
category: "procedural",
|
|
2986
|
+
heroCandidate: false,
|
|
2987
|
+
bestStyles: [
|
|
2988
|
+
"fill-only",
|
|
2989
|
+
"watercolor"
|
|
2990
|
+
]
|
|
2991
|
+
},
|
|
2992
|
+
inkSplat: {
|
|
2993
|
+
tier: 2,
|
|
2994
|
+
minSizeFraction: 0.1,
|
|
2995
|
+
maxSizeFraction: 0.8,
|
|
2996
|
+
affinities: [
|
|
2997
|
+
"blob",
|
|
2998
|
+
"tendril",
|
|
2999
|
+
"shardField",
|
|
3000
|
+
"star"
|
|
3001
|
+
],
|
|
3002
|
+
category: "procedural",
|
|
3003
|
+
heroCandidate: false,
|
|
3004
|
+
bestStyles: [
|
|
3005
|
+
"fill-only",
|
|
3006
|
+
"watercolor",
|
|
3007
|
+
"fill-and-stroke"
|
|
3008
|
+
]
|
|
3009
|
+
},
|
|
3010
|
+
geodesicDome: {
|
|
3011
|
+
tier: 2,
|
|
3012
|
+
minSizeFraction: 0.2,
|
|
3013
|
+
maxSizeFraction: 0.9,
|
|
3014
|
+
affinities: [
|
|
3015
|
+
"metatronsCube",
|
|
3016
|
+
"platonicSolid",
|
|
3017
|
+
"hexagon",
|
|
3018
|
+
"triangle"
|
|
3019
|
+
],
|
|
3020
|
+
category: "procedural",
|
|
3021
|
+
heroCandidate: true,
|
|
3022
|
+
bestStyles: [
|
|
3023
|
+
"stroke-only",
|
|
3024
|
+
"dashed",
|
|
3025
|
+
"double-stroke"
|
|
3026
|
+
]
|
|
3027
|
+
},
|
|
3028
|
+
penroseTile: {
|
|
3029
|
+
tier: 2,
|
|
3030
|
+
minSizeFraction: 0.06,
|
|
3031
|
+
maxSizeFraction: 0.6,
|
|
3032
|
+
affinities: [
|
|
3033
|
+
"diamond",
|
|
3034
|
+
"triangle",
|
|
3035
|
+
"shardField",
|
|
3036
|
+
"voronoiCell"
|
|
3037
|
+
],
|
|
3038
|
+
category: "procedural",
|
|
3039
|
+
heroCandidate: false,
|
|
3040
|
+
bestStyles: [
|
|
3041
|
+
"fill-and-stroke",
|
|
3042
|
+
"fill-only",
|
|
3043
|
+
"double-stroke"
|
|
3044
|
+
]
|
|
3045
|
+
},
|
|
3046
|
+
reuleauxTriangle: {
|
|
3047
|
+
tier: 1,
|
|
3048
|
+
minSizeFraction: 0.08,
|
|
3049
|
+
maxSizeFraction: 1.0,
|
|
3050
|
+
affinities: [
|
|
3051
|
+
"triangle",
|
|
3052
|
+
"circle",
|
|
3053
|
+
"superellipse",
|
|
3054
|
+
"vesicaPiscis"
|
|
3055
|
+
],
|
|
3056
|
+
category: "procedural",
|
|
3057
|
+
heroCandidate: true,
|
|
3058
|
+
bestStyles: [
|
|
3059
|
+
"fill-and-stroke",
|
|
3060
|
+
"fill-only",
|
|
3061
|
+
"watercolor"
|
|
3062
|
+
]
|
|
3063
|
+
},
|
|
3064
|
+
dotCluster: {
|
|
3065
|
+
tier: 3,
|
|
3066
|
+
minSizeFraction: 0.05,
|
|
3067
|
+
maxSizeFraction: 0.5,
|
|
3068
|
+
affinities: [
|
|
3069
|
+
"cloudForm",
|
|
3070
|
+
"inkSplat",
|
|
3071
|
+
"blob"
|
|
3072
|
+
],
|
|
3073
|
+
category: "procedural",
|
|
3074
|
+
heroCandidate: false,
|
|
3075
|
+
bestStyles: [
|
|
3076
|
+
"fill-only",
|
|
3077
|
+
"stipple"
|
|
3078
|
+
]
|
|
3079
|
+
},
|
|
3080
|
+
crosshatchPatch: {
|
|
3081
|
+
tier: 3,
|
|
3082
|
+
minSizeFraction: 0.1,
|
|
3083
|
+
maxSizeFraction: 0.6,
|
|
3084
|
+
affinities: [
|
|
3085
|
+
"voronoiCell",
|
|
3086
|
+
"ngon",
|
|
3087
|
+
"superellipse"
|
|
3088
|
+
],
|
|
3089
|
+
category: "procedural",
|
|
3090
|
+
heroCandidate: false,
|
|
3091
|
+
bestStyles: [
|
|
3092
|
+
"stroke-only",
|
|
3093
|
+
"hatched",
|
|
3094
|
+
"fabric-weave"
|
|
3095
|
+
]
|
|
3096
|
+
}
|
|
3097
|
+
};
|
|
3098
|
+
function $24064302523652b1$export$4a95df8944b5033b(rng, shapeNames, archetypeName) {
|
|
3099
|
+
const available = shapeNames.filter((s)=>$24064302523652b1$export$4343b39fe47bd82c[s]);
|
|
3100
|
+
// Pick a seed shape — tier 1 shapes that are hero candidates
|
|
3101
|
+
const heroPool = available.filter((s)=>$24064302523652b1$export$4343b39fe47bd82c[s].tier === 1 && $24064302523652b1$export$4343b39fe47bd82c[s].heroCandidate);
|
|
3102
|
+
const seedShape = heroPool.length > 0 ? heroPool[Math.floor(rng() * heroPool.length)] : available[Math.floor(rng() * available.length)];
|
|
3103
|
+
const seedProfile = $24064302523652b1$export$4343b39fe47bd82c[seedShape];
|
|
3104
|
+
// Primary: seed shape + its direct affinities (tier 1-2 only)
|
|
3105
|
+
const primaryCandidates = [
|
|
3106
|
+
seedShape,
|
|
3107
|
+
...seedProfile.affinities
|
|
3108
|
+
].filter((s)=>available.includes(s)).filter((s)=>$24064302523652b1$export$4343b39fe47bd82c[s].tier <= 2);
|
|
3109
|
+
const primary = [
|
|
3110
|
+
...new Set(primaryCandidates)
|
|
3111
|
+
].slice(0, 5);
|
|
3112
|
+
// Supporting: affinities of affinities, plus same-category shapes
|
|
3113
|
+
const supportingSet = new Set();
|
|
3114
|
+
for (const p of primary){
|
|
3115
|
+
const profile = $24064302523652b1$export$4343b39fe47bd82c[p];
|
|
3116
|
+
if (!profile) continue;
|
|
3117
|
+
for (const aff of profile.affinities)if (available.includes(aff) && !primary.includes(aff)) supportingSet.add(aff);
|
|
3118
|
+
}
|
|
3119
|
+
// Add same-category tier 1-2 shapes
|
|
3120
|
+
for (const s of available){
|
|
3121
|
+
const p = $24064302523652b1$export$4343b39fe47bd82c[s];
|
|
3122
|
+
if (p.category === seedProfile.category && p.tier <= 2 && !primary.includes(s)) supportingSet.add(s);
|
|
3123
|
+
}
|
|
3124
|
+
const supporting = [
|
|
3125
|
+
...supportingSet
|
|
3126
|
+
].slice(0, 6);
|
|
3127
|
+
// Accents: tier 1 shapes from other categories for contrast
|
|
3128
|
+
const usedCategories = new Set([
|
|
3129
|
+
...primary,
|
|
3130
|
+
...supporting
|
|
3131
|
+
].map((s)=>$24064302523652b1$export$4343b39fe47bd82c[s]?.category));
|
|
3132
|
+
const accentCandidates = available.filter((s)=>!primary.includes(s) && !supporting.includes(s) && $24064302523652b1$export$4343b39fe47bd82c[s].tier <= 2 && !usedCategories.has($24064302523652b1$export$4343b39fe47bd82c[s].category));
|
|
3133
|
+
// Shuffle and take a few
|
|
3134
|
+
const accents = [];
|
|
3135
|
+
const shuffled = [
|
|
3136
|
+
...accentCandidates
|
|
3137
|
+
];
|
|
3138
|
+
for(let i = shuffled.length - 1; i > 0; i--){
|
|
3139
|
+
const j = Math.floor(rng() * (i + 1));
|
|
3140
|
+
[shuffled[i], shuffled[j]] = [
|
|
3141
|
+
shuffled[j],
|
|
3142
|
+
shuffled[i]
|
|
3143
|
+
];
|
|
3144
|
+
}
|
|
3145
|
+
accents.push(...shuffled.slice(0, 3));
|
|
3146
|
+
// For certain archetypes, bias the palette
|
|
3147
|
+
if (archetypeName === "geometric-precision") // Remove blobs and organic shapes from primary
|
|
3148
|
+
return {
|
|
3149
|
+
primary: primary.filter((s)=>$24064302523652b1$export$4343b39fe47bd82c[s]?.category !== "procedural" || s === "ngon"),
|
|
3150
|
+
supporting: supporting.filter((s)=>s !== "blob"),
|
|
3151
|
+
accents: accents
|
|
3152
|
+
};
|
|
3153
|
+
if (archetypeName === "organic-flow") {
|
|
3154
|
+
// Boost procedural/organic shapes
|
|
3155
|
+
const organicBoost = available.filter((s)=>[
|
|
3156
|
+
"blob",
|
|
3157
|
+
"superellipse",
|
|
3158
|
+
"waveRing",
|
|
3159
|
+
"rose"
|
|
3160
|
+
].includes(s) && !primary.includes(s));
|
|
3161
|
+
return {
|
|
3162
|
+
primary: [
|
|
3163
|
+
...primary,
|
|
3164
|
+
...organicBoost.slice(0, 2)
|
|
3165
|
+
],
|
|
3166
|
+
supporting: supporting,
|
|
3167
|
+
accents: accents
|
|
3168
|
+
};
|
|
3169
|
+
}
|
|
3170
|
+
if (archetypeName === "shattered-glass") {
|
|
3171
|
+
// Favor angular, fragmented shapes
|
|
3172
|
+
const shardBoost = available.filter((s)=>[
|
|
3173
|
+
"shardField",
|
|
3174
|
+
"voronoiCell",
|
|
3175
|
+
"penroseTile",
|
|
3176
|
+
"diamond",
|
|
3177
|
+
"triangle",
|
|
3178
|
+
"ngon"
|
|
3179
|
+
].includes(s) && !primary.includes(s));
|
|
3180
|
+
return {
|
|
3181
|
+
primary: [
|
|
3182
|
+
...primary.filter((s)=>s !== "blob" && s !== "cloudForm"),
|
|
3183
|
+
...shardBoost.slice(0, 3)
|
|
3184
|
+
],
|
|
3185
|
+
supporting: supporting.filter((s)=>s !== "blob" && s !== "cloudForm"),
|
|
3186
|
+
accents: accents
|
|
3187
|
+
};
|
|
3188
|
+
}
|
|
3189
|
+
if (archetypeName === "botanical") {
|
|
3190
|
+
// Favor organic, flowing shapes
|
|
3191
|
+
const botanicalBoost = available.filter((s)=>[
|
|
3192
|
+
"tendril",
|
|
3193
|
+
"cloudForm",
|
|
3194
|
+
"blob",
|
|
3195
|
+
"crescent",
|
|
3196
|
+
"rose",
|
|
3197
|
+
"inkSplat"
|
|
3198
|
+
].includes(s) && !primary.includes(s));
|
|
3199
|
+
return {
|
|
3200
|
+
primary: [
|
|
3201
|
+
...primary,
|
|
3202
|
+
...botanicalBoost.slice(0, 3)
|
|
3203
|
+
],
|
|
3204
|
+
supporting: supporting,
|
|
3205
|
+
accents: accents
|
|
3206
|
+
};
|
|
3207
|
+
}
|
|
3208
|
+
if (archetypeName === "stipple-portrait") {
|
|
3209
|
+
// Favor small, dot-friendly shapes
|
|
3210
|
+
const stippleBoost = available.filter((s)=>[
|
|
3211
|
+
"dotCluster",
|
|
3212
|
+
"circle",
|
|
3213
|
+
"crosshatchPatch",
|
|
3214
|
+
"voronoiCell",
|
|
3215
|
+
"blob"
|
|
3216
|
+
].includes(s) && !primary.includes(s));
|
|
3217
|
+
return {
|
|
3218
|
+
primary: [
|
|
3219
|
+
...primary,
|
|
3220
|
+
...stippleBoost.slice(0, 3)
|
|
3221
|
+
],
|
|
3222
|
+
supporting: supporting,
|
|
3223
|
+
accents: accents
|
|
3224
|
+
};
|
|
3225
|
+
}
|
|
3226
|
+
if (archetypeName === "celestial") {
|
|
3227
|
+
// Favor sacred geometry and cosmic shapes
|
|
3228
|
+
const celestialBoost = available.filter((s)=>[
|
|
3229
|
+
"crescent",
|
|
3230
|
+
"geodesicDome",
|
|
3231
|
+
"mandala",
|
|
3232
|
+
"flowerOfLife",
|
|
3233
|
+
"spirograph",
|
|
3234
|
+
"fibonacciSpiral"
|
|
3235
|
+
].includes(s) && !primary.includes(s));
|
|
3236
|
+
return {
|
|
3237
|
+
primary: [
|
|
3238
|
+
...primary,
|
|
3239
|
+
...celestialBoost.slice(0, 3)
|
|
3240
|
+
],
|
|
3241
|
+
supporting: supporting,
|
|
3242
|
+
accents: accents
|
|
3243
|
+
};
|
|
3244
|
+
}
|
|
3245
|
+
return {
|
|
3246
|
+
primary: primary,
|
|
3247
|
+
supporting: supporting,
|
|
3248
|
+
accents: accents
|
|
2358
3249
|
};
|
|
2359
3250
|
}
|
|
2360
3251
|
function $24064302523652b1$export$3c37d9a045754d0e(palette, rng, sizeFraction) {
|
|
@@ -2688,10 +3579,138 @@ const $3faa2521b78398cf$var$ARCHETYPES = [
|
|
|
2688
3579
|
glowMultiplier: 1,
|
|
2689
3580
|
sizePower: 1.8,
|
|
2690
3581
|
invertForeground: false
|
|
3582
|
+
},
|
|
3583
|
+
{
|
|
3584
|
+
name: "shattered-glass",
|
|
3585
|
+
gridSize: 8,
|
|
3586
|
+
layers: 3,
|
|
3587
|
+
baseOpacity: 0.85,
|
|
3588
|
+
opacityReduction: 0.1,
|
|
3589
|
+
minShapeSize: 15,
|
|
3590
|
+
maxShapeSize: 250,
|
|
3591
|
+
backgroundStyle: "solid-dark",
|
|
3592
|
+
paletteMode: "high-contrast",
|
|
3593
|
+
preferredStyles: [
|
|
3594
|
+
"fill-and-stroke",
|
|
3595
|
+
"stroke-only",
|
|
3596
|
+
"fill-only"
|
|
3597
|
+
],
|
|
3598
|
+
flowLineMultiplier: 0,
|
|
3599
|
+
heroShape: false,
|
|
3600
|
+
glowMultiplier: 0.3,
|
|
3601
|
+
sizePower: 1.0,
|
|
3602
|
+
invertForeground: false
|
|
3603
|
+
},
|
|
3604
|
+
{
|
|
3605
|
+
name: "botanical",
|
|
3606
|
+
gridSize: 4,
|
|
3607
|
+
layers: 4,
|
|
3608
|
+
baseOpacity: 0.5,
|
|
3609
|
+
opacityReduction: 0.06,
|
|
3610
|
+
minShapeSize: 30,
|
|
3611
|
+
maxShapeSize: 400,
|
|
3612
|
+
backgroundStyle: "radial-light",
|
|
3613
|
+
paletteMode: "earth",
|
|
3614
|
+
preferredStyles: [
|
|
3615
|
+
"watercolor",
|
|
3616
|
+
"fill-only",
|
|
3617
|
+
"incomplete"
|
|
3618
|
+
],
|
|
3619
|
+
flowLineMultiplier: 3,
|
|
3620
|
+
heroShape: true,
|
|
3621
|
+
glowMultiplier: 0.2,
|
|
3622
|
+
sizePower: 1.6,
|
|
3623
|
+
invertForeground: false
|
|
3624
|
+
},
|
|
3625
|
+
{
|
|
3626
|
+
name: "stipple-portrait",
|
|
3627
|
+
gridSize: 9,
|
|
3628
|
+
layers: 2,
|
|
3629
|
+
baseOpacity: 0.8,
|
|
3630
|
+
opacityReduction: 0.05,
|
|
3631
|
+
minShapeSize: 5,
|
|
3632
|
+
maxShapeSize: 120,
|
|
3633
|
+
backgroundStyle: "solid-light",
|
|
3634
|
+
paletteMode: "monochrome",
|
|
3635
|
+
preferredStyles: [
|
|
3636
|
+
"stipple",
|
|
3637
|
+
"fill-only",
|
|
3638
|
+
"hatched"
|
|
3639
|
+
],
|
|
3640
|
+
flowLineMultiplier: 0,
|
|
3641
|
+
heroShape: false,
|
|
3642
|
+
glowMultiplier: 0,
|
|
3643
|
+
sizePower: 2.8,
|
|
3644
|
+
invertForeground: false
|
|
3645
|
+
},
|
|
3646
|
+
{
|
|
3647
|
+
name: "celestial",
|
|
3648
|
+
gridSize: 7,
|
|
3649
|
+
layers: 5,
|
|
3650
|
+
baseOpacity: 0.45,
|
|
3651
|
+
opacityReduction: 0.04,
|
|
3652
|
+
minShapeSize: 8,
|
|
3653
|
+
maxShapeSize: 450,
|
|
3654
|
+
backgroundStyle: "radial-dark",
|
|
3655
|
+
paletteMode: "neon",
|
|
3656
|
+
preferredStyles: [
|
|
3657
|
+
"fill-only",
|
|
3658
|
+
"watercolor",
|
|
3659
|
+
"stroke-only",
|
|
3660
|
+
"incomplete"
|
|
3661
|
+
],
|
|
3662
|
+
flowLineMultiplier: 2,
|
|
3663
|
+
heroShape: true,
|
|
3664
|
+
glowMultiplier: 2.5,
|
|
3665
|
+
sizePower: 2.2,
|
|
3666
|
+
invertForeground: false
|
|
2691
3667
|
}
|
|
2692
3668
|
];
|
|
3669
|
+
/**
|
|
3670
|
+
* Linearly interpolate between two archetype numeric parameters.
|
|
3671
|
+
*/ function $3faa2521b78398cf$var$lerpNum(a, b, t) {
|
|
3672
|
+
return a + (b - a) * t;
|
|
3673
|
+
}
|
|
3674
|
+
/**
|
|
3675
|
+
* Blend two archetypes by interpolating their numeric parameters
|
|
3676
|
+
* and merging their style arrays.
|
|
3677
|
+
*/ function $3faa2521b78398cf$var$blendArchetypes(a, b, t) {
|
|
3678
|
+
// Merge preferred styles — unique union, primary archetype first
|
|
3679
|
+
const mergedStyles = [
|
|
3680
|
+
...new Set([
|
|
3681
|
+
...a.preferredStyles,
|
|
3682
|
+
...b.preferredStyles
|
|
3683
|
+
])
|
|
3684
|
+
];
|
|
3685
|
+
return {
|
|
3686
|
+
name: `${a.name}+${b.name}`,
|
|
3687
|
+
gridSize: Math.round($3faa2521b78398cf$var$lerpNum(a.gridSize, b.gridSize, t)),
|
|
3688
|
+
layers: Math.round($3faa2521b78398cf$var$lerpNum(a.layers, b.layers, t)),
|
|
3689
|
+
baseOpacity: $3faa2521b78398cf$var$lerpNum(a.baseOpacity, b.baseOpacity, t),
|
|
3690
|
+
opacityReduction: $3faa2521b78398cf$var$lerpNum(a.opacityReduction, b.opacityReduction, t),
|
|
3691
|
+
minShapeSize: Math.round($3faa2521b78398cf$var$lerpNum(a.minShapeSize, b.minShapeSize, t)),
|
|
3692
|
+
maxShapeSize: Math.round($3faa2521b78398cf$var$lerpNum(a.maxShapeSize, b.maxShapeSize, t)),
|
|
3693
|
+
backgroundStyle: t < 0.5 ? a.backgroundStyle : b.backgroundStyle,
|
|
3694
|
+
paletteMode: t < 0.5 ? a.paletteMode : b.paletteMode,
|
|
3695
|
+
preferredStyles: mergedStyles,
|
|
3696
|
+
flowLineMultiplier: $3faa2521b78398cf$var$lerpNum(a.flowLineMultiplier, b.flowLineMultiplier, t),
|
|
3697
|
+
heroShape: t < 0.5 ? a.heroShape : b.heroShape,
|
|
3698
|
+
glowMultiplier: $3faa2521b78398cf$var$lerpNum(a.glowMultiplier, b.glowMultiplier, t),
|
|
3699
|
+
sizePower: $3faa2521b78398cf$var$lerpNum(a.sizePower, b.sizePower, t),
|
|
3700
|
+
invertForeground: t < 0.5 ? a.invertForeground : b.invertForeground
|
|
3701
|
+
};
|
|
3702
|
+
}
|
|
2693
3703
|
function $3faa2521b78398cf$export$f1142fd7da4d6590(rng) {
|
|
2694
|
-
|
|
3704
|
+
const primary = $3faa2521b78398cf$var$ARCHETYPES[Math.floor(rng() * $3faa2521b78398cf$var$ARCHETYPES.length)];
|
|
3705
|
+
// ~15% chance of blending with a second archetype
|
|
3706
|
+
if (rng() < 0.15) {
|
|
3707
|
+
const secondary = $3faa2521b78398cf$var$ARCHETYPES[Math.floor(rng() * $3faa2521b78398cf$var$ARCHETYPES.length)];
|
|
3708
|
+
if (secondary.name !== primary.name) {
|
|
3709
|
+
const blendT = 0.25 + rng() * 0.25; // 25-50% blend toward secondary
|
|
3710
|
+
return $3faa2521b78398cf$var$blendArchetypes(primary, secondary, blendT);
|
|
3711
|
+
}
|
|
3712
|
+
}
|
|
3713
|
+
return primary;
|
|
2695
3714
|
}
|
|
2696
3715
|
|
|
2697
3716
|
|
|
@@ -2867,6 +3886,135 @@ function $b623126c6e9cbb71$var$drawBackground(ctx, style, bgStart, bgEnd, width,
|
|
|
2867
3886
|
}
|
|
2868
3887
|
}
|
|
2869
3888
|
}
|
|
3889
|
+
const $b623126c6e9cbb71$var$CONSTELLATIONS = [
|
|
3890
|
+
{
|
|
3891
|
+
name: "flanked-triangle",
|
|
3892
|
+
build: (rng, baseSize)=>{
|
|
3893
|
+
const gap = baseSize * (0.6 + rng() * 0.3);
|
|
3894
|
+
return [
|
|
3895
|
+
{
|
|
3896
|
+
dx: 0,
|
|
3897
|
+
dy: 0,
|
|
3898
|
+
shape: "triangle",
|
|
3899
|
+
size: baseSize,
|
|
3900
|
+
rotation: rng() * 360
|
|
3901
|
+
},
|
|
3902
|
+
{
|
|
3903
|
+
dx: -gap,
|
|
3904
|
+
dy: gap * 0.3,
|
|
3905
|
+
shape: "circle",
|
|
3906
|
+
size: baseSize * 0.35,
|
|
3907
|
+
rotation: 0
|
|
3908
|
+
},
|
|
3909
|
+
{
|
|
3910
|
+
dx: gap,
|
|
3911
|
+
dy: gap * 0.3,
|
|
3912
|
+
shape: "circle",
|
|
3913
|
+
size: baseSize * 0.35,
|
|
3914
|
+
rotation: 0
|
|
3915
|
+
}
|
|
3916
|
+
];
|
|
3917
|
+
}
|
|
3918
|
+
},
|
|
3919
|
+
{
|
|
3920
|
+
name: "hexagon-ring",
|
|
3921
|
+
build: (rng, baseSize)=>{
|
|
3922
|
+
const members = [];
|
|
3923
|
+
const count = 5 + Math.floor(rng() * 2);
|
|
3924
|
+
const ringR = baseSize * 0.6;
|
|
3925
|
+
for(let i = 0; i < count; i++){
|
|
3926
|
+
const angle = i / count * Math.PI * 2;
|
|
3927
|
+
members.push({
|
|
3928
|
+
dx: Math.cos(angle) * ringR,
|
|
3929
|
+
dy: Math.sin(angle) * ringR,
|
|
3930
|
+
shape: "hexagon",
|
|
3931
|
+
size: baseSize * (0.25 + rng() * 0.1),
|
|
3932
|
+
rotation: angle * 180 / Math.PI
|
|
3933
|
+
});
|
|
3934
|
+
}
|
|
3935
|
+
return members;
|
|
3936
|
+
}
|
|
3937
|
+
},
|
|
3938
|
+
{
|
|
3939
|
+
name: "spiral-dots",
|
|
3940
|
+
build: (rng, baseSize)=>{
|
|
3941
|
+
const members = [];
|
|
3942
|
+
const count = 7 + Math.floor(rng() * 5);
|
|
3943
|
+
const turns = 1.5 + rng();
|
|
3944
|
+
for(let i = 0; i < count; i++){
|
|
3945
|
+
const t = i / count;
|
|
3946
|
+
const angle = t * Math.PI * 2 * turns;
|
|
3947
|
+
const r = t * baseSize * 0.7;
|
|
3948
|
+
members.push({
|
|
3949
|
+
dx: Math.cos(angle) * r,
|
|
3950
|
+
dy: Math.sin(angle) * r,
|
|
3951
|
+
shape: "circle",
|
|
3952
|
+
size: baseSize * (0.08 + (1 - t) * 0.12),
|
|
3953
|
+
rotation: 0
|
|
3954
|
+
});
|
|
3955
|
+
}
|
|
3956
|
+
return members;
|
|
3957
|
+
}
|
|
3958
|
+
},
|
|
3959
|
+
{
|
|
3960
|
+
name: "diamond-cluster",
|
|
3961
|
+
build: (rng, baseSize)=>{
|
|
3962
|
+
const gap = baseSize * 0.45;
|
|
3963
|
+
return [
|
|
3964
|
+
{
|
|
3965
|
+
dx: 0,
|
|
3966
|
+
dy: -gap,
|
|
3967
|
+
shape: "diamond",
|
|
3968
|
+
size: baseSize * 0.4,
|
|
3969
|
+
rotation: 0
|
|
3970
|
+
},
|
|
3971
|
+
{
|
|
3972
|
+
dx: gap,
|
|
3973
|
+
dy: 0,
|
|
3974
|
+
shape: "diamond",
|
|
3975
|
+
size: baseSize * 0.35,
|
|
3976
|
+
rotation: 15
|
|
3977
|
+
},
|
|
3978
|
+
{
|
|
3979
|
+
dx: 0,
|
|
3980
|
+
dy: gap,
|
|
3981
|
+
shape: "diamond",
|
|
3982
|
+
size: baseSize * 0.3,
|
|
3983
|
+
rotation: 30
|
|
3984
|
+
},
|
|
3985
|
+
{
|
|
3986
|
+
dx: -gap,
|
|
3987
|
+
dy: 0,
|
|
3988
|
+
shape: "diamond",
|
|
3989
|
+
size: baseSize * 0.35,
|
|
3990
|
+
rotation: -15
|
|
3991
|
+
}
|
|
3992
|
+
];
|
|
3993
|
+
}
|
|
3994
|
+
},
|
|
3995
|
+
{
|
|
3996
|
+
name: "crescent-pair",
|
|
3997
|
+
build: (rng, baseSize)=>{
|
|
3998
|
+
const gap = baseSize * 0.5;
|
|
3999
|
+
return [
|
|
4000
|
+
{
|
|
4001
|
+
dx: -gap * 0.4,
|
|
4002
|
+
dy: 0,
|
|
4003
|
+
shape: "crescent",
|
|
4004
|
+
size: baseSize * 0.5,
|
|
4005
|
+
rotation: rng() * 30
|
|
4006
|
+
},
|
|
4007
|
+
{
|
|
4008
|
+
dx: gap * 0.4,
|
|
4009
|
+
dy: 0,
|
|
4010
|
+
shape: "crescent",
|
|
4011
|
+
size: baseSize * 0.45,
|
|
4012
|
+
rotation: 180 + rng() * 30
|
|
4013
|
+
}
|
|
4014
|
+
];
|
|
4015
|
+
}
|
|
4016
|
+
}
|
|
4017
|
+
];
|
|
2870
4018
|
function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
2871
4019
|
const finalConfig = {
|
|
2872
4020
|
...(0, $2bfb6a1ccb7a82ae$export$c2f8e0cc249a8d8f),
|
|
@@ -2898,6 +4046,8 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2898
4046
|
const colorGrade = (0, $9d614e7d77fc2947$export$6d1620b367f86f7a)(rng);
|
|
2899
4047
|
// ── 0e. Light direction — consistent shadow angle ──────────────
|
|
2900
4048
|
const lightAngle = rng() * Math.PI * 2;
|
|
4049
|
+
// ── 0f. Palette evolution — hue drift direction across layers ──
|
|
4050
|
+
const paletteHueShift = (rng() - 0.5) * 40; // -20° to +20° total drift
|
|
2901
4051
|
const scaleFactor = Math.min(width, height) / 1024;
|
|
2902
4052
|
const adjustedMinSize = minShapeSize * scaleFactor;
|
|
2903
4053
|
const adjustedMaxSize = maxShapeSize * scaleFactor;
|
|
@@ -2953,6 +4103,65 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2953
4103
|
ctx.stroke();
|
|
2954
4104
|
}
|
|
2955
4105
|
ctx.globalCompositeOperation = "source-over";
|
|
4106
|
+
// ── 1c. Background pattern layer — subtle textured paper ───────
|
|
4107
|
+
const bgPatternRoll = rng();
|
|
4108
|
+
if (bgPatternRoll < 0.6) {
|
|
4109
|
+
ctx.save();
|
|
4110
|
+
ctx.globalCompositeOperation = "soft-light";
|
|
4111
|
+
const patternOpacity = 0.02 + rng() * 0.04;
|
|
4112
|
+
const patternColor = (0, $9d614e7d77fc2947$export$f2121afcad3d553f)(colorHierarchy.dominant, 0.15);
|
|
4113
|
+
if (bgPatternRoll < 0.2) {
|
|
4114
|
+
// Dot grid
|
|
4115
|
+
const dotSpacing = Math.max(8, Math.min(width, height) * (0.015 + rng() * 0.015));
|
|
4116
|
+
const dotR = dotSpacing * 0.08;
|
|
4117
|
+
ctx.globalAlpha = patternOpacity;
|
|
4118
|
+
ctx.fillStyle = patternColor;
|
|
4119
|
+
for(let px = 0; px < width; px += dotSpacing)for(let py = 0; py < height; py += dotSpacing){
|
|
4120
|
+
ctx.beginPath();
|
|
4121
|
+
ctx.arc(px, py, dotR, 0, Math.PI * 2);
|
|
4122
|
+
ctx.fill();
|
|
4123
|
+
}
|
|
4124
|
+
} else if (bgPatternRoll < 0.4) {
|
|
4125
|
+
// Diagonal lines
|
|
4126
|
+
const lineSpacing = Math.max(6, Math.min(width, height) * (0.02 + rng() * 0.02));
|
|
4127
|
+
ctx.globalAlpha = patternOpacity;
|
|
4128
|
+
ctx.strokeStyle = patternColor;
|
|
4129
|
+
ctx.lineWidth = 0.5 * scaleFactor;
|
|
4130
|
+
const diag = Math.hypot(width, height);
|
|
4131
|
+
for(let d = -diag; d < diag; d += lineSpacing){
|
|
4132
|
+
ctx.beginPath();
|
|
4133
|
+
ctx.moveTo(d, 0);
|
|
4134
|
+
ctx.lineTo(d + height, height);
|
|
4135
|
+
ctx.stroke();
|
|
4136
|
+
}
|
|
4137
|
+
} else {
|
|
4138
|
+
// Tessellation — hexagonal grid of tiny shapes
|
|
4139
|
+
const tessSize = Math.max(10, Math.min(width, height) * (0.025 + rng() * 0.02));
|
|
4140
|
+
const tessH = tessSize * Math.sqrt(3);
|
|
4141
|
+
ctx.globalAlpha = patternOpacity * 0.7;
|
|
4142
|
+
ctx.strokeStyle = patternColor;
|
|
4143
|
+
ctx.lineWidth = 0.4 * scaleFactor;
|
|
4144
|
+
for(let row = 0; row * tessH < height + tessH; row++){
|
|
4145
|
+
const offsetX = row % 2 * tessSize * 0.75;
|
|
4146
|
+
for(let col = 0; col * tessSize * 1.5 < width + tessSize * 1.5; col++){
|
|
4147
|
+
const hx = col * tessSize * 1.5 + offsetX;
|
|
4148
|
+
const hy = row * tessH;
|
|
4149
|
+
ctx.beginPath();
|
|
4150
|
+
for(let s = 0; s < 6; s++){
|
|
4151
|
+
const angle = Math.PI / 3 * s - Math.PI / 6;
|
|
4152
|
+
const vx = hx + Math.cos(angle) * tessSize * 0.5;
|
|
4153
|
+
const vy = hy + Math.sin(angle) * tessSize * 0.5;
|
|
4154
|
+
if (s === 0) ctx.moveTo(vx, vy);
|
|
4155
|
+
else ctx.lineTo(vx, vy);
|
|
4156
|
+
}
|
|
4157
|
+
ctx.closePath();
|
|
4158
|
+
ctx.stroke();
|
|
4159
|
+
}
|
|
4160
|
+
}
|
|
4161
|
+
}
|
|
4162
|
+
ctx.restore();
|
|
4163
|
+
}
|
|
4164
|
+
ctx.globalCompositeOperation = "source-over";
|
|
2956
4165
|
// ── 2. Composition mode ────────────────────────────────────────
|
|
2957
4166
|
const compositionMode = $b623126c6e9cbb71$var$COMPOSITION_MODES[Math.floor(rng() * $b623126c6e9cbb71$var$COMPOSITION_MODES.length)];
|
|
2958
4167
|
const symRoll = rng();
|
|
@@ -3013,6 +4222,41 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
3013
4222
|
ry + (nearest.y - ry) * pull
|
|
3014
4223
|
];
|
|
3015
4224
|
}
|
|
4225
|
+
// ── 3b. Void zone decoration — intentional negative space ────
|
|
4226
|
+
for (const zone of voidZones){
|
|
4227
|
+
// Subtle halo ring around void zones
|
|
4228
|
+
ctx.globalAlpha = 0.04 + rng() * 0.04;
|
|
4229
|
+
ctx.strokeStyle = (0, $9d614e7d77fc2947$export$f2121afcad3d553f)(colorHierarchy.accent, 0.2);
|
|
4230
|
+
ctx.lineWidth = 1.5 * scaleFactor;
|
|
4231
|
+
ctx.beginPath();
|
|
4232
|
+
ctx.arc(zone.x, zone.y, zone.radius, 0, Math.PI * 2);
|
|
4233
|
+
ctx.stroke();
|
|
4234
|
+
// ~50% chance: scatter tiny dots inside the void
|
|
4235
|
+
if (rng() < 0.5) {
|
|
4236
|
+
const dotCount = 3 + Math.floor(rng() * 6);
|
|
4237
|
+
ctx.globalAlpha = 0.06 + rng() * 0.04;
|
|
4238
|
+
ctx.fillStyle = (0, $9d614e7d77fc2947$export$f2121afcad3d553f)(colorHierarchy.secondary, 0.15);
|
|
4239
|
+
for(let d = 0; d < dotCount; d++){
|
|
4240
|
+
const angle = rng() * Math.PI * 2;
|
|
4241
|
+
const dist = rng() * zone.radius * 0.7;
|
|
4242
|
+
const dotR = (1 + rng() * 3) * scaleFactor;
|
|
4243
|
+
ctx.beginPath();
|
|
4244
|
+
ctx.arc(zone.x + Math.cos(angle) * dist, zone.y + Math.sin(angle) * dist, dotR, 0, Math.PI * 2);
|
|
4245
|
+
ctx.fill();
|
|
4246
|
+
}
|
|
4247
|
+
}
|
|
4248
|
+
// ~30% chance: thin concentric ring inside
|
|
4249
|
+
if (rng() < 0.3) {
|
|
4250
|
+
ctx.globalAlpha = 0.03 + rng() * 0.03;
|
|
4251
|
+
ctx.strokeStyle = (0, $9d614e7d77fc2947$export$f2121afcad3d553f)(colorHierarchy.dominant, 0.1);
|
|
4252
|
+
ctx.lineWidth = 0.5 * scaleFactor;
|
|
4253
|
+
const innerR = zone.radius * (0.4 + rng() * 0.3);
|
|
4254
|
+
ctx.beginPath();
|
|
4255
|
+
ctx.arc(zone.x, zone.y, innerR, 0, Math.PI * 2);
|
|
4256
|
+
ctx.stroke();
|
|
4257
|
+
}
|
|
4258
|
+
}
|
|
4259
|
+
ctx.globalAlpha = 1;
|
|
3016
4260
|
// ── 4. Flow field seed values ──────────────────────────────────
|
|
3017
4261
|
const fieldAngleBase = rng() * Math.PI * 2;
|
|
3018
4262
|
const fieldFreq = 0.5 + rng() * 2;
|
|
@@ -3080,6 +4324,23 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
3080
4324
|
const layerRenderStyle = rng() < 0.6 ? archetype.preferredStyles[Math.floor(rng() * archetype.preferredStyles.length)] : (0, $9beb8f41637c29fd$export$9fd4e64b2acd410e)(rng);
|
|
3081
4325
|
// Atmospheric desaturation for later layers
|
|
3082
4326
|
const atmosphericDesat = layerRatio * 0.3;
|
|
4327
|
+
// Depth-of-field simulation — later layers are "further away"
|
|
4328
|
+
// Reduce stroke widths and shift colors toward the background
|
|
4329
|
+
const dofFactor = 1 - layerRatio * 0.5; // 1.0 for front layer, 0.5 for back
|
|
4330
|
+
const dofStrokeScale = 0.4 + dofFactor * 0.6; // strokes thin out with depth
|
|
4331
|
+
const dofContrastReduction = layerRatio * 0.2; // colors fade toward bg
|
|
4332
|
+
// Color palette evolution — hue-rotate the hierarchy per layer
|
|
4333
|
+
const layerHierarchy = (0, $9d614e7d77fc2947$export$703ba40a4347f77a)(colorHierarchy, layerRatio, paletteHueShift);
|
|
4334
|
+
// Focal depth: shapes near focal points get more detail
|
|
4335
|
+
const focalDetailBoost = (px, py)=>{
|
|
4336
|
+
let minFocalDist = Infinity;
|
|
4337
|
+
for (const fp of focalPoints){
|
|
4338
|
+
const d = Math.hypot(px - fp.x, py - fp.y);
|
|
4339
|
+
if (d < minFocalDist) minFocalDist = d;
|
|
4340
|
+
}
|
|
4341
|
+
const maxDist = Math.hypot(width, height) * 0.5;
|
|
4342
|
+
return Math.max(0, 1 - minFocalDist / maxDist); // 1.0 at focal, 0.0 at edges
|
|
4343
|
+
};
|
|
3083
4344
|
for(let i = 0; i < numShapes; i++){
|
|
3084
4345
|
// Position from composition mode, then focal bias
|
|
3085
4346
|
const rawPos = $b623126c6e9cbb71$var$getCompositionPosition(compositionMode, rng, width, height, i, numShapes, cx, cy);
|
|
@@ -3110,9 +4371,9 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
3110
4371
|
rotation = rotation + (angleToHero - rotation) * blendFactor * 0.4;
|
|
3111
4372
|
}
|
|
3112
4373
|
}
|
|
3113
|
-
// Positional color from hierarchy + jitter
|
|
3114
|
-
let fillBase = $b623126c6e9cbb71$var$getPositionalColor(x, y, width, height,
|
|
3115
|
-
const strokeBase = (0, $9d614e7d77fc2947$export$b49f62f0a99da0e8)(
|
|
4374
|
+
// Positional color from hierarchy + jitter (using evolved layer palette)
|
|
4375
|
+
let fillBase = $b623126c6e9cbb71$var$getPositionalColor(x, y, width, height, layerHierarchy, rng);
|
|
4376
|
+
const strokeBase = (0, $9d614e7d77fc2947$export$b49f62f0a99da0e8)(layerHierarchy, rng);
|
|
3116
4377
|
// Desaturate colors on later layers for depth
|
|
3117
4378
|
if (atmosphericDesat > 0) fillBase = (0, $9d614e7d77fc2947$export$fb75607d98509d9)(fillBase, atmosphericDesat);
|
|
3118
4379
|
// Temperature contrast: shift foreground shapes opposite to background
|
|
@@ -3122,8 +4383,10 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
3122
4383
|
// Semi-transparent fill
|
|
3123
4384
|
const fillAlpha = 0.2 + rng() * 0.5;
|
|
3124
4385
|
const transparentFill = (0, $9d614e7d77fc2947$export$f2121afcad3d553f)(fillColor, fillAlpha);
|
|
3125
|
-
const strokeWidth = (0.5 + rng() * 2.0) * scaleFactor;
|
|
3126
|
-
|
|
4386
|
+
const strokeWidth = (0.5 + rng() * 2.0) * scaleFactor * dofStrokeScale;
|
|
4387
|
+
// Depth-of-field: reduce opacity slightly for distant layers
|
|
4388
|
+
const dofOpacityScale = 1 - dofContrastReduction;
|
|
4389
|
+
ctx.globalAlpha = layerOpacity * (0.5 + rng() * 0.5) * dofOpacityScale;
|
|
3127
4390
|
// Glow on sacred shapes more often — scaled by archetype
|
|
3128
4391
|
const isSacred = $b623126c6e9cbb71$var$SACRED_SHAPES.includes(shape);
|
|
3129
4392
|
const baseGlowChance = isSacred ? 0.45 : 0.2;
|
|
@@ -3142,7 +4405,48 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
3142
4405
|
const shadowDist = hasGlow ? 0 : size * 0.02;
|
|
3143
4406
|
const shadowOffX = shadowDist * Math.cos(lightAngle);
|
|
3144
4407
|
const shadowOffY = shadowDist * Math.sin(lightAngle);
|
|
3145
|
-
|
|
4408
|
+
// ── 5a. Tangent placement — nudge toward nearest shape edge ──
|
|
4409
|
+
let finalX = x;
|
|
4410
|
+
let finalY = y;
|
|
4411
|
+
if (shapePositions.length > 0 && rng() < 0.25) {
|
|
4412
|
+
// Find nearest placed shape
|
|
4413
|
+
let nearestDist = Infinity;
|
|
4414
|
+
let nearestPos = null;
|
|
4415
|
+
for (const sp of shapePositions){
|
|
4416
|
+
const d = Math.hypot(x - sp.x, y - sp.y);
|
|
4417
|
+
if (d < nearestDist && d > 0) {
|
|
4418
|
+
nearestDist = d;
|
|
4419
|
+
nearestPos = sp;
|
|
4420
|
+
}
|
|
4421
|
+
}
|
|
4422
|
+
if (nearestPos) {
|
|
4423
|
+
// Target distance: edges kissing (sum of half-sizes)
|
|
4424
|
+
const targetDist = (size + nearestPos.size) * 0.5;
|
|
4425
|
+
if (nearestDist > targetDist * 0.5 && nearestDist < targetDist * 3) {
|
|
4426
|
+
const angle = Math.atan2(y - nearestPos.y, x - nearestPos.x);
|
|
4427
|
+
finalX = nearestPos.x + Math.cos(angle) * targetDist;
|
|
4428
|
+
finalY = nearestPos.y + Math.sin(angle) * targetDist;
|
|
4429
|
+
// Keep in bounds
|
|
4430
|
+
finalX = Math.max(0, Math.min(width, finalX));
|
|
4431
|
+
finalY = Math.max(0, Math.min(height, finalY));
|
|
4432
|
+
}
|
|
4433
|
+
}
|
|
4434
|
+
}
|
|
4435
|
+
// ── 5b. Shape mirroring — basic shapes get reflected copies ──
|
|
4436
|
+
const mirrorAxis = (0, $9beb8f41637c29fd$export$879206e23912d1a9)(rng);
|
|
4437
|
+
const isBasicShape = [
|
|
4438
|
+
"circle",
|
|
4439
|
+
"triangle",
|
|
4440
|
+
"square",
|
|
4441
|
+
"hexagon",
|
|
4442
|
+
"star",
|
|
4443
|
+
"diamond",
|
|
4444
|
+
"crescent",
|
|
4445
|
+
"penroseTile",
|
|
4446
|
+
"reuleauxTriangle"
|
|
4447
|
+
].includes(shape);
|
|
4448
|
+
const shouldMirror = mirrorAxis !== null && isBasicShape && size > adjustedMaxSize * 0.2;
|
|
4449
|
+
const shapeConfig = {
|
|
3146
4450
|
fillColor: transparentFill,
|
|
3147
4451
|
strokeColor: strokeColor,
|
|
3148
4452
|
strokeWidth: strokeWidth,
|
|
@@ -3154,22 +4458,47 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
3154
4458
|
gradientFillEnd: gradientEnd,
|
|
3155
4459
|
renderStyle: finalRenderStyle,
|
|
3156
4460
|
rng: rng
|
|
4461
|
+
};
|
|
4462
|
+
if (shouldMirror) (0, $9beb8f41637c29fd$export$8bd8bbd1a8e53689)(ctx, shape, finalX, finalY, {
|
|
4463
|
+
...shapeConfig,
|
|
4464
|
+
mirrorAxis: mirrorAxis,
|
|
4465
|
+
mirrorGap: size * (0.1 + rng() * 0.3)
|
|
3157
4466
|
});
|
|
4467
|
+
else (0, $9beb8f41637c29fd$export$bb35a6995ddbf32d)(ctx, shape, finalX, finalY, shapeConfig);
|
|
4468
|
+
// ── Glazing — luminous multi-pass transparency on ~20% of shapes ──
|
|
4469
|
+
if (rng() < 0.2 && size > adjustedMinSize * 2) {
|
|
4470
|
+
const glazePasses = 2 + Math.floor(rng() * 2);
|
|
4471
|
+
for(let g = 0; g < glazePasses; g++){
|
|
4472
|
+
const glazeScale = 1 - (g + 1) * 0.12; // progressively smaller
|
|
4473
|
+
const glazeAlpha = 0.08 + g * 0.04; // progressively more opaque toward center
|
|
4474
|
+
ctx.globalAlpha = glazeAlpha;
|
|
4475
|
+
(0, $9beb8f41637c29fd$export$bb35a6995ddbf32d)(ctx, shape, finalX, finalY, {
|
|
4476
|
+
fillColor: (0, $9d614e7d77fc2947$export$f2121afcad3d553f)(fillColor, 0.15 + g * 0.1),
|
|
4477
|
+
strokeColor: "rgba(0,0,0,0)",
|
|
4478
|
+
strokeWidth: 0,
|
|
4479
|
+
size: size * glazeScale,
|
|
4480
|
+
rotation: rotation,
|
|
4481
|
+
proportionType: "GOLDEN_RATIO",
|
|
4482
|
+
renderStyle: "fill-only",
|
|
4483
|
+
rng: rng
|
|
4484
|
+
});
|
|
4485
|
+
}
|
|
4486
|
+
}
|
|
3158
4487
|
shapePositions.push({
|
|
3159
|
-
x:
|
|
3160
|
-
y:
|
|
4488
|
+
x: finalX,
|
|
4489
|
+
y: finalY,
|
|
3161
4490
|
size: size,
|
|
3162
4491
|
shape: shape
|
|
3163
4492
|
});
|
|
3164
|
-
// ──
|
|
4493
|
+
// ── 5c. Size echo — large shapes spawn trailing smaller copies ──
|
|
3165
4494
|
if (size > adjustedMaxSize * 0.5 && rng() < 0.2) {
|
|
3166
4495
|
const echoCount = 2 + Math.floor(rng() * 2);
|
|
3167
4496
|
const echoAngle = rng() * Math.PI * 2;
|
|
3168
4497
|
for(let e = 0; e < echoCount; e++){
|
|
3169
4498
|
const echoScale = 0.3 - e * 0.08;
|
|
3170
4499
|
const echoDist = size * (0.6 + e * 0.4);
|
|
3171
|
-
const echoX =
|
|
3172
|
-
const echoY =
|
|
4500
|
+
const echoX = finalX + Math.cos(echoAngle) * echoDist;
|
|
4501
|
+
const echoY = finalY + Math.sin(echoAngle) * echoDist;
|
|
3173
4502
|
const echoSize = size * Math.max(0.1, echoScale);
|
|
3174
4503
|
if (echoX < 0 || echoX > width || echoY < 0 || echoY > height) continue;
|
|
3175
4504
|
ctx.globalAlpha = layerOpacity * (0.4 - e * 0.1);
|
|
@@ -3191,8 +4520,11 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
3191
4520
|
});
|
|
3192
4521
|
}
|
|
3193
4522
|
}
|
|
3194
|
-
// ──
|
|
3195
|
-
|
|
4523
|
+
// ── 5d. Recursive nesting ──────────────────────────────────
|
|
4524
|
+
// Focal depth: shapes near focal points get more detail
|
|
4525
|
+
const focalProximity = focalDetailBoost(finalX, finalY);
|
|
4526
|
+
const nestingChance = 0.15 + focalProximity * 0.15; // 15-30% near focal
|
|
4527
|
+
if (size > adjustedMaxSize * 0.4 && rng() < nestingChance) {
|
|
3196
4528
|
const innerCount = 1 + Math.floor(rng() * 3);
|
|
3197
4529
|
for(let n = 0; n < innerCount; n++){
|
|
3198
4530
|
// Pick inner shape from palette affinities
|
|
@@ -3204,7 +4536,7 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
3204
4536
|
const innerRot = rng() * 360;
|
|
3205
4537
|
const innerFill = (0, $9d614e7d77fc2947$export$f2121afcad3d553f)((0, $9d614e7d77fc2947$export$18a34c25ea7e724b)((0, $9d614e7d77fc2947$export$b49f62f0a99da0e8)(colorHierarchy, rng), rng, 10, 0.1), 0.3 + rng() * 0.4);
|
|
3206
4538
|
ctx.globalAlpha = layerOpacity * 0.7;
|
|
3207
|
-
(0, $9beb8f41637c29fd$export$bb35a6995ddbf32d)(ctx, innerShape,
|
|
4539
|
+
(0, $9beb8f41637c29fd$export$bb35a6995ddbf32d)(ctx, innerShape, finalX + innerOffX, finalY + innerOffY, {
|
|
3208
4540
|
fillColor: innerFill,
|
|
3209
4541
|
strokeColor: (0, $9d614e7d77fc2947$export$f2121afcad3d553f)(strokeColor, 0.5),
|
|
3210
4542
|
strokeWidth: strokeWidth * 0.6,
|
|
@@ -3216,6 +4548,42 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
3216
4548
|
});
|
|
3217
4549
|
}
|
|
3218
4550
|
}
|
|
4551
|
+
// ── 5e. Shape constellations — pre-composed groups ─────────
|
|
4552
|
+
const constellationChance = 0.12 + focalProximity * 0.1; // 12-22% near focal
|
|
4553
|
+
if (size > adjustedMaxSize * 0.35 && rng() < constellationChance) {
|
|
4554
|
+
const constellation = $b623126c6e9cbb71$var$CONSTELLATIONS[Math.floor(rng() * $b623126c6e9cbb71$var$CONSTELLATIONS.length)];
|
|
4555
|
+
const members = constellation.build(rng, size);
|
|
4556
|
+
const groupRotation = rng() * Math.PI * 2;
|
|
4557
|
+
const cosR = Math.cos(groupRotation);
|
|
4558
|
+
const sinR = Math.sin(groupRotation);
|
|
4559
|
+
for (const member of members){
|
|
4560
|
+
// Rotate the group offset by the group rotation
|
|
4561
|
+
const mx = finalX + member.dx * cosR - member.dy * sinR;
|
|
4562
|
+
const my = finalY + member.dx * sinR + member.dy * cosR;
|
|
4563
|
+
if (mx < 0 || mx > width || my < 0 || my > height) continue;
|
|
4564
|
+
const memberFill = (0, $9d614e7d77fc2947$export$f2121afcad3d553f)((0, $9d614e7d77fc2947$export$18a34c25ea7e724b)((0, $9d614e7d77fc2947$export$b49f62f0a99da0e8)(colorHierarchy, rng), rng, 8, 0.06), fillAlpha * 0.8);
|
|
4565
|
+
const memberStroke = (0, $9d614e7d77fc2947$export$90ad0e6170cf6af5)((0, $9d614e7d77fc2947$export$18a34c25ea7e724b)(strokeBase, rng, 5, 0.04), bgLum);
|
|
4566
|
+
ctx.globalAlpha = layerOpacity * 0.6;
|
|
4567
|
+
// Use the member's shape if available, otherwise fall back to palette
|
|
4568
|
+
const memberShape = shapeNames.includes(member.shape) ? member.shape : (0, $24064302523652b1$export$3c37d9a045754d0e)(shapePalette, rng, member.size / adjustedMaxSize);
|
|
4569
|
+
(0, $9beb8f41637c29fd$export$bb35a6995ddbf32d)(ctx, memberShape, mx, my, {
|
|
4570
|
+
fillColor: memberFill,
|
|
4571
|
+
strokeColor: memberStroke,
|
|
4572
|
+
strokeWidth: strokeWidth * 0.7,
|
|
4573
|
+
size: member.size,
|
|
4574
|
+
rotation: member.rotation + groupRotation * 180 / Math.PI,
|
|
4575
|
+
proportionType: "GOLDEN_RATIO",
|
|
4576
|
+
renderStyle: (0, $24064302523652b1$export$ab873bb6fb56c1a8)(memberShape, layerRenderStyle, rng),
|
|
4577
|
+
rng: rng
|
|
4578
|
+
});
|
|
4579
|
+
shapePositions.push({
|
|
4580
|
+
x: mx,
|
|
4581
|
+
y: my,
|
|
4582
|
+
size: member.size,
|
|
4583
|
+
shape: memberShape
|
|
4584
|
+
});
|
|
4585
|
+
}
|
|
4586
|
+
}
|
|
3219
4587
|
}
|
|
3220
4588
|
}
|
|
3221
4589
|
// Reset blend mode for post-processing passes
|
|
@@ -3286,7 +4654,41 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
3286
4654
|
prevY = fy;
|
|
3287
4655
|
}
|
|
3288
4656
|
}
|
|
3289
|
-
// ── 6b.
|
|
4657
|
+
// ── 6b. Motion/energy lines — short directional bursts ─────────
|
|
4658
|
+
const energyArchetypes = [
|
|
4659
|
+
"dense-chaotic",
|
|
4660
|
+
"cosmic",
|
|
4661
|
+
"neon-glow",
|
|
4662
|
+
"bold-graphic"
|
|
4663
|
+
];
|
|
4664
|
+
const hasEnergyLines = energyArchetypes.some((a)=>archetype.name.includes(a)) || rng() < 0.25;
|
|
4665
|
+
if (hasEnergyLines && shapePositions.length > 0) {
|
|
4666
|
+
const energyCount = 5 + Math.floor(rng() * 10);
|
|
4667
|
+
ctx.lineCap = "round";
|
|
4668
|
+
for(let e = 0; e < energyCount; e++){
|
|
4669
|
+
// Pick a random shape to radiate from
|
|
4670
|
+
const source = shapePositions[Math.floor(rng() * shapePositions.length)];
|
|
4671
|
+
const burstCount = 2 + Math.floor(rng() * 4);
|
|
4672
|
+
const baseAngle = flowAngle(source.x, source.y);
|
|
4673
|
+
for(let b = 0; b < burstCount; b++){
|
|
4674
|
+
const angle = baseAngle + (rng() - 0.5) * 1.2;
|
|
4675
|
+
const lineLen = (source.size * 0.3 + rng() * source.size * 0.5) * scaleFactor * 0.3;
|
|
4676
|
+
const startDist = source.size * 0.5;
|
|
4677
|
+
const sx = source.x + Math.cos(angle) * startDist;
|
|
4678
|
+
const sy = source.y + Math.sin(angle) * startDist;
|
|
4679
|
+
const ex = sx + Math.cos(angle) * lineLen;
|
|
4680
|
+
const ey = sy + Math.sin(angle) * lineLen;
|
|
4681
|
+
ctx.globalAlpha = 0.04 + rng() * 0.06;
|
|
4682
|
+
ctx.strokeStyle = (0, $9d614e7d77fc2947$export$f2121afcad3d553f)((0, $9d614e7d77fc2947$export$90ad0e6170cf6af5)((0, $9d614e7d77fc2947$export$b49f62f0a99da0e8)(colorHierarchy, rng), bgLum), 0.3);
|
|
4683
|
+
ctx.lineWidth = (0.5 + rng() * 1.5) * scaleFactor;
|
|
4684
|
+
ctx.beginPath();
|
|
4685
|
+
ctx.moveTo(sx, sy);
|
|
4686
|
+
ctx.lineTo(ex, ey);
|
|
4687
|
+
ctx.stroke();
|
|
4688
|
+
}
|
|
4689
|
+
}
|
|
4690
|
+
}
|
|
4691
|
+
// ── 6c. Apply symmetry mirroring ─────────────────────────────────
|
|
3290
4692
|
if (symmetryMode !== "none") {
|
|
3291
4693
|
const canvas = ctx.canvas;
|
|
3292
4694
|
ctx.save();
|
|
@@ -3397,6 +4799,44 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
3397
4799
|
ctx.restore();
|
|
3398
4800
|
ctx.globalCompositeOperation = "source-over";
|
|
3399
4801
|
}
|
|
4802
|
+
// ── 11. Signature mark — unique geometric chop from hash prefix ──
|
|
4803
|
+
{
|
|
4804
|
+
const sigRng = (0, $461134e0b6ce0619$export$eaf9227667332084)((0, $461134e0b6ce0619$export$e9cc707de01b7042)(gitHash, 42));
|
|
4805
|
+
const sigSize = Math.min(width, height) * 0.025;
|
|
4806
|
+
// Bottom-right corner with padding
|
|
4807
|
+
const sigX = width - sigSize * 2.5;
|
|
4808
|
+
const sigY = height - sigSize * 2.5;
|
|
4809
|
+
const sigSegments = 4 + Math.floor(sigRng() * 4); // 4-7 segments
|
|
4810
|
+
const sigColor = (0, $9d614e7d77fc2947$export$f2121afcad3d553f)(colorHierarchy.accent, 0.15);
|
|
4811
|
+
ctx.save();
|
|
4812
|
+
ctx.globalAlpha = 0.12 + sigRng() * 0.08;
|
|
4813
|
+
ctx.translate(sigX, sigY);
|
|
4814
|
+
ctx.strokeStyle = sigColor;
|
|
4815
|
+
ctx.fillStyle = (0, $9d614e7d77fc2947$export$f2121afcad3d553f)(colorHierarchy.dominant, 0.06);
|
|
4816
|
+
ctx.lineWidth = Math.max(0.5, 0.8 * scaleFactor);
|
|
4817
|
+
// Outer ring
|
|
4818
|
+
ctx.beginPath();
|
|
4819
|
+
ctx.arc(0, 0, sigSize, 0, Math.PI * 2);
|
|
4820
|
+
ctx.stroke();
|
|
4821
|
+
ctx.fill();
|
|
4822
|
+
// Inner geometric pattern — unique per hash
|
|
4823
|
+
ctx.beginPath();
|
|
4824
|
+
for(let s = 0; s < sigSegments; s++){
|
|
4825
|
+
const angle1 = sigRng() * Math.PI * 2;
|
|
4826
|
+
const angle2 = sigRng() * Math.PI * 2;
|
|
4827
|
+
const r1 = sigSize * (0.2 + sigRng() * 0.6);
|
|
4828
|
+
const r2 = sigSize * (0.2 + sigRng() * 0.6);
|
|
4829
|
+
ctx.moveTo(Math.cos(angle1) * r1, Math.sin(angle1) * r1);
|
|
4830
|
+
ctx.lineTo(Math.cos(angle2) * r2, Math.sin(angle2) * r2);
|
|
4831
|
+
}
|
|
4832
|
+
ctx.stroke();
|
|
4833
|
+
// Center dot
|
|
4834
|
+
ctx.beginPath();
|
|
4835
|
+
ctx.arc(0, 0, sigSize * 0.12, 0, Math.PI * 2);
|
|
4836
|
+
ctx.fillStyle = sigColor;
|
|
4837
|
+
ctx.fill();
|
|
4838
|
+
ctx.restore();
|
|
4839
|
+
}
|
|
3400
4840
|
ctx.globalAlpha = 1;
|
|
3401
4841
|
}
|
|
3402
4842
|
|