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/module.js
CHANGED
|
@@ -527,6 +527,18 @@ function $9d614e7d77fc2947$export$6d1620b367f86f7a(rng) {
|
|
|
527
527
|
intensity: intensity
|
|
528
528
|
};
|
|
529
529
|
}
|
|
530
|
+
function $9d614e7d77fc2947$export$1793a1bfbe4f6ff5(hex, degrees) {
|
|
531
|
+
const [h, s, l] = $9d614e7d77fc2947$var$hexToHsl(hex);
|
|
532
|
+
return $9d614e7d77fc2947$var$hslToHex((h + degrees + 360) % 360, s, l);
|
|
533
|
+
}
|
|
534
|
+
function $9d614e7d77fc2947$export$703ba40a4347f77a(base, layerRatio, hueShiftPerLayer) {
|
|
535
|
+
const shift = layerRatio * hueShiftPerLayer;
|
|
536
|
+
return {
|
|
537
|
+
dominant: $9d614e7d77fc2947$export$1793a1bfbe4f6ff5(base.dominant, shift),
|
|
538
|
+
secondary: $9d614e7d77fc2947$export$1793a1bfbe4f6ff5(base.secondary, shift * 0.7),
|
|
539
|
+
accent: $9d614e7d77fc2947$export$1793a1bfbe4f6ff5(base.accent, shift * 0.5)
|
|
540
|
+
};
|
|
541
|
+
}
|
|
530
542
|
|
|
531
543
|
|
|
532
544
|
|
|
@@ -1810,7 +1822,8 @@ const $9beb8f41637c29fd$var$RENDER_STYLES = [
|
|
|
1810
1822
|
"noise-grain",
|
|
1811
1823
|
"wood-grain",
|
|
1812
1824
|
"marble-vein",
|
|
1813
|
-
"fabric-weave"
|
|
1825
|
+
"fabric-weave",
|
|
1826
|
+
"hand-drawn"
|
|
1814
1827
|
];
|
|
1815
1828
|
function $9beb8f41637c29fd$export$9fd4e64b2acd410e(rng) {
|
|
1816
1829
|
return $9beb8f41637c29fd$var$RENDER_STYLES[Math.floor(rng() * $9beb8f41637c29fd$var$RENDER_STYLES.length)];
|
|
@@ -2191,6 +2204,33 @@ function $9beb8f41637c29fd$export$71b514a25c47df50(ctx, shape, x, y, config) {
|
|
|
2191
2204
|
ctx.globalAlpha /= 0.3;
|
|
2192
2205
|
break;
|
|
2193
2206
|
}
|
|
2207
|
+
case "hand-drawn":
|
|
2208
|
+
{
|
|
2209
|
+
// Wobbly hand-drawn edge treatment — fill normally, then redraw
|
|
2210
|
+
// the outline with perturbed control points for a sketchy feel
|
|
2211
|
+
const savedAlphaHD = ctx.globalAlpha;
|
|
2212
|
+
ctx.globalAlpha = savedAlphaHD * 0.85;
|
|
2213
|
+
ctx.fill();
|
|
2214
|
+
ctx.globalAlpha = savedAlphaHD;
|
|
2215
|
+
// Draw 2-3 slightly offset wobbly strokes for a sketchy look
|
|
2216
|
+
const wobblePasses = 2 + (rng ? Math.floor(rng() * 2) : 0);
|
|
2217
|
+
ctx.lineWidth = strokeWidth * 0.8;
|
|
2218
|
+
for(let wp = 0; wp < wobblePasses; wp++){
|
|
2219
|
+
ctx.globalAlpha = savedAlphaHD * (0.4 - wp * 0.1);
|
|
2220
|
+
ctx.save();
|
|
2221
|
+
// Slight random offset per pass
|
|
2222
|
+
const wobbleX = rng ? (rng() - 0.5) * size * 0.02 : 0;
|
|
2223
|
+
const wobbleY = rng ? (rng() - 0.5) * size * 0.02 : 0;
|
|
2224
|
+
ctx.translate(wobbleX, wobbleY);
|
|
2225
|
+
// Slightly different scale per pass for edge variation
|
|
2226
|
+
const wobbleScale = 1 + (rng ? (rng() - 0.5) * 0.03 : 0);
|
|
2227
|
+
ctx.scale(wobbleScale, wobbleScale);
|
|
2228
|
+
ctx.stroke();
|
|
2229
|
+
ctx.restore();
|
|
2230
|
+
}
|
|
2231
|
+
ctx.globalAlpha = savedAlphaHD;
|
|
2232
|
+
break;
|
|
2233
|
+
}
|
|
2194
2234
|
case "fill-and-stroke":
|
|
2195
2235
|
default:
|
|
2196
2236
|
ctx.fill();
|
|
@@ -2326,7 +2366,8 @@ const $24064302523652b1$export$4343b39fe47bd82c = {
|
|
|
2326
2366
|
bestStyles: [
|
|
2327
2367
|
"fill-only",
|
|
2328
2368
|
"watercolor",
|
|
2329
|
-
"fill-and-stroke"
|
|
2369
|
+
"fill-and-stroke",
|
|
2370
|
+
"hand-drawn"
|
|
2330
2371
|
]
|
|
2331
2372
|
},
|
|
2332
2373
|
square: {
|
|
@@ -2363,7 +2404,8 @@ const $24064302523652b1$export$4343b39fe47bd82c = {
|
|
|
2363
2404
|
bestStyles: [
|
|
2364
2405
|
"fill-and-stroke",
|
|
2365
2406
|
"fill-only",
|
|
2366
|
-
"watercolor"
|
|
2407
|
+
"watercolor",
|
|
2408
|
+
"hand-drawn"
|
|
2367
2409
|
]
|
|
2368
2410
|
},
|
|
2369
2411
|
hexagon: {
|
|
@@ -2743,7 +2785,8 @@ const $24064302523652b1$export$4343b39fe47bd82c = {
|
|
|
2743
2785
|
bestStyles: [
|
|
2744
2786
|
"fill-only",
|
|
2745
2787
|
"watercolor",
|
|
2746
|
-
"fill-and-stroke"
|
|
2788
|
+
"fill-and-stroke",
|
|
2789
|
+
"hand-drawn"
|
|
2747
2790
|
]
|
|
2748
2791
|
},
|
|
2749
2792
|
ngon: {
|
|
@@ -3623,8 +3666,51 @@ const $3faa2521b78398cf$var$ARCHETYPES = [
|
|
|
3623
3666
|
invertForeground: false
|
|
3624
3667
|
}
|
|
3625
3668
|
];
|
|
3669
|
+
/**
|
|
3670
|
+
* Linearly interpolate between two archetype numeric parameters.
|
|
3671
|
+
*/ function $3faa2521b78398cf$var$lerpNum(a, b, t) {
|
|
3672
|
+
return a + (b - a) * t;
|
|
3673
|
+
}
|
|
3674
|
+
/**
|
|
3675
|
+
* Blend two archetypes by interpolating their numeric parameters
|
|
3676
|
+
* and merging their style arrays.
|
|
3677
|
+
*/ function $3faa2521b78398cf$var$blendArchetypes(a, b, t) {
|
|
3678
|
+
// Merge preferred styles — unique union, primary archetype first
|
|
3679
|
+
const mergedStyles = [
|
|
3680
|
+
...new Set([
|
|
3681
|
+
...a.preferredStyles,
|
|
3682
|
+
...b.preferredStyles
|
|
3683
|
+
])
|
|
3684
|
+
];
|
|
3685
|
+
return {
|
|
3686
|
+
name: `${a.name}+${b.name}`,
|
|
3687
|
+
gridSize: Math.round($3faa2521b78398cf$var$lerpNum(a.gridSize, b.gridSize, t)),
|
|
3688
|
+
layers: Math.round($3faa2521b78398cf$var$lerpNum(a.layers, b.layers, t)),
|
|
3689
|
+
baseOpacity: $3faa2521b78398cf$var$lerpNum(a.baseOpacity, b.baseOpacity, t),
|
|
3690
|
+
opacityReduction: $3faa2521b78398cf$var$lerpNum(a.opacityReduction, b.opacityReduction, t),
|
|
3691
|
+
minShapeSize: Math.round($3faa2521b78398cf$var$lerpNum(a.minShapeSize, b.minShapeSize, t)),
|
|
3692
|
+
maxShapeSize: Math.round($3faa2521b78398cf$var$lerpNum(a.maxShapeSize, b.maxShapeSize, t)),
|
|
3693
|
+
backgroundStyle: t < 0.5 ? a.backgroundStyle : b.backgroundStyle,
|
|
3694
|
+
paletteMode: t < 0.5 ? a.paletteMode : b.paletteMode,
|
|
3695
|
+
preferredStyles: mergedStyles,
|
|
3696
|
+
flowLineMultiplier: $3faa2521b78398cf$var$lerpNum(a.flowLineMultiplier, b.flowLineMultiplier, t),
|
|
3697
|
+
heroShape: t < 0.5 ? a.heroShape : b.heroShape,
|
|
3698
|
+
glowMultiplier: $3faa2521b78398cf$var$lerpNum(a.glowMultiplier, b.glowMultiplier, t),
|
|
3699
|
+
sizePower: $3faa2521b78398cf$var$lerpNum(a.sizePower, b.sizePower, t),
|
|
3700
|
+
invertForeground: t < 0.5 ? a.invertForeground : b.invertForeground
|
|
3701
|
+
};
|
|
3702
|
+
}
|
|
3626
3703
|
function $3faa2521b78398cf$export$f1142fd7da4d6590(rng) {
|
|
3627
|
-
|
|
3704
|
+
const primary = $3faa2521b78398cf$var$ARCHETYPES[Math.floor(rng() * $3faa2521b78398cf$var$ARCHETYPES.length)];
|
|
3705
|
+
// ~15% chance of blending with a second archetype
|
|
3706
|
+
if (rng() < 0.15) {
|
|
3707
|
+
const secondary = $3faa2521b78398cf$var$ARCHETYPES[Math.floor(rng() * $3faa2521b78398cf$var$ARCHETYPES.length)];
|
|
3708
|
+
if (secondary.name !== primary.name) {
|
|
3709
|
+
const blendT = 0.25 + rng() * 0.25; // 25-50% blend toward secondary
|
|
3710
|
+
return $3faa2521b78398cf$var$blendArchetypes(primary, secondary, blendT);
|
|
3711
|
+
}
|
|
3712
|
+
}
|
|
3713
|
+
return primary;
|
|
3628
3714
|
}
|
|
3629
3715
|
|
|
3630
3716
|
|
|
@@ -3960,6 +4046,8 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
3960
4046
|
const colorGrade = (0, $9d614e7d77fc2947$export$6d1620b367f86f7a)(rng);
|
|
3961
4047
|
// ── 0e. Light direction — consistent shadow angle ──────────────
|
|
3962
4048
|
const lightAngle = rng() * Math.PI * 2;
|
|
4049
|
+
// ── 0f. Palette evolution — hue drift direction across layers ──
|
|
4050
|
+
const paletteHueShift = (rng() - 0.5) * 40; // -20° to +20° total drift
|
|
3963
4051
|
const scaleFactor = Math.min(width, height) / 1024;
|
|
3964
4052
|
const adjustedMinSize = minShapeSize * scaleFactor;
|
|
3965
4053
|
const adjustedMaxSize = maxShapeSize * scaleFactor;
|
|
@@ -4134,6 +4222,41 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
4134
4222
|
ry + (nearest.y - ry) * pull
|
|
4135
4223
|
];
|
|
4136
4224
|
}
|
|
4225
|
+
// ── 3b. Void zone decoration — intentional negative space ────
|
|
4226
|
+
for (const zone of voidZones){
|
|
4227
|
+
// Subtle halo ring around void zones
|
|
4228
|
+
ctx.globalAlpha = 0.04 + rng() * 0.04;
|
|
4229
|
+
ctx.strokeStyle = (0, $9d614e7d77fc2947$export$f2121afcad3d553f)(colorHierarchy.accent, 0.2);
|
|
4230
|
+
ctx.lineWidth = 1.5 * scaleFactor;
|
|
4231
|
+
ctx.beginPath();
|
|
4232
|
+
ctx.arc(zone.x, zone.y, zone.radius, 0, Math.PI * 2);
|
|
4233
|
+
ctx.stroke();
|
|
4234
|
+
// ~50% chance: scatter tiny dots inside the void
|
|
4235
|
+
if (rng() < 0.5) {
|
|
4236
|
+
const dotCount = 3 + Math.floor(rng() * 6);
|
|
4237
|
+
ctx.globalAlpha = 0.06 + rng() * 0.04;
|
|
4238
|
+
ctx.fillStyle = (0, $9d614e7d77fc2947$export$f2121afcad3d553f)(colorHierarchy.secondary, 0.15);
|
|
4239
|
+
for(let d = 0; d < dotCount; d++){
|
|
4240
|
+
const angle = rng() * Math.PI * 2;
|
|
4241
|
+
const dist = rng() * zone.radius * 0.7;
|
|
4242
|
+
const dotR = (1 + rng() * 3) * scaleFactor;
|
|
4243
|
+
ctx.beginPath();
|
|
4244
|
+
ctx.arc(zone.x + Math.cos(angle) * dist, zone.y + Math.sin(angle) * dist, dotR, 0, Math.PI * 2);
|
|
4245
|
+
ctx.fill();
|
|
4246
|
+
}
|
|
4247
|
+
}
|
|
4248
|
+
// ~30% chance: thin concentric ring inside
|
|
4249
|
+
if (rng() < 0.3) {
|
|
4250
|
+
ctx.globalAlpha = 0.03 + rng() * 0.03;
|
|
4251
|
+
ctx.strokeStyle = (0, $9d614e7d77fc2947$export$f2121afcad3d553f)(colorHierarchy.dominant, 0.1);
|
|
4252
|
+
ctx.lineWidth = 0.5 * scaleFactor;
|
|
4253
|
+
const innerR = zone.radius * (0.4 + rng() * 0.3);
|
|
4254
|
+
ctx.beginPath();
|
|
4255
|
+
ctx.arc(zone.x, zone.y, innerR, 0, Math.PI * 2);
|
|
4256
|
+
ctx.stroke();
|
|
4257
|
+
}
|
|
4258
|
+
}
|
|
4259
|
+
ctx.globalAlpha = 1;
|
|
4137
4260
|
// ── 4. Flow field seed values ──────────────────────────────────
|
|
4138
4261
|
const fieldAngleBase = rng() * Math.PI * 2;
|
|
4139
4262
|
const fieldFreq = 0.5 + rng() * 2;
|
|
@@ -4206,6 +4329,18 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
4206
4329
|
const dofFactor = 1 - layerRatio * 0.5; // 1.0 for front layer, 0.5 for back
|
|
4207
4330
|
const dofStrokeScale = 0.4 + dofFactor * 0.6; // strokes thin out with depth
|
|
4208
4331
|
const dofContrastReduction = layerRatio * 0.2; // colors fade toward bg
|
|
4332
|
+
// Color palette evolution — hue-rotate the hierarchy per layer
|
|
4333
|
+
const layerHierarchy = (0, $9d614e7d77fc2947$export$703ba40a4347f77a)(colorHierarchy, layerRatio, paletteHueShift);
|
|
4334
|
+
// Focal depth: shapes near focal points get more detail
|
|
4335
|
+
const focalDetailBoost = (px, py)=>{
|
|
4336
|
+
let minFocalDist = Infinity;
|
|
4337
|
+
for (const fp of focalPoints){
|
|
4338
|
+
const d = Math.hypot(px - fp.x, py - fp.y);
|
|
4339
|
+
if (d < minFocalDist) minFocalDist = d;
|
|
4340
|
+
}
|
|
4341
|
+
const maxDist = Math.hypot(width, height) * 0.5;
|
|
4342
|
+
return Math.max(0, 1 - minFocalDist / maxDist); // 1.0 at focal, 0.0 at edges
|
|
4343
|
+
};
|
|
4209
4344
|
for(let i = 0; i < numShapes; i++){
|
|
4210
4345
|
// Position from composition mode, then focal bias
|
|
4211
4346
|
const rawPos = $b623126c6e9cbb71$var$getCompositionPosition(compositionMode, rng, width, height, i, numShapes, cx, cy);
|
|
@@ -4236,9 +4371,9 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
4236
4371
|
rotation = rotation + (angleToHero - rotation) * blendFactor * 0.4;
|
|
4237
4372
|
}
|
|
4238
4373
|
}
|
|
4239
|
-
// Positional color from hierarchy + jitter
|
|
4240
|
-
let fillBase = $b623126c6e9cbb71$var$getPositionalColor(x, y, width, height,
|
|
4241
|
-
const strokeBase = (0, $9d614e7d77fc2947$export$b49f62f0a99da0e8)(
|
|
4374
|
+
// Positional color from hierarchy + jitter (using evolved layer palette)
|
|
4375
|
+
let fillBase = $b623126c6e9cbb71$var$getPositionalColor(x, y, width, height, layerHierarchy, rng);
|
|
4376
|
+
const strokeBase = (0, $9d614e7d77fc2947$export$b49f62f0a99da0e8)(layerHierarchy, rng);
|
|
4242
4377
|
// Desaturate colors on later layers for depth
|
|
4243
4378
|
if (atmosphericDesat > 0) fillBase = (0, $9d614e7d77fc2947$export$fb75607d98509d9)(fillBase, atmosphericDesat);
|
|
4244
4379
|
// Temperature contrast: shift foreground shapes opposite to background
|
|
@@ -4330,6 +4465,25 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
4330
4465
|
mirrorGap: size * (0.1 + rng() * 0.3)
|
|
4331
4466
|
});
|
|
4332
4467
|
else (0, $9beb8f41637c29fd$export$bb35a6995ddbf32d)(ctx, shape, finalX, finalY, shapeConfig);
|
|
4468
|
+
// ── Glazing — luminous multi-pass transparency on ~20% of shapes ──
|
|
4469
|
+
if (rng() < 0.2 && size > adjustedMinSize * 2) {
|
|
4470
|
+
const glazePasses = 2 + Math.floor(rng() * 2);
|
|
4471
|
+
for(let g = 0; g < glazePasses; g++){
|
|
4472
|
+
const glazeScale = 1 - (g + 1) * 0.12; // progressively smaller
|
|
4473
|
+
const glazeAlpha = 0.08 + g * 0.04; // progressively more opaque toward center
|
|
4474
|
+
ctx.globalAlpha = glazeAlpha;
|
|
4475
|
+
(0, $9beb8f41637c29fd$export$bb35a6995ddbf32d)(ctx, shape, finalX, finalY, {
|
|
4476
|
+
fillColor: (0, $9d614e7d77fc2947$export$f2121afcad3d553f)(fillColor, 0.15 + g * 0.1),
|
|
4477
|
+
strokeColor: "rgba(0,0,0,0)",
|
|
4478
|
+
strokeWidth: 0,
|
|
4479
|
+
size: size * glazeScale,
|
|
4480
|
+
rotation: rotation,
|
|
4481
|
+
proportionType: "GOLDEN_RATIO",
|
|
4482
|
+
renderStyle: "fill-only",
|
|
4483
|
+
rng: rng
|
|
4484
|
+
});
|
|
4485
|
+
}
|
|
4486
|
+
}
|
|
4333
4487
|
shapePositions.push({
|
|
4334
4488
|
x: finalX,
|
|
4335
4489
|
y: finalY,
|
|
@@ -4367,7 +4521,10 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
4367
4521
|
}
|
|
4368
4522
|
}
|
|
4369
4523
|
// ── 5d. Recursive nesting ──────────────────────────────────
|
|
4370
|
-
|
|
4524
|
+
// Focal depth: shapes near focal points get more detail
|
|
4525
|
+
const focalProximity = focalDetailBoost(finalX, finalY);
|
|
4526
|
+
const nestingChance = 0.15 + focalProximity * 0.15; // 15-30% near focal
|
|
4527
|
+
if (size > adjustedMaxSize * 0.4 && rng() < nestingChance) {
|
|
4371
4528
|
const innerCount = 1 + Math.floor(rng() * 3);
|
|
4372
4529
|
for(let n = 0; n < innerCount; n++){
|
|
4373
4530
|
// Pick inner shape from palette affinities
|
|
@@ -4392,7 +4549,8 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
4392
4549
|
}
|
|
4393
4550
|
}
|
|
4394
4551
|
// ── 5e. Shape constellations — pre-composed groups ─────────
|
|
4395
|
-
|
|
4552
|
+
const constellationChance = 0.12 + focalProximity * 0.1; // 12-22% near focal
|
|
4553
|
+
if (size > adjustedMaxSize * 0.35 && rng() < constellationChance) {
|
|
4396
4554
|
const constellation = $b623126c6e9cbb71$var$CONSTELLATIONS[Math.floor(rng() * $b623126c6e9cbb71$var$CONSTELLATIONS.length)];
|
|
4397
4555
|
const members = constellation.build(rng, size);
|
|
4398
4556
|
const groupRotation = rng() * Math.PI * 2;
|
|
@@ -4496,7 +4654,41 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
4496
4654
|
prevY = fy;
|
|
4497
4655
|
}
|
|
4498
4656
|
}
|
|
4499
|
-
// ── 6b.
|
|
4657
|
+
// ── 6b. Motion/energy lines — short directional bursts ─────────
|
|
4658
|
+
const energyArchetypes = [
|
|
4659
|
+
"dense-chaotic",
|
|
4660
|
+
"cosmic",
|
|
4661
|
+
"neon-glow",
|
|
4662
|
+
"bold-graphic"
|
|
4663
|
+
];
|
|
4664
|
+
const hasEnergyLines = energyArchetypes.some((a)=>archetype.name.includes(a)) || rng() < 0.25;
|
|
4665
|
+
if (hasEnergyLines && shapePositions.length > 0) {
|
|
4666
|
+
const energyCount = 5 + Math.floor(rng() * 10);
|
|
4667
|
+
ctx.lineCap = "round";
|
|
4668
|
+
for(let e = 0; e < energyCount; e++){
|
|
4669
|
+
// Pick a random shape to radiate from
|
|
4670
|
+
const source = shapePositions[Math.floor(rng() * shapePositions.length)];
|
|
4671
|
+
const burstCount = 2 + Math.floor(rng() * 4);
|
|
4672
|
+
const baseAngle = flowAngle(source.x, source.y);
|
|
4673
|
+
for(let b = 0; b < burstCount; b++){
|
|
4674
|
+
const angle = baseAngle + (rng() - 0.5) * 1.2;
|
|
4675
|
+
const lineLen = (source.size * 0.3 + rng() * source.size * 0.5) * scaleFactor * 0.3;
|
|
4676
|
+
const startDist = source.size * 0.5;
|
|
4677
|
+
const sx = source.x + Math.cos(angle) * startDist;
|
|
4678
|
+
const sy = source.y + Math.sin(angle) * startDist;
|
|
4679
|
+
const ex = sx + Math.cos(angle) * lineLen;
|
|
4680
|
+
const ey = sy + Math.sin(angle) * lineLen;
|
|
4681
|
+
ctx.globalAlpha = 0.04 + rng() * 0.06;
|
|
4682
|
+
ctx.strokeStyle = (0, $9d614e7d77fc2947$export$f2121afcad3d553f)((0, $9d614e7d77fc2947$export$90ad0e6170cf6af5)((0, $9d614e7d77fc2947$export$b49f62f0a99da0e8)(colorHierarchy, rng), bgLum), 0.3);
|
|
4683
|
+
ctx.lineWidth = (0.5 + rng() * 1.5) * scaleFactor;
|
|
4684
|
+
ctx.beginPath();
|
|
4685
|
+
ctx.moveTo(sx, sy);
|
|
4686
|
+
ctx.lineTo(ex, ey);
|
|
4687
|
+
ctx.stroke();
|
|
4688
|
+
}
|
|
4689
|
+
}
|
|
4690
|
+
}
|
|
4691
|
+
// ── 6c. Apply symmetry mirroring ─────────────────────────────────
|
|
4500
4692
|
if (symmetryMode !== "none") {
|
|
4501
4693
|
const canvas = ctx.canvas;
|
|
4502
4694
|
ctx.save();
|
|
@@ -4607,6 +4799,44 @@ function $b623126c6e9cbb71$export$29a844702096332e(ctx, gitHash, config = {}) {
|
|
|
4607
4799
|
ctx.restore();
|
|
4608
4800
|
ctx.globalCompositeOperation = "source-over";
|
|
4609
4801
|
}
|
|
4802
|
+
// ── 11. Signature mark — unique geometric chop from hash prefix ──
|
|
4803
|
+
{
|
|
4804
|
+
const sigRng = (0, $461134e0b6ce0619$export$eaf9227667332084)((0, $461134e0b6ce0619$export$e9cc707de01b7042)(gitHash, 42));
|
|
4805
|
+
const sigSize = Math.min(width, height) * 0.025;
|
|
4806
|
+
// Bottom-right corner with padding
|
|
4807
|
+
const sigX = width - sigSize * 2.5;
|
|
4808
|
+
const sigY = height - sigSize * 2.5;
|
|
4809
|
+
const sigSegments = 4 + Math.floor(sigRng() * 4); // 4-7 segments
|
|
4810
|
+
const sigColor = (0, $9d614e7d77fc2947$export$f2121afcad3d553f)(colorHierarchy.accent, 0.15);
|
|
4811
|
+
ctx.save();
|
|
4812
|
+
ctx.globalAlpha = 0.12 + sigRng() * 0.08;
|
|
4813
|
+
ctx.translate(sigX, sigY);
|
|
4814
|
+
ctx.strokeStyle = sigColor;
|
|
4815
|
+
ctx.fillStyle = (0, $9d614e7d77fc2947$export$f2121afcad3d553f)(colorHierarchy.dominant, 0.06);
|
|
4816
|
+
ctx.lineWidth = Math.max(0.5, 0.8 * scaleFactor);
|
|
4817
|
+
// Outer ring
|
|
4818
|
+
ctx.beginPath();
|
|
4819
|
+
ctx.arc(0, 0, sigSize, 0, Math.PI * 2);
|
|
4820
|
+
ctx.stroke();
|
|
4821
|
+
ctx.fill();
|
|
4822
|
+
// Inner geometric pattern — unique per hash
|
|
4823
|
+
ctx.beginPath();
|
|
4824
|
+
for(let s = 0; s < sigSegments; s++){
|
|
4825
|
+
const angle1 = sigRng() * Math.PI * 2;
|
|
4826
|
+
const angle2 = sigRng() * Math.PI * 2;
|
|
4827
|
+
const r1 = sigSize * (0.2 + sigRng() * 0.6);
|
|
4828
|
+
const r2 = sigSize * (0.2 + sigRng() * 0.6);
|
|
4829
|
+
ctx.moveTo(Math.cos(angle1) * r1, Math.sin(angle1) * r1);
|
|
4830
|
+
ctx.lineTo(Math.cos(angle2) * r2, Math.sin(angle2) * r2);
|
|
4831
|
+
}
|
|
4832
|
+
ctx.stroke();
|
|
4833
|
+
// Center dot
|
|
4834
|
+
ctx.beginPath();
|
|
4835
|
+
ctx.arc(0, 0, sigSize * 0.12, 0, Math.PI * 2);
|
|
4836
|
+
ctx.fillStyle = sigColor;
|
|
4837
|
+
ctx.fill();
|
|
4838
|
+
ctx.restore();
|
|
4839
|
+
}
|
|
4610
4840
|
ctx.globalAlpha = 1;
|
|
4611
4841
|
}
|
|
4612
4842
|
|