git-hash-art 0.10.1 → 0.11.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/.github/workflows/deploy-www.yml +13 -3
- package/CHANGELOG.md +10 -0
- package/dist/browser.js +360 -56
- package/dist/browser.js.map +1 -1
- package/dist/main.js +362 -56
- package/dist/main.js.map +1 -1
- package/dist/module.js +362 -56
- package/dist/module.js.map +1 -1
- package/package.json +1 -1
- package/src/lib/archetypes.ts +29 -0
- package/src/lib/canvas/colors.ts +7 -5
- package/src/lib/canvas/draw.ts +14 -4
- package/src/lib/render.ts +249 -60
package/package.json
CHANGED
package/src/lib/archetypes.ts
CHANGED
|
@@ -34,6 +34,14 @@ export type PaletteMode =
|
|
|
34
34
|
|
|
35
35
|
// ── Archetype definition ────────────────────────────────────────────
|
|
36
36
|
|
|
37
|
+
export type CompositionMode =
|
|
38
|
+
| "radial"
|
|
39
|
+
| "flow-field"
|
|
40
|
+
| "spiral"
|
|
41
|
+
| "grid-subdivision"
|
|
42
|
+
| "clustered"
|
|
43
|
+
| "golden-spiral";
|
|
44
|
+
|
|
37
45
|
export interface Archetype {
|
|
38
46
|
name: string;
|
|
39
47
|
/** Override gridSize (controls shape count) */
|
|
@@ -54,6 +62,8 @@ export interface Archetype {
|
|
|
54
62
|
paletteMode: PaletteMode;
|
|
55
63
|
/** Preferred render styles (weighted toward these) */
|
|
56
64
|
preferredStyles: RenderStyle[];
|
|
65
|
+
/** Preferred composition modes (70% chance of using one of these) */
|
|
66
|
+
preferredCompositions: CompositionMode[];
|
|
57
67
|
/** Flow line count multiplier (1 = default) */
|
|
58
68
|
flowLineMultiplier: number;
|
|
59
69
|
/** Whether to draw the hero shape */
|
|
@@ -80,6 +90,7 @@ const ARCHETYPES: Archetype[] = [
|
|
|
80
90
|
backgroundStyle: "radial-dark",
|
|
81
91
|
paletteMode: "harmonious",
|
|
82
92
|
preferredStyles: ["fill-and-stroke", "watercolor", "fill-only"],
|
|
93
|
+
preferredCompositions: ["clustered", "flow-field", "radial"],
|
|
83
94
|
flowLineMultiplier: 2.5,
|
|
84
95
|
heroShape: false,
|
|
85
96
|
glowMultiplier: 0.5,
|
|
@@ -97,6 +108,7 @@ const ARCHETYPES: Archetype[] = [
|
|
|
97
108
|
backgroundStyle: "solid-light",
|
|
98
109
|
paletteMode: "duotone",
|
|
99
110
|
preferredStyles: ["fill-and-stroke", "stroke-only", "incomplete"],
|
|
111
|
+
preferredCompositions: ["golden-spiral", "grid-subdivision"],
|
|
100
112
|
flowLineMultiplier: 0.3,
|
|
101
113
|
heroShape: true,
|
|
102
114
|
glowMultiplier: 0,
|
|
@@ -114,6 +126,7 @@ const ARCHETYPES: Archetype[] = [
|
|
|
114
126
|
backgroundStyle: "radial-dark",
|
|
115
127
|
paletteMode: "earth",
|
|
116
128
|
preferredStyles: ["watercolor", "fill-only", "incomplete"],
|
|
129
|
+
preferredCompositions: ["flow-field", "golden-spiral", "spiral"],
|
|
117
130
|
flowLineMultiplier: 4,
|
|
118
131
|
heroShape: false,
|
|
119
132
|
glowMultiplier: 0.3,
|
|
@@ -131,6 +144,7 @@ const ARCHETYPES: Archetype[] = [
|
|
|
131
144
|
backgroundStyle: "solid-dark",
|
|
132
145
|
paletteMode: "high-contrast",
|
|
133
146
|
preferredStyles: ["stroke-only", "dashed", "double-stroke", "hatched"],
|
|
147
|
+
preferredCompositions: ["grid-subdivision", "radial"],
|
|
134
148
|
flowLineMultiplier: 0,
|
|
135
149
|
heroShape: false,
|
|
136
150
|
glowMultiplier: 0,
|
|
@@ -148,6 +162,7 @@ const ARCHETYPES: Archetype[] = [
|
|
|
148
162
|
backgroundStyle: "radial-light",
|
|
149
163
|
paletteMode: "pastel-light",
|
|
150
164
|
preferredStyles: ["watercolor", "incomplete", "fill-only"],
|
|
165
|
+
preferredCompositions: ["golden-spiral", "radial", "spiral"],
|
|
151
166
|
flowLineMultiplier: 1.5,
|
|
152
167
|
heroShape: true,
|
|
153
168
|
glowMultiplier: 2,
|
|
@@ -165,6 +180,7 @@ const ARCHETYPES: Archetype[] = [
|
|
|
165
180
|
backgroundStyle: "linear-diagonal",
|
|
166
181
|
paletteMode: "duotone",
|
|
167
182
|
preferredStyles: ["fill-and-stroke", "double-stroke"],
|
|
183
|
+
preferredCompositions: ["grid-subdivision", "golden-spiral"],
|
|
168
184
|
flowLineMultiplier: 0,
|
|
169
185
|
heroShape: true,
|
|
170
186
|
glowMultiplier: 0,
|
|
@@ -182,6 +198,7 @@ const ARCHETYPES: Archetype[] = [
|
|
|
182
198
|
backgroundStyle: "solid-dark",
|
|
183
199
|
paletteMode: "neon",
|
|
184
200
|
preferredStyles: ["stroke-only", "double-stroke", "dashed"],
|
|
201
|
+
preferredCompositions: ["radial", "spiral", "clustered"],
|
|
185
202
|
flowLineMultiplier: 2,
|
|
186
203
|
heroShape: true,
|
|
187
204
|
glowMultiplier: 3,
|
|
@@ -199,6 +216,7 @@ const ARCHETYPES: Archetype[] = [
|
|
|
199
216
|
backgroundStyle: "solid-light",
|
|
200
217
|
paletteMode: "monochrome",
|
|
201
218
|
preferredStyles: ["hatched", "incomplete", "stroke-only", "dashed"],
|
|
219
|
+
preferredCompositions: ["flow-field", "grid-subdivision", "clustered"],
|
|
202
220
|
flowLineMultiplier: 1.5,
|
|
203
221
|
heroShape: false,
|
|
204
222
|
glowMultiplier: 0,
|
|
@@ -216,6 +234,7 @@ const ARCHETYPES: Archetype[] = [
|
|
|
216
234
|
backgroundStyle: "radial-dark",
|
|
217
235
|
paletteMode: "neon",
|
|
218
236
|
preferredStyles: ["fill-only", "watercolor", "fill-and-stroke"],
|
|
237
|
+
preferredCompositions: ["radial", "spiral", "golden-spiral"],
|
|
219
238
|
flowLineMultiplier: 3,
|
|
220
239
|
heroShape: true,
|
|
221
240
|
glowMultiplier: 2.5,
|
|
@@ -233,6 +252,7 @@ const ARCHETYPES: Archetype[] = [
|
|
|
233
252
|
backgroundStyle: "radial-light",
|
|
234
253
|
paletteMode: "harmonious",
|
|
235
254
|
preferredStyles: ["watercolor", "fill-only", "incomplete"],
|
|
255
|
+
preferredCompositions: ["golden-spiral", "flow-field", "radial"],
|
|
236
256
|
flowLineMultiplier: 0.5,
|
|
237
257
|
heroShape: false,
|
|
238
258
|
glowMultiplier: 0.3,
|
|
@@ -250,6 +270,7 @@ const ARCHETYPES: Archetype[] = [
|
|
|
250
270
|
backgroundStyle: "solid-light",
|
|
251
271
|
paletteMode: "high-contrast",
|
|
252
272
|
preferredStyles: ["fill-and-stroke", "stroke-only", "dashed"],
|
|
273
|
+
preferredCompositions: ["grid-subdivision", "radial"],
|
|
253
274
|
flowLineMultiplier: 0,
|
|
254
275
|
heroShape: false,
|
|
255
276
|
glowMultiplier: 0,
|
|
@@ -267,6 +288,7 @@ const ARCHETYPES: Archetype[] = [
|
|
|
267
288
|
backgroundStyle: "solid-light",
|
|
268
289
|
paletteMode: "duotone",
|
|
269
290
|
preferredStyles: ["fill-and-stroke", "fill-only", "double-stroke"],
|
|
291
|
+
preferredCompositions: ["grid-subdivision", "clustered"],
|
|
270
292
|
flowLineMultiplier: 0,
|
|
271
293
|
heroShape: true,
|
|
272
294
|
glowMultiplier: 0,
|
|
@@ -284,6 +306,7 @@ const ARCHETYPES: Archetype[] = [
|
|
|
284
306
|
backgroundStyle: "radial-dark",
|
|
285
307
|
paletteMode: "harmonious",
|
|
286
308
|
preferredStyles: ["fill-and-stroke", "watercolor", "fill-only"],
|
|
309
|
+
preferredCompositions: ["radial", "golden-spiral", "flow-field"],
|
|
287
310
|
flowLineMultiplier: 1,
|
|
288
311
|
heroShape: true,
|
|
289
312
|
glowMultiplier: 1,
|
|
@@ -301,6 +324,7 @@ const ARCHETYPES: Archetype[] = [
|
|
|
301
324
|
backgroundStyle: "solid-dark",
|
|
302
325
|
paletteMode: "high-contrast",
|
|
303
326
|
preferredStyles: ["fill-and-stroke", "stroke-only", "fill-only"],
|
|
327
|
+
preferredCompositions: ["clustered", "grid-subdivision", "radial"],
|
|
304
328
|
flowLineMultiplier: 0,
|
|
305
329
|
heroShape: false,
|
|
306
330
|
glowMultiplier: 0.3,
|
|
@@ -318,6 +342,7 @@ const ARCHETYPES: Archetype[] = [
|
|
|
318
342
|
backgroundStyle: "radial-light",
|
|
319
343
|
paletteMode: "earth",
|
|
320
344
|
preferredStyles: ["watercolor", "fill-only", "incomplete"],
|
|
345
|
+
preferredCompositions: ["flow-field", "golden-spiral", "spiral"],
|
|
321
346
|
flowLineMultiplier: 3,
|
|
322
347
|
heroShape: true,
|
|
323
348
|
glowMultiplier: 0.2,
|
|
@@ -335,6 +360,7 @@ const ARCHETYPES: Archetype[] = [
|
|
|
335
360
|
backgroundStyle: "solid-light",
|
|
336
361
|
paletteMode: "monochrome",
|
|
337
362
|
preferredStyles: ["stipple", "fill-only", "hatched"],
|
|
363
|
+
preferredCompositions: ["radial", "clustered", "flow-field"],
|
|
338
364
|
flowLineMultiplier: 0,
|
|
339
365
|
heroShape: false,
|
|
340
366
|
glowMultiplier: 0,
|
|
@@ -352,6 +378,7 @@ const ARCHETYPES: Archetype[] = [
|
|
|
352
378
|
backgroundStyle: "radial-dark",
|
|
353
379
|
paletteMode: "neon",
|
|
354
380
|
preferredStyles: ["fill-only", "watercolor", "stroke-only", "incomplete"],
|
|
381
|
+
preferredCompositions: ["spiral", "radial", "golden-spiral"],
|
|
355
382
|
flowLineMultiplier: 2,
|
|
356
383
|
heroShape: true,
|
|
357
384
|
glowMultiplier: 2.5,
|
|
@@ -374,6 +401,7 @@ function lerpNum(a: number, b: number, t: number): number {
|
|
|
374
401
|
function blendArchetypes(a: Archetype, b: Archetype, t: number): Archetype {
|
|
375
402
|
// Merge preferred styles — unique union, primary archetype first
|
|
376
403
|
const mergedStyles = [...new Set([...a.preferredStyles, ...b.preferredStyles])] as RenderStyle[];
|
|
404
|
+
const mergedCompositions = [...new Set([...a.preferredCompositions, ...b.preferredCompositions])] as CompositionMode[];
|
|
377
405
|
|
|
378
406
|
return {
|
|
379
407
|
name: `${a.name}+${b.name}`,
|
|
@@ -386,6 +414,7 @@ function blendArchetypes(a: Archetype, b: Archetype, t: number): Archetype {
|
|
|
386
414
|
backgroundStyle: t < 0.5 ? a.backgroundStyle : b.backgroundStyle,
|
|
387
415
|
paletteMode: t < 0.5 ? a.paletteMode : b.paletteMode,
|
|
388
416
|
preferredStyles: mergedStyles,
|
|
417
|
+
preferredCompositions: mergedCompositions,
|
|
389
418
|
flowLineMultiplier: lerpNum(a.flowLineMultiplier, b.flowLineMultiplier, t),
|
|
390
419
|
heroShape: t < 0.5 ? a.heroShape : b.heroShape,
|
|
391
420
|
glowMultiplier: lerpNum(a.glowMultiplier, b.glowMultiplier, t),
|
package/src/lib/canvas/colors.ts
CHANGED
|
@@ -388,14 +388,16 @@ export function buildColorHierarchy(colors: string[], rng: () => number): ColorH
|
|
|
388
388
|
all: colors,
|
|
389
389
|
};
|
|
390
390
|
}
|
|
391
|
-
// Pick dominant as the color
|
|
391
|
+
// Pick dominant as the color with the highest chroma (saturation × distance from gray)
|
|
392
|
+
// This selects the most visually prominent color rather than the average
|
|
392
393
|
const hsls = colors.map((c) => hexToHsl(c));
|
|
393
|
-
const avgHue = hsls.reduce((s, h) => s + h[0], 0) / hsls.length;
|
|
394
394
|
let dominantIdx = 0;
|
|
395
|
-
let
|
|
395
|
+
let maxChroma = -1;
|
|
396
396
|
for (let i = 0; i < hsls.length; i++) {
|
|
397
|
-
|
|
398
|
-
|
|
397
|
+
// Chroma approximation: saturation × how far lightness is from 50% (gray)
|
|
398
|
+
const lightnessVibrancy = 1 - Math.abs(hsls[i][2] - 0.5) * 2; // peaks at L=0.5
|
|
399
|
+
const chroma = hsls[i][1] * lightnessVibrancy;
|
|
400
|
+
if (chroma > maxChroma) { maxChroma = chroma; dominantIdx = i; }
|
|
399
401
|
}
|
|
400
402
|
// Accent is the color most distant from dominant in hue
|
|
401
403
|
let accentIdx = 0;
|
package/src/lib/canvas/draw.ts
CHANGED
|
@@ -668,16 +668,26 @@ export function enhanceShapeGeneration(
|
|
|
668
668
|
ctx.shadowOffsetY = 0;
|
|
669
669
|
ctx.shadowColor = "transparent";
|
|
670
670
|
|
|
671
|
-
// ── Specular highlight —
|
|
671
|
+
// ── Specular highlight — tinted arc on the light-facing side ──
|
|
672
672
|
if (lightAngle !== undefined && size > 15 && rng) {
|
|
673
673
|
const hlRadius = size * 0.35;
|
|
674
674
|
const hlDist = size * 0.15;
|
|
675
675
|
const hlX = Math.cos(lightAngle) * hlDist;
|
|
676
676
|
const hlY = Math.sin(lightAngle) * hlDist;
|
|
677
677
|
const hlGrad = ctx.createRadialGradient(hlX, hlY, 0, hlX, hlY, hlRadius);
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
678
|
+
// Tint highlight warm/cool based on fill color for cohesion
|
|
679
|
+
// Parse fill to detect warmth — fallback to white for non-parseable
|
|
680
|
+
let hlBase = "255,255,255";
|
|
681
|
+
if (typeof fillColor === "string" && fillColor.startsWith("#") && fillColor.length >= 7) {
|
|
682
|
+
const r = parseInt(fillColor.slice(1, 3), 16);
|
|
683
|
+
const g = parseInt(fillColor.slice(3, 5), 16);
|
|
684
|
+
const b = parseInt(fillColor.slice(5, 7), 16);
|
|
685
|
+
// Blend toward white but keep a hint of the fill's warmth
|
|
686
|
+
hlBase = `${Math.round(r * 0.15 + 255 * 0.85)},${Math.round(g * 0.15 + 255 * 0.85)},${Math.round(b * 0.15 + 255 * 0.85)}`;
|
|
687
|
+
}
|
|
688
|
+
hlGrad.addColorStop(0, `rgba(${hlBase},0.18)`);
|
|
689
|
+
hlGrad.addColorStop(0.5, `rgba(${hlBase},0.05)`);
|
|
690
|
+
hlGrad.addColorStop(1, `rgba(${hlBase},0)`);
|
|
681
691
|
const savedOp = ctx.globalCompositeOperation;
|
|
682
692
|
ctx.globalCompositeOperation = "soft-light";
|
|
683
693
|
ctx.fillStyle = hlGrad;
|