git-hash-art 0.0.4 → 0.2.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/.kiro/steering/product.md +16 -0
- package/.kiro/steering/structure.md +40 -0
- package/.kiro/steering/tech.md +24 -0
- package/CHANGELOG.md +18 -1
- package/README.md +165 -57
- package/dist/browser.js +1183 -0
- package/dist/browser.js.map +1 -0
- package/dist/main.js +424 -330
- package/dist/main.js.map +1 -1
- package/dist/module.js +417 -326
- package/dist/module.js.map +1 -1
- package/dist/types.d.ts +2 -9
- package/dist/types.d.ts.map +1 -1
- package/package.json +47 -3
- package/src/__tests__/compatibility.test.ts +42 -0
- package/src/__tests__/generation.test.ts +58 -0
- package/src/__tests__/render.test.ts +81 -0
- package/src/__tests__/shapes.test.ts +71 -0
- package/src/browser.ts +105 -0
- package/src/index.ts +39 -200
- package/src/lib/canvas/colors.ts +90 -66
- package/src/lib/canvas/draw.ts +38 -7
- package/src/lib/canvas/shapes/basic.ts +1 -1
- package/src/lib/canvas/shapes/complex.ts +4 -1
- package/src/lib/render.ts +152 -0
- package/src/lib/utils.ts +33 -6
- package/src/types.ts +35 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { createCanvas } from "@napi-rs/canvas";
|
|
3
|
+
import { renderHashArt } from "../lib/render";
|
|
4
|
+
|
|
5
|
+
const TEST_HASH = "46192e59d42f741c761cbea79462a8b3815dd905";
|
|
6
|
+
|
|
7
|
+
function createTestCtx(width = 128, height = 128) {
|
|
8
|
+
const canvas = createCanvas(width, height);
|
|
9
|
+
return canvas.getContext("2d") as unknown as CanvasRenderingContext2D;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
describe("renderHashArt (core renderer)", () => {
|
|
13
|
+
it("renders without error on a provided context", () => {
|
|
14
|
+
const ctx = createTestCtx();
|
|
15
|
+
expect(() =>
|
|
16
|
+
renderHashArt(ctx, TEST_HASH, { width: 128, height: 128 }),
|
|
17
|
+
).not.toThrow();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("is deterministic — same hash + config produces identical pixel data", () => {
|
|
21
|
+
const canvas1 = createCanvas(64, 64);
|
|
22
|
+
const canvas2 = createCanvas(64, 64);
|
|
23
|
+
const config = { width: 64, height: 64, gridSize: 3 };
|
|
24
|
+
|
|
25
|
+
renderHashArt(
|
|
26
|
+
canvas1.getContext("2d") as unknown as CanvasRenderingContext2D,
|
|
27
|
+
TEST_HASH,
|
|
28
|
+
config,
|
|
29
|
+
);
|
|
30
|
+
renderHashArt(
|
|
31
|
+
canvas2.getContext("2d") as unknown as CanvasRenderingContext2D,
|
|
32
|
+
TEST_HASH,
|
|
33
|
+
config,
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
const buf1 = canvas1.toBuffer("image/png");
|
|
37
|
+
const buf2 = canvas2.toBuffer("image/png");
|
|
38
|
+
expect(buf1.equals(buf2)).toBe(true);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("different hashes produce different output", () => {
|
|
42
|
+
const canvas1 = createCanvas(64, 64);
|
|
43
|
+
const canvas2 = createCanvas(64, 64);
|
|
44
|
+
const config = { width: 64, height: 64, gridSize: 3 };
|
|
45
|
+
|
|
46
|
+
renderHashArt(
|
|
47
|
+
canvas1.getContext("2d") as unknown as CanvasRenderingContext2D,
|
|
48
|
+
TEST_HASH,
|
|
49
|
+
config,
|
|
50
|
+
);
|
|
51
|
+
renderHashArt(
|
|
52
|
+
canvas2.getContext("2d") as unknown as CanvasRenderingContext2D,
|
|
53
|
+
"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef",
|
|
54
|
+
config,
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const buf1 = canvas1.toBuffer("image/png");
|
|
58
|
+
const buf2 = canvas2.toBuffer("image/png");
|
|
59
|
+
expect(buf1.equals(buf2)).toBe(false);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("uses defaults when no config is provided", () => {
|
|
63
|
+
// Large default canvas — just verify it doesn't throw
|
|
64
|
+
const canvas = createCanvas(2048, 2048);
|
|
65
|
+
const ctx = canvas.getContext("2d") as unknown as CanvasRenderingContext2D;
|
|
66
|
+
expect(() => renderHashArt(ctx, TEST_HASH)).not.toThrow();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it("respects custom config values", () => {
|
|
70
|
+
const ctx = createTestCtx(256, 128);
|
|
71
|
+
expect(() =>
|
|
72
|
+
renderHashArt(ctx, TEST_HASH, {
|
|
73
|
+
width: 256,
|
|
74
|
+
height: 128,
|
|
75
|
+
layers: 2,
|
|
76
|
+
gridSize: 3,
|
|
77
|
+
baseOpacity: 0.5,
|
|
78
|
+
}),
|
|
79
|
+
).not.toThrow();
|
|
80
|
+
});
|
|
81
|
+
});
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { createCanvas } from "@napi-rs/canvas";
|
|
3
|
+
import { shapes } from "../lib/canvas/shapes";
|
|
4
|
+
import { drawShape, enhanceShapeGeneration } from "../lib/canvas/draw";
|
|
5
|
+
|
|
6
|
+
const TEST_HASH = "46192e59d42f741c761cbea79462a8b3815dd905";
|
|
7
|
+
|
|
8
|
+
function createTestCtx(size = 256) {
|
|
9
|
+
const canvas = createCanvas(size, size);
|
|
10
|
+
return canvas.getContext("2d") as unknown as CanvasRenderingContext2D;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
describe("all shapes render without crashing", () => {
|
|
14
|
+
const shapeNames = Object.keys(shapes);
|
|
15
|
+
|
|
16
|
+
it.each(shapeNames)("shape '%s' draws without error", (name) => {
|
|
17
|
+
const ctx = createTestCtx();
|
|
18
|
+
expect(() => shapes[name](ctx, 100)).not.toThrow();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it.each(shapeNames)("shape '%s' works through drawShape()", (name) => {
|
|
22
|
+
const ctx = createTestCtx();
|
|
23
|
+
expect(() =>
|
|
24
|
+
drawShape(ctx, name, 128, 128, {
|
|
25
|
+
fillColor: "#ff0000",
|
|
26
|
+
strokeColor: "#000000",
|
|
27
|
+
strokeWidth: 2,
|
|
28
|
+
size: 80,
|
|
29
|
+
rotation: 45,
|
|
30
|
+
}),
|
|
31
|
+
).not.toThrow();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it.each(shapeNames)(
|
|
35
|
+
"shape '%s' works through enhanceShapeGeneration()",
|
|
36
|
+
(name) => {
|
|
37
|
+
const ctx = createTestCtx();
|
|
38
|
+
expect(() =>
|
|
39
|
+
enhanceShapeGeneration(ctx, name, 128, 128, {
|
|
40
|
+
fillColor: "#ff0000",
|
|
41
|
+
strokeColor: "#000000",
|
|
42
|
+
strokeWidth: 2,
|
|
43
|
+
size: 80,
|
|
44
|
+
rotation: 45,
|
|
45
|
+
proportionType: "GOLDEN_RATIO",
|
|
46
|
+
}),
|
|
47
|
+
).not.toThrow();
|
|
48
|
+
},
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe("platonicSolid handles missing config.type gracefully", () => {
|
|
53
|
+
it("renders with no config at all", () => {
|
|
54
|
+
const ctx = createTestCtx();
|
|
55
|
+
expect(() => shapes["platonicSolid"](ctx, 100)).not.toThrow();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it("renders with explicit type", () => {
|
|
59
|
+
const ctx = createTestCtx();
|
|
60
|
+
expect(() =>
|
|
61
|
+
shapes["platonicSolid"](ctx, 100, { type: "tetrahedron" }),
|
|
62
|
+
).not.toThrow();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("renders with invalid type (falls back)", () => {
|
|
66
|
+
const ctx = createTestCtx();
|
|
67
|
+
expect(() =>
|
|
68
|
+
shapes["platonicSolid"](ctx, 100, { type: "nonexistent" }),
|
|
69
|
+
).not.toThrow();
|
|
70
|
+
});
|
|
71
|
+
});
|
package/src/browser.ts
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser entry point for git-hash-art.
|
|
3
|
+
*
|
|
4
|
+
* This module has zero Node.js dependencies — it works with a standard
|
|
5
|
+
* HTMLCanvasElement or OffscreenCanvas and the native Canvas 2D API.
|
|
6
|
+
*/
|
|
7
|
+
import { renderHashArt } from "./lib/render";
|
|
8
|
+
import type { GenerationConfig } from "./types";
|
|
9
|
+
import { DEFAULT_CONFIG } from "./types";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Render hash-derived art directly onto an HTMLCanvasElement.
|
|
13
|
+
*
|
|
14
|
+
* The canvas should already have the desired width/height set.
|
|
15
|
+
* Config width/height will be inferred from the canvas if not provided.
|
|
16
|
+
*
|
|
17
|
+
* @param canvas - An HTMLCanvasElement (or OffscreenCanvas)
|
|
18
|
+
* @param gitHash - Hex hash string used as the deterministic seed
|
|
19
|
+
* @param config - Partial generation config (merged with defaults)
|
|
20
|
+
*/
|
|
21
|
+
function renderToCanvas(
|
|
22
|
+
canvas: HTMLCanvasElement | OffscreenCanvas,
|
|
23
|
+
gitHash: string,
|
|
24
|
+
config: Partial<GenerationConfig> = {},
|
|
25
|
+
): void {
|
|
26
|
+
const ctx = canvas.getContext("2d") as CanvasRenderingContext2D;
|
|
27
|
+
if (!ctx) {
|
|
28
|
+
throw new Error("Failed to get 2D rendering context from canvas");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const finalConfig: Partial<GenerationConfig> = {
|
|
32
|
+
width: canvas.width,
|
|
33
|
+
height: canvas.height,
|
|
34
|
+
...config,
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
renderHashArt(ctx, gitHash, finalConfig);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Render hash-derived art and return it as a Blob (browser-native).
|
|
42
|
+
*
|
|
43
|
+
* @param gitHash - Hex hash string used as the deterministic seed
|
|
44
|
+
* @param config - Partial generation config (merged with defaults)
|
|
45
|
+
* @returns A Promise that resolves to a PNG Blob
|
|
46
|
+
*/
|
|
47
|
+
async function generateImageBlob(
|
|
48
|
+
gitHash: string,
|
|
49
|
+
config: Partial<GenerationConfig> = {},
|
|
50
|
+
): Promise<Blob> {
|
|
51
|
+
const finalConfig: GenerationConfig = { ...DEFAULT_CONFIG, ...config };
|
|
52
|
+
const { width, height } = finalConfig;
|
|
53
|
+
|
|
54
|
+
const canvas = new OffscreenCanvas(width, height);
|
|
55
|
+
const ctx = canvas.getContext("2d") as OffscreenCanvasRenderingContext2D;
|
|
56
|
+
if (!ctx) {
|
|
57
|
+
throw new Error("Failed to get 2D rendering context from OffscreenCanvas");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
renderHashArt(
|
|
61
|
+
ctx as unknown as CanvasRenderingContext2D,
|
|
62
|
+
gitHash,
|
|
63
|
+
finalConfig,
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
return canvas.convertToBlob({ type: "image/png" });
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Render hash-derived art and return it as a data URL string.
|
|
71
|
+
*
|
|
72
|
+
* @param gitHash - Hex hash string used as the deterministic seed
|
|
73
|
+
* @param config - Partial generation config (merged with defaults)
|
|
74
|
+
* @returns A data:image/png;base64,… string
|
|
75
|
+
*/
|
|
76
|
+
function generateDataURL(
|
|
77
|
+
gitHash: string,
|
|
78
|
+
config: Partial<GenerationConfig> = {},
|
|
79
|
+
): string {
|
|
80
|
+
const finalConfig: GenerationConfig = { ...DEFAULT_CONFIG, ...config };
|
|
81
|
+
const { width, height } = finalConfig;
|
|
82
|
+
|
|
83
|
+
const canvas = document.createElement("canvas");
|
|
84
|
+
canvas.width = width;
|
|
85
|
+
canvas.height = height;
|
|
86
|
+
|
|
87
|
+
const ctx = canvas.getContext("2d");
|
|
88
|
+
if (!ctx) {
|
|
89
|
+
throw new Error("Failed to get 2D rendering context");
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
renderHashArt(ctx, gitHash, finalConfig);
|
|
93
|
+
|
|
94
|
+
return canvas.toDataURL("image/png");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export {
|
|
98
|
+
renderToCanvas,
|
|
99
|
+
generateImageBlob,
|
|
100
|
+
generateDataURL,
|
|
101
|
+
renderHashArt,
|
|
102
|
+
};
|
|
103
|
+
export { PRESETS } from "./lib/constants";
|
|
104
|
+
export type { GenerationConfig } from "./types";
|
|
105
|
+
export { DEFAULT_CONFIG } from "./types";
|
package/src/index.ts
CHANGED
|
@@ -1,208 +1,54 @@
|
|
|
1
|
-
import { createCanvas } from "canvas";
|
|
2
|
-
import fs from "fs";
|
|
3
|
-
import path from "path";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { getRandomFromHash } from "./lib/utils";
|
|
1
|
+
import { createCanvas } from "@napi-rs/canvas";
|
|
2
|
+
import * as fs from "fs";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
import { renderHashArt } from "./lib/render";
|
|
5
|
+
import type { GenerationConfig } from "./types";
|
|
6
|
+
import { DEFAULT_CONFIG } from "./types";
|
|
8
7
|
|
|
9
8
|
/**
|
|
10
|
-
* Generate an abstract art
|
|
11
|
-
*
|
|
12
|
-
* @
|
|
13
|
-
*
|
|
9
|
+
* Generate an abstract art PNG buffer from a git hash (Node.js only).
|
|
10
|
+
*
|
|
11
|
+
* Uses @napi-rs/canvas under the hood to create an off-screen canvas,
|
|
12
|
+
* renders the hash-derived art, and returns the result as a PNG Buffer.
|
|
13
|
+
*
|
|
14
|
+
* @param gitHash - Hex hash string used as the deterministic seed
|
|
15
|
+
* @param config - Partial generation config (merged with defaults)
|
|
16
|
+
* @returns PNG buffer of the generated image
|
|
14
17
|
*/
|
|
15
|
-
function generateImageFromHash(
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
layers: 2,
|
|
22
|
-
minShapeSize: 20,
|
|
23
|
-
maxShapeSize: 600,
|
|
24
|
-
baseOpacity: 0.8,
|
|
25
|
-
opacityReduction: 0.4,
|
|
26
|
-
shapesPerLayer: 0,
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
// Merge provided config with defaults
|
|
30
|
-
const finalConfig = { ...defaultConfig, ...config };
|
|
31
|
-
const {
|
|
32
|
-
width,
|
|
33
|
-
height,
|
|
34
|
-
gridSize,
|
|
35
|
-
layers,
|
|
36
|
-
minShapeSize,
|
|
37
|
-
maxShapeSize,
|
|
38
|
-
baseOpacity,
|
|
39
|
-
opacityReduction,
|
|
40
|
-
} = finalConfig;
|
|
41
|
-
|
|
42
|
-
// Calculate shapes per layer based on grid size if not provided
|
|
43
|
-
finalConfig.shapesPerLayer =
|
|
44
|
-
finalConfig.shapesPerLayer || Math.floor(gridSize * gridSize * 1.5);
|
|
18
|
+
function generateImageFromHash(
|
|
19
|
+
gitHash: string,
|
|
20
|
+
config: Partial<GenerationConfig> = {},
|
|
21
|
+
): Buffer {
|
|
22
|
+
const finalConfig: GenerationConfig = { ...DEFAULT_CONFIG, ...config };
|
|
23
|
+
const { width, height } = finalConfig;
|
|
45
24
|
|
|
46
25
|
const canvas = createCanvas(width, height);
|
|
47
26
|
const ctx = canvas.getContext("2d") as unknown as CanvasRenderingContext2D;
|
|
48
27
|
|
|
49
|
-
|
|
50
|
-
const colors = colorScheme.getColorPalette("chakra");
|
|
51
|
-
|
|
52
|
-
// Create a gradient background
|
|
53
|
-
const gradient = ctx.createLinearGradient(0, 0, width, height);
|
|
54
|
-
gradient.addColorStop(0, colorScheme.baseScheme[0]);
|
|
55
|
-
gradient.addColorStop(1, colorScheme.baseScheme[1]);
|
|
56
|
-
ctx.fillStyle = gradient;
|
|
57
|
-
ctx.fillRect(0, 0, width, height);
|
|
58
|
-
|
|
59
|
-
const shapeNames = Object.keys(shapes);
|
|
60
|
-
|
|
61
|
-
const cellWidth = width / gridSize;
|
|
62
|
-
const cellHeight = height / gridSize;
|
|
63
|
-
|
|
64
|
-
// Scale shape sizes based on canvas dimensions
|
|
65
|
-
const scaleFactor = Math.min(width, height) / 1024;
|
|
66
|
-
const adjustedMinSize = minShapeSize * scaleFactor;
|
|
67
|
-
const adjustedMaxSize = maxShapeSize * scaleFactor;
|
|
68
|
-
|
|
69
|
-
for (let layer = 0; layer < layers; layer++) {
|
|
70
|
-
const numShapes =
|
|
71
|
-
finalConfig.shapesPerLayer +
|
|
72
|
-
Math.floor(
|
|
73
|
-
getRandomFromHash(gitHash, layer, 0, finalConfig.shapesPerLayer / 2),
|
|
74
|
-
);
|
|
75
|
-
const layerOpacity = baseOpacity - layer * opacityReduction;
|
|
76
|
-
|
|
77
|
-
for (let i = 0; i < numShapes; i++) {
|
|
78
|
-
const gridX = Math.floor(i / gridSize);
|
|
79
|
-
const gridY = i % gridSize;
|
|
80
|
-
|
|
81
|
-
const cellOffsetX = getRandomFromHash(
|
|
82
|
-
gitHash,
|
|
83
|
-
layer * numShapes + i * 2,
|
|
84
|
-
0,
|
|
85
|
-
cellWidth,
|
|
86
|
-
);
|
|
87
|
-
const cellOffsetY = getRandomFromHash(
|
|
88
|
-
gitHash,
|
|
89
|
-
layer * numShapes + i * 2 + 1,
|
|
90
|
-
0,
|
|
91
|
-
cellHeight,
|
|
92
|
-
);
|
|
93
|
-
|
|
94
|
-
const x = gridX * cellWidth + cellOffsetX;
|
|
95
|
-
const y = gridY * cellHeight + cellOffsetY;
|
|
96
|
-
|
|
97
|
-
const shape =
|
|
98
|
-
shapeNames[
|
|
99
|
-
Math.floor(
|
|
100
|
-
getRandomFromHash(
|
|
101
|
-
gitHash,
|
|
102
|
-
layer * numShapes + i * 3,
|
|
103
|
-
0,
|
|
104
|
-
shapeNames.length,
|
|
105
|
-
),
|
|
106
|
-
)
|
|
107
|
-
];
|
|
108
|
-
const size =
|
|
109
|
-
adjustedMinSize +
|
|
110
|
-
getRandomFromHash(
|
|
111
|
-
gitHash,
|
|
112
|
-
layer * numShapes + i * 4,
|
|
113
|
-
0,
|
|
114
|
-
adjustedMaxSize - adjustedMinSize,
|
|
115
|
-
);
|
|
116
|
-
const rotation = getRandomFromHash(
|
|
117
|
-
gitHash,
|
|
118
|
-
layer * numShapes + i * 5,
|
|
119
|
-
0,
|
|
120
|
-
360,
|
|
121
|
-
);
|
|
122
|
-
|
|
123
|
-
const fillColorIndex = Math.floor(
|
|
124
|
-
getRandomFromHash(
|
|
125
|
-
gitHash,
|
|
126
|
-
layer * numShapes + i * 6,
|
|
127
|
-
0,
|
|
128
|
-
Object.keys(colors).length,
|
|
129
|
-
),
|
|
130
|
-
);
|
|
131
|
-
const strokeColorIndex = Math.floor(
|
|
132
|
-
getRandomFromHash(
|
|
133
|
-
gitHash,
|
|
134
|
-
layer * numShapes + i * 7,
|
|
135
|
-
0,
|
|
136
|
-
Object.keys(colors).length,
|
|
137
|
-
),
|
|
138
|
-
);
|
|
139
|
-
|
|
140
|
-
ctx.globalAlpha = layerOpacity;
|
|
141
|
-
// drawShape(
|
|
142
|
-
// ctx,
|
|
143
|
-
// shape,
|
|
144
|
-
// x,
|
|
145
|
-
// y,
|
|
146
|
-
// colors[fillColorIndex],
|
|
147
|
-
// colors[strokeColorIndex],
|
|
148
|
-
// 2 * scaleFactor,
|
|
149
|
-
// size,
|
|
150
|
-
// rotation
|
|
151
|
-
// );
|
|
152
|
-
|
|
153
|
-
enhanceShapeGeneration(ctx, shape, x, y, {
|
|
154
|
-
fillColor: Object.values(colors)[fillColorIndex],
|
|
155
|
-
strokeColor: Object.values(colors)[strokeColorIndex],
|
|
156
|
-
strokeWidth: 1.5 * scaleFactor,
|
|
157
|
-
size,
|
|
158
|
-
rotation,
|
|
159
|
-
// Optionally add pattern combinations
|
|
160
|
-
// patterns:
|
|
161
|
-
// Math.random() > 0.7 ? PatternPresets.cosmicTree(size) : [],
|
|
162
|
-
proportionType: "GOLDEN_RATIO",
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Add connecting lines scaled to canvas size
|
|
167
|
-
ctx.globalAlpha = 0.2;
|
|
168
|
-
ctx.strokeStyle = Object.values(colors)[Object.keys(colors).length - 1];
|
|
169
|
-
ctx.lineWidth = 1 * scaleFactor;
|
|
170
|
-
|
|
171
|
-
const numLines = Math.floor((15 * (width * height)) / (1024 * 1024));
|
|
172
|
-
for (let i = 0; i < numLines; i++) {
|
|
173
|
-
const x1 = getRandomFromHash(gitHash, i * 4, 0, width);
|
|
174
|
-
const y1 = getRandomFromHash(gitHash, i * 4 + 1, 0, height);
|
|
175
|
-
const x2 = getRandomFromHash(gitHash, i * 4 + 2, 0, width);
|
|
176
|
-
const y2 = getRandomFromHash(gitHash, i * 4 + 3, 0, height);
|
|
177
|
-
|
|
178
|
-
ctx.beginPath();
|
|
179
|
-
ctx.moveTo(x1, y1);
|
|
180
|
-
ctx.lineTo(x2, y2);
|
|
181
|
-
ctx.stroke();
|
|
182
|
-
}
|
|
183
|
-
}
|
|
28
|
+
renderHashArt(ctx, gitHash, finalConfig);
|
|
184
29
|
|
|
185
30
|
return canvas.toBuffer("image/png");
|
|
186
31
|
}
|
|
187
32
|
|
|
188
33
|
/**
|
|
189
|
-
* Save the generated image to a file
|
|
190
|
-
*
|
|
191
|
-
* @param
|
|
192
|
-
* @param
|
|
193
|
-
* @param
|
|
194
|
-
* @param
|
|
195
|
-
* @param
|
|
196
|
-
* @
|
|
34
|
+
* Save the generated image to a file (Node.js only).
|
|
35
|
+
*
|
|
36
|
+
* @param imageBuffer - The PNG buffer of the generated image
|
|
37
|
+
* @param outputDir - The directory to save the image
|
|
38
|
+
* @param gitHash - The git hash used to generate the image
|
|
39
|
+
* @param label - Optional label for the output filename
|
|
40
|
+
* @param width - The width of the generated image
|
|
41
|
+
* @param height - The height of the generated image
|
|
42
|
+
* @returns Path to the saved image
|
|
197
43
|
*/
|
|
198
44
|
function saveImageToFile(
|
|
199
|
-
imageBuffer:
|
|
45
|
+
imageBuffer: Buffer,
|
|
200
46
|
outputDir: string,
|
|
201
|
-
gitHash: string
|
|
47
|
+
gitHash: string,
|
|
202
48
|
label = "",
|
|
203
|
-
width:
|
|
204
|
-
height:
|
|
205
|
-
) {
|
|
49
|
+
width: number,
|
|
50
|
+
height: number,
|
|
51
|
+
): string {
|
|
206
52
|
if (!fs.existsSync(outputDir)) {
|
|
207
53
|
fs.mkdirSync(outputDir, { recursive: true });
|
|
208
54
|
}
|
|
@@ -218,14 +64,7 @@ function saveImageToFile(
|
|
|
218
64
|
return outputPath;
|
|
219
65
|
}
|
|
220
66
|
|
|
221
|
-
export { generateImageFromHash, saveImageToFile };
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
import { generateImageFromHash, saveImageToFile } from 'git-hash-art';
|
|
226
|
-
|
|
227
|
-
const gitHash = '1234567890abcdef1234567890abcdef12345678';
|
|
228
|
-
const imageBuffer = generateImageFromHash(gitHash, { width: 1024, height: 1024 });
|
|
229
|
-
const savedImagePath = saveImageToFile(imageBuffer, './output', gitHash, 'example', 1024, 1024);
|
|
230
|
-
console.log(`Image saved to: ${savedImagePath}`);
|
|
231
|
-
*/
|
|
67
|
+
export { generateImageFromHash, saveImageToFile, renderHashArt };
|
|
68
|
+
export { PRESETS } from "./lib/constants";
|
|
69
|
+
export type { GenerationConfig } from "./types";
|
|
70
|
+
export { DEFAULT_CONFIG } from "./types";
|