git-hash-art 0.7.0 → 0.8.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/CHANGELOG.md +8 -0
- package/bin/generateExamples.js +6 -14
- package/dist/browser.js +1097 -134
- package/dist/browser.js.map +1 -1
- package/dist/main.js +1097 -134
- package/dist/main.js.map +1 -1
- package/dist/module.js +1097 -134
- package/dist/module.js.map +1 -1
- package/package.json +1 -1
- package/src/lib/archetypes.ts +51 -0
- package/src/lib/canvas/colors.ts +110 -2
- package/src/lib/canvas/draw.ts +41 -8
- package/src/lib/canvas/shapes/affinity.ts +479 -0
- package/src/lib/render.ts +277 -147
package/dist/main.js
CHANGED
|
@@ -27,17 +27,22 @@ $parcel$export(module.exports, "DEFAULT_CONFIG", () => $93cf69256c93baa9$export$
|
|
|
27
27
|
* identically in Node (@napi-rs/canvas) and browsers.
|
|
28
28
|
*
|
|
29
29
|
* Generation pipeline:
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
30
|
+
* 0. Archetype selection + shape palette + color hierarchy
|
|
31
|
+
* 1. Background — style from archetype, gradient mesh for depth
|
|
32
|
+
* 1b. Layered background — archetype-coherent shapes
|
|
33
|
+
* 2. Composition mode + symmetry
|
|
34
|
+
* 3. Focal points + void zones + hero avoidance field
|
|
35
|
+
* 4. Flow field
|
|
36
|
+
* 4b. Hero shape
|
|
37
|
+
* 5. Shape layers — palette-driven selection, affinity-aware styles,
|
|
38
|
+
* size echo, tangent placement, atmospheric depth
|
|
37
39
|
* 5b. Recursive nesting
|
|
38
|
-
* 6. Flow
|
|
39
|
-
*
|
|
40
|
-
*
|
|
40
|
+
* 6. Flow lines — variable color, branching, pressure simulation
|
|
41
|
+
* 6b. Symmetry mirroring
|
|
42
|
+
* 7. Noise texture
|
|
43
|
+
* 8. Vignette
|
|
44
|
+
* 9. Organic connecting curves
|
|
45
|
+
* 10. Post-processing — color grading, chromatic aberration, bloom
|
|
41
46
|
*/
|
|
42
47
|
// declare module 'color-scheme';
|
|
43
48
|
|
|
@@ -411,6 +416,67 @@ function $d016ad53434219a1$export$f2121afcad3d553f(hex, alpha) {
|
|
|
411
416
|
const [r, g, b] = $d016ad53434219a1$var$hexToRgb(hex);
|
|
412
417
|
return `rgba(${r},${g},${b},${alpha.toFixed(3)})`;
|
|
413
418
|
}
|
|
419
|
+
function $d016ad53434219a1$export$fabac4600b87056(colors, rng) {
|
|
420
|
+
if (colors.length < 3) return {
|
|
421
|
+
dominant: colors[0] || "#888888",
|
|
422
|
+
secondary: colors[1] || colors[0] || "#888888",
|
|
423
|
+
accent: colors[colors.length - 1] || "#888888",
|
|
424
|
+
all: colors
|
|
425
|
+
};
|
|
426
|
+
// Pick dominant as the color closest to the palette's average hue
|
|
427
|
+
const hsls = colors.map((c)=>$d016ad53434219a1$var$hexToHsl(c));
|
|
428
|
+
const avgHue = hsls.reduce((s, h)=>s + h[0], 0) / hsls.length;
|
|
429
|
+
let dominantIdx = 0;
|
|
430
|
+
let minDist = 360;
|
|
431
|
+
for(let i = 0; i < hsls.length; i++){
|
|
432
|
+
const d = Math.min(Math.abs(hsls[i][0] - avgHue), 360 - Math.abs(hsls[i][0] - avgHue));
|
|
433
|
+
if (d < minDist) {
|
|
434
|
+
minDist = d;
|
|
435
|
+
dominantIdx = i;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
// Accent is the color most distant from dominant in hue
|
|
439
|
+
let accentIdx = 0;
|
|
440
|
+
let maxDist = 0;
|
|
441
|
+
for(let i = 0; i < hsls.length; i++){
|
|
442
|
+
if (i === dominantIdx) continue;
|
|
443
|
+
const d = Math.min(Math.abs(hsls[i][0] - hsls[dominantIdx][0]), 360 - Math.abs(hsls[i][0] - hsls[dominantIdx][0]));
|
|
444
|
+
if (d > maxDist) {
|
|
445
|
+
maxDist = d;
|
|
446
|
+
accentIdx = i;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
// Secondary is the remaining color with highest saturation
|
|
450
|
+
let secondaryIdx = 0;
|
|
451
|
+
let maxSat = -1;
|
|
452
|
+
for(let i = 0; i < hsls.length; i++){
|
|
453
|
+
if (i === dominantIdx || i === accentIdx) continue;
|
|
454
|
+
if (hsls[i][1] > maxSat) {
|
|
455
|
+
maxSat = hsls[i][1];
|
|
456
|
+
secondaryIdx = i;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
if (secondaryIdx === dominantIdx) secondaryIdx = accentIdx === 0 ? 1 : 0;
|
|
460
|
+
return {
|
|
461
|
+
dominant: colors[dominantIdx],
|
|
462
|
+
secondary: colors[secondaryIdx],
|
|
463
|
+
accent: colors[accentIdx],
|
|
464
|
+
all: colors
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
function $d016ad53434219a1$export$b49f62f0a99da0e8(hierarchy, rng) {
|
|
468
|
+
const roll = rng();
|
|
469
|
+
if (roll < 0.60) return hierarchy.dominant;
|
|
470
|
+
if (roll < 0.85) return hierarchy.secondary;
|
|
471
|
+
return hierarchy.accent;
|
|
472
|
+
}
|
|
473
|
+
function $d016ad53434219a1$export$18a34c25ea7e724b(hex, rng, hueAmount = 8, slAmount = 0.06) {
|
|
474
|
+
const [h, s, l] = $d016ad53434219a1$var$hexToHsl(hex);
|
|
475
|
+
const newH = (h + (rng() - 0.5) * hueAmount * 2 + 360) % 360;
|
|
476
|
+
const newS = Math.max(0, Math.min(1, s + (rng() - 0.5) * slAmount * 2));
|
|
477
|
+
const newL = Math.max(0, Math.min(1, l + (rng() - 0.5) * slAmount * 2));
|
|
478
|
+
return $d016ad53434219a1$var$hslToHex(newH, newS, newL);
|
|
479
|
+
}
|
|
414
480
|
function $d016ad53434219a1$export$59539d800dbe6858(hex, rng, amount = 0.1) {
|
|
415
481
|
const [r, g, b] = $d016ad53434219a1$var$hexToRgb(hex);
|
|
416
482
|
const jit = ()=>(rng() - 0.5) * 2 * amount * 255;
|
|
@@ -450,6 +516,31 @@ function $d016ad53434219a1$export$90ad0e6170cf6af5(fgHex, bgLuminance, minContra
|
|
|
450
516
|
return $d016ad53434219a1$var$hslToHex(h, targetS, targetL);
|
|
451
517
|
}
|
|
452
518
|
}
|
|
519
|
+
function $d016ad53434219a1$export$4a3734b8c4b5c0e(hex, gradeHue, intensity) {
|
|
520
|
+
const [h, s, l] = $d016ad53434219a1$var$hexToHsl(hex);
|
|
521
|
+
// Blend hue toward the grade hue
|
|
522
|
+
const hueDiff = (gradeHue - h + 540) % 360 - 180;
|
|
523
|
+
const newH = (h + hueDiff * intensity * 0.3 + 360) % 360;
|
|
524
|
+
// Slightly unify saturation
|
|
525
|
+
const newS = Math.max(0, Math.min(1, s + (0.5 - s) * intensity * 0.15));
|
|
526
|
+
return $d016ad53434219a1$var$hslToHex(newH, newS, l);
|
|
527
|
+
}
|
|
528
|
+
function $d016ad53434219a1$export$6d1620b367f86f7a(rng) {
|
|
529
|
+
// Warm golden, cool blue, rosy, teal, amber
|
|
530
|
+
const GRADE_HUES = [
|
|
531
|
+
40,
|
|
532
|
+
220,
|
|
533
|
+
340,
|
|
534
|
+
175,
|
|
535
|
+
30
|
|
536
|
+
];
|
|
537
|
+
const hue = GRADE_HUES[Math.floor(rng() * GRADE_HUES.length)] + (rng() - 0.5) * 20;
|
|
538
|
+
const intensity = 0.15 + rng() * 0.25;
|
|
539
|
+
return {
|
|
540
|
+
hue: (hue + 360) % 360,
|
|
541
|
+
intensity: intensity
|
|
542
|
+
};
|
|
543
|
+
}
|
|
453
544
|
|
|
454
545
|
|
|
455
546
|
|
|
@@ -1480,23 +1571,50 @@ function $c3de8257a8baa3b0$export$71b514a25c47df50(ctx, shape, x, y, config) {
|
|
|
1480
1571
|
break;
|
|
1481
1572
|
case "watercolor":
|
|
1482
1573
|
{
|
|
1483
|
-
//
|
|
1484
|
-
const passes =
|
|
1574
|
+
// Improved watercolor: edge darkening + radial bleed + layered washes
|
|
1575
|
+
const passes = 4 + (rng ? Math.floor(rng() * 2) : 0);
|
|
1485
1576
|
const savedAlpha = ctx.globalAlpha;
|
|
1486
|
-
|
|
1577
|
+
// Pass 1: Base wash — large, soft fill at low opacity
|
|
1578
|
+
ctx.globalAlpha = savedAlpha * 0.15;
|
|
1579
|
+
ctx.save();
|
|
1580
|
+
const baseScale = 1.08 + (rng ? rng() * 0.04 : 0);
|
|
1581
|
+
ctx.scale(baseScale, baseScale);
|
|
1582
|
+
ctx.fill();
|
|
1583
|
+
ctx.restore();
|
|
1584
|
+
// Pass 2: Multiple offset washes with radial displacement
|
|
1585
|
+
ctx.globalAlpha = savedAlpha * (0.25 / passes * 2);
|
|
1487
1586
|
for(let p = 0; p < passes; p++){
|
|
1488
|
-
|
|
1489
|
-
const
|
|
1587
|
+
// Radial outward displacement (not uniform) for organic bleed
|
|
1588
|
+
const angle = rng ? rng() * Math.PI * 2 : p * Math.PI / 2;
|
|
1589
|
+
const dist = rng ? rng() * size * 0.05 : size * 0.02;
|
|
1590
|
+
const jx = Math.cos(angle) * dist;
|
|
1591
|
+
const jy = Math.sin(angle) * dist;
|
|
1490
1592
|
ctx.save();
|
|
1491
1593
|
ctx.translate(jx, jy);
|
|
1492
1594
|
ctx.fill();
|
|
1493
1595
|
ctx.restore();
|
|
1494
1596
|
}
|
|
1597
|
+
// Pass 3: Edge darkening — draw a slightly smaller shape with lighter fill
|
|
1598
|
+
// to simulate pigment pooling at boundaries
|
|
1599
|
+
ctx.globalAlpha = savedAlpha * 0.35;
|
|
1600
|
+
ctx.save();
|
|
1601
|
+
const innerScale = 0.85 + (rng ? rng() * 0.08 : 0);
|
|
1602
|
+
ctx.scale(innerScale, innerScale);
|
|
1603
|
+
// Lighten the fill for the inner area
|
|
1604
|
+
const origFill = ctx.fillStyle;
|
|
1605
|
+
if (typeof fillColor === "string") ctx.fillStyle = fillColor.replace(/[\d.]+\)$/, (m)=>{
|
|
1606
|
+
const v = parseFloat(m);
|
|
1607
|
+
return Math.min(1, v * 1.4).toFixed(2) + ")";
|
|
1608
|
+
});
|
|
1609
|
+
ctx.fill();
|
|
1610
|
+
ctx.fillStyle = origFill;
|
|
1611
|
+
ctx.restore();
|
|
1495
1612
|
ctx.globalAlpha = savedAlpha;
|
|
1496
|
-
//
|
|
1497
|
-
ctx.globalAlpha *= 0.
|
|
1613
|
+
// Soft stroke on top — thinner than normal for delicacy
|
|
1614
|
+
ctx.globalAlpha *= 0.25;
|
|
1615
|
+
ctx.lineWidth = strokeWidth * 0.6;
|
|
1498
1616
|
ctx.stroke();
|
|
1499
|
-
ctx.globalAlpha /= 0.
|
|
1617
|
+
ctx.globalAlpha /= 0.25;
|
|
1500
1618
|
break;
|
|
1501
1619
|
}
|
|
1502
1620
|
case "hatched":
|
|
@@ -1618,6 +1736,675 @@ function $c3de8257a8baa3b0$export$bb35a6995ddbf32d(ctx, shape, x, y, config) {
|
|
|
1618
1736
|
|
|
1619
1737
|
|
|
1620
1738
|
|
|
1739
|
+
/**
|
|
1740
|
+
* Shape affinity system — controls which shapes look good together,
|
|
1741
|
+
* quality tiers for different rendering contexts, and size preferences.
|
|
1742
|
+
*
|
|
1743
|
+
* This replaces the naive "pick any shape" approach with intentional
|
|
1744
|
+
* curation that produces more cohesive compositions.
|
|
1745
|
+
*/ // ── Quality tiers ───────────────────────────────────────────────────
|
|
1746
|
+
// Not all shapes render equally well at all sizes or in all contexts.
|
|
1747
|
+
// Tier 1 shapes are visually strong at any size; Tier 3 shapes need
|
|
1748
|
+
// specific conditions to look good.
|
|
1749
|
+
const $e73976f898150d4d$export$4343b39fe47bd82c = {
|
|
1750
|
+
// ── Basic shapes ──────────────────────────────────────────────
|
|
1751
|
+
circle: {
|
|
1752
|
+
tier: 1,
|
|
1753
|
+
minSizeFraction: 0.05,
|
|
1754
|
+
maxSizeFraction: 1.0,
|
|
1755
|
+
affinities: [
|
|
1756
|
+
"circle",
|
|
1757
|
+
"blob",
|
|
1758
|
+
"hexagon",
|
|
1759
|
+
"flowerOfLife",
|
|
1760
|
+
"seedOfLife"
|
|
1761
|
+
],
|
|
1762
|
+
category: "basic",
|
|
1763
|
+
heroCandidate: false,
|
|
1764
|
+
bestStyles: [
|
|
1765
|
+
"fill-only",
|
|
1766
|
+
"watercolor",
|
|
1767
|
+
"fill-and-stroke"
|
|
1768
|
+
]
|
|
1769
|
+
},
|
|
1770
|
+
square: {
|
|
1771
|
+
tier: 2,
|
|
1772
|
+
minSizeFraction: 0.08,
|
|
1773
|
+
maxSizeFraction: 0.7,
|
|
1774
|
+
affinities: [
|
|
1775
|
+
"square",
|
|
1776
|
+
"diamond",
|
|
1777
|
+
"superellipse",
|
|
1778
|
+
"islamicPattern"
|
|
1779
|
+
],
|
|
1780
|
+
category: "basic",
|
|
1781
|
+
heroCandidate: false,
|
|
1782
|
+
bestStyles: [
|
|
1783
|
+
"fill-and-stroke",
|
|
1784
|
+
"stroke-only",
|
|
1785
|
+
"hatched"
|
|
1786
|
+
]
|
|
1787
|
+
},
|
|
1788
|
+
triangle: {
|
|
1789
|
+
tier: 1,
|
|
1790
|
+
minSizeFraction: 0.06,
|
|
1791
|
+
maxSizeFraction: 0.9,
|
|
1792
|
+
affinities: [
|
|
1793
|
+
"triangle",
|
|
1794
|
+
"diamond",
|
|
1795
|
+
"hexagon",
|
|
1796
|
+
"merkaba",
|
|
1797
|
+
"sriYantra"
|
|
1798
|
+
],
|
|
1799
|
+
category: "basic",
|
|
1800
|
+
heroCandidate: false,
|
|
1801
|
+
bestStyles: [
|
|
1802
|
+
"fill-and-stroke",
|
|
1803
|
+
"fill-only",
|
|
1804
|
+
"watercolor"
|
|
1805
|
+
]
|
|
1806
|
+
},
|
|
1807
|
+
hexagon: {
|
|
1808
|
+
tier: 1,
|
|
1809
|
+
minSizeFraction: 0.05,
|
|
1810
|
+
maxSizeFraction: 1.0,
|
|
1811
|
+
affinities: [
|
|
1812
|
+
"hexagon",
|
|
1813
|
+
"circle",
|
|
1814
|
+
"flowerOfLife",
|
|
1815
|
+
"metatronsCube",
|
|
1816
|
+
"triangle"
|
|
1817
|
+
],
|
|
1818
|
+
category: "basic",
|
|
1819
|
+
heroCandidate: false,
|
|
1820
|
+
bestStyles: [
|
|
1821
|
+
"fill-only",
|
|
1822
|
+
"fill-and-stroke",
|
|
1823
|
+
"watercolor"
|
|
1824
|
+
]
|
|
1825
|
+
},
|
|
1826
|
+
star: {
|
|
1827
|
+
tier: 2,
|
|
1828
|
+
minSizeFraction: 0.08,
|
|
1829
|
+
maxSizeFraction: 0.6,
|
|
1830
|
+
affinities: [
|
|
1831
|
+
"star",
|
|
1832
|
+
"circle",
|
|
1833
|
+
"mandala",
|
|
1834
|
+
"spirograph"
|
|
1835
|
+
],
|
|
1836
|
+
category: "basic",
|
|
1837
|
+
heroCandidate: false,
|
|
1838
|
+
bestStyles: [
|
|
1839
|
+
"fill-and-stroke",
|
|
1840
|
+
"stroke-only",
|
|
1841
|
+
"dashed"
|
|
1842
|
+
]
|
|
1843
|
+
},
|
|
1844
|
+
"jacked-star": {
|
|
1845
|
+
tier: 3,
|
|
1846
|
+
minSizeFraction: 0.1,
|
|
1847
|
+
maxSizeFraction: 0.4,
|
|
1848
|
+
affinities: [
|
|
1849
|
+
"star",
|
|
1850
|
+
"circle"
|
|
1851
|
+
],
|
|
1852
|
+
category: "basic",
|
|
1853
|
+
heroCandidate: false,
|
|
1854
|
+
bestStyles: [
|
|
1855
|
+
"stroke-only",
|
|
1856
|
+
"dashed"
|
|
1857
|
+
]
|
|
1858
|
+
},
|
|
1859
|
+
heart: {
|
|
1860
|
+
tier: 3,
|
|
1861
|
+
minSizeFraction: 0.1,
|
|
1862
|
+
maxSizeFraction: 0.5,
|
|
1863
|
+
affinities: [
|
|
1864
|
+
"circle",
|
|
1865
|
+
"blob"
|
|
1866
|
+
],
|
|
1867
|
+
category: "basic",
|
|
1868
|
+
heroCandidate: false,
|
|
1869
|
+
bestStyles: [
|
|
1870
|
+
"fill-only",
|
|
1871
|
+
"watercolor"
|
|
1872
|
+
]
|
|
1873
|
+
},
|
|
1874
|
+
diamond: {
|
|
1875
|
+
tier: 2,
|
|
1876
|
+
minSizeFraction: 0.06,
|
|
1877
|
+
maxSizeFraction: 0.8,
|
|
1878
|
+
affinities: [
|
|
1879
|
+
"diamond",
|
|
1880
|
+
"triangle",
|
|
1881
|
+
"square",
|
|
1882
|
+
"merkaba"
|
|
1883
|
+
],
|
|
1884
|
+
category: "basic",
|
|
1885
|
+
heroCandidate: false,
|
|
1886
|
+
bestStyles: [
|
|
1887
|
+
"fill-and-stroke",
|
|
1888
|
+
"fill-only",
|
|
1889
|
+
"double-stroke"
|
|
1890
|
+
]
|
|
1891
|
+
},
|
|
1892
|
+
cube: {
|
|
1893
|
+
tier: 3,
|
|
1894
|
+
minSizeFraction: 0.08,
|
|
1895
|
+
maxSizeFraction: 0.5,
|
|
1896
|
+
affinities: [
|
|
1897
|
+
"square",
|
|
1898
|
+
"diamond"
|
|
1899
|
+
],
|
|
1900
|
+
category: "basic",
|
|
1901
|
+
heroCandidate: false,
|
|
1902
|
+
bestStyles: [
|
|
1903
|
+
"stroke-only",
|
|
1904
|
+
"fill-and-stroke"
|
|
1905
|
+
]
|
|
1906
|
+
},
|
|
1907
|
+
// ── Complex shapes ────────────────────────────────────────────
|
|
1908
|
+
platonicSolid: {
|
|
1909
|
+
tier: 2,
|
|
1910
|
+
minSizeFraction: 0.15,
|
|
1911
|
+
maxSizeFraction: 0.8,
|
|
1912
|
+
affinities: [
|
|
1913
|
+
"metatronsCube",
|
|
1914
|
+
"merkaba",
|
|
1915
|
+
"hexagon",
|
|
1916
|
+
"triangle"
|
|
1917
|
+
],
|
|
1918
|
+
category: "complex",
|
|
1919
|
+
heroCandidate: true,
|
|
1920
|
+
bestStyles: [
|
|
1921
|
+
"stroke-only",
|
|
1922
|
+
"double-stroke",
|
|
1923
|
+
"dashed"
|
|
1924
|
+
]
|
|
1925
|
+
},
|
|
1926
|
+
fibonacciSpiral: {
|
|
1927
|
+
tier: 1,
|
|
1928
|
+
minSizeFraction: 0.2,
|
|
1929
|
+
maxSizeFraction: 1.0,
|
|
1930
|
+
affinities: [
|
|
1931
|
+
"circle",
|
|
1932
|
+
"rose",
|
|
1933
|
+
"spirograph",
|
|
1934
|
+
"flowerOfLife"
|
|
1935
|
+
],
|
|
1936
|
+
category: "complex",
|
|
1937
|
+
heroCandidate: true,
|
|
1938
|
+
bestStyles: [
|
|
1939
|
+
"stroke-only",
|
|
1940
|
+
"incomplete",
|
|
1941
|
+
"watercolor"
|
|
1942
|
+
]
|
|
1943
|
+
},
|
|
1944
|
+
islamicPattern: {
|
|
1945
|
+
tier: 2,
|
|
1946
|
+
minSizeFraction: 0.25,
|
|
1947
|
+
maxSizeFraction: 0.9,
|
|
1948
|
+
affinities: [
|
|
1949
|
+
"square",
|
|
1950
|
+
"hexagon",
|
|
1951
|
+
"star",
|
|
1952
|
+
"mandala"
|
|
1953
|
+
],
|
|
1954
|
+
category: "complex",
|
|
1955
|
+
heroCandidate: true,
|
|
1956
|
+
bestStyles: [
|
|
1957
|
+
"stroke-only",
|
|
1958
|
+
"dashed",
|
|
1959
|
+
"hatched"
|
|
1960
|
+
]
|
|
1961
|
+
},
|
|
1962
|
+
celticKnot: {
|
|
1963
|
+
tier: 2,
|
|
1964
|
+
minSizeFraction: 0.2,
|
|
1965
|
+
maxSizeFraction: 0.7,
|
|
1966
|
+
affinities: [
|
|
1967
|
+
"circle",
|
|
1968
|
+
"lissajous",
|
|
1969
|
+
"spirograph"
|
|
1970
|
+
],
|
|
1971
|
+
category: "complex",
|
|
1972
|
+
heroCandidate: true,
|
|
1973
|
+
bestStyles: [
|
|
1974
|
+
"stroke-only",
|
|
1975
|
+
"double-stroke"
|
|
1976
|
+
]
|
|
1977
|
+
},
|
|
1978
|
+
merkaba: {
|
|
1979
|
+
tier: 1,
|
|
1980
|
+
minSizeFraction: 0.15,
|
|
1981
|
+
maxSizeFraction: 1.0,
|
|
1982
|
+
affinities: [
|
|
1983
|
+
"triangle",
|
|
1984
|
+
"diamond",
|
|
1985
|
+
"sriYantra",
|
|
1986
|
+
"metatronsCube"
|
|
1987
|
+
],
|
|
1988
|
+
category: "complex",
|
|
1989
|
+
heroCandidate: true,
|
|
1990
|
+
bestStyles: [
|
|
1991
|
+
"stroke-only",
|
|
1992
|
+
"fill-and-stroke",
|
|
1993
|
+
"double-stroke"
|
|
1994
|
+
]
|
|
1995
|
+
},
|
|
1996
|
+
mandala: {
|
|
1997
|
+
tier: 1,
|
|
1998
|
+
minSizeFraction: 0.2,
|
|
1999
|
+
maxSizeFraction: 1.0,
|
|
2000
|
+
affinities: [
|
|
2001
|
+
"circle",
|
|
2002
|
+
"flowerOfLife",
|
|
2003
|
+
"spirograph",
|
|
2004
|
+
"rose"
|
|
2005
|
+
],
|
|
2006
|
+
category: "complex",
|
|
2007
|
+
heroCandidate: true,
|
|
2008
|
+
bestStyles: [
|
|
2009
|
+
"stroke-only",
|
|
2010
|
+
"dashed",
|
|
2011
|
+
"incomplete"
|
|
2012
|
+
]
|
|
2013
|
+
},
|
|
2014
|
+
fractal: {
|
|
2015
|
+
tier: 2,
|
|
2016
|
+
minSizeFraction: 0.2,
|
|
2017
|
+
maxSizeFraction: 0.8,
|
|
2018
|
+
affinities: [
|
|
2019
|
+
"blob",
|
|
2020
|
+
"lissajous",
|
|
2021
|
+
"circle"
|
|
2022
|
+
],
|
|
2023
|
+
category: "complex",
|
|
2024
|
+
heroCandidate: true,
|
|
2025
|
+
bestStyles: [
|
|
2026
|
+
"stroke-only",
|
|
2027
|
+
"incomplete"
|
|
2028
|
+
]
|
|
2029
|
+
},
|
|
2030
|
+
// ── Sacred shapes ─────────────────────────────────────────────
|
|
2031
|
+
flowerOfLife: {
|
|
2032
|
+
tier: 1,
|
|
2033
|
+
minSizeFraction: 0.2,
|
|
2034
|
+
maxSizeFraction: 1.0,
|
|
2035
|
+
affinities: [
|
|
2036
|
+
"circle",
|
|
2037
|
+
"hexagon",
|
|
2038
|
+
"seedOfLife",
|
|
2039
|
+
"eggOfLife",
|
|
2040
|
+
"metatronsCube"
|
|
2041
|
+
],
|
|
2042
|
+
category: "sacred",
|
|
2043
|
+
heroCandidate: true,
|
|
2044
|
+
bestStyles: [
|
|
2045
|
+
"stroke-only",
|
|
2046
|
+
"watercolor",
|
|
2047
|
+
"incomplete"
|
|
2048
|
+
]
|
|
2049
|
+
},
|
|
2050
|
+
treeOfLife: {
|
|
2051
|
+
tier: 2,
|
|
2052
|
+
minSizeFraction: 0.25,
|
|
2053
|
+
maxSizeFraction: 0.9,
|
|
2054
|
+
affinities: [
|
|
2055
|
+
"circle",
|
|
2056
|
+
"flowerOfLife",
|
|
2057
|
+
"metatronsCube"
|
|
2058
|
+
],
|
|
2059
|
+
category: "sacred",
|
|
2060
|
+
heroCandidate: true,
|
|
2061
|
+
bestStyles: [
|
|
2062
|
+
"stroke-only",
|
|
2063
|
+
"double-stroke"
|
|
2064
|
+
]
|
|
2065
|
+
},
|
|
2066
|
+
metatronsCube: {
|
|
2067
|
+
tier: 1,
|
|
2068
|
+
minSizeFraction: 0.2,
|
|
2069
|
+
maxSizeFraction: 1.0,
|
|
2070
|
+
affinities: [
|
|
2071
|
+
"hexagon",
|
|
2072
|
+
"flowerOfLife",
|
|
2073
|
+
"platonicSolid",
|
|
2074
|
+
"merkaba"
|
|
2075
|
+
],
|
|
2076
|
+
category: "sacred",
|
|
2077
|
+
heroCandidate: true,
|
|
2078
|
+
bestStyles: [
|
|
2079
|
+
"stroke-only",
|
|
2080
|
+
"dashed",
|
|
2081
|
+
"incomplete"
|
|
2082
|
+
]
|
|
2083
|
+
},
|
|
2084
|
+
sriYantra: {
|
|
2085
|
+
tier: 1,
|
|
2086
|
+
minSizeFraction: 0.2,
|
|
2087
|
+
maxSizeFraction: 1.0,
|
|
2088
|
+
affinities: [
|
|
2089
|
+
"triangle",
|
|
2090
|
+
"merkaba",
|
|
2091
|
+
"mandala",
|
|
2092
|
+
"diamond"
|
|
2093
|
+
],
|
|
2094
|
+
category: "sacred",
|
|
2095
|
+
heroCandidate: true,
|
|
2096
|
+
bestStyles: [
|
|
2097
|
+
"stroke-only",
|
|
2098
|
+
"fill-and-stroke",
|
|
2099
|
+
"double-stroke"
|
|
2100
|
+
]
|
|
2101
|
+
},
|
|
2102
|
+
seedOfLife: {
|
|
2103
|
+
tier: 1,
|
|
2104
|
+
minSizeFraction: 0.15,
|
|
2105
|
+
maxSizeFraction: 0.9,
|
|
2106
|
+
affinities: [
|
|
2107
|
+
"circle",
|
|
2108
|
+
"flowerOfLife",
|
|
2109
|
+
"eggOfLife",
|
|
2110
|
+
"hexagon"
|
|
2111
|
+
],
|
|
2112
|
+
category: "sacred",
|
|
2113
|
+
heroCandidate: true,
|
|
2114
|
+
bestStyles: [
|
|
2115
|
+
"stroke-only",
|
|
2116
|
+
"watercolor",
|
|
2117
|
+
"fill-only"
|
|
2118
|
+
]
|
|
2119
|
+
},
|
|
2120
|
+
vesicaPiscis: {
|
|
2121
|
+
tier: 2,
|
|
2122
|
+
minSizeFraction: 0.15,
|
|
2123
|
+
maxSizeFraction: 0.7,
|
|
2124
|
+
affinities: [
|
|
2125
|
+
"circle",
|
|
2126
|
+
"seedOfLife",
|
|
2127
|
+
"flowerOfLife"
|
|
2128
|
+
],
|
|
2129
|
+
category: "sacred",
|
|
2130
|
+
heroCandidate: false,
|
|
2131
|
+
bestStyles: [
|
|
2132
|
+
"stroke-only",
|
|
2133
|
+
"watercolor"
|
|
2134
|
+
]
|
|
2135
|
+
},
|
|
2136
|
+
torus: {
|
|
2137
|
+
tier: 3,
|
|
2138
|
+
minSizeFraction: 0.2,
|
|
2139
|
+
maxSizeFraction: 0.6,
|
|
2140
|
+
affinities: [
|
|
2141
|
+
"circle",
|
|
2142
|
+
"spirograph",
|
|
2143
|
+
"waveRing"
|
|
2144
|
+
],
|
|
2145
|
+
category: "sacred",
|
|
2146
|
+
heroCandidate: false,
|
|
2147
|
+
bestStyles: [
|
|
2148
|
+
"stroke-only",
|
|
2149
|
+
"dashed"
|
|
2150
|
+
]
|
|
2151
|
+
},
|
|
2152
|
+
eggOfLife: {
|
|
2153
|
+
tier: 2,
|
|
2154
|
+
minSizeFraction: 0.15,
|
|
2155
|
+
maxSizeFraction: 0.8,
|
|
2156
|
+
affinities: [
|
|
2157
|
+
"circle",
|
|
2158
|
+
"seedOfLife",
|
|
2159
|
+
"flowerOfLife"
|
|
2160
|
+
],
|
|
2161
|
+
category: "sacred",
|
|
2162
|
+
heroCandidate: true,
|
|
2163
|
+
bestStyles: [
|
|
2164
|
+
"stroke-only",
|
|
2165
|
+
"watercolor"
|
|
2166
|
+
]
|
|
2167
|
+
},
|
|
2168
|
+
// ── Procedural shapes ─────────────────────────────────────────
|
|
2169
|
+
blob: {
|
|
2170
|
+
tier: 1,
|
|
2171
|
+
minSizeFraction: 0.05,
|
|
2172
|
+
maxSizeFraction: 1.0,
|
|
2173
|
+
affinities: [
|
|
2174
|
+
"blob",
|
|
2175
|
+
"circle",
|
|
2176
|
+
"superellipse",
|
|
2177
|
+
"waveRing"
|
|
2178
|
+
],
|
|
2179
|
+
category: "procedural",
|
|
2180
|
+
heroCandidate: false,
|
|
2181
|
+
bestStyles: [
|
|
2182
|
+
"fill-only",
|
|
2183
|
+
"watercolor",
|
|
2184
|
+
"fill-and-stroke"
|
|
2185
|
+
]
|
|
2186
|
+
},
|
|
2187
|
+
ngon: {
|
|
2188
|
+
tier: 2,
|
|
2189
|
+
minSizeFraction: 0.06,
|
|
2190
|
+
maxSizeFraction: 0.8,
|
|
2191
|
+
affinities: [
|
|
2192
|
+
"hexagon",
|
|
2193
|
+
"triangle",
|
|
2194
|
+
"diamond",
|
|
2195
|
+
"superellipse"
|
|
2196
|
+
],
|
|
2197
|
+
category: "procedural",
|
|
2198
|
+
heroCandidate: false,
|
|
2199
|
+
bestStyles: [
|
|
2200
|
+
"fill-and-stroke",
|
|
2201
|
+
"fill-only",
|
|
2202
|
+
"hatched"
|
|
2203
|
+
]
|
|
2204
|
+
},
|
|
2205
|
+
lissajous: {
|
|
2206
|
+
tier: 2,
|
|
2207
|
+
minSizeFraction: 0.15,
|
|
2208
|
+
maxSizeFraction: 0.8,
|
|
2209
|
+
affinities: [
|
|
2210
|
+
"spirograph",
|
|
2211
|
+
"rose",
|
|
2212
|
+
"celticKnot",
|
|
2213
|
+
"fibonacciSpiral"
|
|
2214
|
+
],
|
|
2215
|
+
category: "procedural",
|
|
2216
|
+
heroCandidate: false,
|
|
2217
|
+
bestStyles: [
|
|
2218
|
+
"stroke-only",
|
|
2219
|
+
"incomplete",
|
|
2220
|
+
"dashed"
|
|
2221
|
+
]
|
|
2222
|
+
},
|
|
2223
|
+
superellipse: {
|
|
2224
|
+
tier: 1,
|
|
2225
|
+
minSizeFraction: 0.05,
|
|
2226
|
+
maxSizeFraction: 1.0,
|
|
2227
|
+
affinities: [
|
|
2228
|
+
"circle",
|
|
2229
|
+
"square",
|
|
2230
|
+
"blob",
|
|
2231
|
+
"hexagon"
|
|
2232
|
+
],
|
|
2233
|
+
category: "procedural",
|
|
2234
|
+
heroCandidate: false,
|
|
2235
|
+
bestStyles: [
|
|
2236
|
+
"fill-only",
|
|
2237
|
+
"watercolor",
|
|
2238
|
+
"fill-and-stroke"
|
|
2239
|
+
]
|
|
2240
|
+
},
|
|
2241
|
+
spirograph: {
|
|
2242
|
+
tier: 1,
|
|
2243
|
+
minSizeFraction: 0.15,
|
|
2244
|
+
maxSizeFraction: 0.9,
|
|
2245
|
+
affinities: [
|
|
2246
|
+
"rose",
|
|
2247
|
+
"lissajous",
|
|
2248
|
+
"mandala",
|
|
2249
|
+
"flowerOfLife"
|
|
2250
|
+
],
|
|
2251
|
+
category: "procedural",
|
|
2252
|
+
heroCandidate: true,
|
|
2253
|
+
bestStyles: [
|
|
2254
|
+
"stroke-only",
|
|
2255
|
+
"incomplete",
|
|
2256
|
+
"dashed"
|
|
2257
|
+
]
|
|
2258
|
+
},
|
|
2259
|
+
waveRing: {
|
|
2260
|
+
tier: 2,
|
|
2261
|
+
minSizeFraction: 0.1,
|
|
2262
|
+
maxSizeFraction: 0.8,
|
|
2263
|
+
affinities: [
|
|
2264
|
+
"circle",
|
|
2265
|
+
"blob",
|
|
2266
|
+
"torus",
|
|
2267
|
+
"spirograph"
|
|
2268
|
+
],
|
|
2269
|
+
category: "procedural",
|
|
2270
|
+
heroCandidate: false,
|
|
2271
|
+
bestStyles: [
|
|
2272
|
+
"stroke-only",
|
|
2273
|
+
"dashed",
|
|
2274
|
+
"incomplete"
|
|
2275
|
+
]
|
|
2276
|
+
},
|
|
2277
|
+
rose: {
|
|
2278
|
+
tier: 1,
|
|
2279
|
+
minSizeFraction: 0.1,
|
|
2280
|
+
maxSizeFraction: 0.9,
|
|
2281
|
+
affinities: [
|
|
2282
|
+
"spirograph",
|
|
2283
|
+
"mandala",
|
|
2284
|
+
"flowerOfLife",
|
|
2285
|
+
"circle"
|
|
2286
|
+
],
|
|
2287
|
+
category: "procedural",
|
|
2288
|
+
heroCandidate: true,
|
|
2289
|
+
bestStyles: [
|
|
2290
|
+
"stroke-only",
|
|
2291
|
+
"fill-only",
|
|
2292
|
+
"watercolor"
|
|
2293
|
+
]
|
|
2294
|
+
}
|
|
2295
|
+
};
|
|
2296
|
+
function $e73976f898150d4d$export$4a95df8944b5033b(rng, shapeNames, archetypeName) {
|
|
2297
|
+
const available = shapeNames.filter((s)=>$e73976f898150d4d$export$4343b39fe47bd82c[s]);
|
|
2298
|
+
// Pick a seed shape — tier 1 shapes that are hero candidates
|
|
2299
|
+
const heroPool = available.filter((s)=>$e73976f898150d4d$export$4343b39fe47bd82c[s].tier === 1 && $e73976f898150d4d$export$4343b39fe47bd82c[s].heroCandidate);
|
|
2300
|
+
const seedShape = heroPool.length > 0 ? heroPool[Math.floor(rng() * heroPool.length)] : available[Math.floor(rng() * available.length)];
|
|
2301
|
+
const seedProfile = $e73976f898150d4d$export$4343b39fe47bd82c[seedShape];
|
|
2302
|
+
// Primary: seed shape + its direct affinities (tier 1-2 only)
|
|
2303
|
+
const primaryCandidates = [
|
|
2304
|
+
seedShape,
|
|
2305
|
+
...seedProfile.affinities
|
|
2306
|
+
].filter((s)=>available.includes(s)).filter((s)=>$e73976f898150d4d$export$4343b39fe47bd82c[s].tier <= 2);
|
|
2307
|
+
const primary = [
|
|
2308
|
+
...new Set(primaryCandidates)
|
|
2309
|
+
].slice(0, 5);
|
|
2310
|
+
// Supporting: affinities of affinities, plus same-category shapes
|
|
2311
|
+
const supportingSet = new Set();
|
|
2312
|
+
for (const p of primary){
|
|
2313
|
+
const profile = $e73976f898150d4d$export$4343b39fe47bd82c[p];
|
|
2314
|
+
if (!profile) continue;
|
|
2315
|
+
for (const aff of profile.affinities)if (available.includes(aff) && !primary.includes(aff)) supportingSet.add(aff);
|
|
2316
|
+
}
|
|
2317
|
+
// Add same-category tier 1-2 shapes
|
|
2318
|
+
for (const s of available){
|
|
2319
|
+
const p = $e73976f898150d4d$export$4343b39fe47bd82c[s];
|
|
2320
|
+
if (p.category === seedProfile.category && p.tier <= 2 && !primary.includes(s)) supportingSet.add(s);
|
|
2321
|
+
}
|
|
2322
|
+
const supporting = [
|
|
2323
|
+
...supportingSet
|
|
2324
|
+
].slice(0, 6);
|
|
2325
|
+
// Accents: tier 1 shapes from other categories for contrast
|
|
2326
|
+
const usedCategories = new Set([
|
|
2327
|
+
...primary,
|
|
2328
|
+
...supporting
|
|
2329
|
+
].map((s)=>$e73976f898150d4d$export$4343b39fe47bd82c[s]?.category));
|
|
2330
|
+
const accentCandidates = available.filter((s)=>!primary.includes(s) && !supporting.includes(s) && $e73976f898150d4d$export$4343b39fe47bd82c[s].tier <= 2 && !usedCategories.has($e73976f898150d4d$export$4343b39fe47bd82c[s].category));
|
|
2331
|
+
// Shuffle and take a few
|
|
2332
|
+
const accents = [];
|
|
2333
|
+
const shuffled = [
|
|
2334
|
+
...accentCandidates
|
|
2335
|
+
];
|
|
2336
|
+
for(let i = shuffled.length - 1; i > 0; i--){
|
|
2337
|
+
const j = Math.floor(rng() * (i + 1));
|
|
2338
|
+
[shuffled[i], shuffled[j]] = [
|
|
2339
|
+
shuffled[j],
|
|
2340
|
+
shuffled[i]
|
|
2341
|
+
];
|
|
2342
|
+
}
|
|
2343
|
+
accents.push(...shuffled.slice(0, 3));
|
|
2344
|
+
// For certain archetypes, bias the palette
|
|
2345
|
+
if (archetypeName === "geometric-precision") // Remove blobs and organic shapes from primary
|
|
2346
|
+
return {
|
|
2347
|
+
primary: primary.filter((s)=>$e73976f898150d4d$export$4343b39fe47bd82c[s]?.category !== "procedural" || s === "ngon"),
|
|
2348
|
+
supporting: supporting.filter((s)=>s !== "blob"),
|
|
2349
|
+
accents: accents
|
|
2350
|
+
};
|
|
2351
|
+
if (archetypeName === "organic-flow") {
|
|
2352
|
+
// Boost procedural/organic shapes
|
|
2353
|
+
const organicBoost = available.filter((s)=>[
|
|
2354
|
+
"blob",
|
|
2355
|
+
"superellipse",
|
|
2356
|
+
"waveRing",
|
|
2357
|
+
"rose"
|
|
2358
|
+
].includes(s) && !primary.includes(s));
|
|
2359
|
+
return {
|
|
2360
|
+
primary: [
|
|
2361
|
+
...primary,
|
|
2362
|
+
...organicBoost.slice(0, 2)
|
|
2363
|
+
],
|
|
2364
|
+
supporting: supporting,
|
|
2365
|
+
accents: accents
|
|
2366
|
+
};
|
|
2367
|
+
}
|
|
2368
|
+
return {
|
|
2369
|
+
primary: primary,
|
|
2370
|
+
supporting: supporting,
|
|
2371
|
+
accents: accents
|
|
2372
|
+
};
|
|
2373
|
+
}
|
|
2374
|
+
function $e73976f898150d4d$export$3c37d9a045754d0e(palette, rng, sizeFraction) {
|
|
2375
|
+
// Filter each tier by size constraints
|
|
2376
|
+
const validPrimary = palette.primary.filter((s)=>{
|
|
2377
|
+
const p = $e73976f898150d4d$export$4343b39fe47bd82c[s];
|
|
2378
|
+
return p && sizeFraction >= p.minSizeFraction && sizeFraction <= p.maxSizeFraction;
|
|
2379
|
+
});
|
|
2380
|
+
const validSupporting = palette.supporting.filter((s)=>{
|
|
2381
|
+
const p = $e73976f898150d4d$export$4343b39fe47bd82c[s];
|
|
2382
|
+
return p && sizeFraction >= p.minSizeFraction && sizeFraction <= p.maxSizeFraction;
|
|
2383
|
+
});
|
|
2384
|
+
const validAccents = palette.accents.filter((s)=>{
|
|
2385
|
+
const p = $e73976f898150d4d$export$4343b39fe47bd82c[s];
|
|
2386
|
+
return p && sizeFraction >= p.minSizeFraction && sizeFraction <= p.maxSizeFraction;
|
|
2387
|
+
});
|
|
2388
|
+
const roll = rng();
|
|
2389
|
+
if (roll < 0.60 && validPrimary.length > 0) return validPrimary[Math.floor(rng() * validPrimary.length)];
|
|
2390
|
+
if (roll < 0.90 && validSupporting.length > 0) return validSupporting[Math.floor(rng() * validSupporting.length)];
|
|
2391
|
+
if (validAccents.length > 0) return validAccents[Math.floor(rng() * validAccents.length)];
|
|
2392
|
+
// Fallback: any valid primary or supporting
|
|
2393
|
+
const fallback = [
|
|
2394
|
+
...validPrimary,
|
|
2395
|
+
...validSupporting
|
|
2396
|
+
];
|
|
2397
|
+
if (fallback.length > 0) return fallback[Math.floor(rng() * fallback.length)];
|
|
2398
|
+
// Ultimate fallback
|
|
2399
|
+
return palette.primary[0] || "circle";
|
|
2400
|
+
}
|
|
2401
|
+
function $e73976f898150d4d$export$ab873bb6fb56c1a8(shapeName, layerStyle, rng) {
|
|
2402
|
+
const profile = $e73976f898150d4d$export$4343b39fe47bd82c[shapeName];
|
|
2403
|
+
if (!profile || rng() > 0.7) return layerStyle;
|
|
2404
|
+
return profile.bestStyles[Math.floor(rng() * profile.bestStyles.length)];
|
|
2405
|
+
}
|
|
2406
|
+
|
|
2407
|
+
|
|
1621
2408
|
|
|
1622
2409
|
/**
|
|
1623
2410
|
* Configuration options for image generation.
|
|
@@ -1832,6 +2619,69 @@ const $f89bc858f7202849$var$ARCHETYPES = [
|
|
|
1832
2619
|
sizePower: 2.5,
|
|
1833
2620
|
invertForeground: false
|
|
1834
2621
|
},
|
|
2622
|
+
{
|
|
2623
|
+
name: "watercolor-wash",
|
|
2624
|
+
gridSize: 3,
|
|
2625
|
+
layers: 3,
|
|
2626
|
+
baseOpacity: 0.25,
|
|
2627
|
+
opacityReduction: 0.03,
|
|
2628
|
+
minShapeSize: 200,
|
|
2629
|
+
maxShapeSize: 700,
|
|
2630
|
+
backgroundStyle: "radial-light",
|
|
2631
|
+
paletteMode: "harmonious",
|
|
2632
|
+
preferredStyles: [
|
|
2633
|
+
"watercolor",
|
|
2634
|
+
"fill-only",
|
|
2635
|
+
"incomplete"
|
|
2636
|
+
],
|
|
2637
|
+
flowLineMultiplier: 0.5,
|
|
2638
|
+
heroShape: false,
|
|
2639
|
+
glowMultiplier: 0.3,
|
|
2640
|
+
sizePower: 0.6,
|
|
2641
|
+
invertForeground: false
|
|
2642
|
+
},
|
|
2643
|
+
{
|
|
2644
|
+
name: "op-art",
|
|
2645
|
+
gridSize: 8,
|
|
2646
|
+
layers: 2,
|
|
2647
|
+
baseOpacity: 0.95,
|
|
2648
|
+
opacityReduction: 0.05,
|
|
2649
|
+
minShapeSize: 20,
|
|
2650
|
+
maxShapeSize: 200,
|
|
2651
|
+
backgroundStyle: "solid-light",
|
|
2652
|
+
paletteMode: "high-contrast",
|
|
2653
|
+
preferredStyles: [
|
|
2654
|
+
"fill-and-stroke",
|
|
2655
|
+
"stroke-only",
|
|
2656
|
+
"dashed"
|
|
2657
|
+
],
|
|
2658
|
+
flowLineMultiplier: 0,
|
|
2659
|
+
heroShape: false,
|
|
2660
|
+
glowMultiplier: 0,
|
|
2661
|
+
sizePower: 0.4,
|
|
2662
|
+
invertForeground: false
|
|
2663
|
+
},
|
|
2664
|
+
{
|
|
2665
|
+
name: "collage",
|
|
2666
|
+
gridSize: 4,
|
|
2667
|
+
layers: 3,
|
|
2668
|
+
baseOpacity: 0.9,
|
|
2669
|
+
opacityReduction: 0.08,
|
|
2670
|
+
minShapeSize: 80,
|
|
2671
|
+
maxShapeSize: 500,
|
|
2672
|
+
backgroundStyle: "solid-light",
|
|
2673
|
+
paletteMode: "duotone",
|
|
2674
|
+
preferredStyles: [
|
|
2675
|
+
"fill-and-stroke",
|
|
2676
|
+
"fill-only",
|
|
2677
|
+
"double-stroke"
|
|
2678
|
+
],
|
|
2679
|
+
flowLineMultiplier: 0,
|
|
2680
|
+
heroShape: true,
|
|
2681
|
+
glowMultiplier: 0,
|
|
2682
|
+
sizePower: 0.7,
|
|
2683
|
+
invertForeground: false
|
|
2684
|
+
},
|
|
1835
2685
|
{
|
|
1836
2686
|
name: "classic",
|
|
1837
2687
|
gridSize: 5,
|
|
@@ -1859,26 +2709,7 @@ function $f89bc858f7202849$export$f1142fd7da4d6590(rng) {
|
|
|
1859
2709
|
}
|
|
1860
2710
|
|
|
1861
2711
|
|
|
1862
|
-
// ── Shape categories for weighted selection
|
|
1863
|
-
const $4f72c5a314eddf25$var$BASIC_SHAPES = [
|
|
1864
|
-
"circle",
|
|
1865
|
-
"square",
|
|
1866
|
-
"triangle",
|
|
1867
|
-
"hexagon",
|
|
1868
|
-
"diamond",
|
|
1869
|
-
"cube"
|
|
1870
|
-
];
|
|
1871
|
-
const $4f72c5a314eddf25$var$COMPLEX_SHAPES = [
|
|
1872
|
-
"star",
|
|
1873
|
-
"jacked-star",
|
|
1874
|
-
"heart",
|
|
1875
|
-
"platonicSolid",
|
|
1876
|
-
"fibonacciSpiral",
|
|
1877
|
-
"islamicPattern",
|
|
1878
|
-
"celticKnot",
|
|
1879
|
-
"merkaba",
|
|
1880
|
-
"fractal"
|
|
1881
|
-
];
|
|
2712
|
+
// ── Shape categories for weighted selection (legacy fallback) ───────
|
|
1882
2713
|
const $4f72c5a314eddf25$var$SACRED_SHAPES = [
|
|
1883
2714
|
"mandala",
|
|
1884
2715
|
"flowerOfLife",
|
|
@@ -1890,15 +2721,6 @@ const $4f72c5a314eddf25$var$SACRED_SHAPES = [
|
|
|
1890
2721
|
"torus",
|
|
1891
2722
|
"eggOfLife"
|
|
1892
2723
|
];
|
|
1893
|
-
const $4f72c5a314eddf25$var$PROCEDURAL_SHAPES = [
|
|
1894
|
-
"blob",
|
|
1895
|
-
"ngon",
|
|
1896
|
-
"lissajous",
|
|
1897
|
-
"superellipse",
|
|
1898
|
-
"spirograph",
|
|
1899
|
-
"waveRing",
|
|
1900
|
-
"rose"
|
|
1901
|
-
];
|
|
1902
2724
|
const $4f72c5a314eddf25$var$COMPOSITION_MODES = [
|
|
1903
2725
|
"radial",
|
|
1904
2726
|
"flow-field",
|
|
@@ -1906,23 +2728,6 @@ const $4f72c5a314eddf25$var$COMPOSITION_MODES = [
|
|
|
1906
2728
|
"grid-subdivision",
|
|
1907
2729
|
"clustered"
|
|
1908
2730
|
];
|
|
1909
|
-
// ── Helper: pick shape with layer-aware weighting ───────────────────
|
|
1910
|
-
function $4f72c5a314eddf25$var$pickShape(rng, layerRatio, shapeNames) {
|
|
1911
|
-
const basicW = 1 - layerRatio * 0.6;
|
|
1912
|
-
const complexW = 0.3 + layerRatio * 0.3;
|
|
1913
|
-
const sacredW = 0.1 + layerRatio * 0.4;
|
|
1914
|
-
const proceduralW = 0.25 + layerRatio * 0.2; // always present, grows with depth
|
|
1915
|
-
const total = basicW + complexW + sacredW + proceduralW;
|
|
1916
|
-
const roll = rng() * total;
|
|
1917
|
-
let pool;
|
|
1918
|
-
if (roll < basicW) pool = $4f72c5a314eddf25$var$BASIC_SHAPES;
|
|
1919
|
-
else if (roll < basicW + complexW) pool = $4f72c5a314eddf25$var$COMPLEX_SHAPES;
|
|
1920
|
-
else if (roll < basicW + complexW + sacredW) pool = $4f72c5a314eddf25$var$SACRED_SHAPES;
|
|
1921
|
-
else pool = $4f72c5a314eddf25$var$PROCEDURAL_SHAPES;
|
|
1922
|
-
const available = pool.filter((s)=>shapeNames.includes(s));
|
|
1923
|
-
if (available.length === 0) return shapeNames[Math.floor(rng() * shapeNames.length)];
|
|
1924
|
-
return available[Math.floor(rng() * available.length)];
|
|
1925
|
-
}
|
|
1926
2731
|
// ── Helper: get position based on composition mode ──────────────────
|
|
1927
2732
|
function $4f72c5a314eddf25$var$getCompositionPosition(mode, rng, width, height, shapeIndex, totalShapes, cx, cy) {
|
|
1928
2733
|
switch(mode){
|
|
@@ -1981,22 +2786,28 @@ function $4f72c5a314eddf25$var$getCompositionPosition(mode, rng, width, height,
|
|
|
1981
2786
|
};
|
|
1982
2787
|
}
|
|
1983
2788
|
}
|
|
1984
|
-
// ── Helper: positional color
|
|
1985
|
-
function $4f72c5a314eddf25$var$getPositionalColor(x, y, width, height,
|
|
1986
|
-
|
|
1987
|
-
const
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
return (0, $d016ad53434219a1$export$
|
|
2789
|
+
// ── Helper: positional color from hierarchy ─────────────────────────
|
|
2790
|
+
function $4f72c5a314eddf25$var$getPositionalColor(x, y, width, height, hierarchy, rng) {
|
|
2791
|
+
// Blend position into color selection — shapes near center lean dominant
|
|
2792
|
+
const distFromCenter = Math.hypot(x - width / 2, y - height / 2) / Math.hypot(width / 2, height / 2);
|
|
2793
|
+
// Center = more dominant, edges = more accent
|
|
2794
|
+
if (distFromCenter < 0.35) return (0, $d016ad53434219a1$export$18a34c25ea7e724b)(hierarchy.dominant, rng, 10, 0.08);
|
|
2795
|
+
else if (distFromCenter < 0.7) return (0, $d016ad53434219a1$export$18a34c25ea7e724b)((0, $d016ad53434219a1$export$b49f62f0a99da0e8)(hierarchy, rng), rng, 8, 0.06);
|
|
2796
|
+
else {
|
|
2797
|
+
// Edges: bias toward secondary/accent
|
|
2798
|
+
const roll = rng();
|
|
2799
|
+
const color = roll < 0.4 ? hierarchy.secondary : roll < 0.75 ? hierarchy.accent : hierarchy.dominant;
|
|
2800
|
+
return (0, $d016ad53434219a1$export$18a34c25ea7e724b)(color, rng, 12, 0.08);
|
|
2801
|
+
}
|
|
1991
2802
|
}
|
|
1992
|
-
// ── Helper: check if a position is inside a void zone
|
|
2803
|
+
// ── Helper: check if a position is inside a void zone ───────────────
|
|
1993
2804
|
function $4f72c5a314eddf25$var$isInVoidZone(x, y, voidZones) {
|
|
1994
2805
|
for (const zone of voidZones){
|
|
1995
2806
|
if (Math.hypot(x - zone.x, y - zone.y) < zone.radius) return true;
|
|
1996
2807
|
}
|
|
1997
2808
|
return false;
|
|
1998
2809
|
}
|
|
1999
|
-
// ── Helper: density check
|
|
2810
|
+
// ── Helper: density check ───────────────────────────────────────────
|
|
2000
2811
|
function $4f72c5a314eddf25$var$localDensity(x, y, positions, radius) {
|
|
2001
2812
|
let count = 0;
|
|
2002
2813
|
for (const p of positions)if (Math.hypot(x - p.x, y - p.y) < radius) count++;
|
|
@@ -2092,7 +2903,15 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2092
2903
|
const [bgStart, bgEnd] = colorScheme.getBackgroundColorsByMode(archetype.paletteMode);
|
|
2093
2904
|
const tempMode = colorScheme.getTemperatureMode();
|
|
2094
2905
|
const fgTempTarget = tempMode === "warm-bg" ? "cool" : tempMode === "cool-bg" ? "warm" : null;
|
|
2906
|
+
// ── 0b. Color hierarchy — dominant/secondary/accent weighting ──
|
|
2907
|
+
const colorHierarchy = (0, $d016ad53434219a1$export$fabac4600b87056)(colors, rng);
|
|
2908
|
+
// ── 0c. Shape palette — curated shapes that work well together ──
|
|
2095
2909
|
const shapeNames = Object.keys((0, $9c828bde2acaae64$export$4ff7fc6f1af248b5));
|
|
2910
|
+
const shapePalette = (0, $e73976f898150d4d$export$4a95df8944b5033b)(rng, shapeNames, archetype.name);
|
|
2911
|
+
// ── 0d. Color grading — unified tone for the whole image ───────
|
|
2912
|
+
const colorGrade = (0, $d016ad53434219a1$export$6d1620b367f86f7a)(rng);
|
|
2913
|
+
// ── 0e. Light direction — consistent shadow angle ──────────────
|
|
2914
|
+
const lightAngle = rng() * Math.PI * 2;
|
|
2096
2915
|
const scaleFactor = Math.min(width, height) / 1024;
|
|
2097
2916
|
const adjustedMinSize = minShapeSize * scaleFactor;
|
|
2098
2917
|
const adjustedMaxSize = maxShapeSize * scaleFactor;
|
|
@@ -2101,27 +2920,45 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2101
2920
|
// ── 1. Background ──────────────────────────────────────────────
|
|
2102
2921
|
const bgRadius = Math.hypot(cx, cy);
|
|
2103
2922
|
$4f72c5a314eddf25$var$drawBackground(ctx, archetype.backgroundStyle, bgStart, bgEnd, width, height, cx, cy, bgRadius, rng, colors);
|
|
2923
|
+
// Gradient mesh overlay — 3-4 color control points for richer backgrounds
|
|
2924
|
+
const meshPoints = 3 + Math.floor(rng() * 2);
|
|
2925
|
+
ctx.globalCompositeOperation = "soft-light";
|
|
2926
|
+
for(let i = 0; i < meshPoints; i++){
|
|
2927
|
+
const mx = rng() * width;
|
|
2928
|
+
const my = rng() * height;
|
|
2929
|
+
const mRadius = Math.min(width, height) * (0.3 + rng() * 0.4);
|
|
2930
|
+
const mColor = (0, $d016ad53434219a1$export$b49f62f0a99da0e8)(colorHierarchy, rng);
|
|
2931
|
+
const grad = ctx.createRadialGradient(mx, my, 0, mx, my, mRadius);
|
|
2932
|
+
grad.addColorStop(0, (0, $d016ad53434219a1$export$f2121afcad3d553f)(mColor, 0.08 + rng() * 0.06));
|
|
2933
|
+
grad.addColorStop(1, "rgba(0,0,0,0)");
|
|
2934
|
+
ctx.globalAlpha = 1;
|
|
2935
|
+
ctx.fillStyle = grad;
|
|
2936
|
+
ctx.fillRect(0, 0, width, height);
|
|
2937
|
+
}
|
|
2938
|
+
ctx.globalCompositeOperation = "source-over";
|
|
2104
2939
|
// Compute average background luminance for contrast enforcement
|
|
2105
2940
|
const bgLum = ((0, $d016ad53434219a1$export$5c6e3c2b59b7fbbe)(bgStart) + (0, $d016ad53434219a1$export$5c6e3c2b59b7fbbe)(bgEnd)) / 2;
|
|
2106
|
-
// ── 1b. Layered background
|
|
2107
|
-
// Draw large, very faint shapes to give the background texture
|
|
2941
|
+
// ── 1b. Layered background — archetype-coherent shapes ─────────
|
|
2108
2942
|
const bgShapeCount = 3 + Math.floor(rng() * 4);
|
|
2109
2943
|
ctx.globalCompositeOperation = "soft-light";
|
|
2110
2944
|
for(let i = 0; i < bgShapeCount; i++){
|
|
2111
2945
|
const bx = rng() * width;
|
|
2112
2946
|
const by = rng() * height;
|
|
2113
2947
|
const bSize = width * 0.3 + rng() * width * 0.5;
|
|
2114
|
-
const bColor =
|
|
2948
|
+
const bColor = (0, $d016ad53434219a1$export$b49f62f0a99da0e8)(colorHierarchy, rng);
|
|
2115
2949
|
ctx.globalAlpha = 0.03 + rng() * 0.05;
|
|
2116
2950
|
ctx.fillStyle = (0, $d016ad53434219a1$export$f2121afcad3d553f)(bColor, 0.15);
|
|
2117
2951
|
ctx.beginPath();
|
|
2118
|
-
|
|
2952
|
+
// Use archetype-appropriate background shapes
|
|
2953
|
+
if (archetype.name === "geometric-precision" || archetype.name === "op-art") // Rectangular shapes for geometric archetypes
|
|
2954
|
+
ctx.rect(bx - bSize / 2, by - bSize / 2, bSize, bSize * (0.5 + rng() * 0.5));
|
|
2955
|
+
else ctx.arc(bx, by, bSize / 2, 0, Math.PI * 2);
|
|
2119
2956
|
ctx.fill();
|
|
2120
2957
|
}
|
|
2121
2958
|
// Subtle concentric rings from center
|
|
2122
2959
|
const ringCount = 2 + Math.floor(rng() * 3);
|
|
2123
2960
|
ctx.globalAlpha = 0.02 + rng() * 0.03;
|
|
2124
|
-
ctx.strokeStyle = (0, $d016ad53434219a1$export$f2121afcad3d553f)(
|
|
2961
|
+
ctx.strokeStyle = (0, $d016ad53434219a1$export$f2121afcad3d553f)(colorHierarchy.dominant, 0.1);
|
|
2125
2962
|
ctx.lineWidth = 1 * scaleFactor;
|
|
2126
2963
|
for(let i = 1; i <= ringCount; i++){
|
|
2127
2964
|
const r = Math.min(width, height) * 0.15 * i;
|
|
@@ -2135,7 +2972,6 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2135
2972
|
const symRoll = rng();
|
|
2136
2973
|
const symmetryMode = symRoll < 0.10 ? "bilateral-x" : symRoll < 0.20 ? "bilateral-y" : symRoll < 0.25 ? "quad" : "none";
|
|
2137
2974
|
// ── 3. Focal points + void zones ───────────────────────────────
|
|
2138
|
-
// Rule-of-thirds intersection points for intentional composition
|
|
2139
2975
|
const THIRDS_POINTS = [
|
|
2140
2976
|
{
|
|
2141
2977
|
x: 1 / 3,
|
|
@@ -2156,10 +2992,8 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2156
2992
|
];
|
|
2157
2993
|
const numFocal = 1 + Math.floor(rng() * 2);
|
|
2158
2994
|
const focalPoints = [];
|
|
2159
|
-
for(let f = 0; f < numFocal; f++)
|
|
2160
|
-
if (rng() < 0.7) {
|
|
2995
|
+
for(let f = 0; f < numFocal; f++)if (rng() < 0.7) {
|
|
2161
2996
|
const tp = THIRDS_POINTS[Math.floor(rng() * THIRDS_POINTS.length)];
|
|
2162
|
-
// Small jitter around the thirds point so it's not robotic
|
|
2163
2997
|
focalPoints.push({
|
|
2164
2998
|
x: width * (tp.x + (rng() - 0.5) * 0.08),
|
|
2165
2999
|
y: height * (tp.y + (rng() - 0.5) * 0.08),
|
|
@@ -2170,7 +3004,6 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2170
3004
|
y: height * (0.2 + rng() * 0.6),
|
|
2171
3005
|
strength: 0.3 + rng() * 0.4
|
|
2172
3006
|
});
|
|
2173
|
-
// Feature E: 1-2 void zones where shapes are sparse (negative space)
|
|
2174
3007
|
const numVoids = Math.floor(rng() * 2) + 1;
|
|
2175
3008
|
const voidZones = [];
|
|
2176
3009
|
for(let v = 0; v < numVoids; v++)voidZones.push({
|
|
@@ -2202,20 +3035,24 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2202
3035
|
}
|
|
2203
3036
|
// Track all placed shapes for density checks and connecting curves
|
|
2204
3037
|
const shapePositions = [];
|
|
3038
|
+
// Hero avoidance radius — shapes near the hero orient toward it
|
|
3039
|
+
let heroCenter = null;
|
|
2205
3040
|
// ── 4b. Hero shape — a dominant focal element ───────────────────
|
|
2206
3041
|
if (archetype.heroShape && rng() < 0.6) {
|
|
2207
3042
|
const heroFocal = focalPoints[0];
|
|
3043
|
+
// Use shape palette hero candidates
|
|
2208
3044
|
const heroPool = [
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
];
|
|
2214
|
-
const heroShape = heroPool.filter((s)=>shapeNames.includes(s))[Math.floor(rng() * heroPool.filter((s)=>shapeNames.includes(s)).length)] || shapeNames[Math.floor(rng() * shapeNames.length)];
|
|
3045
|
+
...shapePalette.primary,
|
|
3046
|
+
...shapePalette.supporting
|
|
3047
|
+
].filter((s)=>(0, $e73976f898150d4d$export$4343b39fe47bd82c)[s]?.heroCandidate && shapeNames.includes(s));
|
|
3048
|
+
const heroShape = heroPool.length > 0 ? heroPool[Math.floor(rng() * heroPool.length)] : shapeNames[Math.floor(rng() * shapeNames.length)];
|
|
2215
3049
|
const heroSize = adjustedMaxSize * (0.8 + rng() * 0.5);
|
|
2216
3050
|
const heroRotation = rng() * 360;
|
|
2217
|
-
const heroFill = (0, $d016ad53434219a1$export$f2121afcad3d553f)((0, $d016ad53434219a1$export$90ad0e6170cf6af5)((0, $d016ad53434219a1$export$
|
|
2218
|
-
const heroStroke = (0, $d016ad53434219a1$export$90ad0e6170cf6af5)((0, $d016ad53434219a1$export$
|
|
3051
|
+
const heroFill = (0, $d016ad53434219a1$export$f2121afcad3d553f)((0, $d016ad53434219a1$export$90ad0e6170cf6af5)((0, $d016ad53434219a1$export$18a34c25ea7e724b)(colorHierarchy.dominant, rng, 6, 0.05), bgLum), 0.15 + rng() * 0.2);
|
|
3052
|
+
const heroStroke = (0, $d016ad53434219a1$export$90ad0e6170cf6af5)((0, $d016ad53434219a1$export$18a34c25ea7e724b)(colorHierarchy.accent, rng, 6, 0.05), bgLum);
|
|
3053
|
+
// Get best style for this hero shape
|
|
3054
|
+
const heroProfile = (0, $e73976f898150d4d$export$4343b39fe47bd82c)[heroShape];
|
|
3055
|
+
const heroStyle = heroProfile ? heroProfile.bestStyles[Math.floor(rng() * heroProfile.bestStyles.length)] : rng() < 0.4 ? "watercolor" : "fill-and-stroke";
|
|
2219
3056
|
ctx.globalAlpha = 0.5 + rng() * 0.2;
|
|
2220
3057
|
(0, $c3de8257a8baa3b0$export$bb35a6995ddbf32d)(ctx, heroShape, heroFocal.x, heroFocal.y, {
|
|
2221
3058
|
fillColor: heroFill,
|
|
@@ -2226,14 +3063,20 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2226
3063
|
proportionType: "GOLDEN_RATIO",
|
|
2227
3064
|
glowRadius: (12 + rng() * 20) * scaleFactor,
|
|
2228
3065
|
glowColor: (0, $d016ad53434219a1$export$f2121afcad3d553f)(heroStroke, 0.4),
|
|
2229
|
-
gradientFillEnd: (0, $d016ad53434219a1$export$
|
|
2230
|
-
renderStyle:
|
|
3066
|
+
gradientFillEnd: (0, $d016ad53434219a1$export$18a34c25ea7e724b)(colorHierarchy.secondary, rng, 10, 0.1),
|
|
3067
|
+
renderStyle: heroStyle,
|
|
2231
3068
|
rng: rng
|
|
2232
3069
|
});
|
|
2233
|
-
|
|
3070
|
+
heroCenter = {
|
|
2234
3071
|
x: heroFocal.x,
|
|
2235
3072
|
y: heroFocal.y,
|
|
2236
3073
|
size: heroSize
|
|
3074
|
+
};
|
|
3075
|
+
shapePositions.push({
|
|
3076
|
+
x: heroFocal.x,
|
|
3077
|
+
y: heroFocal.y,
|
|
3078
|
+
size: heroSize,
|
|
3079
|
+
shape: heroShape
|
|
2237
3080
|
});
|
|
2238
3081
|
}
|
|
2239
3082
|
// ── 5. Shape layers ────────────────────────────────────────────
|
|
@@ -2244,41 +3087,52 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2244
3087
|
const numShapes = shapesPerLayer + Math.floor(rng() * shapesPerLayer * 0.3);
|
|
2245
3088
|
const layerOpacity = Math.max(0.15, baseOpacity - layer * opacityReduction);
|
|
2246
3089
|
const layerSizeScale = 1 - layer * 0.15;
|
|
2247
|
-
//
|
|
3090
|
+
// Per-layer blend mode
|
|
2248
3091
|
const layerBlend = (0, $c3de8257a8baa3b0$export$7bb7bff4e26fa06b)(rng);
|
|
2249
3092
|
ctx.globalCompositeOperation = layerBlend;
|
|
2250
|
-
//
|
|
3093
|
+
// Per-layer render style bias — prefer archetype styles
|
|
2251
3094
|
const layerRenderStyle = rng() < 0.6 ? archetype.preferredStyles[Math.floor(rng() * archetype.preferredStyles.length)] : (0, $c3de8257a8baa3b0$export$9fd4e64b2acd410e)(rng);
|
|
2252
|
-
//
|
|
2253
|
-
const atmosphericDesat = layerRatio * 0.3;
|
|
3095
|
+
// Atmospheric desaturation for later layers
|
|
3096
|
+
const atmosphericDesat = layerRatio * 0.3;
|
|
2254
3097
|
for(let i = 0; i < numShapes; i++){
|
|
2255
3098
|
// Position from composition mode, then focal bias
|
|
2256
3099
|
const rawPos = $4f72c5a314eddf25$var$getCompositionPosition(compositionMode, rng, width, height, i, numShapes, cx, cy);
|
|
2257
3100
|
const [x, y] = applyFocalBias(rawPos.x, rawPos.y);
|
|
2258
|
-
//
|
|
3101
|
+
// Skip shapes in void zones, reduce in dense areas
|
|
2259
3102
|
if ($4f72c5a314eddf25$var$isInVoidZone(x, y, voidZones)) {
|
|
2260
|
-
// 85% chance to skip — allows a few shapes to bleed in
|
|
2261
3103
|
if (rng() < 0.85) continue;
|
|
2262
3104
|
}
|
|
2263
3105
|
if ($4f72c5a314eddf25$var$localDensity(x, y, shapePositions, densityCheckRadius) > maxLocalDensity) {
|
|
2264
|
-
if (rng() < 0.6) continue;
|
|
3106
|
+
if (rng() < 0.6) continue;
|
|
2265
3107
|
}
|
|
2266
|
-
// Weighted shape selection
|
|
2267
|
-
const shape = $4f72c5a314eddf25$var$pickShape(rng, layerRatio, shapeNames);
|
|
2268
3108
|
// Power distribution for size — archetype controls the curve
|
|
2269
3109
|
const sizeT = Math.pow(rng(), archetype.sizePower);
|
|
2270
3110
|
const size = (adjustedMinSize + sizeT * (adjustedMaxSize - adjustedMinSize)) * layerSizeScale;
|
|
3111
|
+
// Size fraction for affinity-aware shape selection
|
|
3112
|
+
const sizeFraction = size / adjustedMaxSize;
|
|
3113
|
+
// Palette-driven shape selection (replaces naive pickShape)
|
|
3114
|
+
const shape = (0, $e73976f898150d4d$export$3c37d9a045754d0e)(shapePalette, rng, sizeFraction);
|
|
2271
3115
|
// Flow-field rotation in flow-field mode, random otherwise
|
|
2272
|
-
|
|
2273
|
-
//
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
3116
|
+
let rotation = compositionMode === "flow-field" ? flowAngle(x, y) * 180 / Math.PI + (rng() - 0.5) * 30 : rng() * 360;
|
|
3117
|
+
// Hero avoidance: shapes near the hero orient toward it
|
|
3118
|
+
if (heroCenter) {
|
|
3119
|
+
const distToHero = Math.hypot(x - heroCenter.x, y - heroCenter.y);
|
|
3120
|
+
const heroInfluence = heroCenter.size * 1.5;
|
|
3121
|
+
if (distToHero < heroInfluence && distToHero > 0) {
|
|
3122
|
+
const angleToHero = Math.atan2(heroCenter.y - y, heroCenter.x - x) * 180 / Math.PI;
|
|
3123
|
+
const blendFactor = 1 - distToHero / heroInfluence;
|
|
3124
|
+
rotation = rotation + (angleToHero - rotation) * blendFactor * 0.4;
|
|
3125
|
+
}
|
|
3126
|
+
}
|
|
3127
|
+
// Positional color from hierarchy + jitter
|
|
3128
|
+
let fillBase = $4f72c5a314eddf25$var$getPositionalColor(x, y, width, height, colorHierarchy, rng);
|
|
3129
|
+
const strokeBase = (0, $d016ad53434219a1$export$b49f62f0a99da0e8)(colorHierarchy, rng);
|
|
3130
|
+
// Desaturate colors on later layers for depth
|
|
2277
3131
|
if (atmosphericDesat > 0) fillBase = (0, $d016ad53434219a1$export$fb75607d98509d9)(fillBase, atmosphericDesat);
|
|
2278
3132
|
// Temperature contrast: shift foreground shapes opposite to background
|
|
2279
3133
|
if (fgTempTarget) fillBase = (0, $d016ad53434219a1$export$51ea55f869b7e0d3)(fillBase, fgTempTarget, 0.15 + layerRatio * 0.1);
|
|
2280
|
-
const fillColor = (0, $d016ad53434219a1$export$90ad0e6170cf6af5)((0, $d016ad53434219a1$export$
|
|
2281
|
-
const strokeColor = (0, $d016ad53434219a1$export$90ad0e6170cf6af5)((0, $d016ad53434219a1$export$
|
|
3134
|
+
const fillColor = (0, $d016ad53434219a1$export$90ad0e6170cf6af5)((0, $d016ad53434219a1$export$18a34c25ea7e724b)(fillBase, rng, 6, 0.05), bgLum);
|
|
3135
|
+
const strokeColor = (0, $d016ad53434219a1$export$90ad0e6170cf6af5)((0, $d016ad53434219a1$export$18a34c25ea7e724b)(strokeBase, rng, 5, 0.04), bgLum);
|
|
2282
3136
|
// Semi-transparent fill
|
|
2283
3137
|
const fillAlpha = 0.2 + rng() * 0.5;
|
|
2284
3138
|
const transparentFill = (0, $d016ad53434219a1$export$f2121afcad3d553f)(fillColor, fillAlpha);
|
|
@@ -2292,12 +3146,16 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2292
3146
|
const glowRadius = hasGlow ? (8 + rng() * 20) * scaleFactor : 0;
|
|
2293
3147
|
// Gradient fill on ~30%
|
|
2294
3148
|
const hasGradient = rng() < 0.3;
|
|
2295
|
-
const gradientEnd = hasGradient ? (0, $d016ad53434219a1$export$
|
|
2296
|
-
//
|
|
2297
|
-
const shapeRenderStyle =
|
|
2298
|
-
//
|
|
3149
|
+
const gradientEnd = hasGradient ? (0, $d016ad53434219a1$export$18a34c25ea7e724b)((0, $d016ad53434219a1$export$b49f62f0a99da0e8)(colorHierarchy, rng), rng, 10, 0.1) : undefined;
|
|
3150
|
+
// Affinity-aware render style selection
|
|
3151
|
+
const shapeRenderStyle = (0, $e73976f898150d4d$export$ab873bb6fb56c1a8)(shape, layerRenderStyle, rng);
|
|
3152
|
+
// Organic edge jitter — applied via watercolor style on ~15% of shapes
|
|
2299
3153
|
const useOrganicEdges = rng() < 0.15 && shapeRenderStyle === "fill-and-stroke";
|
|
2300
3154
|
const finalRenderStyle = useOrganicEdges ? "watercolor" : shapeRenderStyle;
|
|
3155
|
+
// Consistent light direction — subtle shadow offset
|
|
3156
|
+
const shadowDist = hasGlow ? 0 : size * 0.02;
|
|
3157
|
+
const shadowOffX = shadowDist * Math.cos(lightAngle);
|
|
3158
|
+
const shadowOffY = shadowDist * Math.sin(lightAngle);
|
|
2301
3159
|
(0, $c3de8257a8baa3b0$export$bb35a6995ddbf32d)(ctx, shape, x, y, {
|
|
2302
3160
|
fillColor: transparentFill,
|
|
2303
3161
|
strokeColor: strokeColor,
|
|
@@ -2305,8 +3163,8 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2305
3163
|
size: size,
|
|
2306
3164
|
rotation: rotation,
|
|
2307
3165
|
proportionType: "GOLDEN_RATIO",
|
|
2308
|
-
glowRadius: glowRadius,
|
|
2309
|
-
glowColor: hasGlow ? (0, $d016ad53434219a1$export$f2121afcad3d553f)(fillColor, 0.6) : undefined,
|
|
3166
|
+
glowRadius: glowRadius || (shadowDist > 0 ? shadowDist * 2 : 0),
|
|
3167
|
+
glowColor: hasGlow ? (0, $d016ad53434219a1$export$f2121afcad3d553f)(fillColor, 0.6) : shadowDist > 0 ? "rgba(0,0,0,0.08)" : undefined,
|
|
2310
3168
|
gradientFillEnd: gradientEnd,
|
|
2311
3169
|
renderStyle: finalRenderStyle,
|
|
2312
3170
|
rng: rng
|
|
@@ -2314,18 +3172,51 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2314
3172
|
shapePositions.push({
|
|
2315
3173
|
x: x,
|
|
2316
3174
|
y: y,
|
|
2317
|
-
size: size
|
|
3175
|
+
size: size,
|
|
3176
|
+
shape: shape
|
|
2318
3177
|
});
|
|
2319
|
-
// ── 5b.
|
|
3178
|
+
// ── 5b. Size echo — large shapes spawn trailing smaller copies ──
|
|
3179
|
+
if (size > adjustedMaxSize * 0.5 && rng() < 0.2) {
|
|
3180
|
+
const echoCount = 2 + Math.floor(rng() * 2);
|
|
3181
|
+
const echoAngle = rng() * Math.PI * 2;
|
|
3182
|
+
for(let e = 0; e < echoCount; e++){
|
|
3183
|
+
const echoScale = 0.3 - e * 0.08;
|
|
3184
|
+
const echoDist = size * (0.6 + e * 0.4);
|
|
3185
|
+
const echoX = x + Math.cos(echoAngle) * echoDist;
|
|
3186
|
+
const echoY = y + Math.sin(echoAngle) * echoDist;
|
|
3187
|
+
const echoSize = size * Math.max(0.1, echoScale);
|
|
3188
|
+
if (echoX < 0 || echoX > width || echoY < 0 || echoY > height) continue;
|
|
3189
|
+
ctx.globalAlpha = layerOpacity * (0.4 - e * 0.1);
|
|
3190
|
+
(0, $c3de8257a8baa3b0$export$bb35a6995ddbf32d)(ctx, shape, echoX, echoY, {
|
|
3191
|
+
fillColor: (0, $d016ad53434219a1$export$f2121afcad3d553f)(fillColor, fillAlpha * 0.6),
|
|
3192
|
+
strokeColor: (0, $d016ad53434219a1$export$f2121afcad3d553f)(strokeColor, 0.4),
|
|
3193
|
+
strokeWidth: strokeWidth * 0.6,
|
|
3194
|
+
size: echoSize,
|
|
3195
|
+
rotation: rotation + (e + 1) * 15,
|
|
3196
|
+
proportionType: "GOLDEN_RATIO",
|
|
3197
|
+
renderStyle: finalRenderStyle,
|
|
3198
|
+
rng: rng
|
|
3199
|
+
});
|
|
3200
|
+
shapePositions.push({
|
|
3201
|
+
x: echoX,
|
|
3202
|
+
y: echoY,
|
|
3203
|
+
size: echoSize,
|
|
3204
|
+
shape: shape
|
|
3205
|
+
});
|
|
3206
|
+
}
|
|
3207
|
+
}
|
|
3208
|
+
// ── 5c. Recursive nesting ──────────────────────────────────
|
|
2320
3209
|
if (size > adjustedMaxSize * 0.4 && rng() < 0.15) {
|
|
2321
3210
|
const innerCount = 1 + Math.floor(rng() * 3);
|
|
2322
3211
|
for(let n = 0; n < innerCount; n++){
|
|
2323
|
-
|
|
3212
|
+
// Pick inner shape from palette affinities
|
|
3213
|
+
const innerSizeFraction = size * 0.25 / adjustedMaxSize;
|
|
3214
|
+
const innerShape = (0, $e73976f898150d4d$export$3c37d9a045754d0e)(shapePalette, rng, innerSizeFraction);
|
|
2324
3215
|
const innerSize = size * (0.15 + rng() * 0.25);
|
|
2325
3216
|
const innerOffX = (rng() - 0.5) * size * 0.4;
|
|
2326
3217
|
const innerOffY = (rng() - 0.5) * size * 0.4;
|
|
2327
3218
|
const innerRot = rng() * 360;
|
|
2328
|
-
const innerFill = (0, $d016ad53434219a1$export$f2121afcad3d553f)((0, $d016ad53434219a1$export$
|
|
3219
|
+
const innerFill = (0, $d016ad53434219a1$export$f2121afcad3d553f)((0, $d016ad53434219a1$export$18a34c25ea7e724b)((0, $d016ad53434219a1$export$b49f62f0a99da0e8)(colorHierarchy, rng), rng, 10, 0.1), 0.3 + rng() * 0.4);
|
|
2329
3220
|
ctx.globalAlpha = layerOpacity * 0.7;
|
|
2330
3221
|
(0, $c3de8257a8baa3b0$export$bb35a6995ddbf32d)(ctx, innerShape, x + innerOffX, y + innerOffY, {
|
|
2331
3222
|
fillColor: innerFill,
|
|
@@ -2334,7 +3225,7 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2334
3225
|
size: innerSize,
|
|
2335
3226
|
rotation: innerRot,
|
|
2336
3227
|
proportionType: "GOLDEN_RATIO",
|
|
2337
|
-
renderStyle:
|
|
3228
|
+
renderStyle: (0, $e73976f898150d4d$export$ab873bb6fb56c1a8)(innerShape, layerRenderStyle, rng),
|
|
2338
3229
|
rng: rng
|
|
2339
3230
|
});
|
|
2340
3231
|
}
|
|
@@ -2343,7 +3234,7 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2343
3234
|
}
|
|
2344
3235
|
// Reset blend mode for post-processing passes
|
|
2345
3236
|
ctx.globalCompositeOperation = "source-over";
|
|
2346
|
-
// ── 6. Flow-line pass
|
|
3237
|
+
// ── 6. Flow-line pass — variable color, branching, pressure ────
|
|
2347
3238
|
const baseFlowLines = 6 + Math.floor(rng() * 10);
|
|
2348
3239
|
const numFlowLines = Math.round(baseFlowLines * archetype.flowLineMultiplier);
|
|
2349
3240
|
for(let i = 0; i < numFlowLines; i++){
|
|
@@ -2352,9 +3243,13 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2352
3243
|
const steps = 30 + Math.floor(rng() * 40);
|
|
2353
3244
|
const stepLen = (3 + rng() * 5) * scaleFactor;
|
|
2354
3245
|
const startWidth = (1 + rng() * 3) * scaleFactor;
|
|
2355
|
-
|
|
3246
|
+
// Variable color: interpolate between two hierarchy colors along the stroke
|
|
3247
|
+
const lineColorStart = (0, $d016ad53434219a1$export$90ad0e6170cf6af5)((0, $d016ad53434219a1$export$b49f62f0a99da0e8)(colorHierarchy, rng), bgLum);
|
|
3248
|
+
const lineColorEnd = (0, $d016ad53434219a1$export$90ad0e6170cf6af5)((0, $d016ad53434219a1$export$b49f62f0a99da0e8)(colorHierarchy, rng), bgLum);
|
|
2356
3249
|
const lineAlpha = 0.06 + rng() * 0.1;
|
|
2357
|
-
//
|
|
3250
|
+
// Pressure simulation: sinusoidal width variation
|
|
3251
|
+
const pressureFreq = 2 + rng() * 4;
|
|
3252
|
+
const pressurePhase = rng() * Math.PI * 2;
|
|
2358
3253
|
let prevX = fx;
|
|
2359
3254
|
let prevY = fy;
|
|
2360
3255
|
for(let s = 0; s < steps; s++){
|
|
@@ -2362,37 +3257,61 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2362
3257
|
fx += Math.cos(angle) * stepLen;
|
|
2363
3258
|
fy += Math.sin(angle) * stepLen;
|
|
2364
3259
|
if (fx < 0 || fx > width || fy < 0 || fy > height) break;
|
|
2365
|
-
|
|
2366
|
-
|
|
3260
|
+
const t = s / steps;
|
|
3261
|
+
// Taper + pressure
|
|
3262
|
+
const taper = 1 - t * 0.8;
|
|
3263
|
+
const pressure = 0.6 + 0.4 * Math.sin(t * pressureFreq * Math.PI + pressurePhase);
|
|
2367
3264
|
ctx.globalAlpha = lineAlpha * taper;
|
|
3265
|
+
// Interpolate color along stroke
|
|
3266
|
+
const lineColor = t < 0.5 ? (0, $d016ad53434219a1$export$f2121afcad3d553f)(lineColorStart, 0.4 + t * 0.2) : (0, $d016ad53434219a1$export$f2121afcad3d553f)(lineColorEnd, 0.4 + (1 - t) * 0.2);
|
|
2368
3267
|
ctx.strokeStyle = lineColor;
|
|
2369
|
-
ctx.lineWidth = startWidth * taper;
|
|
3268
|
+
ctx.lineWidth = startWidth * taper * pressure;
|
|
2370
3269
|
ctx.lineCap = "round";
|
|
2371
3270
|
ctx.beginPath();
|
|
2372
3271
|
ctx.moveTo(prevX, prevY);
|
|
2373
3272
|
ctx.lineTo(fx, fy);
|
|
2374
3273
|
ctx.stroke();
|
|
3274
|
+
// Branching: ~12% chance per step to spawn a thinner child stroke
|
|
3275
|
+
if (rng() < 0.12 && s > 5 && s < steps - 10) {
|
|
3276
|
+
const branchAngle = angle + (rng() < 0.5 ? 1 : -1) * (0.3 + rng() * 0.5);
|
|
3277
|
+
let bx = fx;
|
|
3278
|
+
let by = fy;
|
|
3279
|
+
let bPrevX = fx;
|
|
3280
|
+
let bPrevY = fy;
|
|
3281
|
+
const branchSteps = 5 + Math.floor(rng() * 10);
|
|
3282
|
+
const branchWidth = startWidth * taper * 0.4;
|
|
3283
|
+
for(let bs = 0; bs < branchSteps; bs++){
|
|
3284
|
+
const bAngle = branchAngle + (rng() - 0.5) * 0.2;
|
|
3285
|
+
bx += Math.cos(bAngle) * stepLen * 0.8;
|
|
3286
|
+
by += Math.sin(bAngle) * stepLen * 0.8;
|
|
3287
|
+
if (bx < 0 || bx > width || by < 0 || by > height) break;
|
|
3288
|
+
const bTaper = 1 - bs / branchSteps * 0.9;
|
|
3289
|
+
ctx.globalAlpha = lineAlpha * taper * bTaper * 0.6;
|
|
3290
|
+
ctx.lineWidth = branchWidth * bTaper;
|
|
3291
|
+
ctx.beginPath();
|
|
3292
|
+
ctx.moveTo(bPrevX, bPrevY);
|
|
3293
|
+
ctx.lineTo(bx, by);
|
|
3294
|
+
ctx.stroke();
|
|
3295
|
+
bPrevX = bx;
|
|
3296
|
+
bPrevY = by;
|
|
3297
|
+
}
|
|
3298
|
+
}
|
|
2375
3299
|
prevX = fx;
|
|
2376
3300
|
prevY = fy;
|
|
2377
3301
|
}
|
|
2378
3302
|
}
|
|
2379
3303
|
// ── 6b. Apply symmetry mirroring ─────────────────────────────────
|
|
2380
|
-
// Mirror the rendered content (shapes + flow lines) before post-processing.
|
|
2381
|
-
// Uses ctx.canvas which is available in both Node (@napi-rs/canvas) and browsers.
|
|
2382
3304
|
if (symmetryMode !== "none") {
|
|
2383
3305
|
const canvas = ctx.canvas;
|
|
2384
3306
|
ctx.save();
|
|
2385
3307
|
if (symmetryMode === "bilateral-x" || symmetryMode === "quad") {
|
|
2386
|
-
// Mirror left half onto right half
|
|
2387
3308
|
ctx.save();
|
|
2388
3309
|
ctx.translate(width, 0);
|
|
2389
3310
|
ctx.scale(-1, 1);
|
|
2390
|
-
// Draw the left half (0 to cx) onto the mirrored right side
|
|
2391
3311
|
ctx.drawImage(canvas, 0, 0, Math.ceil(cx), height, 0, 0, Math.ceil(cx), height);
|
|
2392
3312
|
ctx.restore();
|
|
2393
3313
|
}
|
|
2394
3314
|
if (symmetryMode === "bilateral-y" || symmetryMode === "quad") {
|
|
2395
|
-
// Mirror top half onto bottom half
|
|
2396
3315
|
ctx.save();
|
|
2397
3316
|
ctx.translate(0, height);
|
|
2398
3317
|
ctx.scale(1, -1);
|
|
@@ -2415,7 +3334,7 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2415
3334
|
}
|
|
2416
3335
|
// ── 8. Vignette — darken edges to draw the eye inward ───────────
|
|
2417
3336
|
ctx.globalAlpha = 1;
|
|
2418
|
-
const vignetteStrength = 0.25 + rng() * 0.2;
|
|
3337
|
+
const vignetteStrength = 0.25 + rng() * 0.2;
|
|
2419
3338
|
const vigGrad = ctx.createRadialGradient(cx, cy, Math.min(width, height) * 0.3, cx, cy, bgRadius);
|
|
2420
3339
|
vigGrad.addColorStop(0, "rgba(0,0,0,0)");
|
|
2421
3340
|
vigGrad.addColorStop(0.6, "rgba(0,0,0,0)");
|
|
@@ -2441,13 +3360,57 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2441
3360
|
const cpx = mx + -dy / (dist || 1) * bulge;
|
|
2442
3361
|
const cpy = my + dx / (dist || 1) * bulge;
|
|
2443
3362
|
ctx.globalAlpha = 0.06 + rng() * 0.1;
|
|
2444
|
-
ctx.strokeStyle = (0, $d016ad53434219a1$export$f2121afcad3d553f)((0, $d016ad53434219a1$export$90ad0e6170cf6af5)(
|
|
3363
|
+
ctx.strokeStyle = (0, $d016ad53434219a1$export$f2121afcad3d553f)((0, $d016ad53434219a1$export$90ad0e6170cf6af5)((0, $d016ad53434219a1$export$b49f62f0a99da0e8)(colorHierarchy, rng), bgLum), 0.3);
|
|
2445
3364
|
ctx.beginPath();
|
|
2446
3365
|
ctx.moveTo(a.x, a.y);
|
|
2447
3366
|
ctx.quadraticCurveTo(cpx, cpy, b.x, b.y);
|
|
2448
3367
|
ctx.stroke();
|
|
2449
3368
|
}
|
|
2450
3369
|
}
|
|
3370
|
+
// ── 10. Post-processing ────────────────────────────────────────
|
|
3371
|
+
// 10a. Color grading — unified tone across the whole image
|
|
3372
|
+
// Apply as a semi-transparent overlay in the grade hue
|
|
3373
|
+
ctx.globalAlpha = colorGrade.intensity * 0.25;
|
|
3374
|
+
ctx.globalCompositeOperation = "soft-light";
|
|
3375
|
+
const gradeHsl = `hsl(${Math.round(colorGrade.hue)}, 40%, 50%)`;
|
|
3376
|
+
ctx.fillStyle = gradeHsl;
|
|
3377
|
+
ctx.fillRect(0, 0, width, height);
|
|
3378
|
+
ctx.globalCompositeOperation = "source-over";
|
|
3379
|
+
// 10b. Chromatic aberration — subtle RGB channel offset at edges
|
|
3380
|
+
// Only apply for neon/cosmic/ethereal archetypes where it fits
|
|
3381
|
+
const chromaArchetypes = [
|
|
3382
|
+
"neon-glow",
|
|
3383
|
+
"cosmic",
|
|
3384
|
+
"ethereal"
|
|
3385
|
+
];
|
|
3386
|
+
if (chromaArchetypes.includes(archetype.name)) {
|
|
3387
|
+
const chromaOffset = Math.ceil(2 * scaleFactor);
|
|
3388
|
+
const canvas = ctx.canvas;
|
|
3389
|
+
// Shift red channel slightly
|
|
3390
|
+
ctx.globalAlpha = 0.03;
|
|
3391
|
+
ctx.globalCompositeOperation = "screen";
|
|
3392
|
+
ctx.drawImage(canvas, chromaOffset, 0, width, height, 0, 0, width, height);
|
|
3393
|
+
// Shift blue channel opposite
|
|
3394
|
+
ctx.drawImage(canvas, -chromaOffset, 0, width, height, 0, 0, width, height);
|
|
3395
|
+
ctx.globalCompositeOperation = "source-over";
|
|
3396
|
+
}
|
|
3397
|
+
// 10c. Bloom — soft glow on bright areas for neon/cosmic archetypes
|
|
3398
|
+
const bloomArchetypes = [
|
|
3399
|
+
"neon-glow",
|
|
3400
|
+
"cosmic"
|
|
3401
|
+
];
|
|
3402
|
+
if (bloomArchetypes.includes(archetype.name)) {
|
|
3403
|
+
const canvas = ctx.canvas;
|
|
3404
|
+
ctx.globalAlpha = 0.08;
|
|
3405
|
+
ctx.globalCompositeOperation = "screen";
|
|
3406
|
+
// Draw the image slightly scaled up and blurred via shadow
|
|
3407
|
+
ctx.save();
|
|
3408
|
+
ctx.shadowBlur = 30 * scaleFactor;
|
|
3409
|
+
ctx.shadowColor = "rgba(255,255,255,0.3)";
|
|
3410
|
+
ctx.drawImage(canvas, 0, 0, width, height);
|
|
3411
|
+
ctx.restore();
|
|
3412
|
+
ctx.globalCompositeOperation = "source-over";
|
|
3413
|
+
}
|
|
2451
3414
|
ctx.globalAlpha = 1;
|
|
2452
3415
|
}
|
|
2453
3416
|
|