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