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/browser.js
CHANGED
|
@@ -12,17 +12,22 @@ import $4wRzV$colorscheme from "color-scheme";
|
|
|
12
12
|
* identically in Node (@napi-rs/canvas) and browsers.
|
|
13
13
|
*
|
|
14
14
|
* Generation pipeline:
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
15
|
+
* 0. Archetype selection + shape palette + color hierarchy
|
|
16
|
+
* 1. Background — style from archetype, gradient mesh for depth
|
|
17
|
+
* 1b. Layered background — archetype-coherent shapes
|
|
18
|
+
* 2. Composition mode + symmetry
|
|
19
|
+
* 3. Focal points + void zones + hero avoidance field
|
|
20
|
+
* 4. Flow field
|
|
21
|
+
* 4b. Hero shape
|
|
22
|
+
* 5. Shape layers — palette-driven selection, affinity-aware styles,
|
|
23
|
+
* size echo, tangent placement, atmospheric depth
|
|
22
24
|
* 5b. Recursive nesting
|
|
23
|
-
* 6. Flow
|
|
24
|
-
*
|
|
25
|
-
*
|
|
25
|
+
* 6. Flow lines — variable color, branching, pressure simulation
|
|
26
|
+
* 6b. Symmetry mirroring
|
|
27
|
+
* 7. Noise texture
|
|
28
|
+
* 8. Vignette
|
|
29
|
+
* 9. Organic connecting curves
|
|
30
|
+
* 10. Post-processing — color grading, chromatic aberration, bloom
|
|
26
31
|
*/
|
|
27
32
|
// declare module 'color-scheme';
|
|
28
33
|
|
|
@@ -388,6 +393,67 @@ function $b5a262d09b87e373$export$f2121afcad3d553f(hex, alpha) {
|
|
|
388
393
|
const [r, g, b] = $b5a262d09b87e373$var$hexToRgb(hex);
|
|
389
394
|
return `rgba(${r},${g},${b},${alpha.toFixed(3)})`;
|
|
390
395
|
}
|
|
396
|
+
function $b5a262d09b87e373$export$fabac4600b87056(colors, rng) {
|
|
397
|
+
if (colors.length < 3) return {
|
|
398
|
+
dominant: colors[0] || "#888888",
|
|
399
|
+
secondary: colors[1] || colors[0] || "#888888",
|
|
400
|
+
accent: colors[colors.length - 1] || "#888888",
|
|
401
|
+
all: colors
|
|
402
|
+
};
|
|
403
|
+
// Pick dominant as the color closest to the palette's average hue
|
|
404
|
+
const hsls = colors.map((c)=>$b5a262d09b87e373$var$hexToHsl(c));
|
|
405
|
+
const avgHue = hsls.reduce((s, h)=>s + h[0], 0) / hsls.length;
|
|
406
|
+
let dominantIdx = 0;
|
|
407
|
+
let minDist = 360;
|
|
408
|
+
for(let i = 0; i < hsls.length; i++){
|
|
409
|
+
const d = Math.min(Math.abs(hsls[i][0] - avgHue), 360 - Math.abs(hsls[i][0] - avgHue));
|
|
410
|
+
if (d < minDist) {
|
|
411
|
+
minDist = d;
|
|
412
|
+
dominantIdx = i;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
// Accent is the color most distant from dominant in hue
|
|
416
|
+
let accentIdx = 0;
|
|
417
|
+
let maxDist = 0;
|
|
418
|
+
for(let i = 0; i < hsls.length; i++){
|
|
419
|
+
if (i === dominantIdx) continue;
|
|
420
|
+
const d = Math.min(Math.abs(hsls[i][0] - hsls[dominantIdx][0]), 360 - Math.abs(hsls[i][0] - hsls[dominantIdx][0]));
|
|
421
|
+
if (d > maxDist) {
|
|
422
|
+
maxDist = d;
|
|
423
|
+
accentIdx = i;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
// Secondary is the remaining color with highest saturation
|
|
427
|
+
let secondaryIdx = 0;
|
|
428
|
+
let maxSat = -1;
|
|
429
|
+
for(let i = 0; i < hsls.length; i++){
|
|
430
|
+
if (i === dominantIdx || i === accentIdx) continue;
|
|
431
|
+
if (hsls[i][1] > maxSat) {
|
|
432
|
+
maxSat = hsls[i][1];
|
|
433
|
+
secondaryIdx = i;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
if (secondaryIdx === dominantIdx) secondaryIdx = accentIdx === 0 ? 1 : 0;
|
|
437
|
+
return {
|
|
438
|
+
dominant: colors[dominantIdx],
|
|
439
|
+
secondary: colors[secondaryIdx],
|
|
440
|
+
accent: colors[accentIdx],
|
|
441
|
+
all: colors
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
function $b5a262d09b87e373$export$b49f62f0a99da0e8(hierarchy, rng) {
|
|
445
|
+
const roll = rng();
|
|
446
|
+
if (roll < 0.60) return hierarchy.dominant;
|
|
447
|
+
if (roll < 0.85) return hierarchy.secondary;
|
|
448
|
+
return hierarchy.accent;
|
|
449
|
+
}
|
|
450
|
+
function $b5a262d09b87e373$export$18a34c25ea7e724b(hex, rng, hueAmount = 8, slAmount = 0.06) {
|
|
451
|
+
const [h, s, l] = $b5a262d09b87e373$var$hexToHsl(hex);
|
|
452
|
+
const newH = (h + (rng() - 0.5) * hueAmount * 2 + 360) % 360;
|
|
453
|
+
const newS = Math.max(0, Math.min(1, s + (rng() - 0.5) * slAmount * 2));
|
|
454
|
+
const newL = Math.max(0, Math.min(1, l + (rng() - 0.5) * slAmount * 2));
|
|
455
|
+
return $b5a262d09b87e373$var$hslToHex(newH, newS, newL);
|
|
456
|
+
}
|
|
391
457
|
function $b5a262d09b87e373$export$59539d800dbe6858(hex, rng, amount = 0.1) {
|
|
392
458
|
const [r, g, b] = $b5a262d09b87e373$var$hexToRgb(hex);
|
|
393
459
|
const jit = ()=>(rng() - 0.5) * 2 * amount * 255;
|
|
@@ -427,6 +493,31 @@ function $b5a262d09b87e373$export$90ad0e6170cf6af5(fgHex, bgLuminance, minContra
|
|
|
427
493
|
return $b5a262d09b87e373$var$hslToHex(h, targetS, targetL);
|
|
428
494
|
}
|
|
429
495
|
}
|
|
496
|
+
function $b5a262d09b87e373$export$4a3734b8c4b5c0e(hex, gradeHue, intensity) {
|
|
497
|
+
const [h, s, l] = $b5a262d09b87e373$var$hexToHsl(hex);
|
|
498
|
+
// Blend hue toward the grade hue
|
|
499
|
+
const hueDiff = (gradeHue - h + 540) % 360 - 180;
|
|
500
|
+
const newH = (h + hueDiff * intensity * 0.3 + 360) % 360;
|
|
501
|
+
// Slightly unify saturation
|
|
502
|
+
const newS = Math.max(0, Math.min(1, s + (0.5 - s) * intensity * 0.15));
|
|
503
|
+
return $b5a262d09b87e373$var$hslToHex(newH, newS, l);
|
|
504
|
+
}
|
|
505
|
+
function $b5a262d09b87e373$export$6d1620b367f86f7a(rng) {
|
|
506
|
+
// Warm golden, cool blue, rosy, teal, amber
|
|
507
|
+
const GRADE_HUES = [
|
|
508
|
+
40,
|
|
509
|
+
220,
|
|
510
|
+
340,
|
|
511
|
+
175,
|
|
512
|
+
30
|
|
513
|
+
];
|
|
514
|
+
const hue = GRADE_HUES[Math.floor(rng() * GRADE_HUES.length)] + (rng() - 0.5) * 20;
|
|
515
|
+
const intensity = 0.15 + rng() * 0.25;
|
|
516
|
+
return {
|
|
517
|
+
hue: (hue + 360) % 360,
|
|
518
|
+
intensity: intensity
|
|
519
|
+
};
|
|
520
|
+
}
|
|
430
521
|
|
|
431
522
|
|
|
432
523
|
|
|
@@ -1457,23 +1548,50 @@ function $e0f99502ff383dd8$export$71b514a25c47df50(ctx, shape, x, y, config) {
|
|
|
1457
1548
|
break;
|
|
1458
1549
|
case "watercolor":
|
|
1459
1550
|
{
|
|
1460
|
-
//
|
|
1461
|
-
const passes =
|
|
1551
|
+
// Improved watercolor: edge darkening + radial bleed + layered washes
|
|
1552
|
+
const passes = 4 + (rng ? Math.floor(rng() * 2) : 0);
|
|
1462
1553
|
const savedAlpha = ctx.globalAlpha;
|
|
1463
|
-
|
|
1554
|
+
// Pass 1: Base wash — large, soft fill at low opacity
|
|
1555
|
+
ctx.globalAlpha = savedAlpha * 0.15;
|
|
1556
|
+
ctx.save();
|
|
1557
|
+
const baseScale = 1.08 + (rng ? rng() * 0.04 : 0);
|
|
1558
|
+
ctx.scale(baseScale, baseScale);
|
|
1559
|
+
ctx.fill();
|
|
1560
|
+
ctx.restore();
|
|
1561
|
+
// Pass 2: Multiple offset washes with radial displacement
|
|
1562
|
+
ctx.globalAlpha = savedAlpha * (0.25 / passes * 2);
|
|
1464
1563
|
for(let p = 0; p < passes; p++){
|
|
1465
|
-
|
|
1466
|
-
const
|
|
1564
|
+
// Radial outward displacement (not uniform) for organic bleed
|
|
1565
|
+
const angle = rng ? rng() * Math.PI * 2 : p * Math.PI / 2;
|
|
1566
|
+
const dist = rng ? rng() * size * 0.05 : size * 0.02;
|
|
1567
|
+
const jx = Math.cos(angle) * dist;
|
|
1568
|
+
const jy = Math.sin(angle) * dist;
|
|
1467
1569
|
ctx.save();
|
|
1468
1570
|
ctx.translate(jx, jy);
|
|
1469
1571
|
ctx.fill();
|
|
1470
1572
|
ctx.restore();
|
|
1471
1573
|
}
|
|
1574
|
+
// Pass 3: Edge darkening — draw a slightly smaller shape with lighter fill
|
|
1575
|
+
// to simulate pigment pooling at boundaries
|
|
1576
|
+
ctx.globalAlpha = savedAlpha * 0.35;
|
|
1577
|
+
ctx.save();
|
|
1578
|
+
const innerScale = 0.85 + (rng ? rng() * 0.08 : 0);
|
|
1579
|
+
ctx.scale(innerScale, innerScale);
|
|
1580
|
+
// Lighten the fill for the inner area
|
|
1581
|
+
const origFill = ctx.fillStyle;
|
|
1582
|
+
if (typeof fillColor === "string") ctx.fillStyle = fillColor.replace(/[\d.]+\)$/, (m)=>{
|
|
1583
|
+
const v = parseFloat(m);
|
|
1584
|
+
return Math.min(1, v * 1.4).toFixed(2) + ")";
|
|
1585
|
+
});
|
|
1586
|
+
ctx.fill();
|
|
1587
|
+
ctx.fillStyle = origFill;
|
|
1588
|
+
ctx.restore();
|
|
1472
1589
|
ctx.globalAlpha = savedAlpha;
|
|
1473
|
-
//
|
|
1474
|
-
ctx.globalAlpha *= 0.
|
|
1590
|
+
// Soft stroke on top — thinner than normal for delicacy
|
|
1591
|
+
ctx.globalAlpha *= 0.25;
|
|
1592
|
+
ctx.lineWidth = strokeWidth * 0.6;
|
|
1475
1593
|
ctx.stroke();
|
|
1476
|
-
ctx.globalAlpha /= 0.
|
|
1594
|
+
ctx.globalAlpha /= 0.25;
|
|
1477
1595
|
break;
|
|
1478
1596
|
}
|
|
1479
1597
|
case "hatched":
|
|
@@ -1595,6 +1713,675 @@ function $e0f99502ff383dd8$export$bb35a6995ddbf32d(ctx, shape, x, y, config) {
|
|
|
1595
1713
|
|
|
1596
1714
|
|
|
1597
1715
|
|
|
1716
|
+
/**
|
|
1717
|
+
* Shape affinity system — controls which shapes look good together,
|
|
1718
|
+
* quality tiers for different rendering contexts, and size preferences.
|
|
1719
|
+
*
|
|
1720
|
+
* This replaces the naive "pick any shape" approach with intentional
|
|
1721
|
+
* curation that produces more cohesive compositions.
|
|
1722
|
+
*/ // ── Quality tiers ───────────────────────────────────────────────────
|
|
1723
|
+
// Not all shapes render equally well at all sizes or in all contexts.
|
|
1724
|
+
// Tier 1 shapes are visually strong at any size; Tier 3 shapes need
|
|
1725
|
+
// specific conditions to look good.
|
|
1726
|
+
const $8286059160ee2e04$export$4343b39fe47bd82c = {
|
|
1727
|
+
// ── Basic shapes ──────────────────────────────────────────────
|
|
1728
|
+
circle: {
|
|
1729
|
+
tier: 1,
|
|
1730
|
+
minSizeFraction: 0.05,
|
|
1731
|
+
maxSizeFraction: 1.0,
|
|
1732
|
+
affinities: [
|
|
1733
|
+
"circle",
|
|
1734
|
+
"blob",
|
|
1735
|
+
"hexagon",
|
|
1736
|
+
"flowerOfLife",
|
|
1737
|
+
"seedOfLife"
|
|
1738
|
+
],
|
|
1739
|
+
category: "basic",
|
|
1740
|
+
heroCandidate: false,
|
|
1741
|
+
bestStyles: [
|
|
1742
|
+
"fill-only",
|
|
1743
|
+
"watercolor",
|
|
1744
|
+
"fill-and-stroke"
|
|
1745
|
+
]
|
|
1746
|
+
},
|
|
1747
|
+
square: {
|
|
1748
|
+
tier: 2,
|
|
1749
|
+
minSizeFraction: 0.08,
|
|
1750
|
+
maxSizeFraction: 0.7,
|
|
1751
|
+
affinities: [
|
|
1752
|
+
"square",
|
|
1753
|
+
"diamond",
|
|
1754
|
+
"superellipse",
|
|
1755
|
+
"islamicPattern"
|
|
1756
|
+
],
|
|
1757
|
+
category: "basic",
|
|
1758
|
+
heroCandidate: false,
|
|
1759
|
+
bestStyles: [
|
|
1760
|
+
"fill-and-stroke",
|
|
1761
|
+
"stroke-only",
|
|
1762
|
+
"hatched"
|
|
1763
|
+
]
|
|
1764
|
+
},
|
|
1765
|
+
triangle: {
|
|
1766
|
+
tier: 1,
|
|
1767
|
+
minSizeFraction: 0.06,
|
|
1768
|
+
maxSizeFraction: 0.9,
|
|
1769
|
+
affinities: [
|
|
1770
|
+
"triangle",
|
|
1771
|
+
"diamond",
|
|
1772
|
+
"hexagon",
|
|
1773
|
+
"merkaba",
|
|
1774
|
+
"sriYantra"
|
|
1775
|
+
],
|
|
1776
|
+
category: "basic",
|
|
1777
|
+
heroCandidate: false,
|
|
1778
|
+
bestStyles: [
|
|
1779
|
+
"fill-and-stroke",
|
|
1780
|
+
"fill-only",
|
|
1781
|
+
"watercolor"
|
|
1782
|
+
]
|
|
1783
|
+
},
|
|
1784
|
+
hexagon: {
|
|
1785
|
+
tier: 1,
|
|
1786
|
+
minSizeFraction: 0.05,
|
|
1787
|
+
maxSizeFraction: 1.0,
|
|
1788
|
+
affinities: [
|
|
1789
|
+
"hexagon",
|
|
1790
|
+
"circle",
|
|
1791
|
+
"flowerOfLife",
|
|
1792
|
+
"metatronsCube",
|
|
1793
|
+
"triangle"
|
|
1794
|
+
],
|
|
1795
|
+
category: "basic",
|
|
1796
|
+
heroCandidate: false,
|
|
1797
|
+
bestStyles: [
|
|
1798
|
+
"fill-only",
|
|
1799
|
+
"fill-and-stroke",
|
|
1800
|
+
"watercolor"
|
|
1801
|
+
]
|
|
1802
|
+
},
|
|
1803
|
+
star: {
|
|
1804
|
+
tier: 2,
|
|
1805
|
+
minSizeFraction: 0.08,
|
|
1806
|
+
maxSizeFraction: 0.6,
|
|
1807
|
+
affinities: [
|
|
1808
|
+
"star",
|
|
1809
|
+
"circle",
|
|
1810
|
+
"mandala",
|
|
1811
|
+
"spirograph"
|
|
1812
|
+
],
|
|
1813
|
+
category: "basic",
|
|
1814
|
+
heroCandidate: false,
|
|
1815
|
+
bestStyles: [
|
|
1816
|
+
"fill-and-stroke",
|
|
1817
|
+
"stroke-only",
|
|
1818
|
+
"dashed"
|
|
1819
|
+
]
|
|
1820
|
+
},
|
|
1821
|
+
"jacked-star": {
|
|
1822
|
+
tier: 3,
|
|
1823
|
+
minSizeFraction: 0.1,
|
|
1824
|
+
maxSizeFraction: 0.4,
|
|
1825
|
+
affinities: [
|
|
1826
|
+
"star",
|
|
1827
|
+
"circle"
|
|
1828
|
+
],
|
|
1829
|
+
category: "basic",
|
|
1830
|
+
heroCandidate: false,
|
|
1831
|
+
bestStyles: [
|
|
1832
|
+
"stroke-only",
|
|
1833
|
+
"dashed"
|
|
1834
|
+
]
|
|
1835
|
+
},
|
|
1836
|
+
heart: {
|
|
1837
|
+
tier: 3,
|
|
1838
|
+
minSizeFraction: 0.1,
|
|
1839
|
+
maxSizeFraction: 0.5,
|
|
1840
|
+
affinities: [
|
|
1841
|
+
"circle",
|
|
1842
|
+
"blob"
|
|
1843
|
+
],
|
|
1844
|
+
category: "basic",
|
|
1845
|
+
heroCandidate: false,
|
|
1846
|
+
bestStyles: [
|
|
1847
|
+
"fill-only",
|
|
1848
|
+
"watercolor"
|
|
1849
|
+
]
|
|
1850
|
+
},
|
|
1851
|
+
diamond: {
|
|
1852
|
+
tier: 2,
|
|
1853
|
+
minSizeFraction: 0.06,
|
|
1854
|
+
maxSizeFraction: 0.8,
|
|
1855
|
+
affinities: [
|
|
1856
|
+
"diamond",
|
|
1857
|
+
"triangle",
|
|
1858
|
+
"square",
|
|
1859
|
+
"merkaba"
|
|
1860
|
+
],
|
|
1861
|
+
category: "basic",
|
|
1862
|
+
heroCandidate: false,
|
|
1863
|
+
bestStyles: [
|
|
1864
|
+
"fill-and-stroke",
|
|
1865
|
+
"fill-only",
|
|
1866
|
+
"double-stroke"
|
|
1867
|
+
]
|
|
1868
|
+
},
|
|
1869
|
+
cube: {
|
|
1870
|
+
tier: 3,
|
|
1871
|
+
minSizeFraction: 0.08,
|
|
1872
|
+
maxSizeFraction: 0.5,
|
|
1873
|
+
affinities: [
|
|
1874
|
+
"square",
|
|
1875
|
+
"diamond"
|
|
1876
|
+
],
|
|
1877
|
+
category: "basic",
|
|
1878
|
+
heroCandidate: false,
|
|
1879
|
+
bestStyles: [
|
|
1880
|
+
"stroke-only",
|
|
1881
|
+
"fill-and-stroke"
|
|
1882
|
+
]
|
|
1883
|
+
},
|
|
1884
|
+
// ── Complex shapes ────────────────────────────────────────────
|
|
1885
|
+
platonicSolid: {
|
|
1886
|
+
tier: 2,
|
|
1887
|
+
minSizeFraction: 0.15,
|
|
1888
|
+
maxSizeFraction: 0.8,
|
|
1889
|
+
affinities: [
|
|
1890
|
+
"metatronsCube",
|
|
1891
|
+
"merkaba",
|
|
1892
|
+
"hexagon",
|
|
1893
|
+
"triangle"
|
|
1894
|
+
],
|
|
1895
|
+
category: "complex",
|
|
1896
|
+
heroCandidate: true,
|
|
1897
|
+
bestStyles: [
|
|
1898
|
+
"stroke-only",
|
|
1899
|
+
"double-stroke",
|
|
1900
|
+
"dashed"
|
|
1901
|
+
]
|
|
1902
|
+
},
|
|
1903
|
+
fibonacciSpiral: {
|
|
1904
|
+
tier: 1,
|
|
1905
|
+
minSizeFraction: 0.2,
|
|
1906
|
+
maxSizeFraction: 1.0,
|
|
1907
|
+
affinities: [
|
|
1908
|
+
"circle",
|
|
1909
|
+
"rose",
|
|
1910
|
+
"spirograph",
|
|
1911
|
+
"flowerOfLife"
|
|
1912
|
+
],
|
|
1913
|
+
category: "complex",
|
|
1914
|
+
heroCandidate: true,
|
|
1915
|
+
bestStyles: [
|
|
1916
|
+
"stroke-only",
|
|
1917
|
+
"incomplete",
|
|
1918
|
+
"watercolor"
|
|
1919
|
+
]
|
|
1920
|
+
},
|
|
1921
|
+
islamicPattern: {
|
|
1922
|
+
tier: 2,
|
|
1923
|
+
minSizeFraction: 0.25,
|
|
1924
|
+
maxSizeFraction: 0.9,
|
|
1925
|
+
affinities: [
|
|
1926
|
+
"square",
|
|
1927
|
+
"hexagon",
|
|
1928
|
+
"star",
|
|
1929
|
+
"mandala"
|
|
1930
|
+
],
|
|
1931
|
+
category: "complex",
|
|
1932
|
+
heroCandidate: true,
|
|
1933
|
+
bestStyles: [
|
|
1934
|
+
"stroke-only",
|
|
1935
|
+
"dashed",
|
|
1936
|
+
"hatched"
|
|
1937
|
+
]
|
|
1938
|
+
},
|
|
1939
|
+
celticKnot: {
|
|
1940
|
+
tier: 2,
|
|
1941
|
+
minSizeFraction: 0.2,
|
|
1942
|
+
maxSizeFraction: 0.7,
|
|
1943
|
+
affinities: [
|
|
1944
|
+
"circle",
|
|
1945
|
+
"lissajous",
|
|
1946
|
+
"spirograph"
|
|
1947
|
+
],
|
|
1948
|
+
category: "complex",
|
|
1949
|
+
heroCandidate: true,
|
|
1950
|
+
bestStyles: [
|
|
1951
|
+
"stroke-only",
|
|
1952
|
+
"double-stroke"
|
|
1953
|
+
]
|
|
1954
|
+
},
|
|
1955
|
+
merkaba: {
|
|
1956
|
+
tier: 1,
|
|
1957
|
+
minSizeFraction: 0.15,
|
|
1958
|
+
maxSizeFraction: 1.0,
|
|
1959
|
+
affinities: [
|
|
1960
|
+
"triangle",
|
|
1961
|
+
"diamond",
|
|
1962
|
+
"sriYantra",
|
|
1963
|
+
"metatronsCube"
|
|
1964
|
+
],
|
|
1965
|
+
category: "complex",
|
|
1966
|
+
heroCandidate: true,
|
|
1967
|
+
bestStyles: [
|
|
1968
|
+
"stroke-only",
|
|
1969
|
+
"fill-and-stroke",
|
|
1970
|
+
"double-stroke"
|
|
1971
|
+
]
|
|
1972
|
+
},
|
|
1973
|
+
mandala: {
|
|
1974
|
+
tier: 1,
|
|
1975
|
+
minSizeFraction: 0.2,
|
|
1976
|
+
maxSizeFraction: 1.0,
|
|
1977
|
+
affinities: [
|
|
1978
|
+
"circle",
|
|
1979
|
+
"flowerOfLife",
|
|
1980
|
+
"spirograph",
|
|
1981
|
+
"rose"
|
|
1982
|
+
],
|
|
1983
|
+
category: "complex",
|
|
1984
|
+
heroCandidate: true,
|
|
1985
|
+
bestStyles: [
|
|
1986
|
+
"stroke-only",
|
|
1987
|
+
"dashed",
|
|
1988
|
+
"incomplete"
|
|
1989
|
+
]
|
|
1990
|
+
},
|
|
1991
|
+
fractal: {
|
|
1992
|
+
tier: 2,
|
|
1993
|
+
minSizeFraction: 0.2,
|
|
1994
|
+
maxSizeFraction: 0.8,
|
|
1995
|
+
affinities: [
|
|
1996
|
+
"blob",
|
|
1997
|
+
"lissajous",
|
|
1998
|
+
"circle"
|
|
1999
|
+
],
|
|
2000
|
+
category: "complex",
|
|
2001
|
+
heroCandidate: true,
|
|
2002
|
+
bestStyles: [
|
|
2003
|
+
"stroke-only",
|
|
2004
|
+
"incomplete"
|
|
2005
|
+
]
|
|
2006
|
+
},
|
|
2007
|
+
// ── Sacred shapes ─────────────────────────────────────────────
|
|
2008
|
+
flowerOfLife: {
|
|
2009
|
+
tier: 1,
|
|
2010
|
+
minSizeFraction: 0.2,
|
|
2011
|
+
maxSizeFraction: 1.0,
|
|
2012
|
+
affinities: [
|
|
2013
|
+
"circle",
|
|
2014
|
+
"hexagon",
|
|
2015
|
+
"seedOfLife",
|
|
2016
|
+
"eggOfLife",
|
|
2017
|
+
"metatronsCube"
|
|
2018
|
+
],
|
|
2019
|
+
category: "sacred",
|
|
2020
|
+
heroCandidate: true,
|
|
2021
|
+
bestStyles: [
|
|
2022
|
+
"stroke-only",
|
|
2023
|
+
"watercolor",
|
|
2024
|
+
"incomplete"
|
|
2025
|
+
]
|
|
2026
|
+
},
|
|
2027
|
+
treeOfLife: {
|
|
2028
|
+
tier: 2,
|
|
2029
|
+
minSizeFraction: 0.25,
|
|
2030
|
+
maxSizeFraction: 0.9,
|
|
2031
|
+
affinities: [
|
|
2032
|
+
"circle",
|
|
2033
|
+
"flowerOfLife",
|
|
2034
|
+
"metatronsCube"
|
|
2035
|
+
],
|
|
2036
|
+
category: "sacred",
|
|
2037
|
+
heroCandidate: true,
|
|
2038
|
+
bestStyles: [
|
|
2039
|
+
"stroke-only",
|
|
2040
|
+
"double-stroke"
|
|
2041
|
+
]
|
|
2042
|
+
},
|
|
2043
|
+
metatronsCube: {
|
|
2044
|
+
tier: 1,
|
|
2045
|
+
minSizeFraction: 0.2,
|
|
2046
|
+
maxSizeFraction: 1.0,
|
|
2047
|
+
affinities: [
|
|
2048
|
+
"hexagon",
|
|
2049
|
+
"flowerOfLife",
|
|
2050
|
+
"platonicSolid",
|
|
2051
|
+
"merkaba"
|
|
2052
|
+
],
|
|
2053
|
+
category: "sacred",
|
|
2054
|
+
heroCandidate: true,
|
|
2055
|
+
bestStyles: [
|
|
2056
|
+
"stroke-only",
|
|
2057
|
+
"dashed",
|
|
2058
|
+
"incomplete"
|
|
2059
|
+
]
|
|
2060
|
+
},
|
|
2061
|
+
sriYantra: {
|
|
2062
|
+
tier: 1,
|
|
2063
|
+
minSizeFraction: 0.2,
|
|
2064
|
+
maxSizeFraction: 1.0,
|
|
2065
|
+
affinities: [
|
|
2066
|
+
"triangle",
|
|
2067
|
+
"merkaba",
|
|
2068
|
+
"mandala",
|
|
2069
|
+
"diamond"
|
|
2070
|
+
],
|
|
2071
|
+
category: "sacred",
|
|
2072
|
+
heroCandidate: true,
|
|
2073
|
+
bestStyles: [
|
|
2074
|
+
"stroke-only",
|
|
2075
|
+
"fill-and-stroke",
|
|
2076
|
+
"double-stroke"
|
|
2077
|
+
]
|
|
2078
|
+
},
|
|
2079
|
+
seedOfLife: {
|
|
2080
|
+
tier: 1,
|
|
2081
|
+
minSizeFraction: 0.15,
|
|
2082
|
+
maxSizeFraction: 0.9,
|
|
2083
|
+
affinities: [
|
|
2084
|
+
"circle",
|
|
2085
|
+
"flowerOfLife",
|
|
2086
|
+
"eggOfLife",
|
|
2087
|
+
"hexagon"
|
|
2088
|
+
],
|
|
2089
|
+
category: "sacred",
|
|
2090
|
+
heroCandidate: true,
|
|
2091
|
+
bestStyles: [
|
|
2092
|
+
"stroke-only",
|
|
2093
|
+
"watercolor",
|
|
2094
|
+
"fill-only"
|
|
2095
|
+
]
|
|
2096
|
+
},
|
|
2097
|
+
vesicaPiscis: {
|
|
2098
|
+
tier: 2,
|
|
2099
|
+
minSizeFraction: 0.15,
|
|
2100
|
+
maxSizeFraction: 0.7,
|
|
2101
|
+
affinities: [
|
|
2102
|
+
"circle",
|
|
2103
|
+
"seedOfLife",
|
|
2104
|
+
"flowerOfLife"
|
|
2105
|
+
],
|
|
2106
|
+
category: "sacred",
|
|
2107
|
+
heroCandidate: false,
|
|
2108
|
+
bestStyles: [
|
|
2109
|
+
"stroke-only",
|
|
2110
|
+
"watercolor"
|
|
2111
|
+
]
|
|
2112
|
+
},
|
|
2113
|
+
torus: {
|
|
2114
|
+
tier: 3,
|
|
2115
|
+
minSizeFraction: 0.2,
|
|
2116
|
+
maxSizeFraction: 0.6,
|
|
2117
|
+
affinities: [
|
|
2118
|
+
"circle",
|
|
2119
|
+
"spirograph",
|
|
2120
|
+
"waveRing"
|
|
2121
|
+
],
|
|
2122
|
+
category: "sacred",
|
|
2123
|
+
heroCandidate: false,
|
|
2124
|
+
bestStyles: [
|
|
2125
|
+
"stroke-only",
|
|
2126
|
+
"dashed"
|
|
2127
|
+
]
|
|
2128
|
+
},
|
|
2129
|
+
eggOfLife: {
|
|
2130
|
+
tier: 2,
|
|
2131
|
+
minSizeFraction: 0.15,
|
|
2132
|
+
maxSizeFraction: 0.8,
|
|
2133
|
+
affinities: [
|
|
2134
|
+
"circle",
|
|
2135
|
+
"seedOfLife",
|
|
2136
|
+
"flowerOfLife"
|
|
2137
|
+
],
|
|
2138
|
+
category: "sacred",
|
|
2139
|
+
heroCandidate: true,
|
|
2140
|
+
bestStyles: [
|
|
2141
|
+
"stroke-only",
|
|
2142
|
+
"watercolor"
|
|
2143
|
+
]
|
|
2144
|
+
},
|
|
2145
|
+
// ── Procedural shapes ─────────────────────────────────────────
|
|
2146
|
+
blob: {
|
|
2147
|
+
tier: 1,
|
|
2148
|
+
minSizeFraction: 0.05,
|
|
2149
|
+
maxSizeFraction: 1.0,
|
|
2150
|
+
affinities: [
|
|
2151
|
+
"blob",
|
|
2152
|
+
"circle",
|
|
2153
|
+
"superellipse",
|
|
2154
|
+
"waveRing"
|
|
2155
|
+
],
|
|
2156
|
+
category: "procedural",
|
|
2157
|
+
heroCandidate: false,
|
|
2158
|
+
bestStyles: [
|
|
2159
|
+
"fill-only",
|
|
2160
|
+
"watercolor",
|
|
2161
|
+
"fill-and-stroke"
|
|
2162
|
+
]
|
|
2163
|
+
},
|
|
2164
|
+
ngon: {
|
|
2165
|
+
tier: 2,
|
|
2166
|
+
minSizeFraction: 0.06,
|
|
2167
|
+
maxSizeFraction: 0.8,
|
|
2168
|
+
affinities: [
|
|
2169
|
+
"hexagon",
|
|
2170
|
+
"triangle",
|
|
2171
|
+
"diamond",
|
|
2172
|
+
"superellipse"
|
|
2173
|
+
],
|
|
2174
|
+
category: "procedural",
|
|
2175
|
+
heroCandidate: false,
|
|
2176
|
+
bestStyles: [
|
|
2177
|
+
"fill-and-stroke",
|
|
2178
|
+
"fill-only",
|
|
2179
|
+
"hatched"
|
|
2180
|
+
]
|
|
2181
|
+
},
|
|
2182
|
+
lissajous: {
|
|
2183
|
+
tier: 2,
|
|
2184
|
+
minSizeFraction: 0.15,
|
|
2185
|
+
maxSizeFraction: 0.8,
|
|
2186
|
+
affinities: [
|
|
2187
|
+
"spirograph",
|
|
2188
|
+
"rose",
|
|
2189
|
+
"celticKnot",
|
|
2190
|
+
"fibonacciSpiral"
|
|
2191
|
+
],
|
|
2192
|
+
category: "procedural",
|
|
2193
|
+
heroCandidate: false,
|
|
2194
|
+
bestStyles: [
|
|
2195
|
+
"stroke-only",
|
|
2196
|
+
"incomplete",
|
|
2197
|
+
"dashed"
|
|
2198
|
+
]
|
|
2199
|
+
},
|
|
2200
|
+
superellipse: {
|
|
2201
|
+
tier: 1,
|
|
2202
|
+
minSizeFraction: 0.05,
|
|
2203
|
+
maxSizeFraction: 1.0,
|
|
2204
|
+
affinities: [
|
|
2205
|
+
"circle",
|
|
2206
|
+
"square",
|
|
2207
|
+
"blob",
|
|
2208
|
+
"hexagon"
|
|
2209
|
+
],
|
|
2210
|
+
category: "procedural",
|
|
2211
|
+
heroCandidate: false,
|
|
2212
|
+
bestStyles: [
|
|
2213
|
+
"fill-only",
|
|
2214
|
+
"watercolor",
|
|
2215
|
+
"fill-and-stroke"
|
|
2216
|
+
]
|
|
2217
|
+
},
|
|
2218
|
+
spirograph: {
|
|
2219
|
+
tier: 1,
|
|
2220
|
+
minSizeFraction: 0.15,
|
|
2221
|
+
maxSizeFraction: 0.9,
|
|
2222
|
+
affinities: [
|
|
2223
|
+
"rose",
|
|
2224
|
+
"lissajous",
|
|
2225
|
+
"mandala",
|
|
2226
|
+
"flowerOfLife"
|
|
2227
|
+
],
|
|
2228
|
+
category: "procedural",
|
|
2229
|
+
heroCandidate: true,
|
|
2230
|
+
bestStyles: [
|
|
2231
|
+
"stroke-only",
|
|
2232
|
+
"incomplete",
|
|
2233
|
+
"dashed"
|
|
2234
|
+
]
|
|
2235
|
+
},
|
|
2236
|
+
waveRing: {
|
|
2237
|
+
tier: 2,
|
|
2238
|
+
minSizeFraction: 0.1,
|
|
2239
|
+
maxSizeFraction: 0.8,
|
|
2240
|
+
affinities: [
|
|
2241
|
+
"circle",
|
|
2242
|
+
"blob",
|
|
2243
|
+
"torus",
|
|
2244
|
+
"spirograph"
|
|
2245
|
+
],
|
|
2246
|
+
category: "procedural",
|
|
2247
|
+
heroCandidate: false,
|
|
2248
|
+
bestStyles: [
|
|
2249
|
+
"stroke-only",
|
|
2250
|
+
"dashed",
|
|
2251
|
+
"incomplete"
|
|
2252
|
+
]
|
|
2253
|
+
},
|
|
2254
|
+
rose: {
|
|
2255
|
+
tier: 1,
|
|
2256
|
+
minSizeFraction: 0.1,
|
|
2257
|
+
maxSizeFraction: 0.9,
|
|
2258
|
+
affinities: [
|
|
2259
|
+
"spirograph",
|
|
2260
|
+
"mandala",
|
|
2261
|
+
"flowerOfLife",
|
|
2262
|
+
"circle"
|
|
2263
|
+
],
|
|
2264
|
+
category: "procedural",
|
|
2265
|
+
heroCandidate: true,
|
|
2266
|
+
bestStyles: [
|
|
2267
|
+
"stroke-only",
|
|
2268
|
+
"fill-only",
|
|
2269
|
+
"watercolor"
|
|
2270
|
+
]
|
|
2271
|
+
}
|
|
2272
|
+
};
|
|
2273
|
+
function $8286059160ee2e04$export$4a95df8944b5033b(rng, shapeNames, archetypeName) {
|
|
2274
|
+
const available = shapeNames.filter((s)=>$8286059160ee2e04$export$4343b39fe47bd82c[s]);
|
|
2275
|
+
// Pick a seed shape — tier 1 shapes that are hero candidates
|
|
2276
|
+
const heroPool = available.filter((s)=>$8286059160ee2e04$export$4343b39fe47bd82c[s].tier === 1 && $8286059160ee2e04$export$4343b39fe47bd82c[s].heroCandidate);
|
|
2277
|
+
const seedShape = heroPool.length > 0 ? heroPool[Math.floor(rng() * heroPool.length)] : available[Math.floor(rng() * available.length)];
|
|
2278
|
+
const seedProfile = $8286059160ee2e04$export$4343b39fe47bd82c[seedShape];
|
|
2279
|
+
// Primary: seed shape + its direct affinities (tier 1-2 only)
|
|
2280
|
+
const primaryCandidates = [
|
|
2281
|
+
seedShape,
|
|
2282
|
+
...seedProfile.affinities
|
|
2283
|
+
].filter((s)=>available.includes(s)).filter((s)=>$8286059160ee2e04$export$4343b39fe47bd82c[s].tier <= 2);
|
|
2284
|
+
const primary = [
|
|
2285
|
+
...new Set(primaryCandidates)
|
|
2286
|
+
].slice(0, 5);
|
|
2287
|
+
// Supporting: affinities of affinities, plus same-category shapes
|
|
2288
|
+
const supportingSet = new Set();
|
|
2289
|
+
for (const p of primary){
|
|
2290
|
+
const profile = $8286059160ee2e04$export$4343b39fe47bd82c[p];
|
|
2291
|
+
if (!profile) continue;
|
|
2292
|
+
for (const aff of profile.affinities)if (available.includes(aff) && !primary.includes(aff)) supportingSet.add(aff);
|
|
2293
|
+
}
|
|
2294
|
+
// Add same-category tier 1-2 shapes
|
|
2295
|
+
for (const s of available){
|
|
2296
|
+
const p = $8286059160ee2e04$export$4343b39fe47bd82c[s];
|
|
2297
|
+
if (p.category === seedProfile.category && p.tier <= 2 && !primary.includes(s)) supportingSet.add(s);
|
|
2298
|
+
}
|
|
2299
|
+
const supporting = [
|
|
2300
|
+
...supportingSet
|
|
2301
|
+
].slice(0, 6);
|
|
2302
|
+
// Accents: tier 1 shapes from other categories for contrast
|
|
2303
|
+
const usedCategories = new Set([
|
|
2304
|
+
...primary,
|
|
2305
|
+
...supporting
|
|
2306
|
+
].map((s)=>$8286059160ee2e04$export$4343b39fe47bd82c[s]?.category));
|
|
2307
|
+
const accentCandidates = available.filter((s)=>!primary.includes(s) && !supporting.includes(s) && $8286059160ee2e04$export$4343b39fe47bd82c[s].tier <= 2 && !usedCategories.has($8286059160ee2e04$export$4343b39fe47bd82c[s].category));
|
|
2308
|
+
// Shuffle and take a few
|
|
2309
|
+
const accents = [];
|
|
2310
|
+
const shuffled = [
|
|
2311
|
+
...accentCandidates
|
|
2312
|
+
];
|
|
2313
|
+
for(let i = shuffled.length - 1; i > 0; i--){
|
|
2314
|
+
const j = Math.floor(rng() * (i + 1));
|
|
2315
|
+
[shuffled[i], shuffled[j]] = [
|
|
2316
|
+
shuffled[j],
|
|
2317
|
+
shuffled[i]
|
|
2318
|
+
];
|
|
2319
|
+
}
|
|
2320
|
+
accents.push(...shuffled.slice(0, 3));
|
|
2321
|
+
// For certain archetypes, bias the palette
|
|
2322
|
+
if (archetypeName === "geometric-precision") // Remove blobs and organic shapes from primary
|
|
2323
|
+
return {
|
|
2324
|
+
primary: primary.filter((s)=>$8286059160ee2e04$export$4343b39fe47bd82c[s]?.category !== "procedural" || s === "ngon"),
|
|
2325
|
+
supporting: supporting.filter((s)=>s !== "blob"),
|
|
2326
|
+
accents: accents
|
|
2327
|
+
};
|
|
2328
|
+
if (archetypeName === "organic-flow") {
|
|
2329
|
+
// Boost procedural/organic shapes
|
|
2330
|
+
const organicBoost = available.filter((s)=>[
|
|
2331
|
+
"blob",
|
|
2332
|
+
"superellipse",
|
|
2333
|
+
"waveRing",
|
|
2334
|
+
"rose"
|
|
2335
|
+
].includes(s) && !primary.includes(s));
|
|
2336
|
+
return {
|
|
2337
|
+
primary: [
|
|
2338
|
+
...primary,
|
|
2339
|
+
...organicBoost.slice(0, 2)
|
|
2340
|
+
],
|
|
2341
|
+
supporting: supporting,
|
|
2342
|
+
accents: accents
|
|
2343
|
+
};
|
|
2344
|
+
}
|
|
2345
|
+
return {
|
|
2346
|
+
primary: primary,
|
|
2347
|
+
supporting: supporting,
|
|
2348
|
+
accents: accents
|
|
2349
|
+
};
|
|
2350
|
+
}
|
|
2351
|
+
function $8286059160ee2e04$export$3c37d9a045754d0e(palette, rng, sizeFraction) {
|
|
2352
|
+
// Filter each tier by size constraints
|
|
2353
|
+
const validPrimary = palette.primary.filter((s)=>{
|
|
2354
|
+
const p = $8286059160ee2e04$export$4343b39fe47bd82c[s];
|
|
2355
|
+
return p && sizeFraction >= p.minSizeFraction && sizeFraction <= p.maxSizeFraction;
|
|
2356
|
+
});
|
|
2357
|
+
const validSupporting = palette.supporting.filter((s)=>{
|
|
2358
|
+
const p = $8286059160ee2e04$export$4343b39fe47bd82c[s];
|
|
2359
|
+
return p && sizeFraction >= p.minSizeFraction && sizeFraction <= p.maxSizeFraction;
|
|
2360
|
+
});
|
|
2361
|
+
const validAccents = palette.accents.filter((s)=>{
|
|
2362
|
+
const p = $8286059160ee2e04$export$4343b39fe47bd82c[s];
|
|
2363
|
+
return p && sizeFraction >= p.minSizeFraction && sizeFraction <= p.maxSizeFraction;
|
|
2364
|
+
});
|
|
2365
|
+
const roll = rng();
|
|
2366
|
+
if (roll < 0.60 && validPrimary.length > 0) return validPrimary[Math.floor(rng() * validPrimary.length)];
|
|
2367
|
+
if (roll < 0.90 && validSupporting.length > 0) return validSupporting[Math.floor(rng() * validSupporting.length)];
|
|
2368
|
+
if (validAccents.length > 0) return validAccents[Math.floor(rng() * validAccents.length)];
|
|
2369
|
+
// Fallback: any valid primary or supporting
|
|
2370
|
+
const fallback = [
|
|
2371
|
+
...validPrimary,
|
|
2372
|
+
...validSupporting
|
|
2373
|
+
];
|
|
2374
|
+
if (fallback.length > 0) return fallback[Math.floor(rng() * fallback.length)];
|
|
2375
|
+
// Ultimate fallback
|
|
2376
|
+
return palette.primary[0] || "circle";
|
|
2377
|
+
}
|
|
2378
|
+
function $8286059160ee2e04$export$ab873bb6fb56c1a8(shapeName, layerStyle, rng) {
|
|
2379
|
+
const profile = $8286059160ee2e04$export$4343b39fe47bd82c[shapeName];
|
|
2380
|
+
if (!profile || rng() > 0.7) return layerStyle;
|
|
2381
|
+
return profile.bestStyles[Math.floor(rng() * profile.bestStyles.length)];
|
|
2382
|
+
}
|
|
2383
|
+
|
|
2384
|
+
|
|
1598
2385
|
|
|
1599
2386
|
/**
|
|
1600
2387
|
* Configuration options for image generation.
|
|
@@ -1809,6 +2596,69 @@ const $68a238ccd77f2bcd$var$ARCHETYPES = [
|
|
|
1809
2596
|
sizePower: 2.5,
|
|
1810
2597
|
invertForeground: false
|
|
1811
2598
|
},
|
|
2599
|
+
{
|
|
2600
|
+
name: "watercolor-wash",
|
|
2601
|
+
gridSize: 3,
|
|
2602
|
+
layers: 3,
|
|
2603
|
+
baseOpacity: 0.25,
|
|
2604
|
+
opacityReduction: 0.03,
|
|
2605
|
+
minShapeSize: 200,
|
|
2606
|
+
maxShapeSize: 700,
|
|
2607
|
+
backgroundStyle: "radial-light",
|
|
2608
|
+
paletteMode: "harmonious",
|
|
2609
|
+
preferredStyles: [
|
|
2610
|
+
"watercolor",
|
|
2611
|
+
"fill-only",
|
|
2612
|
+
"incomplete"
|
|
2613
|
+
],
|
|
2614
|
+
flowLineMultiplier: 0.5,
|
|
2615
|
+
heroShape: false,
|
|
2616
|
+
glowMultiplier: 0.3,
|
|
2617
|
+
sizePower: 0.6,
|
|
2618
|
+
invertForeground: false
|
|
2619
|
+
},
|
|
2620
|
+
{
|
|
2621
|
+
name: "op-art",
|
|
2622
|
+
gridSize: 8,
|
|
2623
|
+
layers: 2,
|
|
2624
|
+
baseOpacity: 0.95,
|
|
2625
|
+
opacityReduction: 0.05,
|
|
2626
|
+
minShapeSize: 20,
|
|
2627
|
+
maxShapeSize: 200,
|
|
2628
|
+
backgroundStyle: "solid-light",
|
|
2629
|
+
paletteMode: "high-contrast",
|
|
2630
|
+
preferredStyles: [
|
|
2631
|
+
"fill-and-stroke",
|
|
2632
|
+
"stroke-only",
|
|
2633
|
+
"dashed"
|
|
2634
|
+
],
|
|
2635
|
+
flowLineMultiplier: 0,
|
|
2636
|
+
heroShape: false,
|
|
2637
|
+
glowMultiplier: 0,
|
|
2638
|
+
sizePower: 0.4,
|
|
2639
|
+
invertForeground: false
|
|
2640
|
+
},
|
|
2641
|
+
{
|
|
2642
|
+
name: "collage",
|
|
2643
|
+
gridSize: 4,
|
|
2644
|
+
layers: 3,
|
|
2645
|
+
baseOpacity: 0.9,
|
|
2646
|
+
opacityReduction: 0.08,
|
|
2647
|
+
minShapeSize: 80,
|
|
2648
|
+
maxShapeSize: 500,
|
|
2649
|
+
backgroundStyle: "solid-light",
|
|
2650
|
+
paletteMode: "duotone",
|
|
2651
|
+
preferredStyles: [
|
|
2652
|
+
"fill-and-stroke",
|
|
2653
|
+
"fill-only",
|
|
2654
|
+
"double-stroke"
|
|
2655
|
+
],
|
|
2656
|
+
flowLineMultiplier: 0,
|
|
2657
|
+
heroShape: true,
|
|
2658
|
+
glowMultiplier: 0,
|
|
2659
|
+
sizePower: 0.7,
|
|
2660
|
+
invertForeground: false
|
|
2661
|
+
},
|
|
1812
2662
|
{
|
|
1813
2663
|
name: "classic",
|
|
1814
2664
|
gridSize: 5,
|
|
@@ -1836,26 +2686,7 @@ function $68a238ccd77f2bcd$export$f1142fd7da4d6590(rng) {
|
|
|
1836
2686
|
}
|
|
1837
2687
|
|
|
1838
2688
|
|
|
1839
|
-
// ── Shape categories for weighted selection
|
|
1840
|
-
const $1f63dc64b5593c73$var$BASIC_SHAPES = [
|
|
1841
|
-
"circle",
|
|
1842
|
-
"square",
|
|
1843
|
-
"triangle",
|
|
1844
|
-
"hexagon",
|
|
1845
|
-
"diamond",
|
|
1846
|
-
"cube"
|
|
1847
|
-
];
|
|
1848
|
-
const $1f63dc64b5593c73$var$COMPLEX_SHAPES = [
|
|
1849
|
-
"star",
|
|
1850
|
-
"jacked-star",
|
|
1851
|
-
"heart",
|
|
1852
|
-
"platonicSolid",
|
|
1853
|
-
"fibonacciSpiral",
|
|
1854
|
-
"islamicPattern",
|
|
1855
|
-
"celticKnot",
|
|
1856
|
-
"merkaba",
|
|
1857
|
-
"fractal"
|
|
1858
|
-
];
|
|
2689
|
+
// ── Shape categories for weighted selection (legacy fallback) ───────
|
|
1859
2690
|
const $1f63dc64b5593c73$var$SACRED_SHAPES = [
|
|
1860
2691
|
"mandala",
|
|
1861
2692
|
"flowerOfLife",
|
|
@@ -1867,15 +2698,6 @@ const $1f63dc64b5593c73$var$SACRED_SHAPES = [
|
|
|
1867
2698
|
"torus",
|
|
1868
2699
|
"eggOfLife"
|
|
1869
2700
|
];
|
|
1870
|
-
const $1f63dc64b5593c73$var$PROCEDURAL_SHAPES = [
|
|
1871
|
-
"blob",
|
|
1872
|
-
"ngon",
|
|
1873
|
-
"lissajous",
|
|
1874
|
-
"superellipse",
|
|
1875
|
-
"spirograph",
|
|
1876
|
-
"waveRing",
|
|
1877
|
-
"rose"
|
|
1878
|
-
];
|
|
1879
2701
|
const $1f63dc64b5593c73$var$COMPOSITION_MODES = [
|
|
1880
2702
|
"radial",
|
|
1881
2703
|
"flow-field",
|
|
@@ -1883,23 +2705,6 @@ const $1f63dc64b5593c73$var$COMPOSITION_MODES = [
|
|
|
1883
2705
|
"grid-subdivision",
|
|
1884
2706
|
"clustered"
|
|
1885
2707
|
];
|
|
1886
|
-
// ── Helper: pick shape with layer-aware weighting ───────────────────
|
|
1887
|
-
function $1f63dc64b5593c73$var$pickShape(rng, layerRatio, shapeNames) {
|
|
1888
|
-
const basicW = 1 - layerRatio * 0.6;
|
|
1889
|
-
const complexW = 0.3 + layerRatio * 0.3;
|
|
1890
|
-
const sacredW = 0.1 + layerRatio * 0.4;
|
|
1891
|
-
const proceduralW = 0.25 + layerRatio * 0.2; // always present, grows with depth
|
|
1892
|
-
const total = basicW + complexW + sacredW + proceduralW;
|
|
1893
|
-
const roll = rng() * total;
|
|
1894
|
-
let pool;
|
|
1895
|
-
if (roll < basicW) pool = $1f63dc64b5593c73$var$BASIC_SHAPES;
|
|
1896
|
-
else if (roll < basicW + complexW) pool = $1f63dc64b5593c73$var$COMPLEX_SHAPES;
|
|
1897
|
-
else if (roll < basicW + complexW + sacredW) pool = $1f63dc64b5593c73$var$SACRED_SHAPES;
|
|
1898
|
-
else pool = $1f63dc64b5593c73$var$PROCEDURAL_SHAPES;
|
|
1899
|
-
const available = pool.filter((s)=>shapeNames.includes(s));
|
|
1900
|
-
if (available.length === 0) return shapeNames[Math.floor(rng() * shapeNames.length)];
|
|
1901
|
-
return available[Math.floor(rng() * available.length)];
|
|
1902
|
-
}
|
|
1903
2708
|
// ── Helper: get position based on composition mode ──────────────────
|
|
1904
2709
|
function $1f63dc64b5593c73$var$getCompositionPosition(mode, rng, width, height, shapeIndex, totalShapes, cx, cy) {
|
|
1905
2710
|
switch(mode){
|
|
@@ -1958,22 +2763,28 @@ function $1f63dc64b5593c73$var$getCompositionPosition(mode, rng, width, height,
|
|
|
1958
2763
|
};
|
|
1959
2764
|
}
|
|
1960
2765
|
}
|
|
1961
|
-
// ── Helper: positional color
|
|
1962
|
-
function $1f63dc64b5593c73$var$getPositionalColor(x, y, width, height,
|
|
1963
|
-
|
|
1964
|
-
const
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
return (0, $b5a262d09b87e373$export$
|
|
2766
|
+
// ── Helper: positional color from hierarchy ─────────────────────────
|
|
2767
|
+
function $1f63dc64b5593c73$var$getPositionalColor(x, y, width, height, hierarchy, rng) {
|
|
2768
|
+
// Blend position into color selection — shapes near center lean dominant
|
|
2769
|
+
const distFromCenter = Math.hypot(x - width / 2, y - height / 2) / Math.hypot(width / 2, height / 2);
|
|
2770
|
+
// Center = more dominant, edges = more accent
|
|
2771
|
+
if (distFromCenter < 0.35) return (0, $b5a262d09b87e373$export$18a34c25ea7e724b)(hierarchy.dominant, rng, 10, 0.08);
|
|
2772
|
+
else if (distFromCenter < 0.7) return (0, $b5a262d09b87e373$export$18a34c25ea7e724b)((0, $b5a262d09b87e373$export$b49f62f0a99da0e8)(hierarchy, rng), rng, 8, 0.06);
|
|
2773
|
+
else {
|
|
2774
|
+
// Edges: bias toward secondary/accent
|
|
2775
|
+
const roll = rng();
|
|
2776
|
+
const color = roll < 0.4 ? hierarchy.secondary : roll < 0.75 ? hierarchy.accent : hierarchy.dominant;
|
|
2777
|
+
return (0, $b5a262d09b87e373$export$18a34c25ea7e724b)(color, rng, 12, 0.08);
|
|
2778
|
+
}
|
|
1968
2779
|
}
|
|
1969
|
-
// ── Helper: check if a position is inside a void zone
|
|
2780
|
+
// ── Helper: check if a position is inside a void zone ───────────────
|
|
1970
2781
|
function $1f63dc64b5593c73$var$isInVoidZone(x, y, voidZones) {
|
|
1971
2782
|
for (const zone of voidZones){
|
|
1972
2783
|
if (Math.hypot(x - zone.x, y - zone.y) < zone.radius) return true;
|
|
1973
2784
|
}
|
|
1974
2785
|
return false;
|
|
1975
2786
|
}
|
|
1976
|
-
// ── Helper: density check
|
|
2787
|
+
// ── Helper: density check ───────────────────────────────────────────
|
|
1977
2788
|
function $1f63dc64b5593c73$var$localDensity(x, y, positions, radius) {
|
|
1978
2789
|
let count = 0;
|
|
1979
2790
|
for (const p of positions)if (Math.hypot(x - p.x, y - p.y) < radius) count++;
|
|
@@ -2069,7 +2880,15 @@ function $1f63dc64b5593c73$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2069
2880
|
const [bgStart, bgEnd] = colorScheme.getBackgroundColorsByMode(archetype.paletteMode);
|
|
2070
2881
|
const tempMode = colorScheme.getTemperatureMode();
|
|
2071
2882
|
const fgTempTarget = tempMode === "warm-bg" ? "cool" : tempMode === "cool-bg" ? "warm" : null;
|
|
2883
|
+
// ── 0b. Color hierarchy — dominant/secondary/accent weighting ──
|
|
2884
|
+
const colorHierarchy = (0, $b5a262d09b87e373$export$fabac4600b87056)(colors, rng);
|
|
2885
|
+
// ── 0c. Shape palette — curated shapes that work well together ──
|
|
2072
2886
|
const shapeNames = Object.keys((0, $e41b41d8dcf837ad$export$4ff7fc6f1af248b5));
|
|
2887
|
+
const shapePalette = (0, $8286059160ee2e04$export$4a95df8944b5033b)(rng, shapeNames, archetype.name);
|
|
2888
|
+
// ── 0d. Color grading — unified tone for the whole image ───────
|
|
2889
|
+
const colorGrade = (0, $b5a262d09b87e373$export$6d1620b367f86f7a)(rng);
|
|
2890
|
+
// ── 0e. Light direction — consistent shadow angle ──────────────
|
|
2891
|
+
const lightAngle = rng() * Math.PI * 2;
|
|
2073
2892
|
const scaleFactor = Math.min(width, height) / 1024;
|
|
2074
2893
|
const adjustedMinSize = minShapeSize * scaleFactor;
|
|
2075
2894
|
const adjustedMaxSize = maxShapeSize * scaleFactor;
|
|
@@ -2078,27 +2897,45 @@ function $1f63dc64b5593c73$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2078
2897
|
// ── 1. Background ──────────────────────────────────────────────
|
|
2079
2898
|
const bgRadius = Math.hypot(cx, cy);
|
|
2080
2899
|
$1f63dc64b5593c73$var$drawBackground(ctx, archetype.backgroundStyle, bgStart, bgEnd, width, height, cx, cy, bgRadius, rng, colors);
|
|
2900
|
+
// Gradient mesh overlay — 3-4 color control points for richer backgrounds
|
|
2901
|
+
const meshPoints = 3 + Math.floor(rng() * 2);
|
|
2902
|
+
ctx.globalCompositeOperation = "soft-light";
|
|
2903
|
+
for(let i = 0; i < meshPoints; i++){
|
|
2904
|
+
const mx = rng() * width;
|
|
2905
|
+
const my = rng() * height;
|
|
2906
|
+
const mRadius = Math.min(width, height) * (0.3 + rng() * 0.4);
|
|
2907
|
+
const mColor = (0, $b5a262d09b87e373$export$b49f62f0a99da0e8)(colorHierarchy, rng);
|
|
2908
|
+
const grad = ctx.createRadialGradient(mx, my, 0, mx, my, mRadius);
|
|
2909
|
+
grad.addColorStop(0, (0, $b5a262d09b87e373$export$f2121afcad3d553f)(mColor, 0.08 + rng() * 0.06));
|
|
2910
|
+
grad.addColorStop(1, "rgba(0,0,0,0)");
|
|
2911
|
+
ctx.globalAlpha = 1;
|
|
2912
|
+
ctx.fillStyle = grad;
|
|
2913
|
+
ctx.fillRect(0, 0, width, height);
|
|
2914
|
+
}
|
|
2915
|
+
ctx.globalCompositeOperation = "source-over";
|
|
2081
2916
|
// Compute average background luminance for contrast enforcement
|
|
2082
2917
|
const bgLum = ((0, $b5a262d09b87e373$export$5c6e3c2b59b7fbbe)(bgStart) + (0, $b5a262d09b87e373$export$5c6e3c2b59b7fbbe)(bgEnd)) / 2;
|
|
2083
|
-
// ── 1b. Layered background
|
|
2084
|
-
// Draw large, very faint shapes to give the background texture
|
|
2918
|
+
// ── 1b. Layered background — archetype-coherent shapes ─────────
|
|
2085
2919
|
const bgShapeCount = 3 + Math.floor(rng() * 4);
|
|
2086
2920
|
ctx.globalCompositeOperation = "soft-light";
|
|
2087
2921
|
for(let i = 0; i < bgShapeCount; i++){
|
|
2088
2922
|
const bx = rng() * width;
|
|
2089
2923
|
const by = rng() * height;
|
|
2090
2924
|
const bSize = width * 0.3 + rng() * width * 0.5;
|
|
2091
|
-
const bColor =
|
|
2925
|
+
const bColor = (0, $b5a262d09b87e373$export$b49f62f0a99da0e8)(colorHierarchy, rng);
|
|
2092
2926
|
ctx.globalAlpha = 0.03 + rng() * 0.05;
|
|
2093
2927
|
ctx.fillStyle = (0, $b5a262d09b87e373$export$f2121afcad3d553f)(bColor, 0.15);
|
|
2094
2928
|
ctx.beginPath();
|
|
2095
|
-
|
|
2929
|
+
// Use archetype-appropriate background shapes
|
|
2930
|
+
if (archetype.name === "geometric-precision" || archetype.name === "op-art") // Rectangular shapes for geometric archetypes
|
|
2931
|
+
ctx.rect(bx - bSize / 2, by - bSize / 2, bSize, bSize * (0.5 + rng() * 0.5));
|
|
2932
|
+
else ctx.arc(bx, by, bSize / 2, 0, Math.PI * 2);
|
|
2096
2933
|
ctx.fill();
|
|
2097
2934
|
}
|
|
2098
2935
|
// Subtle concentric rings from center
|
|
2099
2936
|
const ringCount = 2 + Math.floor(rng() * 3);
|
|
2100
2937
|
ctx.globalAlpha = 0.02 + rng() * 0.03;
|
|
2101
|
-
ctx.strokeStyle = (0, $b5a262d09b87e373$export$f2121afcad3d553f)(
|
|
2938
|
+
ctx.strokeStyle = (0, $b5a262d09b87e373$export$f2121afcad3d553f)(colorHierarchy.dominant, 0.1);
|
|
2102
2939
|
ctx.lineWidth = 1 * scaleFactor;
|
|
2103
2940
|
for(let i = 1; i <= ringCount; i++){
|
|
2104
2941
|
const r = Math.min(width, height) * 0.15 * i;
|
|
@@ -2112,7 +2949,6 @@ function $1f63dc64b5593c73$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2112
2949
|
const symRoll = rng();
|
|
2113
2950
|
const symmetryMode = symRoll < 0.10 ? "bilateral-x" : symRoll < 0.20 ? "bilateral-y" : symRoll < 0.25 ? "quad" : "none";
|
|
2114
2951
|
// ── 3. Focal points + void zones ───────────────────────────────
|
|
2115
|
-
// Rule-of-thirds intersection points for intentional composition
|
|
2116
2952
|
const THIRDS_POINTS = [
|
|
2117
2953
|
{
|
|
2118
2954
|
x: 1 / 3,
|
|
@@ -2133,10 +2969,8 @@ function $1f63dc64b5593c73$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2133
2969
|
];
|
|
2134
2970
|
const numFocal = 1 + Math.floor(rng() * 2);
|
|
2135
2971
|
const focalPoints = [];
|
|
2136
|
-
for(let f = 0; f < numFocal; f++)
|
|
2137
|
-
if (rng() < 0.7) {
|
|
2972
|
+
for(let f = 0; f < numFocal; f++)if (rng() < 0.7) {
|
|
2138
2973
|
const tp = THIRDS_POINTS[Math.floor(rng() * THIRDS_POINTS.length)];
|
|
2139
|
-
// Small jitter around the thirds point so it's not robotic
|
|
2140
2974
|
focalPoints.push({
|
|
2141
2975
|
x: width * (tp.x + (rng() - 0.5) * 0.08),
|
|
2142
2976
|
y: height * (tp.y + (rng() - 0.5) * 0.08),
|
|
@@ -2147,7 +2981,6 @@ function $1f63dc64b5593c73$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2147
2981
|
y: height * (0.2 + rng() * 0.6),
|
|
2148
2982
|
strength: 0.3 + rng() * 0.4
|
|
2149
2983
|
});
|
|
2150
|
-
// Feature E: 1-2 void zones where shapes are sparse (negative space)
|
|
2151
2984
|
const numVoids = Math.floor(rng() * 2) + 1;
|
|
2152
2985
|
const voidZones = [];
|
|
2153
2986
|
for(let v = 0; v < numVoids; v++)voidZones.push({
|
|
@@ -2179,20 +3012,24 @@ function $1f63dc64b5593c73$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2179
3012
|
}
|
|
2180
3013
|
// Track all placed shapes for density checks and connecting curves
|
|
2181
3014
|
const shapePositions = [];
|
|
3015
|
+
// Hero avoidance radius — shapes near the hero orient toward it
|
|
3016
|
+
let heroCenter = null;
|
|
2182
3017
|
// ── 4b. Hero shape — a dominant focal element ───────────────────
|
|
2183
3018
|
if (archetype.heroShape && rng() < 0.6) {
|
|
2184
3019
|
const heroFocal = focalPoints[0];
|
|
3020
|
+
// Use shape palette hero candidates
|
|
2185
3021
|
const heroPool = [
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
];
|
|
2191
|
-
const heroShape = heroPool.filter((s)=>shapeNames.includes(s))[Math.floor(rng() * heroPool.filter((s)=>shapeNames.includes(s)).length)] || shapeNames[Math.floor(rng() * shapeNames.length)];
|
|
3022
|
+
...shapePalette.primary,
|
|
3023
|
+
...shapePalette.supporting
|
|
3024
|
+
].filter((s)=>(0, $8286059160ee2e04$export$4343b39fe47bd82c)[s]?.heroCandidate && shapeNames.includes(s));
|
|
3025
|
+
const heroShape = heroPool.length > 0 ? heroPool[Math.floor(rng() * heroPool.length)] : shapeNames[Math.floor(rng() * shapeNames.length)];
|
|
2192
3026
|
const heroSize = adjustedMaxSize * (0.8 + rng() * 0.5);
|
|
2193
3027
|
const heroRotation = rng() * 360;
|
|
2194
|
-
const heroFill = (0, $b5a262d09b87e373$export$f2121afcad3d553f)((0, $b5a262d09b87e373$export$90ad0e6170cf6af5)((0, $b5a262d09b87e373$export$
|
|
2195
|
-
const heroStroke = (0, $b5a262d09b87e373$export$90ad0e6170cf6af5)((0, $b5a262d09b87e373$export$
|
|
3028
|
+
const heroFill = (0, $b5a262d09b87e373$export$f2121afcad3d553f)((0, $b5a262d09b87e373$export$90ad0e6170cf6af5)((0, $b5a262d09b87e373$export$18a34c25ea7e724b)(colorHierarchy.dominant, rng, 6, 0.05), bgLum), 0.15 + rng() * 0.2);
|
|
3029
|
+
const heroStroke = (0, $b5a262d09b87e373$export$90ad0e6170cf6af5)((0, $b5a262d09b87e373$export$18a34c25ea7e724b)(colorHierarchy.accent, rng, 6, 0.05), bgLum);
|
|
3030
|
+
// Get best style for this hero shape
|
|
3031
|
+
const heroProfile = (0, $8286059160ee2e04$export$4343b39fe47bd82c)[heroShape];
|
|
3032
|
+
const heroStyle = heroProfile ? heroProfile.bestStyles[Math.floor(rng() * heroProfile.bestStyles.length)] : rng() < 0.4 ? "watercolor" : "fill-and-stroke";
|
|
2196
3033
|
ctx.globalAlpha = 0.5 + rng() * 0.2;
|
|
2197
3034
|
(0, $e0f99502ff383dd8$export$bb35a6995ddbf32d)(ctx, heroShape, heroFocal.x, heroFocal.y, {
|
|
2198
3035
|
fillColor: heroFill,
|
|
@@ -2203,14 +3040,20 @@ function $1f63dc64b5593c73$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2203
3040
|
proportionType: "GOLDEN_RATIO",
|
|
2204
3041
|
glowRadius: (12 + rng() * 20) * scaleFactor,
|
|
2205
3042
|
glowColor: (0, $b5a262d09b87e373$export$f2121afcad3d553f)(heroStroke, 0.4),
|
|
2206
|
-
gradientFillEnd: (0, $b5a262d09b87e373$export$
|
|
2207
|
-
renderStyle:
|
|
3043
|
+
gradientFillEnd: (0, $b5a262d09b87e373$export$18a34c25ea7e724b)(colorHierarchy.secondary, rng, 10, 0.1),
|
|
3044
|
+
renderStyle: heroStyle,
|
|
2208
3045
|
rng: rng
|
|
2209
3046
|
});
|
|
2210
|
-
|
|
3047
|
+
heroCenter = {
|
|
2211
3048
|
x: heroFocal.x,
|
|
2212
3049
|
y: heroFocal.y,
|
|
2213
3050
|
size: heroSize
|
|
3051
|
+
};
|
|
3052
|
+
shapePositions.push({
|
|
3053
|
+
x: heroFocal.x,
|
|
3054
|
+
y: heroFocal.y,
|
|
3055
|
+
size: heroSize,
|
|
3056
|
+
shape: heroShape
|
|
2214
3057
|
});
|
|
2215
3058
|
}
|
|
2216
3059
|
// ── 5. Shape layers ────────────────────────────────────────────
|
|
@@ -2221,41 +3064,52 @@ function $1f63dc64b5593c73$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2221
3064
|
const numShapes = shapesPerLayer + Math.floor(rng() * shapesPerLayer * 0.3);
|
|
2222
3065
|
const layerOpacity = Math.max(0.15, baseOpacity - layer * opacityReduction);
|
|
2223
3066
|
const layerSizeScale = 1 - layer * 0.15;
|
|
2224
|
-
//
|
|
3067
|
+
// Per-layer blend mode
|
|
2225
3068
|
const layerBlend = (0, $e0f99502ff383dd8$export$7bb7bff4e26fa06b)(rng);
|
|
2226
3069
|
ctx.globalCompositeOperation = layerBlend;
|
|
2227
|
-
//
|
|
3070
|
+
// Per-layer render style bias — prefer archetype styles
|
|
2228
3071
|
const layerRenderStyle = rng() < 0.6 ? archetype.preferredStyles[Math.floor(rng() * archetype.preferredStyles.length)] : (0, $e0f99502ff383dd8$export$9fd4e64b2acd410e)(rng);
|
|
2229
|
-
//
|
|
2230
|
-
const atmosphericDesat = layerRatio * 0.3;
|
|
3072
|
+
// Atmospheric desaturation for later layers
|
|
3073
|
+
const atmosphericDesat = layerRatio * 0.3;
|
|
2231
3074
|
for(let i = 0; i < numShapes; i++){
|
|
2232
3075
|
// Position from composition mode, then focal bias
|
|
2233
3076
|
const rawPos = $1f63dc64b5593c73$var$getCompositionPosition(compositionMode, rng, width, height, i, numShapes, cx, cy);
|
|
2234
3077
|
const [x, y] = applyFocalBias(rawPos.x, rawPos.y);
|
|
2235
|
-
//
|
|
3078
|
+
// Skip shapes in void zones, reduce in dense areas
|
|
2236
3079
|
if ($1f63dc64b5593c73$var$isInVoidZone(x, y, voidZones)) {
|
|
2237
|
-
// 85% chance to skip — allows a few shapes to bleed in
|
|
2238
3080
|
if (rng() < 0.85) continue;
|
|
2239
3081
|
}
|
|
2240
3082
|
if ($1f63dc64b5593c73$var$localDensity(x, y, shapePositions, densityCheckRadius) > maxLocalDensity) {
|
|
2241
|
-
if (rng() < 0.6) continue;
|
|
3083
|
+
if (rng() < 0.6) continue;
|
|
2242
3084
|
}
|
|
2243
|
-
// Weighted shape selection
|
|
2244
|
-
const shape = $1f63dc64b5593c73$var$pickShape(rng, layerRatio, shapeNames);
|
|
2245
3085
|
// Power distribution for size — archetype controls the curve
|
|
2246
3086
|
const sizeT = Math.pow(rng(), archetype.sizePower);
|
|
2247
3087
|
const size = (adjustedMinSize + sizeT * (adjustedMaxSize - adjustedMinSize)) * layerSizeScale;
|
|
3088
|
+
// Size fraction for affinity-aware shape selection
|
|
3089
|
+
const sizeFraction = size / adjustedMaxSize;
|
|
3090
|
+
// Palette-driven shape selection (replaces naive pickShape)
|
|
3091
|
+
const shape = (0, $8286059160ee2e04$export$3c37d9a045754d0e)(shapePalette, rng, sizeFraction);
|
|
2248
3092
|
// Flow-field rotation in flow-field mode, random otherwise
|
|
2249
|
-
|
|
2250
|
-
//
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
3093
|
+
let rotation = compositionMode === "flow-field" ? flowAngle(x, y) * 180 / Math.PI + (rng() - 0.5) * 30 : rng() * 360;
|
|
3094
|
+
// Hero avoidance: shapes near the hero orient toward it
|
|
3095
|
+
if (heroCenter) {
|
|
3096
|
+
const distToHero = Math.hypot(x - heroCenter.x, y - heroCenter.y);
|
|
3097
|
+
const heroInfluence = heroCenter.size * 1.5;
|
|
3098
|
+
if (distToHero < heroInfluence && distToHero > 0) {
|
|
3099
|
+
const angleToHero = Math.atan2(heroCenter.y - y, heroCenter.x - x) * 180 / Math.PI;
|
|
3100
|
+
const blendFactor = 1 - distToHero / heroInfluence;
|
|
3101
|
+
rotation = rotation + (angleToHero - rotation) * blendFactor * 0.4;
|
|
3102
|
+
}
|
|
3103
|
+
}
|
|
3104
|
+
// Positional color from hierarchy + jitter
|
|
3105
|
+
let fillBase = $1f63dc64b5593c73$var$getPositionalColor(x, y, width, height, colorHierarchy, rng);
|
|
3106
|
+
const strokeBase = (0, $b5a262d09b87e373$export$b49f62f0a99da0e8)(colorHierarchy, rng);
|
|
3107
|
+
// Desaturate colors on later layers for depth
|
|
2254
3108
|
if (atmosphericDesat > 0) fillBase = (0, $b5a262d09b87e373$export$fb75607d98509d9)(fillBase, atmosphericDesat);
|
|
2255
3109
|
// Temperature contrast: shift foreground shapes opposite to background
|
|
2256
3110
|
if (fgTempTarget) fillBase = (0, $b5a262d09b87e373$export$51ea55f869b7e0d3)(fillBase, fgTempTarget, 0.15 + layerRatio * 0.1);
|
|
2257
|
-
const fillColor = (0, $b5a262d09b87e373$export$90ad0e6170cf6af5)((0, $b5a262d09b87e373$export$
|
|
2258
|
-
const strokeColor = (0, $b5a262d09b87e373$export$90ad0e6170cf6af5)((0, $b5a262d09b87e373$export$
|
|
3111
|
+
const fillColor = (0, $b5a262d09b87e373$export$90ad0e6170cf6af5)((0, $b5a262d09b87e373$export$18a34c25ea7e724b)(fillBase, rng, 6, 0.05), bgLum);
|
|
3112
|
+
const strokeColor = (0, $b5a262d09b87e373$export$90ad0e6170cf6af5)((0, $b5a262d09b87e373$export$18a34c25ea7e724b)(strokeBase, rng, 5, 0.04), bgLum);
|
|
2259
3113
|
// Semi-transparent fill
|
|
2260
3114
|
const fillAlpha = 0.2 + rng() * 0.5;
|
|
2261
3115
|
const transparentFill = (0, $b5a262d09b87e373$export$f2121afcad3d553f)(fillColor, fillAlpha);
|
|
@@ -2269,12 +3123,16 @@ function $1f63dc64b5593c73$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2269
3123
|
const glowRadius = hasGlow ? (8 + rng() * 20) * scaleFactor : 0;
|
|
2270
3124
|
// Gradient fill on ~30%
|
|
2271
3125
|
const hasGradient = rng() < 0.3;
|
|
2272
|
-
const gradientEnd = hasGradient ? (0, $b5a262d09b87e373$export$
|
|
2273
|
-
//
|
|
2274
|
-
const shapeRenderStyle =
|
|
2275
|
-
//
|
|
3126
|
+
const gradientEnd = hasGradient ? (0, $b5a262d09b87e373$export$18a34c25ea7e724b)((0, $b5a262d09b87e373$export$b49f62f0a99da0e8)(colorHierarchy, rng), rng, 10, 0.1) : undefined;
|
|
3127
|
+
// Affinity-aware render style selection
|
|
3128
|
+
const shapeRenderStyle = (0, $8286059160ee2e04$export$ab873bb6fb56c1a8)(shape, layerRenderStyle, rng);
|
|
3129
|
+
// Organic edge jitter — applied via watercolor style on ~15% of shapes
|
|
2276
3130
|
const useOrganicEdges = rng() < 0.15 && shapeRenderStyle === "fill-and-stroke";
|
|
2277
3131
|
const finalRenderStyle = useOrganicEdges ? "watercolor" : shapeRenderStyle;
|
|
3132
|
+
// Consistent light direction — subtle shadow offset
|
|
3133
|
+
const shadowDist = hasGlow ? 0 : size * 0.02;
|
|
3134
|
+
const shadowOffX = shadowDist * Math.cos(lightAngle);
|
|
3135
|
+
const shadowOffY = shadowDist * Math.sin(lightAngle);
|
|
2278
3136
|
(0, $e0f99502ff383dd8$export$bb35a6995ddbf32d)(ctx, shape, x, y, {
|
|
2279
3137
|
fillColor: transparentFill,
|
|
2280
3138
|
strokeColor: strokeColor,
|
|
@@ -2282,8 +3140,8 @@ function $1f63dc64b5593c73$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2282
3140
|
size: size,
|
|
2283
3141
|
rotation: rotation,
|
|
2284
3142
|
proportionType: "GOLDEN_RATIO",
|
|
2285
|
-
glowRadius: glowRadius,
|
|
2286
|
-
glowColor: hasGlow ? (0, $b5a262d09b87e373$export$f2121afcad3d553f)(fillColor, 0.6) : undefined,
|
|
3143
|
+
glowRadius: glowRadius || (shadowDist > 0 ? shadowDist * 2 : 0),
|
|
3144
|
+
glowColor: hasGlow ? (0, $b5a262d09b87e373$export$f2121afcad3d553f)(fillColor, 0.6) : shadowDist > 0 ? "rgba(0,0,0,0.08)" : undefined,
|
|
2287
3145
|
gradientFillEnd: gradientEnd,
|
|
2288
3146
|
renderStyle: finalRenderStyle,
|
|
2289
3147
|
rng: rng
|
|
@@ -2291,18 +3149,51 @@ function $1f63dc64b5593c73$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2291
3149
|
shapePositions.push({
|
|
2292
3150
|
x: x,
|
|
2293
3151
|
y: y,
|
|
2294
|
-
size: size
|
|
3152
|
+
size: size,
|
|
3153
|
+
shape: shape
|
|
2295
3154
|
});
|
|
2296
|
-
// ── 5b.
|
|
3155
|
+
// ── 5b. Size echo — large shapes spawn trailing smaller copies ──
|
|
3156
|
+
if (size > adjustedMaxSize * 0.5 && rng() < 0.2) {
|
|
3157
|
+
const echoCount = 2 + Math.floor(rng() * 2);
|
|
3158
|
+
const echoAngle = rng() * Math.PI * 2;
|
|
3159
|
+
for(let e = 0; e < echoCount; e++){
|
|
3160
|
+
const echoScale = 0.3 - e * 0.08;
|
|
3161
|
+
const echoDist = size * (0.6 + e * 0.4);
|
|
3162
|
+
const echoX = x + Math.cos(echoAngle) * echoDist;
|
|
3163
|
+
const echoY = y + Math.sin(echoAngle) * echoDist;
|
|
3164
|
+
const echoSize = size * Math.max(0.1, echoScale);
|
|
3165
|
+
if (echoX < 0 || echoX > width || echoY < 0 || echoY > height) continue;
|
|
3166
|
+
ctx.globalAlpha = layerOpacity * (0.4 - e * 0.1);
|
|
3167
|
+
(0, $e0f99502ff383dd8$export$bb35a6995ddbf32d)(ctx, shape, echoX, echoY, {
|
|
3168
|
+
fillColor: (0, $b5a262d09b87e373$export$f2121afcad3d553f)(fillColor, fillAlpha * 0.6),
|
|
3169
|
+
strokeColor: (0, $b5a262d09b87e373$export$f2121afcad3d553f)(strokeColor, 0.4),
|
|
3170
|
+
strokeWidth: strokeWidth * 0.6,
|
|
3171
|
+
size: echoSize,
|
|
3172
|
+
rotation: rotation + (e + 1) * 15,
|
|
3173
|
+
proportionType: "GOLDEN_RATIO",
|
|
3174
|
+
renderStyle: finalRenderStyle,
|
|
3175
|
+
rng: rng
|
|
3176
|
+
});
|
|
3177
|
+
shapePositions.push({
|
|
3178
|
+
x: echoX,
|
|
3179
|
+
y: echoY,
|
|
3180
|
+
size: echoSize,
|
|
3181
|
+
shape: shape
|
|
3182
|
+
});
|
|
3183
|
+
}
|
|
3184
|
+
}
|
|
3185
|
+
// ── 5c. Recursive nesting ──────────────────────────────────
|
|
2297
3186
|
if (size > adjustedMaxSize * 0.4 && rng() < 0.15) {
|
|
2298
3187
|
const innerCount = 1 + Math.floor(rng() * 3);
|
|
2299
3188
|
for(let n = 0; n < innerCount; n++){
|
|
2300
|
-
|
|
3189
|
+
// Pick inner shape from palette affinities
|
|
3190
|
+
const innerSizeFraction = size * 0.25 / adjustedMaxSize;
|
|
3191
|
+
const innerShape = (0, $8286059160ee2e04$export$3c37d9a045754d0e)(shapePalette, rng, innerSizeFraction);
|
|
2301
3192
|
const innerSize = size * (0.15 + rng() * 0.25);
|
|
2302
3193
|
const innerOffX = (rng() - 0.5) * size * 0.4;
|
|
2303
3194
|
const innerOffY = (rng() - 0.5) * size * 0.4;
|
|
2304
3195
|
const innerRot = rng() * 360;
|
|
2305
|
-
const innerFill = (0, $b5a262d09b87e373$export$f2121afcad3d553f)((0, $b5a262d09b87e373$export$
|
|
3196
|
+
const innerFill = (0, $b5a262d09b87e373$export$f2121afcad3d553f)((0, $b5a262d09b87e373$export$18a34c25ea7e724b)((0, $b5a262d09b87e373$export$b49f62f0a99da0e8)(colorHierarchy, rng), rng, 10, 0.1), 0.3 + rng() * 0.4);
|
|
2306
3197
|
ctx.globalAlpha = layerOpacity * 0.7;
|
|
2307
3198
|
(0, $e0f99502ff383dd8$export$bb35a6995ddbf32d)(ctx, innerShape, x + innerOffX, y + innerOffY, {
|
|
2308
3199
|
fillColor: innerFill,
|
|
@@ -2311,7 +3202,7 @@ function $1f63dc64b5593c73$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2311
3202
|
size: innerSize,
|
|
2312
3203
|
rotation: innerRot,
|
|
2313
3204
|
proportionType: "GOLDEN_RATIO",
|
|
2314
|
-
renderStyle:
|
|
3205
|
+
renderStyle: (0, $8286059160ee2e04$export$ab873bb6fb56c1a8)(innerShape, layerRenderStyle, rng),
|
|
2315
3206
|
rng: rng
|
|
2316
3207
|
});
|
|
2317
3208
|
}
|
|
@@ -2320,7 +3211,7 @@ function $1f63dc64b5593c73$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2320
3211
|
}
|
|
2321
3212
|
// Reset blend mode for post-processing passes
|
|
2322
3213
|
ctx.globalCompositeOperation = "source-over";
|
|
2323
|
-
// ── 6. Flow-line pass
|
|
3214
|
+
// ── 6. Flow-line pass — variable color, branching, pressure ────
|
|
2324
3215
|
const baseFlowLines = 6 + Math.floor(rng() * 10);
|
|
2325
3216
|
const numFlowLines = Math.round(baseFlowLines * archetype.flowLineMultiplier);
|
|
2326
3217
|
for(let i = 0; i < numFlowLines; i++){
|
|
@@ -2329,9 +3220,13 @@ function $1f63dc64b5593c73$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2329
3220
|
const steps = 30 + Math.floor(rng() * 40);
|
|
2330
3221
|
const stepLen = (3 + rng() * 5) * scaleFactor;
|
|
2331
3222
|
const startWidth = (1 + rng() * 3) * scaleFactor;
|
|
2332
|
-
|
|
3223
|
+
// Variable color: interpolate between two hierarchy colors along the stroke
|
|
3224
|
+
const lineColorStart = (0, $b5a262d09b87e373$export$90ad0e6170cf6af5)((0, $b5a262d09b87e373$export$b49f62f0a99da0e8)(colorHierarchy, rng), bgLum);
|
|
3225
|
+
const lineColorEnd = (0, $b5a262d09b87e373$export$90ad0e6170cf6af5)((0, $b5a262d09b87e373$export$b49f62f0a99da0e8)(colorHierarchy, rng), bgLum);
|
|
2333
3226
|
const lineAlpha = 0.06 + rng() * 0.1;
|
|
2334
|
-
//
|
|
3227
|
+
// Pressure simulation: sinusoidal width variation
|
|
3228
|
+
const pressureFreq = 2 + rng() * 4;
|
|
3229
|
+
const pressurePhase = rng() * Math.PI * 2;
|
|
2335
3230
|
let prevX = fx;
|
|
2336
3231
|
let prevY = fy;
|
|
2337
3232
|
for(let s = 0; s < steps; s++){
|
|
@@ -2339,37 +3234,61 @@ function $1f63dc64b5593c73$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2339
3234
|
fx += Math.cos(angle) * stepLen;
|
|
2340
3235
|
fy += Math.sin(angle) * stepLen;
|
|
2341
3236
|
if (fx < 0 || fx > width || fy < 0 || fy > height) break;
|
|
2342
|
-
|
|
2343
|
-
|
|
3237
|
+
const t = s / steps;
|
|
3238
|
+
// Taper + pressure
|
|
3239
|
+
const taper = 1 - t * 0.8;
|
|
3240
|
+
const pressure = 0.6 + 0.4 * Math.sin(t * pressureFreq * Math.PI + pressurePhase);
|
|
2344
3241
|
ctx.globalAlpha = lineAlpha * taper;
|
|
3242
|
+
// Interpolate color along stroke
|
|
3243
|
+
const lineColor = t < 0.5 ? (0, $b5a262d09b87e373$export$f2121afcad3d553f)(lineColorStart, 0.4 + t * 0.2) : (0, $b5a262d09b87e373$export$f2121afcad3d553f)(lineColorEnd, 0.4 + (1 - t) * 0.2);
|
|
2345
3244
|
ctx.strokeStyle = lineColor;
|
|
2346
|
-
ctx.lineWidth = startWidth * taper;
|
|
3245
|
+
ctx.lineWidth = startWidth * taper * pressure;
|
|
2347
3246
|
ctx.lineCap = "round";
|
|
2348
3247
|
ctx.beginPath();
|
|
2349
3248
|
ctx.moveTo(prevX, prevY);
|
|
2350
3249
|
ctx.lineTo(fx, fy);
|
|
2351
3250
|
ctx.stroke();
|
|
3251
|
+
// Branching: ~12% chance per step to spawn a thinner child stroke
|
|
3252
|
+
if (rng() < 0.12 && s > 5 && s < steps - 10) {
|
|
3253
|
+
const branchAngle = angle + (rng() < 0.5 ? 1 : -1) * (0.3 + rng() * 0.5);
|
|
3254
|
+
let bx = fx;
|
|
3255
|
+
let by = fy;
|
|
3256
|
+
let bPrevX = fx;
|
|
3257
|
+
let bPrevY = fy;
|
|
3258
|
+
const branchSteps = 5 + Math.floor(rng() * 10);
|
|
3259
|
+
const branchWidth = startWidth * taper * 0.4;
|
|
3260
|
+
for(let bs = 0; bs < branchSteps; bs++){
|
|
3261
|
+
const bAngle = branchAngle + (rng() - 0.5) * 0.2;
|
|
3262
|
+
bx += Math.cos(bAngle) * stepLen * 0.8;
|
|
3263
|
+
by += Math.sin(bAngle) * stepLen * 0.8;
|
|
3264
|
+
if (bx < 0 || bx > width || by < 0 || by > height) break;
|
|
3265
|
+
const bTaper = 1 - bs / branchSteps * 0.9;
|
|
3266
|
+
ctx.globalAlpha = lineAlpha * taper * bTaper * 0.6;
|
|
3267
|
+
ctx.lineWidth = branchWidth * bTaper;
|
|
3268
|
+
ctx.beginPath();
|
|
3269
|
+
ctx.moveTo(bPrevX, bPrevY);
|
|
3270
|
+
ctx.lineTo(bx, by);
|
|
3271
|
+
ctx.stroke();
|
|
3272
|
+
bPrevX = bx;
|
|
3273
|
+
bPrevY = by;
|
|
3274
|
+
}
|
|
3275
|
+
}
|
|
2352
3276
|
prevX = fx;
|
|
2353
3277
|
prevY = fy;
|
|
2354
3278
|
}
|
|
2355
3279
|
}
|
|
2356
3280
|
// ── 6b. Apply symmetry mirroring ─────────────────────────────────
|
|
2357
|
-
// Mirror the rendered content (shapes + flow lines) before post-processing.
|
|
2358
|
-
// Uses ctx.canvas which is available in both Node (@napi-rs/canvas) and browsers.
|
|
2359
3281
|
if (symmetryMode !== "none") {
|
|
2360
3282
|
const canvas = ctx.canvas;
|
|
2361
3283
|
ctx.save();
|
|
2362
3284
|
if (symmetryMode === "bilateral-x" || symmetryMode === "quad") {
|
|
2363
|
-
// Mirror left half onto right half
|
|
2364
3285
|
ctx.save();
|
|
2365
3286
|
ctx.translate(width, 0);
|
|
2366
3287
|
ctx.scale(-1, 1);
|
|
2367
|
-
// Draw the left half (0 to cx) onto the mirrored right side
|
|
2368
3288
|
ctx.drawImage(canvas, 0, 0, Math.ceil(cx), height, 0, 0, Math.ceil(cx), height);
|
|
2369
3289
|
ctx.restore();
|
|
2370
3290
|
}
|
|
2371
3291
|
if (symmetryMode === "bilateral-y" || symmetryMode === "quad") {
|
|
2372
|
-
// Mirror top half onto bottom half
|
|
2373
3292
|
ctx.save();
|
|
2374
3293
|
ctx.translate(0, height);
|
|
2375
3294
|
ctx.scale(1, -1);
|
|
@@ -2392,7 +3311,7 @@ function $1f63dc64b5593c73$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2392
3311
|
}
|
|
2393
3312
|
// ── 8. Vignette — darken edges to draw the eye inward ───────────
|
|
2394
3313
|
ctx.globalAlpha = 1;
|
|
2395
|
-
const vignetteStrength = 0.25 + rng() * 0.2;
|
|
3314
|
+
const vignetteStrength = 0.25 + rng() * 0.2;
|
|
2396
3315
|
const vigGrad = ctx.createRadialGradient(cx, cy, Math.min(width, height) * 0.3, cx, cy, bgRadius);
|
|
2397
3316
|
vigGrad.addColorStop(0, "rgba(0,0,0,0)");
|
|
2398
3317
|
vigGrad.addColorStop(0.6, "rgba(0,0,0,0)");
|
|
@@ -2418,13 +3337,57 @@ function $1f63dc64b5593c73$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
2418
3337
|
const cpx = mx + -dy / (dist || 1) * bulge;
|
|
2419
3338
|
const cpy = my + dx / (dist || 1) * bulge;
|
|
2420
3339
|
ctx.globalAlpha = 0.06 + rng() * 0.1;
|
|
2421
|
-
ctx.strokeStyle = (0, $b5a262d09b87e373$export$f2121afcad3d553f)((0, $b5a262d09b87e373$export$90ad0e6170cf6af5)(
|
|
3340
|
+
ctx.strokeStyle = (0, $b5a262d09b87e373$export$f2121afcad3d553f)((0, $b5a262d09b87e373$export$90ad0e6170cf6af5)((0, $b5a262d09b87e373$export$b49f62f0a99da0e8)(colorHierarchy, rng), bgLum), 0.3);
|
|
2422
3341
|
ctx.beginPath();
|
|
2423
3342
|
ctx.moveTo(a.x, a.y);
|
|
2424
3343
|
ctx.quadraticCurveTo(cpx, cpy, b.x, b.y);
|
|
2425
3344
|
ctx.stroke();
|
|
2426
3345
|
}
|
|
2427
3346
|
}
|
|
3347
|
+
// ── 10. Post-processing ────────────────────────────────────────
|
|
3348
|
+
// 10a. Color grading — unified tone across the whole image
|
|
3349
|
+
// Apply as a semi-transparent overlay in the grade hue
|
|
3350
|
+
ctx.globalAlpha = colorGrade.intensity * 0.25;
|
|
3351
|
+
ctx.globalCompositeOperation = "soft-light";
|
|
3352
|
+
const gradeHsl = `hsl(${Math.round(colorGrade.hue)}, 40%, 50%)`;
|
|
3353
|
+
ctx.fillStyle = gradeHsl;
|
|
3354
|
+
ctx.fillRect(0, 0, width, height);
|
|
3355
|
+
ctx.globalCompositeOperation = "source-over";
|
|
3356
|
+
// 10b. Chromatic aberration — subtle RGB channel offset at edges
|
|
3357
|
+
// Only apply for neon/cosmic/ethereal archetypes where it fits
|
|
3358
|
+
const chromaArchetypes = [
|
|
3359
|
+
"neon-glow",
|
|
3360
|
+
"cosmic",
|
|
3361
|
+
"ethereal"
|
|
3362
|
+
];
|
|
3363
|
+
if (chromaArchetypes.includes(archetype.name)) {
|
|
3364
|
+
const chromaOffset = Math.ceil(2 * scaleFactor);
|
|
3365
|
+
const canvas = ctx.canvas;
|
|
3366
|
+
// Shift red channel slightly
|
|
3367
|
+
ctx.globalAlpha = 0.03;
|
|
3368
|
+
ctx.globalCompositeOperation = "screen";
|
|
3369
|
+
ctx.drawImage(canvas, chromaOffset, 0, width, height, 0, 0, width, height);
|
|
3370
|
+
// Shift blue channel opposite
|
|
3371
|
+
ctx.drawImage(canvas, -chromaOffset, 0, width, height, 0, 0, width, height);
|
|
3372
|
+
ctx.globalCompositeOperation = "source-over";
|
|
3373
|
+
}
|
|
3374
|
+
// 10c. Bloom — soft glow on bright areas for neon/cosmic archetypes
|
|
3375
|
+
const bloomArchetypes = [
|
|
3376
|
+
"neon-glow",
|
|
3377
|
+
"cosmic"
|
|
3378
|
+
];
|
|
3379
|
+
if (bloomArchetypes.includes(archetype.name)) {
|
|
3380
|
+
const canvas = ctx.canvas;
|
|
3381
|
+
ctx.globalAlpha = 0.08;
|
|
3382
|
+
ctx.globalCompositeOperation = "screen";
|
|
3383
|
+
// Draw the image slightly scaled up and blurred via shadow
|
|
3384
|
+
ctx.save();
|
|
3385
|
+
ctx.shadowBlur = 30 * scaleFactor;
|
|
3386
|
+
ctx.shadowColor = "rgba(255,255,255,0.3)";
|
|
3387
|
+
ctx.drawImage(canvas, 0, 0, width, height);
|
|
3388
|
+
ctx.restore();
|
|
3389
|
+
ctx.globalCompositeOperation = "source-over";
|
|
3390
|
+
}
|
|
2428
3391
|
ctx.globalAlpha = 1;
|
|
2429
3392
|
}
|
|
2430
3393
|
|