git-hash-art 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/module.js CHANGED
@@ -1317,35 +1317,32 @@ const $d63629e16208c310$export$c2fc138f94dd4b2a = {
1317
1317
  */ const $a7241acce45d5513$export$580f80cfb9de73bc = (ctx, size, config)=>{
1318
1318
  const rng = config?.rng ?? Math.random;
1319
1319
  const r = size / 2;
1320
- const numPoints = 5 + Math.floor(rng() * 5); // 5-9 lobes
1320
+ const numPoints = 5 + Math.floor(rng() * 5);
1321
1321
  const points = [];
1322
1322
  for(let i = 0; i < numPoints; i++){
1323
1323
  const angle = i / numPoints * Math.PI * 2;
1324
- const jitter = 0.5 + rng() * 0.5; // radius varies 50-100%
1324
+ const jitter = 0.5 + rng() * 0.5;
1325
1325
  points.push({
1326
1326
  x: Math.cos(angle) * r * jitter,
1327
1327
  y: Math.sin(angle) * r * jitter
1328
1328
  });
1329
1329
  }
1330
1330
  ctx.beginPath();
1331
- // Start at midpoint between last and first point
1332
1331
  const last = points[points.length - 1];
1333
1332
  const first = points[0];
1334
1333
  ctx.moveTo((last.x + first.x) / 2, (last.y + first.y) / 2);
1335
1334
  for(let i = 0; i < numPoints; i++){
1336
1335
  const curr = points[i];
1337
1336
  const next = points[(i + 1) % numPoints];
1338
- const midX = (curr.x + next.x) / 2;
1339
- const midY = (curr.y + next.y) / 2;
1340
- ctx.quadraticCurveTo(curr.x, curr.y, midX, midY);
1337
+ ctx.quadraticCurveTo(curr.x, curr.y, (curr.x + next.x) / 2, (curr.y + next.y) / 2);
1341
1338
  }
1342
1339
  ctx.closePath();
1343
1340
  };
1344
1341
  const $a7241acce45d5513$export$7a6094023f0902a6 = (ctx, size, config)=>{
1345
1342
  const rng = config?.rng ?? Math.random;
1346
1343
  const r = size / 2;
1347
- const sides = 3 + Math.floor(rng() * 10); // 3-12 sides
1348
- const jitterAmount = 0.1 + rng() * 0.4; // 10-50% vertex displacement
1344
+ const sides = 3 + Math.floor(rng() * 10);
1345
+ const jitterAmount = 0.1 + rng() * 0.4;
1349
1346
  ctx.beginPath();
1350
1347
  for(let i = 0; i < sides; i++){
1351
1348
  const angle = i / sides * Math.PI * 2 - Math.PI / 2;
@@ -1360,10 +1357,9 @@ const $a7241acce45d5513$export$7a6094023f0902a6 = (ctx, size, config)=>{
1360
1357
  const $a7241acce45d5513$export$ef56b4a8316e47d5 = (ctx, size, config)=>{
1361
1358
  const rng = config?.rng ?? Math.random;
1362
1359
  const r = size / 2;
1363
- // Frequency ratios small integers produce recognizable patterns
1364
- const freqA = 1 + Math.floor(rng() * 5); // 1-5
1365
- const freqB = 1 + Math.floor(rng() * 5); // 1-5
1366
- const phase = rng() * Math.PI; // phase offset
1360
+ const freqA = 1 + Math.floor(rng() * 5);
1361
+ const freqB = 1 + Math.floor(rng() * 5);
1362
+ const phase = rng() * Math.PI;
1367
1363
  const steps = 120;
1368
1364
  ctx.beginPath();
1369
1365
  for(let i = 0; i <= steps; i++){
@@ -1378,7 +1374,6 @@ const $a7241acce45d5513$export$ef56b4a8316e47d5 = (ctx, size, config)=>{
1378
1374
  const $a7241acce45d5513$export$1db9219b4f34658c = (ctx, size, config)=>{
1379
1375
  const rng = config?.rng ?? Math.random;
1380
1376
  const r = size / 2;
1381
- // Exponent range: 0.3 (spiky astroid) to 5 (rounded rectangle)
1382
1377
  const n = 0.3 + rng() * 4.7;
1383
1378
  const steps = 120;
1384
1379
  ctx.beginPath();
@@ -1386,7 +1381,6 @@ const $a7241acce45d5513$export$1db9219b4f34658c = (ctx, size, config)=>{
1386
1381
  const t = i / steps * Math.PI * 2;
1387
1382
  const cosT = Math.cos(t);
1388
1383
  const sinT = Math.sin(t);
1389
- // Superellipse parametric form
1390
1384
  const x = Math.sign(cosT) * Math.pow(Math.abs(cosT), 2 / n) * r;
1391
1385
  const y = Math.sign(sinT) * Math.pow(Math.abs(sinT), 2 / n) * r;
1392
1386
  if (i === 0) ctx.moveTo(x, y);
@@ -1397,11 +1391,9 @@ const $a7241acce45d5513$export$1db9219b4f34658c = (ctx, size, config)=>{
1397
1391
  const $a7241acce45d5513$export$b027c64d22b01985 = (ctx, size, config)=>{
1398
1392
  const rng = config?.rng ?? Math.random;
1399
1393
  const scale = size / 2;
1400
- // R = outer radius, r = inner radius, d = pen distance from inner center
1401
1394
  const R = 1;
1402
- const r = 0.2 + rng() * 0.6; // 0.2-0.8
1403
- const d = 0.3 + rng() * 0.7; // 0.3-1.0
1404
- // Number of full rotations needed to close the curve
1395
+ const r = 0.2 + rng() * 0.6;
1396
+ const d = 0.3 + rng() * 0.7;
1405
1397
  const gcd = (a, b)=>{
1406
1398
  const ai = Math.round(a * 1000);
1407
1399
  const bi = Math.round(b * 1000);
@@ -1409,7 +1401,7 @@ const $a7241acce45d5513$export$b027c64d22b01985 = (ctx, size, config)=>{
1409
1401
  return g(ai, bi) / 1000;
1410
1402
  };
1411
1403
  const period = r / gcd(R, r);
1412
- const maxT = Math.min(period, 10) * Math.PI * 2; // cap at 10 rotations
1404
+ const maxT = Math.min(period, 10) * Math.PI * 2;
1413
1405
  const steps = Math.min(600, Math.floor(maxT * 20));
1414
1406
  ctx.beginPath();
1415
1407
  for(let i = 0; i <= steps; i++){
@@ -1424,9 +1416,9 @@ const $a7241acce45d5513$export$b027c64d22b01985 = (ctx, size, config)=>{
1424
1416
  const $a7241acce45d5513$export$7608ccd03bfb705d = (ctx, size, config)=>{
1425
1417
  const rng = config?.rng ?? Math.random;
1426
1418
  const r = size / 2;
1427
- const rings = 2 + Math.floor(rng() * 4); // 2-5 rings
1428
- const freq = 3 + Math.floor(rng() * 12); // 3-14 waves per ring
1429
- const amp = 0.05 + rng() * 0.15; // 5-20% of radius
1419
+ const rings = 2 + Math.floor(rng() * 4);
1420
+ const freq = 3 + Math.floor(rng() * 12);
1421
+ const amp = 0.05 + rng() * 0.15;
1430
1422
  ctx.beginPath();
1431
1423
  for(let ring = 0; ring < rings; ring++){
1432
1424
  const baseR = r * (0.3 + ring / rings * 0.7);
@@ -1444,7 +1436,7 @@ const $a7241acce45d5513$export$7608ccd03bfb705d = (ctx, size, config)=>{
1444
1436
  const $a7241acce45d5513$export$11a377e7498bb523 = (ctx, size, config)=>{
1445
1437
  const rng = config?.rng ?? Math.random;
1446
1438
  const r = size / 2;
1447
- const k = 2 + Math.floor(rng() * 6); // 2-7 petal parameter
1439
+ const k = 2 + Math.floor(rng() * 6);
1448
1440
  const steps = 200;
1449
1441
  ctx.beginPath();
1450
1442
  for(let i = 0; i <= steps; i++){
@@ -1457,6 +1449,308 @@ const $a7241acce45d5513$export$11a377e7498bb523 = (ctx, size, config)=>{
1457
1449
  }
1458
1450
  ctx.closePath();
1459
1451
  };
1452
+ const $a7241acce45d5513$export$76b6526575ea179b = (ctx, size, config)=>{
1453
+ const rng = config?.rng ?? Math.random;
1454
+ const r = size / 2;
1455
+ const shardCount = 4 + Math.floor(rng() * 5); // 4-8 shards
1456
+ ctx.beginPath();
1457
+ for(let s = 0; s < shardCount; s++){
1458
+ const baseAngle = s / shardCount * Math.PI * 2 + (rng() - 0.5) * 0.3;
1459
+ const dist = r * (0.15 + rng() * 0.35);
1460
+ const cx = Math.cos(baseAngle) * dist;
1461
+ const cy = Math.sin(baseAngle) * dist;
1462
+ const shardSize = r * (0.2 + rng() * 0.4);
1463
+ const verts = 3 + Math.floor(rng() * 3); // 3-5 vertices per shard
1464
+ const shardAngleOffset = rng() * Math.PI * 2;
1465
+ for(let v = 0; v < verts; v++){
1466
+ const angle = shardAngleOffset + v / verts * Math.PI * 2;
1467
+ // Elongate shards along their radial direction
1468
+ const stretch = v % 2 === 0 ? 1.0 : 0.3 + rng() * 0.4;
1469
+ const px = cx + Math.cos(angle) * shardSize * stretch;
1470
+ const py = cy + Math.sin(angle) * shardSize * stretch;
1471
+ if (v === 0) ctx.moveTo(px, py);
1472
+ else ctx.lineTo(px, py);
1473
+ }
1474
+ ctx.closePath();
1475
+ }
1476
+ };
1477
+ const $a7241acce45d5513$export$ed9ff98da5b05073 = (ctx, size, config)=>{
1478
+ const rng = config?.rng ?? Math.random;
1479
+ const r = size / 2;
1480
+ const edgeCount = 5 + Math.floor(rng() * 4); // 5-8 edges
1481
+ const points = [];
1482
+ // Generate edge midpoints at varying distances
1483
+ for(let i = 0; i < edgeCount; i++){
1484
+ const angle = i / edgeCount * Math.PI * 2 + (rng() - 0.5) * 0.4;
1485
+ const dist = r * (0.6 + rng() * 0.4);
1486
+ points.push({
1487
+ angle: angle,
1488
+ x: Math.cos(angle) * dist,
1489
+ y: Math.sin(angle) * dist
1490
+ });
1491
+ }
1492
+ // Sort by angle for proper winding
1493
+ points.sort((a, b)=>a.angle - b.angle);
1494
+ ctx.beginPath();
1495
+ ctx.moveTo(points[0].x, points[0].y);
1496
+ for(let i = 1; i < points.length; i++)ctx.lineTo(points[i].x, points[i].y);
1497
+ ctx.closePath();
1498
+ };
1499
+ const $a7241acce45d5513$export$e0452d9a794fe7e5 = (ctx, size, config)=>{
1500
+ const rng = config?.rng ?? Math.random;
1501
+ const r = size / 2;
1502
+ const biteSize = 0.6 + rng() * 0.3; // 60-90% of radius
1503
+ const biteOffset = r * (0.3 + rng() * 0.4);
1504
+ const biteAngle = rng() * Math.PI * 2;
1505
+ // Outer circle
1506
+ ctx.beginPath();
1507
+ ctx.arc(0, 0, r, 0, Math.PI * 2);
1508
+ // Subtract inner circle using even-odd rule
1509
+ const bx = Math.cos(biteAngle) * biteOffset;
1510
+ const by = Math.sin(biteAngle) * biteOffset;
1511
+ // Draw inner circle counter-clockwise for subtraction
1512
+ ctx.moveTo(bx + r * biteSize, by);
1513
+ ctx.arc(bx, by, r * biteSize, 0, Math.PI * 2, true);
1514
+ };
1515
+ const $a7241acce45d5513$export$38bfe5eb52137e01 = (ctx, size, config)=>{
1516
+ const rng = config?.rng ?? Math.random;
1517
+ const r = size / 2;
1518
+ const segments = 12 + Math.floor(rng() * 8);
1519
+ const startAngle = rng() * Math.PI * 2;
1520
+ const curvature = (rng() - 0.5) * 0.4;
1521
+ // Build spine points
1522
+ const spine = [];
1523
+ let angle = startAngle;
1524
+ let px = 0, py = 0;
1525
+ for(let i = 0; i <= segments; i++){
1526
+ spine.push({
1527
+ x: px,
1528
+ y: py
1529
+ });
1530
+ const stepLen = r / segments * (1.5 + rng() * 0.5);
1531
+ angle += curvature + (rng() - 0.5) * 0.6;
1532
+ px += Math.cos(angle) * stepLen;
1533
+ py += Math.sin(angle) * stepLen;
1534
+ }
1535
+ // Build tapered outline by offsetting perpendicular to spine
1536
+ ctx.beginPath();
1537
+ const leftSide = [];
1538
+ const rightSide = [];
1539
+ for(let i = 0; i < spine.length; i++){
1540
+ const t = i / (spine.length - 1);
1541
+ const width = r * 0.12 * (1 - t * 0.9); // taper from thick to thin
1542
+ const next = spine[Math.min(i + 1, spine.length - 1)];
1543
+ const dx = next.x - spine[i].x;
1544
+ const dy = next.y - spine[i].y;
1545
+ const len = Math.hypot(dx, dy) || 1;
1546
+ const nx = -dy / len;
1547
+ const ny = dx / len;
1548
+ leftSide.push({
1549
+ x: spine[i].x + nx * width,
1550
+ y: spine[i].y + ny * width
1551
+ });
1552
+ rightSide.push({
1553
+ x: spine[i].x - nx * width,
1554
+ y: spine[i].y - ny * width
1555
+ });
1556
+ }
1557
+ ctx.moveTo(leftSide[0].x, leftSide[0].y);
1558
+ for(let i = 1; i < leftSide.length; i++)ctx.lineTo(leftSide[i].x, leftSide[i].y);
1559
+ for(let i = rightSide.length - 1; i >= 0; i--)ctx.lineTo(rightSide[i].x, rightSide[i].y);
1560
+ ctx.closePath();
1561
+ };
1562
+ const $a7241acce45d5513$export$105caa8cfd63c422 = (ctx, size, config)=>{
1563
+ const rng = config?.rng ?? Math.random;
1564
+ const r = size / 2;
1565
+ const lobeCount = 4 + Math.floor(rng() * 4); // 4-7 lobes
1566
+ const spineAngle = rng() * Math.PI * 2;
1567
+ const spineLen = r * 0.6;
1568
+ ctx.beginPath();
1569
+ for(let i = 0; i < lobeCount; i++){
1570
+ const t = i / (lobeCount - 1) - 0.5; // -0.5 to 0.5
1571
+ const sx = Math.cos(spineAngle) * spineLen * t;
1572
+ const sy = Math.sin(spineAngle) * spineLen * t;
1573
+ // Offset perpendicular for cloud shape
1574
+ const perpAngle = spineAngle + Math.PI / 2;
1575
+ const perpOff = (rng() - 0.3) * r * 0.3;
1576
+ const cx = sx + Math.cos(perpAngle) * perpOff;
1577
+ const cy = sy + Math.sin(perpAngle) * perpOff;
1578
+ const lobeR = r * (0.25 + rng() * 0.2);
1579
+ ctx.moveTo(cx + lobeR, cy);
1580
+ ctx.arc(cx, cy, lobeR, 0, Math.PI * 2);
1581
+ }
1582
+ };
1583
+ const $a7241acce45d5513$export$e181e5bd3c539569 = (ctx, size, config)=>{
1584
+ const rng = config?.rng ?? Math.random;
1585
+ const r = size / 2;
1586
+ const spikeCount = 8 + Math.floor(rng() * 8); // 8-15 spikes
1587
+ const points = [];
1588
+ for(let i = 0; i < spikeCount; i++){
1589
+ const angle = i / spikeCount * Math.PI * 2;
1590
+ const isSpike = i % 2 === 0;
1591
+ const dist = isSpike ? r * (0.5 + rng() * 0.5 // spikes reach 50-100% of radius
1592
+ ) : r * (0.15 + rng() * 0.2); // valleys at 15-35%
1593
+ points.push({
1594
+ x: Math.cos(angle) * dist,
1595
+ y: Math.sin(angle) * dist
1596
+ });
1597
+ }
1598
+ ctx.beginPath();
1599
+ ctx.moveTo(points[0].x, points[0].y);
1600
+ for(let i = 0; i < points.length; i++){
1601
+ const curr = points[i];
1602
+ const next = points[(i + 1) % points.length];
1603
+ const cpx = (curr.x + next.x) / 2 + (rng() - 0.5) * r * 0.15;
1604
+ const cpy = (curr.y + next.y) / 2 + (rng() - 0.5) * r * 0.15;
1605
+ ctx.quadraticCurveTo(cpx, cpy, next.x, next.y);
1606
+ }
1607
+ ctx.closePath();
1608
+ };
1609
+ const $a7241acce45d5513$export$155b4780b4c6bb7b = (ctx, size, config)=>{
1610
+ const rng = config?.rng ?? Math.random;
1611
+ const r = size / 2;
1612
+ const subdivisions = 1 + Math.floor(rng() * 3); // 1-3
1613
+ // Start with icosahedron vertices projected to 2D
1614
+ const baseVerts = 6 + subdivisions * 4;
1615
+ const points = [];
1616
+ for(let i = 0; i < baseVerts; i++){
1617
+ const angle = i / baseVerts * Math.PI * 2;
1618
+ const ring = i % 2 === 0 ? 1.0 : 0.5 + rng() * 0.3;
1619
+ points.push({
1620
+ x: Math.cos(angle) * r * ring,
1621
+ y: Math.sin(angle) * r * ring
1622
+ });
1623
+ }
1624
+ ctx.beginPath();
1625
+ // Draw triangulated mesh — connect each point to neighbors and center
1626
+ for(let i = 0; i < points.length; i++){
1627
+ const next = points[(i + 1) % points.length];
1628
+ ctx.moveTo(points[i].x, points[i].y);
1629
+ ctx.lineTo(next.x, next.y);
1630
+ // Connect to center
1631
+ ctx.moveTo(points[i].x, points[i].y);
1632
+ ctx.lineTo(0, 0);
1633
+ // Cross-connect to create triangulation
1634
+ if (i % 2 === 0 && i + 2 < points.length) {
1635
+ ctx.moveTo(points[i].x, points[i].y);
1636
+ ctx.lineTo(points[i + 2].x, points[i + 2].y);
1637
+ }
1638
+ }
1639
+ // Outer ring
1640
+ ctx.moveTo(points[0].x, points[0].y);
1641
+ for(let i = 1; i < points.length; i++)ctx.lineTo(points[i].x, points[i].y);
1642
+ ctx.closePath();
1643
+ };
1644
+ const $a7241acce45d5513$export$9a7e648f11155172 = (ctx, size, config)=>{
1645
+ const rng = config?.rng ?? Math.random;
1646
+ const r = size / 2;
1647
+ const phi = (1 + Math.sqrt(5)) / 2; // golden ratio
1648
+ const isKite = rng() < 0.5;
1649
+ ctx.beginPath();
1650
+ if (isKite) {
1651
+ // Kite: two golden triangles joined at base
1652
+ const topY = -r;
1653
+ const bottomY = r * (1 / phi);
1654
+ const midY = r * (1 / phi - 1) * 0.3;
1655
+ const wingX = r * 0.6;
1656
+ ctx.moveTo(0, topY);
1657
+ ctx.lineTo(wingX, midY);
1658
+ ctx.lineTo(0, bottomY);
1659
+ ctx.lineTo(-wingX, midY);
1660
+ } else {
1661
+ // Dart: concave quadrilateral
1662
+ const topY = -r;
1663
+ const bottomY = r * 0.3;
1664
+ const midY = -r * 0.1;
1665
+ const wingX = r * 0.5;
1666
+ ctx.moveTo(0, topY);
1667
+ ctx.lineTo(wingX, midY);
1668
+ ctx.lineTo(0, bottomY);
1669
+ ctx.lineTo(-wingX, midY);
1670
+ }
1671
+ ctx.closePath();
1672
+ };
1673
+ const $a7241acce45d5513$export$1fc0aedbabd73399 = (ctx, size, config)=>{
1674
+ const r = size / 2;
1675
+ // Vertices of equilateral triangle
1676
+ const verts = [];
1677
+ for(let i = 0; i < 3; i++){
1678
+ const angle = i / 3 * Math.PI * 2 - Math.PI / 2;
1679
+ verts.push({
1680
+ x: Math.cos(angle) * r * 0.7,
1681
+ y: Math.sin(angle) * r * 0.7
1682
+ });
1683
+ }
1684
+ // Side length = distance between vertices
1685
+ const sideLen = Math.hypot(verts[1].x - verts[0].x, verts[1].y - verts[0].y);
1686
+ ctx.beginPath();
1687
+ for(let i = 0; i < 3; i++){
1688
+ const from = verts[(i + 1) % 3];
1689
+ const to = verts[(i + 2) % 3];
1690
+ const center = verts[i];
1691
+ const startAngle = Math.atan2(from.y - center.y, from.x - center.x);
1692
+ const endAngle = Math.atan2(to.y - center.y, to.x - center.x);
1693
+ if (i === 0) ctx.moveTo(from.x, from.y);
1694
+ ctx.arc(center.x, center.y, sideLen, startAngle, endAngle);
1695
+ }
1696
+ ctx.closePath();
1697
+ };
1698
+ const $a7241acce45d5513$export$ef7b5e0c19a21fd1 = (ctx, size, config)=>{
1699
+ const rng = config?.rng ?? Math.random;
1700
+ const r = size / 2;
1701
+ const dotCount = 15 + Math.floor(rng() * 25); // 15-39 dots
1702
+ const clusterTightness = 0.3 + rng() * 0.5;
1703
+ ctx.beginPath();
1704
+ for(let i = 0; i < dotCount; i++){
1705
+ // Gaussian-ish distribution via Box-Muller approximation
1706
+ const u1 = Math.max(0.001, rng());
1707
+ const u2 = rng();
1708
+ const mag = Math.sqrt(-2 * Math.log(u1)) * clusterTightness;
1709
+ const angle = u2 * Math.PI * 2;
1710
+ const dx = Math.cos(angle) * mag * r;
1711
+ const dy = Math.sin(angle) * mag * r;
1712
+ const dotR = r * (0.02 + rng() * 0.04);
1713
+ ctx.moveTo(dx + dotR, dy);
1714
+ ctx.arc(dx, dy, dotR, 0, Math.PI * 2);
1715
+ }
1716
+ };
1717
+ const $a7241acce45d5513$export$f15df8ab60dfcc9a = (ctx, size, config)=>{
1718
+ const rng = config?.rng ?? Math.random;
1719
+ const r = size / 2;
1720
+ const angle1 = rng() * Math.PI;
1721
+ const angle2 = angle1 + Math.PI / 2 + (rng() - 0.5) * 0.3;
1722
+ const spacing = r * (0.08 + rng() * 0.08);
1723
+ const hasCross = rng() < 0.6;
1724
+ // Draw bounding shape (ellipse)
1725
+ const rx = r * (0.7 + rng() * 0.3);
1726
+ const ry = r * (0.5 + rng() * 0.3);
1727
+ // Outer boundary
1728
+ ctx.beginPath();
1729
+ ctx.ellipse(0, 0, rx, ry, 0, 0, Math.PI * 2);
1730
+ // Hatch lines clipped to the ellipse
1731
+ const cos1 = Math.cos(angle1);
1732
+ const sin1 = Math.sin(angle1);
1733
+ for(let d = -r; d <= r; d += spacing){
1734
+ const lx1 = d * cos1 - r * sin1;
1735
+ const ly1 = d * sin1 + r * cos1;
1736
+ const lx2 = d * cos1 + r * sin1;
1737
+ const ly2 = d * sin1 - r * cos1;
1738
+ ctx.moveTo(lx1, ly1);
1739
+ ctx.lineTo(lx2, ly2);
1740
+ }
1741
+ if (hasCross) {
1742
+ const cos2 = Math.cos(angle2);
1743
+ const sin2 = Math.sin(angle2);
1744
+ for(let d = -r; d <= r; d += spacing * 1.3){
1745
+ const lx1 = d * cos2 - r * sin2;
1746
+ const ly1 = d * sin2 + r * cos2;
1747
+ const lx2 = d * cos2 + r * sin2;
1748
+ const ly2 = d * sin2 - r * cos2;
1749
+ ctx.moveTo(lx1, ly1);
1750
+ ctx.lineTo(lx2, ly2);
1751
+ }
1752
+ }
1753
+ };
1460
1754
  const $a7241acce45d5513$export$40cfb4c637f2fbb5 = {
1461
1755
  blob: $a7241acce45d5513$export$580f80cfb9de73bc,
1462
1756
  ngon: $a7241acce45d5513$export$7a6094023f0902a6,
@@ -1464,7 +1758,18 @@ const $a7241acce45d5513$export$40cfb4c637f2fbb5 = {
1464
1758
  superellipse: $a7241acce45d5513$export$1db9219b4f34658c,
1465
1759
  spirograph: $a7241acce45d5513$export$b027c64d22b01985,
1466
1760
  waveRing: $a7241acce45d5513$export$7608ccd03bfb705d,
1467
- rose: $a7241acce45d5513$export$11a377e7498bb523
1761
+ rose: $a7241acce45d5513$export$11a377e7498bb523,
1762
+ shardField: $a7241acce45d5513$export$76b6526575ea179b,
1763
+ voronoiCell: $a7241acce45d5513$export$ed9ff98da5b05073,
1764
+ crescent: $a7241acce45d5513$export$e0452d9a794fe7e5,
1765
+ tendril: $a7241acce45d5513$export$38bfe5eb52137e01,
1766
+ cloudForm: $a7241acce45d5513$export$105caa8cfd63c422,
1767
+ inkSplat: $a7241acce45d5513$export$e181e5bd3c539569,
1768
+ geodesicDome: $a7241acce45d5513$export$155b4780b4c6bb7b,
1769
+ penroseTile: $a7241acce45d5513$export$9a7e648f11155172,
1770
+ reuleauxTriangle: $a7241acce45d5513$export$1fc0aedbabd73399,
1771
+ dotCluster: $a7241acce45d5513$export$ef7b5e0c19a21fd1,
1772
+ crosshatchPatch: $a7241acce45d5513$export$f15df8ab60dfcc9a
1468
1773
  };
1469
1774
 
1470
1775
 
@@ -1499,7 +1804,13 @@ const $9beb8f41637c29fd$var$RENDER_STYLES = [
1499
1804
  "dashed",
1500
1805
  "watercolor",
1501
1806
  "hatched",
1502
- "incomplete"
1807
+ "incomplete",
1808
+ "stipple",
1809
+ "stencil",
1810
+ "noise-grain",
1811
+ "wood-grain",
1812
+ "marble-vein",
1813
+ "fabric-weave"
1503
1814
  ];
1504
1815
  function $9beb8f41637c29fd$export$9fd4e64b2acd410e(rng) {
1505
1816
  return $9beb8f41637c29fd$var$RENDER_STYLES[Math.floor(rng() * $9beb8f41637c29fd$var$RENDER_STYLES.length)];
@@ -1673,6 +1984,213 @@ function $9beb8f41637c29fd$export$71b514a25c47df50(ctx, shape, x, y, config) {
1673
1984
  ctx.lineDashOffset = 0;
1674
1985
  break;
1675
1986
  }
1987
+ case "stipple":
1988
+ {
1989
+ // Dot-fill texture — clip to shape, then scatter dots
1990
+ const savedAlphaS = ctx.globalAlpha;
1991
+ ctx.globalAlpha = savedAlphaS * 0.15;
1992
+ ctx.fill(); // ghost fill
1993
+ ctx.globalAlpha = savedAlphaS;
1994
+ ctx.save();
1995
+ ctx.clip();
1996
+ const dotSpacing = Math.max(2, size * 0.03);
1997
+ const extent = size * 0.55;
1998
+ ctx.globalAlpha = savedAlphaS * 0.7;
1999
+ for(let dx = -extent; dx <= extent; dx += dotSpacing)for(let dy = -extent; dy <= extent; dy += dotSpacing){
2000
+ // Jitter each dot position for organic feel
2001
+ const jx = rng ? (rng() - 0.5) * dotSpacing * 0.6 : 0;
2002
+ const jy = rng ? (rng() - 0.5) * dotSpacing * 0.6 : 0;
2003
+ const dotR = rng ? dotSpacing * (0.15 + rng() * 0.2) : dotSpacing * 0.2;
2004
+ ctx.beginPath();
2005
+ ctx.arc(dx + jx, dy + jy, dotR, 0, Math.PI * 2);
2006
+ ctx.fill();
2007
+ }
2008
+ ctx.restore();
2009
+ ctx.globalAlpha = savedAlphaS;
2010
+ // Outline
2011
+ ctx.globalAlpha *= 0.4;
2012
+ ctx.stroke();
2013
+ ctx.globalAlpha /= 0.4;
2014
+ break;
2015
+ }
2016
+ case "stencil":
2017
+ {
2018
+ // Negative-space cutout — fill a rectangle, then erase the shape
2019
+ const savedAlphaSt = ctx.globalAlpha;
2020
+ // Fill a bounding area with the stroke color
2021
+ ctx.globalAlpha = savedAlphaSt * 0.5;
2022
+ ctx.fillStyle = strokeColor;
2023
+ ctx.fillRect(-size * 0.6, -size * 0.6, size * 1.2, size * 1.2);
2024
+ // Cut out the shape using destination-out
2025
+ ctx.globalCompositeOperation = "destination-out";
2026
+ ctx.globalAlpha = 1;
2027
+ ctx.fill();
2028
+ ctx.globalCompositeOperation = "source-over";
2029
+ ctx.globalAlpha = savedAlphaSt;
2030
+ // Subtle outline of the cutout
2031
+ ctx.globalAlpha *= 0.3;
2032
+ ctx.stroke();
2033
+ ctx.globalAlpha /= 0.3;
2034
+ break;
2035
+ }
2036
+ case "noise-grain":
2037
+ {
2038
+ // Procedural noise grain texture clipped to shape boundary
2039
+ const savedAlphaN = ctx.globalAlpha;
2040
+ ctx.globalAlpha = savedAlphaN * 0.25;
2041
+ ctx.fill(); // base tint
2042
+ ctx.globalAlpha = savedAlphaN;
2043
+ ctx.save();
2044
+ ctx.clip();
2045
+ const grainSpacing = Math.max(1.5, size * 0.015);
2046
+ const extentN = size * 0.55;
2047
+ ctx.globalAlpha = savedAlphaN * 0.6;
2048
+ for(let gx = -extentN; gx <= extentN; gx += grainSpacing)for(let gy = -extentN; gy <= extentN; gy += grainSpacing){
2049
+ if (!rng) break;
2050
+ const jx = (rng() - 0.5) * grainSpacing * 1.2;
2051
+ const jy = (rng() - 0.5) * grainSpacing * 1.2;
2052
+ const brightness = rng() > 0.5 ? 255 : 0;
2053
+ const dotAlpha = 0.15 + rng() * 0.35;
2054
+ ctx.globalAlpha = savedAlphaN * dotAlpha;
2055
+ ctx.fillStyle = `rgba(${brightness},${brightness},${brightness},1)`;
2056
+ const dotSize = grainSpacing * (0.3 + rng() * 0.5);
2057
+ ctx.fillRect(gx + jx, gy + jy, dotSize, dotSize);
2058
+ }
2059
+ ctx.restore();
2060
+ ctx.fillStyle = fillColor;
2061
+ ctx.globalAlpha = savedAlphaN;
2062
+ ctx.globalAlpha *= 0.4;
2063
+ ctx.stroke();
2064
+ ctx.globalAlpha /= 0.4;
2065
+ break;
2066
+ }
2067
+ case "wood-grain":
2068
+ {
2069
+ // Parallel wavy lines simulating wood grain, clipped to shape
2070
+ const savedAlphaW = ctx.globalAlpha;
2071
+ ctx.globalAlpha = savedAlphaW * 0.2;
2072
+ ctx.fill(); // base tint
2073
+ ctx.globalAlpha = savedAlphaW;
2074
+ ctx.save();
2075
+ ctx.clip();
2076
+ const grainLineSpacing = Math.max(2, size * 0.035);
2077
+ const extentW = size * 0.55;
2078
+ const waveFreq = rng ? 3 + rng() * 5 : 5;
2079
+ const waveAmp = rng ? size * (0.01 + rng() * 0.03) : size * 0.02;
2080
+ const grainAngle = rng ? rng() * Math.PI : Math.PI * 0.25;
2081
+ ctx.lineWidth = Math.max(0.5, strokeWidth * 0.3);
2082
+ ctx.globalAlpha = savedAlphaW * 0.5;
2083
+ const cosG = Math.cos(grainAngle);
2084
+ const sinG = Math.sin(grainAngle);
2085
+ for(let d = -extentW; d <= extentW; d += grainLineSpacing){
2086
+ ctx.beginPath();
2087
+ for(let t = -extentW; t <= extentW; t += 2){
2088
+ const wave = Math.sin(t / extentW * waveFreq * Math.PI) * waveAmp;
2089
+ const px = t * cosG - (d + wave) * sinG;
2090
+ const py = t * sinG + (d + wave) * cosG;
2091
+ if (t === -extentW) ctx.moveTo(px, py);
2092
+ else ctx.lineTo(px, py);
2093
+ }
2094
+ ctx.stroke();
2095
+ }
2096
+ ctx.restore();
2097
+ ctx.globalAlpha = savedAlphaW;
2098
+ ctx.globalAlpha *= 0.35;
2099
+ ctx.stroke();
2100
+ ctx.globalAlpha /= 0.35;
2101
+ break;
2102
+ }
2103
+ case "marble-vein":
2104
+ {
2105
+ // Branching vein lines on a soft fill, clipped to shape
2106
+ const savedAlphaM = ctx.globalAlpha;
2107
+ ctx.globalAlpha = savedAlphaM * 0.35;
2108
+ ctx.fill(); // soft base
2109
+ ctx.globalAlpha = savedAlphaM;
2110
+ ctx.save();
2111
+ ctx.clip();
2112
+ const veinCount = rng ? 2 + Math.floor(rng() * 3) : 3;
2113
+ const extentM = size * 0.45;
2114
+ ctx.lineWidth = Math.max(0.5, strokeWidth * 0.5);
2115
+ ctx.globalAlpha = savedAlphaM * 0.4;
2116
+ for(let v = 0; v < veinCount; v++){
2117
+ const startX = rng ? (rng() - 0.5) * extentM * 2 : 0;
2118
+ const startY = rng ? -extentM + rng() * extentM * 0.5 : -extentM;
2119
+ let vx = startX;
2120
+ let vy = startY;
2121
+ const steps = 15 + (rng ? Math.floor(rng() * 15) : 10);
2122
+ const stepLen = size * 0.04;
2123
+ ctx.beginPath();
2124
+ ctx.moveTo(vx, vy);
2125
+ for(let s = 0; s < steps; s++){
2126
+ const drift = rng ? (rng() - 0.5) * stepLen * 1.5 : 0;
2127
+ vx += drift;
2128
+ vy += stepLen;
2129
+ ctx.lineTo(vx, vy);
2130
+ // Branch ~20% of the time
2131
+ if (rng && rng() < 0.2 && s > 2 && s < steps - 3) {
2132
+ const branchDir = rng() < 0.5 ? -1 : 1;
2133
+ let bx = vx;
2134
+ let by = vy;
2135
+ const bSteps = 3 + Math.floor(rng() * 5);
2136
+ ctx.moveTo(bx, by);
2137
+ for(let bs = 0; bs < bSteps; bs++){
2138
+ bx += branchDir * stepLen * (0.5 + rng() * 0.5);
2139
+ by += stepLen * 0.6;
2140
+ ctx.lineTo(bx, by);
2141
+ }
2142
+ ctx.moveTo(vx, vy); // return to main vein
2143
+ }
2144
+ }
2145
+ ctx.stroke();
2146
+ }
2147
+ ctx.restore();
2148
+ ctx.globalAlpha = savedAlphaM;
2149
+ ctx.globalAlpha *= 0.3;
2150
+ ctx.stroke();
2151
+ ctx.globalAlpha /= 0.3;
2152
+ break;
2153
+ }
2154
+ case "fabric-weave":
2155
+ {
2156
+ // Interlocking horizontal/vertical threads clipped to shape
2157
+ const savedAlphaF = ctx.globalAlpha;
2158
+ ctx.globalAlpha = savedAlphaF * 0.15;
2159
+ ctx.fill(); // ghost base
2160
+ ctx.globalAlpha = savedAlphaF;
2161
+ ctx.save();
2162
+ ctx.clip();
2163
+ const threadSpacing = Math.max(2, size * 0.04);
2164
+ const extentF = size * 0.55;
2165
+ ctx.lineWidth = Math.max(0.8, threadSpacing * 0.5);
2166
+ ctx.globalAlpha = savedAlphaF * 0.55;
2167
+ // Horizontal threads
2168
+ for(let y = -extentF; y <= extentF; y += threadSpacing * 2){
2169
+ ctx.beginPath();
2170
+ ctx.moveTo(-extentF, y);
2171
+ ctx.lineTo(extentF, y);
2172
+ ctx.stroke();
2173
+ }
2174
+ // Vertical threads (offset by half spacing for weave effect)
2175
+ ctx.globalAlpha = savedAlphaF * 0.45;
2176
+ ctx.strokeStyle = fillColor;
2177
+ for(let x = -extentF; x <= extentF; x += threadSpacing * 2){
2178
+ ctx.beginPath();
2179
+ for(let y = -extentF; y <= extentF; y += threadSpacing * 2){
2180
+ // Over-under: draw segment, skip segment
2181
+ ctx.moveTo(x, y);
2182
+ ctx.lineTo(x, y + threadSpacing);
2183
+ }
2184
+ ctx.stroke();
2185
+ }
2186
+ ctx.strokeStyle = strokeColor;
2187
+ ctx.restore();
2188
+ ctx.globalAlpha = savedAlphaF;
2189
+ ctx.globalAlpha *= 0.3;
2190
+ ctx.stroke();
2191
+ ctx.globalAlpha /= 0.3;
2192
+ break;
2193
+ }
1676
2194
  case "fill-and-stroke":
1677
2195
  default:
1678
2196
  ctx.fill();
@@ -1719,6 +2237,64 @@ function $9beb8f41637c29fd$export$bb35a6995ddbf32d(ctx, shape, x, y, config) {
1719
2237
  });
1720
2238
  ctx.restore();
1721
2239
  }
2240
+ function $9beb8f41637c29fd$export$8bd8bbd1a8e53689(ctx, shape, x, y, config) {
2241
+ const { mirrorAxis: mirrorAxis = "horizontal", mirrorGap: mirrorGap = 0 } = config;
2242
+ // Draw the primary shape
2243
+ $9beb8f41637c29fd$export$bb35a6995ddbf32d(ctx, shape, x, y, config);
2244
+ // Draw the mirrored copy
2245
+ ctx.save();
2246
+ const savedAlpha = ctx.globalAlpha;
2247
+ ctx.globalAlpha = savedAlpha * 0.7; // mirror is slightly softer
2248
+ switch(mirrorAxis){
2249
+ case "horizontal":
2250
+ // Reflect across vertical axis at shape position
2251
+ $9beb8f41637c29fd$export$bb35a6995ddbf32d(ctx, shape, x, y + mirrorGap, {
2252
+ ...config,
2253
+ rotation: -(config.rotation || 0),
2254
+ size: config.size * 0.95
2255
+ });
2256
+ break;
2257
+ case "vertical":
2258
+ $9beb8f41637c29fd$export$bb35a6995ddbf32d(ctx, shape, x + mirrorGap, y, {
2259
+ ...config,
2260
+ rotation: 180 - (config.rotation || 0),
2261
+ size: config.size * 0.95
2262
+ });
2263
+ break;
2264
+ case "diagonal":
2265
+ // Reflect across 45° axis
2266
+ $9beb8f41637c29fd$export$bb35a6995ddbf32d(ctx, shape, x + mirrorGap * 0.7, y + mirrorGap * 0.7, {
2267
+ ...config,
2268
+ rotation: 90 - (config.rotation || 0),
2269
+ size: config.size * 0.9
2270
+ });
2271
+ break;
2272
+ case "radial-4":
2273
+ // Four-way radial mirror
2274
+ for(let i = 1; i < 4; i++){
2275
+ const angle = i / 4 * Math.PI * 2;
2276
+ const mx = x + Math.cos(angle) * mirrorGap;
2277
+ const my = y + Math.sin(angle) * mirrorGap;
2278
+ ctx.globalAlpha = savedAlpha * (0.7 - i * 0.1);
2279
+ $9beb8f41637c29fd$export$bb35a6995ddbf32d(ctx, shape, mx, my, {
2280
+ ...config,
2281
+ rotation: (config.rotation || 0) + i * 90,
2282
+ size: config.size * (0.95 - i * 0.05)
2283
+ });
2284
+ }
2285
+ break;
2286
+ }
2287
+ ctx.globalAlpha = savedAlpha;
2288
+ ctx.restore();
2289
+ }
2290
+ function $9beb8f41637c29fd$export$879206e23912d1a9(rng) {
2291
+ const roll = rng();
2292
+ if (roll < 0.60) return null;
2293
+ if (roll < 0.75) return "horizontal";
2294
+ if (roll < 0.87) return "vertical";
2295
+ if (roll < 0.95) return "diagonal";
2296
+ return "radial-4";
2297
+ }
1722
2298
 
1723
2299
 
1724
2300
 
@@ -2221,7 +2797,8 @@ const $24064302523652b1$export$4343b39fe47bd82c = {
2221
2797
  bestStyles: [
2222
2798
  "fill-only",
2223
2799
  "watercolor",
2224
- "fill-and-stroke"
2800
+ "fill-and-stroke",
2801
+ "wood-grain"
2225
2802
  ]
2226
2803
  },
2227
2804
  spirograph: {
@@ -2277,6 +2854,202 @@ const $24064302523652b1$export$4343b39fe47bd82c = {
2277
2854
  "fill-only",
2278
2855
  "watercolor"
2279
2856
  ]
2857
+ },
2858
+ // ── New procedural shapes ─────────────────────────────────────
2859
+ shardField: {
2860
+ tier: 2,
2861
+ minSizeFraction: 0.1,
2862
+ maxSizeFraction: 0.7,
2863
+ affinities: [
2864
+ "voronoiCell",
2865
+ "diamond",
2866
+ "triangle",
2867
+ "penroseTile"
2868
+ ],
2869
+ category: "procedural",
2870
+ heroCandidate: false,
2871
+ bestStyles: [
2872
+ "fill-and-stroke",
2873
+ "stroke-only",
2874
+ "fill-only"
2875
+ ]
2876
+ },
2877
+ voronoiCell: {
2878
+ tier: 1,
2879
+ minSizeFraction: 0.08,
2880
+ maxSizeFraction: 0.9,
2881
+ affinities: [
2882
+ "shardField",
2883
+ "ngon",
2884
+ "superellipse",
2885
+ "blob"
2886
+ ],
2887
+ category: "procedural",
2888
+ heroCandidate: false,
2889
+ bestStyles: [
2890
+ "fill-and-stroke",
2891
+ "fill-only",
2892
+ "watercolor",
2893
+ "marble-vein"
2894
+ ]
2895
+ },
2896
+ crescent: {
2897
+ tier: 1,
2898
+ minSizeFraction: 0.1,
2899
+ maxSizeFraction: 1.0,
2900
+ affinities: [
2901
+ "circle",
2902
+ "blob",
2903
+ "cloudForm",
2904
+ "vesicaPiscis"
2905
+ ],
2906
+ category: "procedural",
2907
+ heroCandidate: true,
2908
+ bestStyles: [
2909
+ "fill-only",
2910
+ "watercolor",
2911
+ "fill-and-stroke"
2912
+ ]
2913
+ },
2914
+ tendril: {
2915
+ tier: 2,
2916
+ minSizeFraction: 0.1,
2917
+ maxSizeFraction: 0.8,
2918
+ affinities: [
2919
+ "blob",
2920
+ "inkSplat",
2921
+ "lissajous",
2922
+ "fibonacciSpiral"
2923
+ ],
2924
+ category: "procedural",
2925
+ heroCandidate: false,
2926
+ bestStyles: [
2927
+ "fill-only",
2928
+ "watercolor",
2929
+ "fill-and-stroke"
2930
+ ]
2931
+ },
2932
+ cloudForm: {
2933
+ tier: 1,
2934
+ minSizeFraction: 0.15,
2935
+ maxSizeFraction: 1.0,
2936
+ affinities: [
2937
+ "blob",
2938
+ "circle",
2939
+ "crescent",
2940
+ "superellipse"
2941
+ ],
2942
+ category: "procedural",
2943
+ heroCandidate: false,
2944
+ bestStyles: [
2945
+ "fill-only",
2946
+ "watercolor"
2947
+ ]
2948
+ },
2949
+ inkSplat: {
2950
+ tier: 2,
2951
+ minSizeFraction: 0.1,
2952
+ maxSizeFraction: 0.8,
2953
+ affinities: [
2954
+ "blob",
2955
+ "tendril",
2956
+ "shardField",
2957
+ "star"
2958
+ ],
2959
+ category: "procedural",
2960
+ heroCandidate: false,
2961
+ bestStyles: [
2962
+ "fill-only",
2963
+ "watercolor",
2964
+ "fill-and-stroke"
2965
+ ]
2966
+ },
2967
+ geodesicDome: {
2968
+ tier: 2,
2969
+ minSizeFraction: 0.2,
2970
+ maxSizeFraction: 0.9,
2971
+ affinities: [
2972
+ "metatronsCube",
2973
+ "platonicSolid",
2974
+ "hexagon",
2975
+ "triangle"
2976
+ ],
2977
+ category: "procedural",
2978
+ heroCandidate: true,
2979
+ bestStyles: [
2980
+ "stroke-only",
2981
+ "dashed",
2982
+ "double-stroke"
2983
+ ]
2984
+ },
2985
+ penroseTile: {
2986
+ tier: 2,
2987
+ minSizeFraction: 0.06,
2988
+ maxSizeFraction: 0.6,
2989
+ affinities: [
2990
+ "diamond",
2991
+ "triangle",
2992
+ "shardField",
2993
+ "voronoiCell"
2994
+ ],
2995
+ category: "procedural",
2996
+ heroCandidate: false,
2997
+ bestStyles: [
2998
+ "fill-and-stroke",
2999
+ "fill-only",
3000
+ "double-stroke"
3001
+ ]
3002
+ },
3003
+ reuleauxTriangle: {
3004
+ tier: 1,
3005
+ minSizeFraction: 0.08,
3006
+ maxSizeFraction: 1.0,
3007
+ affinities: [
3008
+ "triangle",
3009
+ "circle",
3010
+ "superellipse",
3011
+ "vesicaPiscis"
3012
+ ],
3013
+ category: "procedural",
3014
+ heroCandidate: true,
3015
+ bestStyles: [
3016
+ "fill-and-stroke",
3017
+ "fill-only",
3018
+ "watercolor"
3019
+ ]
3020
+ },
3021
+ dotCluster: {
3022
+ tier: 3,
3023
+ minSizeFraction: 0.05,
3024
+ maxSizeFraction: 0.5,
3025
+ affinities: [
3026
+ "cloudForm",
3027
+ "inkSplat",
3028
+ "blob"
3029
+ ],
3030
+ category: "procedural",
3031
+ heroCandidate: false,
3032
+ bestStyles: [
3033
+ "fill-only",
3034
+ "stipple"
3035
+ ]
3036
+ },
3037
+ crosshatchPatch: {
3038
+ tier: 3,
3039
+ minSizeFraction: 0.1,
3040
+ maxSizeFraction: 0.6,
3041
+ affinities: [
3042
+ "voronoiCell",
3043
+ "ngon",
3044
+ "superellipse"
3045
+ ],
3046
+ category: "procedural",
3047
+ heroCandidate: false,
3048
+ bestStyles: [
3049
+ "stroke-only",
3050
+ "hatched",
3051
+ "fabric-weave"
3052
+ ]
2280
3053
  }
2281
3054
  };
2282
3055
  function $24064302523652b1$export$4a95df8944b5033b(rng, shapeNames, archetypeName) {
@@ -2351,6 +3124,81 @@ function $24064302523652b1$export$4a95df8944b5033b(rng, shapeNames, archetypeNam
2351
3124
  accents: accents
2352
3125
  };
2353
3126
  }
3127
+ if (archetypeName === "shattered-glass") {
3128
+ // Favor angular, fragmented shapes
3129
+ const shardBoost = available.filter((s)=>[
3130
+ "shardField",
3131
+ "voronoiCell",
3132
+ "penroseTile",
3133
+ "diamond",
3134
+ "triangle",
3135
+ "ngon"
3136
+ ].includes(s) && !primary.includes(s));
3137
+ return {
3138
+ primary: [
3139
+ ...primary.filter((s)=>s !== "blob" && s !== "cloudForm"),
3140
+ ...shardBoost.slice(0, 3)
3141
+ ],
3142
+ supporting: supporting.filter((s)=>s !== "blob" && s !== "cloudForm"),
3143
+ accents: accents
3144
+ };
3145
+ }
3146
+ if (archetypeName === "botanical") {
3147
+ // Favor organic, flowing shapes
3148
+ const botanicalBoost = available.filter((s)=>[
3149
+ "tendril",
3150
+ "cloudForm",
3151
+ "blob",
3152
+ "crescent",
3153
+ "rose",
3154
+ "inkSplat"
3155
+ ].includes(s) && !primary.includes(s));
3156
+ return {
3157
+ primary: [
3158
+ ...primary,
3159
+ ...botanicalBoost.slice(0, 3)
3160
+ ],
3161
+ supporting: supporting,
3162
+ accents: accents
3163
+ };
3164
+ }
3165
+ if (archetypeName === "stipple-portrait") {
3166
+ // Favor small, dot-friendly shapes
3167
+ const stippleBoost = available.filter((s)=>[
3168
+ "dotCluster",
3169
+ "circle",
3170
+ "crosshatchPatch",
3171
+ "voronoiCell",
3172
+ "blob"
3173
+ ].includes(s) && !primary.includes(s));
3174
+ return {
3175
+ primary: [
3176
+ ...primary,
3177
+ ...stippleBoost.slice(0, 3)
3178
+ ],
3179
+ supporting: supporting,
3180
+ accents: accents
3181
+ };
3182
+ }
3183
+ if (archetypeName === "celestial") {
3184
+ // Favor sacred geometry and cosmic shapes
3185
+ const celestialBoost = available.filter((s)=>[
3186
+ "crescent",
3187
+ "geodesicDome",
3188
+ "mandala",
3189
+ "flowerOfLife",
3190
+ "spirograph",
3191
+ "fibonacciSpiral"
3192
+ ].includes(s) && !primary.includes(s));
3193
+ return {
3194
+ primary: [
3195
+ ...primary,
3196
+ ...celestialBoost.slice(0, 3)
3197
+ ],
3198
+ supporting: supporting,
3199
+ accents: accents
3200
+ };
3201
+ }
2354
3202
  return {
2355
3203
  primary: primary,
2356
3204
  supporting: supporting,
@@ -2688,6 +3536,91 @@ const $3faa2521b78398cf$var$ARCHETYPES = [
2688
3536
  glowMultiplier: 1,
2689
3537
  sizePower: 1.8,
2690
3538
  invertForeground: false
3539
+ },
3540
+ {
3541
+ name: "shattered-glass",
3542
+ gridSize: 8,
3543
+ layers: 3,
3544
+ baseOpacity: 0.85,
3545
+ opacityReduction: 0.1,
3546
+ minShapeSize: 15,
3547
+ maxShapeSize: 250,
3548
+ backgroundStyle: "solid-dark",
3549
+ paletteMode: "high-contrast",
3550
+ preferredStyles: [
3551
+ "fill-and-stroke",
3552
+ "stroke-only",
3553
+ "fill-only"
3554
+ ],
3555
+ flowLineMultiplier: 0,
3556
+ heroShape: false,
3557
+ glowMultiplier: 0.3,
3558
+ sizePower: 1.0,
3559
+ invertForeground: false
3560
+ },
3561
+ {
3562
+ name: "botanical",
3563
+ gridSize: 4,
3564
+ layers: 4,
3565
+ baseOpacity: 0.5,
3566
+ opacityReduction: 0.06,
3567
+ minShapeSize: 30,
3568
+ maxShapeSize: 400,
3569
+ backgroundStyle: "radial-light",
3570
+ paletteMode: "earth",
3571
+ preferredStyles: [
3572
+ "watercolor",
3573
+ "fill-only",
3574
+ "incomplete"
3575
+ ],
3576
+ flowLineMultiplier: 3,
3577
+ heroShape: true,
3578
+ glowMultiplier: 0.2,
3579
+ sizePower: 1.6,
3580
+ invertForeground: false
3581
+ },
3582
+ {
3583
+ name: "stipple-portrait",
3584
+ gridSize: 9,
3585
+ layers: 2,
3586
+ baseOpacity: 0.8,
3587
+ opacityReduction: 0.05,
3588
+ minShapeSize: 5,
3589
+ maxShapeSize: 120,
3590
+ backgroundStyle: "solid-light",
3591
+ paletteMode: "monochrome",
3592
+ preferredStyles: [
3593
+ "stipple",
3594
+ "fill-only",
3595
+ "hatched"
3596
+ ],
3597
+ flowLineMultiplier: 0,
3598
+ heroShape: false,
3599
+ glowMultiplier: 0,
3600
+ sizePower: 2.8,
3601
+ invertForeground: false
3602
+ },
3603
+ {
3604
+ name: "celestial",
3605
+ gridSize: 7,
3606
+ layers: 5,
3607
+ baseOpacity: 0.45,
3608
+ opacityReduction: 0.04,
3609
+ minShapeSize: 8,
3610
+ maxShapeSize: 450,
3611
+ backgroundStyle: "radial-dark",
3612
+ paletteMode: "neon",
3613
+ preferredStyles: [
3614
+ "fill-only",
3615
+ "watercolor",
3616
+ "stroke-only",
3617
+ "incomplete"
3618
+ ],
3619
+ flowLineMultiplier: 2,
3620
+ heroShape: true,
3621
+ glowMultiplier: 2.5,
3622
+ sizePower: 2.2,
3623
+ invertForeground: false
2691
3624
  }
2692
3625
  ];
2693
3626
  function $3faa2521b78398cf$export$f1142fd7da4d6590(rng) {
@@ -2867,6 +3800,135 @@ function $b623126c6e9cbb71$var$drawBackground(ctx, style, bgStart, bgEnd, width,
2867
3800
  }
2868
3801
  }
2869
3802
  }
3803
+ const $b623126c6e9cbb71$var$CONSTELLATIONS = [
3804
+ {
3805
+ name: "flanked-triangle",
3806
+ build: (rng, baseSize)=>{
3807
+ const gap = baseSize * (0.6 + rng() * 0.3);
3808
+ return [
3809
+ {
3810
+ dx: 0,
3811
+ dy: 0,
3812
+ shape: "triangle",
3813
+ size: baseSize,
3814
+ rotation: rng() * 360
3815
+ },
3816
+ {
3817
+ dx: -gap,
3818
+ dy: gap * 0.3,
3819
+ shape: "circle",
3820
+ size: baseSize * 0.35,
3821
+ rotation: 0
3822
+ },
3823
+ {
3824
+ dx: gap,
3825
+ dy: gap * 0.3,
3826
+ shape: "circle",
3827
+ size: baseSize * 0.35,
3828
+ rotation: 0
3829
+ }
3830
+ ];
3831
+ }
3832
+ },
3833
+ {
3834
+ name: "hexagon-ring",
3835
+ build: (rng, baseSize)=>{
3836
+ const members = [];
3837
+ const count = 5 + Math.floor(rng() * 2);
3838
+ const ringR = baseSize * 0.6;
3839
+ for(let i = 0; i < count; i++){
3840
+ const angle = i / count * Math.PI * 2;
3841
+ members.push({
3842
+ dx: Math.cos(angle) * ringR,
3843
+ dy: Math.sin(angle) * ringR,
3844
+ shape: "hexagon",
3845
+ size: baseSize * (0.25 + rng() * 0.1),
3846
+ rotation: angle * 180 / Math.PI
3847
+ });
3848
+ }
3849
+ return members;
3850
+ }
3851
+ },
3852
+ {
3853
+ name: "spiral-dots",
3854
+ build: (rng, baseSize)=>{
3855
+ const members = [];
3856
+ const count = 7 + Math.floor(rng() * 5);
3857
+ const turns = 1.5 + rng();
3858
+ for(let i = 0; i < count; i++){
3859
+ const t = i / count;
3860
+ const angle = t * Math.PI * 2 * turns;
3861
+ const r = t * baseSize * 0.7;
3862
+ members.push({
3863
+ dx: Math.cos(angle) * r,
3864
+ dy: Math.sin(angle) * r,
3865
+ shape: "circle",
3866
+ size: baseSize * (0.08 + (1 - t) * 0.12),
3867
+ rotation: 0
3868
+ });
3869
+ }
3870
+ return members;
3871
+ }
3872
+ },
3873
+ {
3874
+ name: "diamond-cluster",
3875
+ build: (rng, baseSize)=>{
3876
+ const gap = baseSize * 0.45;
3877
+ return [
3878
+ {
3879
+ dx: 0,
3880
+ dy: -gap,
3881
+ shape: "diamond",
3882
+ size: baseSize * 0.4,
3883
+ rotation: 0
3884
+ },
3885
+ {
3886
+ dx: gap,
3887
+ dy: 0,
3888
+ shape: "diamond",
3889
+ size: baseSize * 0.35,
3890
+ rotation: 15
3891
+ },
3892
+ {
3893
+ dx: 0,
3894
+ dy: gap,
3895
+ shape: "diamond",
3896
+ size: baseSize * 0.3,
3897
+ rotation: 30
3898
+ },
3899
+ {
3900
+ dx: -gap,
3901
+ dy: 0,
3902
+ shape: "diamond",
3903
+ size: baseSize * 0.35,
3904
+ rotation: -15
3905
+ }
3906
+ ];
3907
+ }
3908
+ },
3909
+ {
3910
+ name: "crescent-pair",
3911
+ build: (rng, baseSize)=>{
3912
+ const gap = baseSize * 0.5;
3913
+ return [
3914
+ {
3915
+ dx: -gap * 0.4,
3916
+ dy: 0,
3917
+ shape: "crescent",
3918
+ size: baseSize * 0.5,
3919
+ rotation: rng() * 30
3920
+ },
3921
+ {
3922
+ dx: gap * 0.4,
3923
+ dy: 0,
3924
+ shape: "crescent",
3925
+ size: baseSize * 0.45,
3926
+ rotation: 180 + rng() * 30
3927
+ }
3928
+ ];
3929
+ }
3930
+ }
3931
+ ];
2870
3932
  function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
2871
3933
  const finalConfig = {
2872
3934
  ...(0, $2bfb6a1ccb7a82ae$export$c2f8e0cc249a8d8f),
@@ -2953,6 +4015,65 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
2953
4015
  ctx.stroke();
2954
4016
  }
2955
4017
  ctx.globalCompositeOperation = "source-over";
4018
+ // ── 1c. Background pattern layer — subtle textured paper ───────
4019
+ const bgPatternRoll = rng();
4020
+ if (bgPatternRoll < 0.6) {
4021
+ ctx.save();
4022
+ ctx.globalCompositeOperation = "soft-light";
4023
+ const patternOpacity = 0.02 + rng() * 0.04;
4024
+ const patternColor = (0, $9d614e7d77fc2947$export$f2121afcad3d553f)(colorHierarchy.dominant, 0.15);
4025
+ if (bgPatternRoll < 0.2) {
4026
+ // Dot grid
4027
+ const dotSpacing = Math.max(8, Math.min(width, height) * (0.015 + rng() * 0.015));
4028
+ const dotR = dotSpacing * 0.08;
4029
+ ctx.globalAlpha = patternOpacity;
4030
+ ctx.fillStyle = patternColor;
4031
+ for(let px = 0; px < width; px += dotSpacing)for(let py = 0; py < height; py += dotSpacing){
4032
+ ctx.beginPath();
4033
+ ctx.arc(px, py, dotR, 0, Math.PI * 2);
4034
+ ctx.fill();
4035
+ }
4036
+ } else if (bgPatternRoll < 0.4) {
4037
+ // Diagonal lines
4038
+ const lineSpacing = Math.max(6, Math.min(width, height) * (0.02 + rng() * 0.02));
4039
+ ctx.globalAlpha = patternOpacity;
4040
+ ctx.strokeStyle = patternColor;
4041
+ ctx.lineWidth = 0.5 * scaleFactor;
4042
+ const diag = Math.hypot(width, height);
4043
+ for(let d = -diag; d < diag; d += lineSpacing){
4044
+ ctx.beginPath();
4045
+ ctx.moveTo(d, 0);
4046
+ ctx.lineTo(d + height, height);
4047
+ ctx.stroke();
4048
+ }
4049
+ } else {
4050
+ // Tessellation — hexagonal grid of tiny shapes
4051
+ const tessSize = Math.max(10, Math.min(width, height) * (0.025 + rng() * 0.02));
4052
+ const tessH = tessSize * Math.sqrt(3);
4053
+ ctx.globalAlpha = patternOpacity * 0.7;
4054
+ ctx.strokeStyle = patternColor;
4055
+ ctx.lineWidth = 0.4 * scaleFactor;
4056
+ for(let row = 0; row * tessH < height + tessH; row++){
4057
+ const offsetX = row % 2 * tessSize * 0.75;
4058
+ for(let col = 0; col * tessSize * 1.5 < width + tessSize * 1.5; col++){
4059
+ const hx = col * tessSize * 1.5 + offsetX;
4060
+ const hy = row * tessH;
4061
+ ctx.beginPath();
4062
+ for(let s = 0; s < 6; s++){
4063
+ const angle = Math.PI / 3 * s - Math.PI / 6;
4064
+ const vx = hx + Math.cos(angle) * tessSize * 0.5;
4065
+ const vy = hy + Math.sin(angle) * tessSize * 0.5;
4066
+ if (s === 0) ctx.moveTo(vx, vy);
4067
+ else ctx.lineTo(vx, vy);
4068
+ }
4069
+ ctx.closePath();
4070
+ ctx.stroke();
4071
+ }
4072
+ }
4073
+ }
4074
+ ctx.restore();
4075
+ }
4076
+ ctx.globalCompositeOperation = "source-over";
2956
4077
  // ── 2. Composition mode ────────────────────────────────────────
2957
4078
  const compositionMode = $b623126c6e9cbb71$var$COMPOSITION_MODES[Math.floor(rng() * $b623126c6e9cbb71$var$COMPOSITION_MODES.length)];
2958
4079
  const symRoll = rng();
@@ -3080,6 +4201,11 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
3080
4201
  const layerRenderStyle = rng() < 0.6 ? archetype.preferredStyles[Math.floor(rng() * archetype.preferredStyles.length)] : (0, $9beb8f41637c29fd$export$9fd4e64b2acd410e)(rng);
3081
4202
  // Atmospheric desaturation for later layers
3082
4203
  const atmosphericDesat = layerRatio * 0.3;
4204
+ // Depth-of-field simulation — later layers are "further away"
4205
+ // Reduce stroke widths and shift colors toward the background
4206
+ const dofFactor = 1 - layerRatio * 0.5; // 1.0 for front layer, 0.5 for back
4207
+ const dofStrokeScale = 0.4 + dofFactor * 0.6; // strokes thin out with depth
4208
+ const dofContrastReduction = layerRatio * 0.2; // colors fade toward bg
3083
4209
  for(let i = 0; i < numShapes; i++){
3084
4210
  // Position from composition mode, then focal bias
3085
4211
  const rawPos = $b623126c6e9cbb71$var$getCompositionPosition(compositionMode, rng, width, height, i, numShapes, cx, cy);
@@ -3122,8 +4248,10 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
3122
4248
  // Semi-transparent fill
3123
4249
  const fillAlpha = 0.2 + rng() * 0.5;
3124
4250
  const transparentFill = (0, $9d614e7d77fc2947$export$f2121afcad3d553f)(fillColor, fillAlpha);
3125
- const strokeWidth = (0.5 + rng() * 2.0) * scaleFactor;
3126
- ctx.globalAlpha = layerOpacity * (0.5 + rng() * 0.5);
4251
+ const strokeWidth = (0.5 + rng() * 2.0) * scaleFactor * dofStrokeScale;
4252
+ // Depth-of-field: reduce opacity slightly for distant layers
4253
+ const dofOpacityScale = 1 - dofContrastReduction;
4254
+ ctx.globalAlpha = layerOpacity * (0.5 + rng() * 0.5) * dofOpacityScale;
3127
4255
  // Glow on sacred shapes more often — scaled by archetype
3128
4256
  const isSacred = $b623126c6e9cbb71$var$SACRED_SHAPES.includes(shape);
3129
4257
  const baseGlowChance = isSacred ? 0.45 : 0.2;
@@ -3142,7 +4270,48 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
3142
4270
  const shadowDist = hasGlow ? 0 : size * 0.02;
3143
4271
  const shadowOffX = shadowDist * Math.cos(lightAngle);
3144
4272
  const shadowOffY = shadowDist * Math.sin(lightAngle);
3145
- (0, $9beb8f41637c29fd$export$bb35a6995ddbf32d)(ctx, shape, x, y, {
4273
+ // ── 5a. Tangent placement — nudge toward nearest shape edge ──
4274
+ let finalX = x;
4275
+ let finalY = y;
4276
+ if (shapePositions.length > 0 && rng() < 0.25) {
4277
+ // Find nearest placed shape
4278
+ let nearestDist = Infinity;
4279
+ let nearestPos = null;
4280
+ for (const sp of shapePositions){
4281
+ const d = Math.hypot(x - sp.x, y - sp.y);
4282
+ if (d < nearestDist && d > 0) {
4283
+ nearestDist = d;
4284
+ nearestPos = sp;
4285
+ }
4286
+ }
4287
+ if (nearestPos) {
4288
+ // Target distance: edges kissing (sum of half-sizes)
4289
+ const targetDist = (size + nearestPos.size) * 0.5;
4290
+ if (nearestDist > targetDist * 0.5 && nearestDist < targetDist * 3) {
4291
+ const angle = Math.atan2(y - nearestPos.y, x - nearestPos.x);
4292
+ finalX = nearestPos.x + Math.cos(angle) * targetDist;
4293
+ finalY = nearestPos.y + Math.sin(angle) * targetDist;
4294
+ // Keep in bounds
4295
+ finalX = Math.max(0, Math.min(width, finalX));
4296
+ finalY = Math.max(0, Math.min(height, finalY));
4297
+ }
4298
+ }
4299
+ }
4300
+ // ── 5b. Shape mirroring — basic shapes get reflected copies ──
4301
+ const mirrorAxis = (0, $9beb8f41637c29fd$export$879206e23912d1a9)(rng);
4302
+ const isBasicShape = [
4303
+ "circle",
4304
+ "triangle",
4305
+ "square",
4306
+ "hexagon",
4307
+ "star",
4308
+ "diamond",
4309
+ "crescent",
4310
+ "penroseTile",
4311
+ "reuleauxTriangle"
4312
+ ].includes(shape);
4313
+ const shouldMirror = mirrorAxis !== null && isBasicShape && size > adjustedMaxSize * 0.2;
4314
+ const shapeConfig = {
3146
4315
  fillColor: transparentFill,
3147
4316
  strokeColor: strokeColor,
3148
4317
  strokeWidth: strokeWidth,
@@ -3154,22 +4323,28 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
3154
4323
  gradientFillEnd: gradientEnd,
3155
4324
  renderStyle: finalRenderStyle,
3156
4325
  rng: rng
4326
+ };
4327
+ if (shouldMirror) (0, $9beb8f41637c29fd$export$8bd8bbd1a8e53689)(ctx, shape, finalX, finalY, {
4328
+ ...shapeConfig,
4329
+ mirrorAxis: mirrorAxis,
4330
+ mirrorGap: size * (0.1 + rng() * 0.3)
3157
4331
  });
4332
+ else (0, $9beb8f41637c29fd$export$bb35a6995ddbf32d)(ctx, shape, finalX, finalY, shapeConfig);
3158
4333
  shapePositions.push({
3159
- x: x,
3160
- y: y,
4334
+ x: finalX,
4335
+ y: finalY,
3161
4336
  size: size,
3162
4337
  shape: shape
3163
4338
  });
3164
- // ── 5b. Size echo — large shapes spawn trailing smaller copies ──
4339
+ // ── 5c. Size echo — large shapes spawn trailing smaller copies ──
3165
4340
  if (size > adjustedMaxSize * 0.5 && rng() < 0.2) {
3166
4341
  const echoCount = 2 + Math.floor(rng() * 2);
3167
4342
  const echoAngle = rng() * Math.PI * 2;
3168
4343
  for(let e = 0; e < echoCount; e++){
3169
4344
  const echoScale = 0.3 - e * 0.08;
3170
4345
  const echoDist = size * (0.6 + e * 0.4);
3171
- const echoX = x + Math.cos(echoAngle) * echoDist;
3172
- const echoY = y + Math.sin(echoAngle) * echoDist;
4346
+ const echoX = finalX + Math.cos(echoAngle) * echoDist;
4347
+ const echoY = finalY + Math.sin(echoAngle) * echoDist;
3173
4348
  const echoSize = size * Math.max(0.1, echoScale);
3174
4349
  if (echoX < 0 || echoX > width || echoY < 0 || echoY > height) continue;
3175
4350
  ctx.globalAlpha = layerOpacity * (0.4 - e * 0.1);
@@ -3191,7 +4366,7 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
3191
4366
  });
3192
4367
  }
3193
4368
  }
3194
- // ── 5c. Recursive nesting ──────────────────────────────────
4369
+ // ── 5d. Recursive nesting ──────────────────────────────────
3195
4370
  if (size > adjustedMaxSize * 0.4 && rng() < 0.15) {
3196
4371
  const innerCount = 1 + Math.floor(rng() * 3);
3197
4372
  for(let n = 0; n < innerCount; n++){
@@ -3204,7 +4379,7 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
3204
4379
  const innerRot = rng() * 360;
3205
4380
  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
4381
  ctx.globalAlpha = layerOpacity * 0.7;
3207
- (0, $9beb8f41637c29fd$export$bb35a6995ddbf32d)(ctx, innerShape, x + innerOffX, y + innerOffY, {
4382
+ (0, $9beb8f41637c29fd$export$bb35a6995ddbf32d)(ctx, innerShape, finalX + innerOffX, finalY + innerOffY, {
3208
4383
  fillColor: innerFill,
3209
4384
  strokeColor: (0, $9d614e7d77fc2947$export$f2121afcad3d553f)(strokeColor, 0.5),
3210
4385
  strokeWidth: strokeWidth * 0.6,
@@ -3216,6 +4391,41 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
3216
4391
  });
3217
4392
  }
3218
4393
  }
4394
+ // ── 5e. Shape constellations — pre-composed groups ─────────
4395
+ if (size > adjustedMaxSize * 0.35 && rng() < 0.12) {
4396
+ const constellation = $b623126c6e9cbb71$var$CONSTELLATIONS[Math.floor(rng() * $b623126c6e9cbb71$var$CONSTELLATIONS.length)];
4397
+ const members = constellation.build(rng, size);
4398
+ const groupRotation = rng() * Math.PI * 2;
4399
+ const cosR = Math.cos(groupRotation);
4400
+ const sinR = Math.sin(groupRotation);
4401
+ for (const member of members){
4402
+ // Rotate the group offset by the group rotation
4403
+ const mx = finalX + member.dx * cosR - member.dy * sinR;
4404
+ const my = finalY + member.dx * sinR + member.dy * cosR;
4405
+ if (mx < 0 || mx > width || my < 0 || my > height) continue;
4406
+ const memberFill = (0, $9d614e7d77fc2947$export$f2121afcad3d553f)((0, $9d614e7d77fc2947$export$18a34c25ea7e724b)((0, $9d614e7d77fc2947$export$b49f62f0a99da0e8)(colorHierarchy, rng), rng, 8, 0.06), fillAlpha * 0.8);
4407
+ const memberStroke = (0, $9d614e7d77fc2947$export$90ad0e6170cf6af5)((0, $9d614e7d77fc2947$export$18a34c25ea7e724b)(strokeBase, rng, 5, 0.04), bgLum);
4408
+ ctx.globalAlpha = layerOpacity * 0.6;
4409
+ // Use the member's shape if available, otherwise fall back to palette
4410
+ const memberShape = shapeNames.includes(member.shape) ? member.shape : (0, $24064302523652b1$export$3c37d9a045754d0e)(shapePalette, rng, member.size / adjustedMaxSize);
4411
+ (0, $9beb8f41637c29fd$export$bb35a6995ddbf32d)(ctx, memberShape, mx, my, {
4412
+ fillColor: memberFill,
4413
+ strokeColor: memberStroke,
4414
+ strokeWidth: strokeWidth * 0.7,
4415
+ size: member.size,
4416
+ rotation: member.rotation + groupRotation * 180 / Math.PI,
4417
+ proportionType: "GOLDEN_RATIO",
4418
+ renderStyle: (0, $24064302523652b1$export$ab873bb6fb56c1a8)(memberShape, layerRenderStyle, rng),
4419
+ rng: rng
4420
+ });
4421
+ shapePositions.push({
4422
+ x: mx,
4423
+ y: my,
4424
+ size: member.size,
4425
+ shape: memberShape
4426
+ });
4427
+ }
4428
+ }
3219
4429
  }
3220
4430
  }
3221
4431
  // Reset blend mode for post-processing passes