git-hash-art 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ALGORITHM.md +105 -7
- package/CHANGELOG.md +8 -0
- package/dist/browser.js +241 -11
- package/dist/browser.js.map +1 -1
- package/dist/main.js +241 -11
- package/dist/main.js.map +1 -1
- package/dist/module.js +241 -11
- package/dist/module.js.map +1 -1
- package/package.json +1 -1
- package/src/lib/archetypes.ts +47 -3
- package/src/lib/canvas/colors.ts +25 -0
- package/src/lib/canvas/draw.ts +31 -1
- package/src/lib/canvas/shapes/affinity.ts +3 -3
- package/src/lib/render.ts +168 -7
package/dist/main.js
CHANGED
|
@@ -541,6 +541,18 @@ function $d016ad53434219a1$export$6d1620b367f86f7a(rng) {
|
|
|
541
541
|
intensity: intensity
|
|
542
542
|
};
|
|
543
543
|
}
|
|
544
|
+
function $d016ad53434219a1$export$1793a1bfbe4f6ff5(hex, degrees) {
|
|
545
|
+
const [h, s, l] = $d016ad53434219a1$var$hexToHsl(hex);
|
|
546
|
+
return $d016ad53434219a1$var$hslToHex((h + degrees + 360) % 360, s, l);
|
|
547
|
+
}
|
|
548
|
+
function $d016ad53434219a1$export$703ba40a4347f77a(base, layerRatio, hueShiftPerLayer) {
|
|
549
|
+
const shift = layerRatio * hueShiftPerLayer;
|
|
550
|
+
return {
|
|
551
|
+
dominant: $d016ad53434219a1$export$1793a1bfbe4f6ff5(base.dominant, shift),
|
|
552
|
+
secondary: $d016ad53434219a1$export$1793a1bfbe4f6ff5(base.secondary, shift * 0.7),
|
|
553
|
+
accent: $d016ad53434219a1$export$1793a1bfbe4f6ff5(base.accent, shift * 0.5)
|
|
554
|
+
};
|
|
555
|
+
}
|
|
544
556
|
|
|
545
557
|
|
|
546
558
|
|
|
@@ -1824,7 +1836,8 @@ const $c3de8257a8baa3b0$var$RENDER_STYLES = [
|
|
|
1824
1836
|
"noise-grain",
|
|
1825
1837
|
"wood-grain",
|
|
1826
1838
|
"marble-vein",
|
|
1827
|
-
"fabric-weave"
|
|
1839
|
+
"fabric-weave",
|
|
1840
|
+
"hand-drawn"
|
|
1828
1841
|
];
|
|
1829
1842
|
function $c3de8257a8baa3b0$export$9fd4e64b2acd410e(rng) {
|
|
1830
1843
|
return $c3de8257a8baa3b0$var$RENDER_STYLES[Math.floor(rng() * $c3de8257a8baa3b0$var$RENDER_STYLES.length)];
|
|
@@ -2205,6 +2218,33 @@ function $c3de8257a8baa3b0$export$71b514a25c47df50(ctx, shape, x, y, config) {
|
|
|
2205
2218
|
ctx.globalAlpha /= 0.3;
|
|
2206
2219
|
break;
|
|
2207
2220
|
}
|
|
2221
|
+
case "hand-drawn":
|
|
2222
|
+
{
|
|
2223
|
+
// Wobbly hand-drawn edge treatment — fill normally, then redraw
|
|
2224
|
+
// the outline with perturbed control points for a sketchy feel
|
|
2225
|
+
const savedAlphaHD = ctx.globalAlpha;
|
|
2226
|
+
ctx.globalAlpha = savedAlphaHD * 0.85;
|
|
2227
|
+
ctx.fill();
|
|
2228
|
+
ctx.globalAlpha = savedAlphaHD;
|
|
2229
|
+
// Draw 2-3 slightly offset wobbly strokes for a sketchy look
|
|
2230
|
+
const wobblePasses = 2 + (rng ? Math.floor(rng() * 2) : 0);
|
|
2231
|
+
ctx.lineWidth = strokeWidth * 0.8;
|
|
2232
|
+
for(let wp = 0; wp < wobblePasses; wp++){
|
|
2233
|
+
ctx.globalAlpha = savedAlphaHD * (0.4 - wp * 0.1);
|
|
2234
|
+
ctx.save();
|
|
2235
|
+
// Slight random offset per pass
|
|
2236
|
+
const wobbleX = rng ? (rng() - 0.5) * size * 0.02 : 0;
|
|
2237
|
+
const wobbleY = rng ? (rng() - 0.5) * size * 0.02 : 0;
|
|
2238
|
+
ctx.translate(wobbleX, wobbleY);
|
|
2239
|
+
// Slightly different scale per pass for edge variation
|
|
2240
|
+
const wobbleScale = 1 + (rng ? (rng() - 0.5) * 0.03 : 0);
|
|
2241
|
+
ctx.scale(wobbleScale, wobbleScale);
|
|
2242
|
+
ctx.stroke();
|
|
2243
|
+
ctx.restore();
|
|
2244
|
+
}
|
|
2245
|
+
ctx.globalAlpha = savedAlphaHD;
|
|
2246
|
+
break;
|
|
2247
|
+
}
|
|
2208
2248
|
case "fill-and-stroke":
|
|
2209
2249
|
default:
|
|
2210
2250
|
ctx.fill();
|
|
@@ -2340,7 +2380,8 @@ const $e73976f898150d4d$export$4343b39fe47bd82c = {
|
|
|
2340
2380
|
bestStyles: [
|
|
2341
2381
|
"fill-only",
|
|
2342
2382
|
"watercolor",
|
|
2343
|
-
"fill-and-stroke"
|
|
2383
|
+
"fill-and-stroke",
|
|
2384
|
+
"hand-drawn"
|
|
2344
2385
|
]
|
|
2345
2386
|
},
|
|
2346
2387
|
square: {
|
|
@@ -2377,7 +2418,8 @@ const $e73976f898150d4d$export$4343b39fe47bd82c = {
|
|
|
2377
2418
|
bestStyles: [
|
|
2378
2419
|
"fill-and-stroke",
|
|
2379
2420
|
"fill-only",
|
|
2380
|
-
"watercolor"
|
|
2421
|
+
"watercolor",
|
|
2422
|
+
"hand-drawn"
|
|
2381
2423
|
]
|
|
2382
2424
|
},
|
|
2383
2425
|
hexagon: {
|
|
@@ -2757,7 +2799,8 @@ const $e73976f898150d4d$export$4343b39fe47bd82c = {
|
|
|
2757
2799
|
bestStyles: [
|
|
2758
2800
|
"fill-only",
|
|
2759
2801
|
"watercolor",
|
|
2760
|
-
"fill-and-stroke"
|
|
2802
|
+
"fill-and-stroke",
|
|
2803
|
+
"hand-drawn"
|
|
2761
2804
|
]
|
|
2762
2805
|
},
|
|
2763
2806
|
ngon: {
|
|
@@ -3637,8 +3680,51 @@ const $f89bc858f7202849$var$ARCHETYPES = [
|
|
|
3637
3680
|
invertForeground: false
|
|
3638
3681
|
}
|
|
3639
3682
|
];
|
|
3683
|
+
/**
|
|
3684
|
+
* Linearly interpolate between two archetype numeric parameters.
|
|
3685
|
+
*/ function $f89bc858f7202849$var$lerpNum(a, b, t) {
|
|
3686
|
+
return a + (b - a) * t;
|
|
3687
|
+
}
|
|
3688
|
+
/**
|
|
3689
|
+
* Blend two archetypes by interpolating their numeric parameters
|
|
3690
|
+
* and merging their style arrays.
|
|
3691
|
+
*/ function $f89bc858f7202849$var$blendArchetypes(a, b, t) {
|
|
3692
|
+
// Merge preferred styles — unique union, primary archetype first
|
|
3693
|
+
const mergedStyles = [
|
|
3694
|
+
...new Set([
|
|
3695
|
+
...a.preferredStyles,
|
|
3696
|
+
...b.preferredStyles
|
|
3697
|
+
])
|
|
3698
|
+
];
|
|
3699
|
+
return {
|
|
3700
|
+
name: `${a.name}+${b.name}`,
|
|
3701
|
+
gridSize: Math.round($f89bc858f7202849$var$lerpNum(a.gridSize, b.gridSize, t)),
|
|
3702
|
+
layers: Math.round($f89bc858f7202849$var$lerpNum(a.layers, b.layers, t)),
|
|
3703
|
+
baseOpacity: $f89bc858f7202849$var$lerpNum(a.baseOpacity, b.baseOpacity, t),
|
|
3704
|
+
opacityReduction: $f89bc858f7202849$var$lerpNum(a.opacityReduction, b.opacityReduction, t),
|
|
3705
|
+
minShapeSize: Math.round($f89bc858f7202849$var$lerpNum(a.minShapeSize, b.minShapeSize, t)),
|
|
3706
|
+
maxShapeSize: Math.round($f89bc858f7202849$var$lerpNum(a.maxShapeSize, b.maxShapeSize, t)),
|
|
3707
|
+
backgroundStyle: t < 0.5 ? a.backgroundStyle : b.backgroundStyle,
|
|
3708
|
+
paletteMode: t < 0.5 ? a.paletteMode : b.paletteMode,
|
|
3709
|
+
preferredStyles: mergedStyles,
|
|
3710
|
+
flowLineMultiplier: $f89bc858f7202849$var$lerpNum(a.flowLineMultiplier, b.flowLineMultiplier, t),
|
|
3711
|
+
heroShape: t < 0.5 ? a.heroShape : b.heroShape,
|
|
3712
|
+
glowMultiplier: $f89bc858f7202849$var$lerpNum(a.glowMultiplier, b.glowMultiplier, t),
|
|
3713
|
+
sizePower: $f89bc858f7202849$var$lerpNum(a.sizePower, b.sizePower, t),
|
|
3714
|
+
invertForeground: t < 0.5 ? a.invertForeground : b.invertForeground
|
|
3715
|
+
};
|
|
3716
|
+
}
|
|
3640
3717
|
function $f89bc858f7202849$export$f1142fd7da4d6590(rng) {
|
|
3641
|
-
|
|
3718
|
+
const primary = $f89bc858f7202849$var$ARCHETYPES[Math.floor(rng() * $f89bc858f7202849$var$ARCHETYPES.length)];
|
|
3719
|
+
// ~15% chance of blending with a second archetype
|
|
3720
|
+
if (rng() < 0.15) {
|
|
3721
|
+
const secondary = $f89bc858f7202849$var$ARCHETYPES[Math.floor(rng() * $f89bc858f7202849$var$ARCHETYPES.length)];
|
|
3722
|
+
if (secondary.name !== primary.name) {
|
|
3723
|
+
const blendT = 0.25 + rng() * 0.25; // 25-50% blend toward secondary
|
|
3724
|
+
return $f89bc858f7202849$var$blendArchetypes(primary, secondary, blendT);
|
|
3725
|
+
}
|
|
3726
|
+
}
|
|
3727
|
+
return primary;
|
|
3642
3728
|
}
|
|
3643
3729
|
|
|
3644
3730
|
|
|
@@ -3974,6 +4060,8 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
3974
4060
|
const colorGrade = (0, $d016ad53434219a1$export$6d1620b367f86f7a)(rng);
|
|
3975
4061
|
// ── 0e. Light direction — consistent shadow angle ──────────────
|
|
3976
4062
|
const lightAngle = rng() * Math.PI * 2;
|
|
4063
|
+
// ── 0f. Palette evolution — hue drift direction across layers ──
|
|
4064
|
+
const paletteHueShift = (rng() - 0.5) * 40; // -20° to +20° total drift
|
|
3977
4065
|
const scaleFactor = Math.min(width, height) / 1024;
|
|
3978
4066
|
const adjustedMinSize = minShapeSize * scaleFactor;
|
|
3979
4067
|
const adjustedMaxSize = maxShapeSize * scaleFactor;
|
|
@@ -4148,6 +4236,41 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
4148
4236
|
ry + (nearest.y - ry) * pull
|
|
4149
4237
|
];
|
|
4150
4238
|
}
|
|
4239
|
+
// ── 3b. Void zone decoration — intentional negative space ────
|
|
4240
|
+
for (const zone of voidZones){
|
|
4241
|
+
// Subtle halo ring around void zones
|
|
4242
|
+
ctx.globalAlpha = 0.04 + rng() * 0.04;
|
|
4243
|
+
ctx.strokeStyle = (0, $d016ad53434219a1$export$f2121afcad3d553f)(colorHierarchy.accent, 0.2);
|
|
4244
|
+
ctx.lineWidth = 1.5 * scaleFactor;
|
|
4245
|
+
ctx.beginPath();
|
|
4246
|
+
ctx.arc(zone.x, zone.y, zone.radius, 0, Math.PI * 2);
|
|
4247
|
+
ctx.stroke();
|
|
4248
|
+
// ~50% chance: scatter tiny dots inside the void
|
|
4249
|
+
if (rng() < 0.5) {
|
|
4250
|
+
const dotCount = 3 + Math.floor(rng() * 6);
|
|
4251
|
+
ctx.globalAlpha = 0.06 + rng() * 0.04;
|
|
4252
|
+
ctx.fillStyle = (0, $d016ad53434219a1$export$f2121afcad3d553f)(colorHierarchy.secondary, 0.15);
|
|
4253
|
+
for(let d = 0; d < dotCount; d++){
|
|
4254
|
+
const angle = rng() * Math.PI * 2;
|
|
4255
|
+
const dist = rng() * zone.radius * 0.7;
|
|
4256
|
+
const dotR = (1 + rng() * 3) * scaleFactor;
|
|
4257
|
+
ctx.beginPath();
|
|
4258
|
+
ctx.arc(zone.x + Math.cos(angle) * dist, zone.y + Math.sin(angle) * dist, dotR, 0, Math.PI * 2);
|
|
4259
|
+
ctx.fill();
|
|
4260
|
+
}
|
|
4261
|
+
}
|
|
4262
|
+
// ~30% chance: thin concentric ring inside
|
|
4263
|
+
if (rng() < 0.3) {
|
|
4264
|
+
ctx.globalAlpha = 0.03 + rng() * 0.03;
|
|
4265
|
+
ctx.strokeStyle = (0, $d016ad53434219a1$export$f2121afcad3d553f)(colorHierarchy.dominant, 0.1);
|
|
4266
|
+
ctx.lineWidth = 0.5 * scaleFactor;
|
|
4267
|
+
const innerR = zone.radius * (0.4 + rng() * 0.3);
|
|
4268
|
+
ctx.beginPath();
|
|
4269
|
+
ctx.arc(zone.x, zone.y, innerR, 0, Math.PI * 2);
|
|
4270
|
+
ctx.stroke();
|
|
4271
|
+
}
|
|
4272
|
+
}
|
|
4273
|
+
ctx.globalAlpha = 1;
|
|
4151
4274
|
// ── 4. Flow field seed values ──────────────────────────────────
|
|
4152
4275
|
const fieldAngleBase = rng() * Math.PI * 2;
|
|
4153
4276
|
const fieldFreq = 0.5 + rng() * 2;
|
|
@@ -4220,6 +4343,18 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
4220
4343
|
const dofFactor = 1 - layerRatio * 0.5; // 1.0 for front layer, 0.5 for back
|
|
4221
4344
|
const dofStrokeScale = 0.4 + dofFactor * 0.6; // strokes thin out with depth
|
|
4222
4345
|
const dofContrastReduction = layerRatio * 0.2; // colors fade toward bg
|
|
4346
|
+
// Color palette evolution — hue-rotate the hierarchy per layer
|
|
4347
|
+
const layerHierarchy = (0, $d016ad53434219a1$export$703ba40a4347f77a)(colorHierarchy, layerRatio, paletteHueShift);
|
|
4348
|
+
// Focal depth: shapes near focal points get more detail
|
|
4349
|
+
const focalDetailBoost = (px, py)=>{
|
|
4350
|
+
let minFocalDist = Infinity;
|
|
4351
|
+
for (const fp of focalPoints){
|
|
4352
|
+
const d = Math.hypot(px - fp.x, py - fp.y);
|
|
4353
|
+
if (d < minFocalDist) minFocalDist = d;
|
|
4354
|
+
}
|
|
4355
|
+
const maxDist = Math.hypot(width, height) * 0.5;
|
|
4356
|
+
return Math.max(0, 1 - minFocalDist / maxDist); // 1.0 at focal, 0.0 at edges
|
|
4357
|
+
};
|
|
4223
4358
|
for(let i = 0; i < numShapes; i++){
|
|
4224
4359
|
// Position from composition mode, then focal bias
|
|
4225
4360
|
const rawPos = $4f72c5a314eddf25$var$getCompositionPosition(compositionMode, rng, width, height, i, numShapes, cx, cy);
|
|
@@ -4250,9 +4385,9 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
4250
4385
|
rotation = rotation + (angleToHero - rotation) * blendFactor * 0.4;
|
|
4251
4386
|
}
|
|
4252
4387
|
}
|
|
4253
|
-
// Positional color from hierarchy + jitter
|
|
4254
|
-
let fillBase = $4f72c5a314eddf25$var$getPositionalColor(x, y, width, height,
|
|
4255
|
-
const strokeBase = (0, $d016ad53434219a1$export$b49f62f0a99da0e8)(
|
|
4388
|
+
// Positional color from hierarchy + jitter (using evolved layer palette)
|
|
4389
|
+
let fillBase = $4f72c5a314eddf25$var$getPositionalColor(x, y, width, height, layerHierarchy, rng);
|
|
4390
|
+
const strokeBase = (0, $d016ad53434219a1$export$b49f62f0a99da0e8)(layerHierarchy, rng);
|
|
4256
4391
|
// Desaturate colors on later layers for depth
|
|
4257
4392
|
if (atmosphericDesat > 0) fillBase = (0, $d016ad53434219a1$export$fb75607d98509d9)(fillBase, atmosphericDesat);
|
|
4258
4393
|
// Temperature contrast: shift foreground shapes opposite to background
|
|
@@ -4344,6 +4479,25 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
4344
4479
|
mirrorGap: size * (0.1 + rng() * 0.3)
|
|
4345
4480
|
});
|
|
4346
4481
|
else (0, $c3de8257a8baa3b0$export$bb35a6995ddbf32d)(ctx, shape, finalX, finalY, shapeConfig);
|
|
4482
|
+
// ── Glazing — luminous multi-pass transparency on ~20% of shapes ──
|
|
4483
|
+
if (rng() < 0.2 && size > adjustedMinSize * 2) {
|
|
4484
|
+
const glazePasses = 2 + Math.floor(rng() * 2);
|
|
4485
|
+
for(let g = 0; g < glazePasses; g++){
|
|
4486
|
+
const glazeScale = 1 - (g + 1) * 0.12; // progressively smaller
|
|
4487
|
+
const glazeAlpha = 0.08 + g * 0.04; // progressively more opaque toward center
|
|
4488
|
+
ctx.globalAlpha = glazeAlpha;
|
|
4489
|
+
(0, $c3de8257a8baa3b0$export$bb35a6995ddbf32d)(ctx, shape, finalX, finalY, {
|
|
4490
|
+
fillColor: (0, $d016ad53434219a1$export$f2121afcad3d553f)(fillColor, 0.15 + g * 0.1),
|
|
4491
|
+
strokeColor: "rgba(0,0,0,0)",
|
|
4492
|
+
strokeWidth: 0,
|
|
4493
|
+
size: size * glazeScale,
|
|
4494
|
+
rotation: rotation,
|
|
4495
|
+
proportionType: "GOLDEN_RATIO",
|
|
4496
|
+
renderStyle: "fill-only",
|
|
4497
|
+
rng: rng
|
|
4498
|
+
});
|
|
4499
|
+
}
|
|
4500
|
+
}
|
|
4347
4501
|
shapePositions.push({
|
|
4348
4502
|
x: finalX,
|
|
4349
4503
|
y: finalY,
|
|
@@ -4381,7 +4535,10 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
4381
4535
|
}
|
|
4382
4536
|
}
|
|
4383
4537
|
// ── 5d. Recursive nesting ──────────────────────────────────
|
|
4384
|
-
|
|
4538
|
+
// Focal depth: shapes near focal points get more detail
|
|
4539
|
+
const focalProximity = focalDetailBoost(finalX, finalY);
|
|
4540
|
+
const nestingChance = 0.15 + focalProximity * 0.15; // 15-30% near focal
|
|
4541
|
+
if (size > adjustedMaxSize * 0.4 && rng() < nestingChance) {
|
|
4385
4542
|
const innerCount = 1 + Math.floor(rng() * 3);
|
|
4386
4543
|
for(let n = 0; n < innerCount; n++){
|
|
4387
4544
|
// Pick inner shape from palette affinities
|
|
@@ -4406,7 +4563,8 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
4406
4563
|
}
|
|
4407
4564
|
}
|
|
4408
4565
|
// ── 5e. Shape constellations — pre-composed groups ─────────
|
|
4409
|
-
|
|
4566
|
+
const constellationChance = 0.12 + focalProximity * 0.1; // 12-22% near focal
|
|
4567
|
+
if (size > adjustedMaxSize * 0.35 && rng() < constellationChance) {
|
|
4410
4568
|
const constellation = $4f72c5a314eddf25$var$CONSTELLATIONS[Math.floor(rng() * $4f72c5a314eddf25$var$CONSTELLATIONS.length)];
|
|
4411
4569
|
const members = constellation.build(rng, size);
|
|
4412
4570
|
const groupRotation = rng() * Math.PI * 2;
|
|
@@ -4510,7 +4668,41 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
4510
4668
|
prevY = fy;
|
|
4511
4669
|
}
|
|
4512
4670
|
}
|
|
4513
|
-
// ── 6b.
|
|
4671
|
+
// ── 6b. Motion/energy lines — short directional bursts ─────────
|
|
4672
|
+
const energyArchetypes = [
|
|
4673
|
+
"dense-chaotic",
|
|
4674
|
+
"cosmic",
|
|
4675
|
+
"neon-glow",
|
|
4676
|
+
"bold-graphic"
|
|
4677
|
+
];
|
|
4678
|
+
const hasEnergyLines = energyArchetypes.some((a)=>archetype.name.includes(a)) || rng() < 0.25;
|
|
4679
|
+
if (hasEnergyLines && shapePositions.length > 0) {
|
|
4680
|
+
const energyCount = 5 + Math.floor(rng() * 10);
|
|
4681
|
+
ctx.lineCap = "round";
|
|
4682
|
+
for(let e = 0; e < energyCount; e++){
|
|
4683
|
+
// Pick a random shape to radiate from
|
|
4684
|
+
const source = shapePositions[Math.floor(rng() * shapePositions.length)];
|
|
4685
|
+
const burstCount = 2 + Math.floor(rng() * 4);
|
|
4686
|
+
const baseAngle = flowAngle(source.x, source.y);
|
|
4687
|
+
for(let b = 0; b < burstCount; b++){
|
|
4688
|
+
const angle = baseAngle + (rng() - 0.5) * 1.2;
|
|
4689
|
+
const lineLen = (source.size * 0.3 + rng() * source.size * 0.5) * scaleFactor * 0.3;
|
|
4690
|
+
const startDist = source.size * 0.5;
|
|
4691
|
+
const sx = source.x + Math.cos(angle) * startDist;
|
|
4692
|
+
const sy = source.y + Math.sin(angle) * startDist;
|
|
4693
|
+
const ex = sx + Math.cos(angle) * lineLen;
|
|
4694
|
+
const ey = sy + Math.sin(angle) * lineLen;
|
|
4695
|
+
ctx.globalAlpha = 0.04 + rng() * 0.06;
|
|
4696
|
+
ctx.strokeStyle = (0, $d016ad53434219a1$export$f2121afcad3d553f)((0, $d016ad53434219a1$export$90ad0e6170cf6af5)((0, $d016ad53434219a1$export$b49f62f0a99da0e8)(colorHierarchy, rng), bgLum), 0.3);
|
|
4697
|
+
ctx.lineWidth = (0.5 + rng() * 1.5) * scaleFactor;
|
|
4698
|
+
ctx.beginPath();
|
|
4699
|
+
ctx.moveTo(sx, sy);
|
|
4700
|
+
ctx.lineTo(ex, ey);
|
|
4701
|
+
ctx.stroke();
|
|
4702
|
+
}
|
|
4703
|
+
}
|
|
4704
|
+
}
|
|
4705
|
+
// ── 6c. Apply symmetry mirroring ─────────────────────────────────
|
|
4514
4706
|
if (symmetryMode !== "none") {
|
|
4515
4707
|
const canvas = ctx.canvas;
|
|
4516
4708
|
ctx.save();
|
|
@@ -4621,6 +4813,44 @@ function $4f72c5a314eddf25$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
4621
4813
|
ctx.restore();
|
|
4622
4814
|
ctx.globalCompositeOperation = "source-over";
|
|
4623
4815
|
}
|
|
4816
|
+
// ── 11. Signature mark — unique geometric chop from hash prefix ──
|
|
4817
|
+
{
|
|
4818
|
+
const sigRng = (0, $e4b03e131ed2a289$export$eaf9227667332084)((0, $e4b03e131ed2a289$export$e9cc707de01b7042)(gitHash, 42));
|
|
4819
|
+
const sigSize = Math.min(width, height) * 0.025;
|
|
4820
|
+
// Bottom-right corner with padding
|
|
4821
|
+
const sigX = width - sigSize * 2.5;
|
|
4822
|
+
const sigY = height - sigSize * 2.5;
|
|
4823
|
+
const sigSegments = 4 + Math.floor(sigRng() * 4); // 4-7 segments
|
|
4824
|
+
const sigColor = (0, $d016ad53434219a1$export$f2121afcad3d553f)(colorHierarchy.accent, 0.15);
|
|
4825
|
+
ctx.save();
|
|
4826
|
+
ctx.globalAlpha = 0.12 + sigRng() * 0.08;
|
|
4827
|
+
ctx.translate(sigX, sigY);
|
|
4828
|
+
ctx.strokeStyle = sigColor;
|
|
4829
|
+
ctx.fillStyle = (0, $d016ad53434219a1$export$f2121afcad3d553f)(colorHierarchy.dominant, 0.06);
|
|
4830
|
+
ctx.lineWidth = Math.max(0.5, 0.8 * scaleFactor);
|
|
4831
|
+
// Outer ring
|
|
4832
|
+
ctx.beginPath();
|
|
4833
|
+
ctx.arc(0, 0, sigSize, 0, Math.PI * 2);
|
|
4834
|
+
ctx.stroke();
|
|
4835
|
+
ctx.fill();
|
|
4836
|
+
// Inner geometric pattern — unique per hash
|
|
4837
|
+
ctx.beginPath();
|
|
4838
|
+
for(let s = 0; s < sigSegments; s++){
|
|
4839
|
+
const angle1 = sigRng() * Math.PI * 2;
|
|
4840
|
+
const angle2 = sigRng() * Math.PI * 2;
|
|
4841
|
+
const r1 = sigSize * (0.2 + sigRng() * 0.6);
|
|
4842
|
+
const r2 = sigSize * (0.2 + sigRng() * 0.6);
|
|
4843
|
+
ctx.moveTo(Math.cos(angle1) * r1, Math.sin(angle1) * r1);
|
|
4844
|
+
ctx.lineTo(Math.cos(angle2) * r2, Math.sin(angle2) * r2);
|
|
4845
|
+
}
|
|
4846
|
+
ctx.stroke();
|
|
4847
|
+
// Center dot
|
|
4848
|
+
ctx.beginPath();
|
|
4849
|
+
ctx.arc(0, 0, sigSize * 0.12, 0, Math.PI * 2);
|
|
4850
|
+
ctx.fillStyle = sigColor;
|
|
4851
|
+
ctx.fill();
|
|
4852
|
+
ctx.restore();
|
|
4853
|
+
}
|
|
4624
4854
|
ctx.globalAlpha = 1;
|
|
4625
4855
|
}
|
|
4626
4856
|
|