git-hash-art 0.0.1
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/.release-it.json +17 -0
- package/README.md +138 -0
- package/bin/generateExamples.js +59 -0
- package/dist/main.js +974 -0
- package/dist/main.js.map +1 -0
- package/eslint.config.mjs +29 -0
- package/examples/angular-1024x1024-f31a6c3e.png +0 -0
- package/examples/banner-1920x480-d847ffd4.png +0 -0
- package/examples/complex-2048x2048-deadbeef.png +0 -0
- package/examples/instagram-square-1080x1080-ff00ff00.png +0 -0
- package/examples/instagram-story-1080x1920-abc123de.png +0 -0
- package/examples/linkedin-banner-1584x396-bbbbbbbb.png +0 -0
- package/examples/minimal-1024x1024-00000000.png +0 -0
- package/examples/phone-wallpaper-1170x2532-ffffffff.png +0 -0
- package/examples/react-1024x1024-46192e59.png +0 -0
- package/examples/tablet-wallpaper-2048x2732-12345678.png +0 -0
- package/examples/twitter-header-1500x500-77777777.png +0 -0
- package/examples/ultrawide-3440x1440-a3e126e5.png +0 -0
- package/package.json +32 -0
- package/src/index.js +226 -0
- package/src/lib/canvas/colors.js +102 -0
- package/src/lib/canvas/draw.js +73 -0
- package/src/lib/canvas/shapes/basic.js +93 -0
- package/src/lib/canvas/shapes/complex.js +205 -0
- package/src/lib/canvas/shapes/index.js +9 -0
- package/src/lib/canvas/shapes/sacred.js +173 -0
- package/src/lib/canvas/shapes/utils.js +37 -0
- package/src/lib/constants.js +138 -0
- package/src/lib/utils.js +59 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
export const drawCircle = (ctx, size) => {
|
|
2
|
+
ctx.beginPath();
|
|
3
|
+
ctx.arc(0, 0, size / 2, 0, Math.PI * 2);
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export const drawSquare = (ctx, size) => {
|
|
7
|
+
ctx.beginPath();
|
|
8
|
+
ctx.rect(-size / 2, -size / 2, size, size);
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const drawTriangle = (ctx, size) => {
|
|
12
|
+
ctx.beginPath();
|
|
13
|
+
ctx.moveTo(0, -size / 2);
|
|
14
|
+
ctx.lineTo(-size / 2, size / 2);
|
|
15
|
+
ctx.lineTo(size / 2, size / 2);
|
|
16
|
+
ctx.closePath();
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const drawHexagon = (ctx, size) => {
|
|
20
|
+
ctx.beginPath();
|
|
21
|
+
for (let i = 0; i < 6; i++) {
|
|
22
|
+
const angle = (Math.PI / 8) * i;
|
|
23
|
+
const x = (size / 2) * Math.cos(angle);
|
|
24
|
+
const y = (size / 2) * Math.sin(angle);
|
|
25
|
+
if (i === 0) ctx.moveTo(x, y);
|
|
26
|
+
else ctx.lineTo(x, y);
|
|
27
|
+
}
|
|
28
|
+
ctx.closePath();
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const drawStar = (ctx, size) => {
|
|
32
|
+
ctx.beginPath();
|
|
33
|
+
for (let i = 0; i < 10; i++) {
|
|
34
|
+
const angle = Math.PI / 5 + (Math.PI / 5) * i * 3;
|
|
35
|
+
const radius = i % 2 === 0 ? size / 2 : size / 4;
|
|
36
|
+
const x = radius * Math.cos(angle);
|
|
37
|
+
const y = radius * Math.sin(angle);
|
|
38
|
+
if (i === 0) ctx.moveTo(x, y);
|
|
39
|
+
else ctx.lineTo(x, y);
|
|
40
|
+
}
|
|
41
|
+
ctx.closePath();
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export const drawJackedStar = (ctx, size) => {
|
|
45
|
+
ctx.beginPath();
|
|
46
|
+
for (let i = 0; i < 10; i++) {
|
|
47
|
+
const angle = Math.PI / 30 + (Math.PI / 30) * i * 8;
|
|
48
|
+
const radius = i % 2 === 0 ? size / 2 : size / 8;
|
|
49
|
+
const x = radius * Math.cos(angle);
|
|
50
|
+
const y = radius * Math.sin(angle);
|
|
51
|
+
if (i === 0) ctx.moveTo(x, y);
|
|
52
|
+
else ctx.lineTo(x, y);
|
|
53
|
+
}
|
|
54
|
+
ctx.closePath();
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export const drawHeart = (ctx, size) => {
|
|
58
|
+
ctx.beginPath();
|
|
59
|
+
ctx.moveTo(0, size / 4);
|
|
60
|
+
ctx.quadraticCurveTo(size / 2, size / 4, 0, -size / 4);
|
|
61
|
+
ctx.quadraticCurveTo(-size / 2, size / 4, 0, size / 4);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export const drawDiamond = (ctx, size) => {
|
|
65
|
+
ctx.beginPath();
|
|
66
|
+
ctx.moveTo(0, -size / 2);
|
|
67
|
+
ctx.lineTo(size / 2, 0);
|
|
68
|
+
ctx.lineTo(0, size / 2);
|
|
69
|
+
ctx.lineTo(-size / 2, 0);
|
|
70
|
+
ctx.closePath();
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export const drawCube = (ctx, size) => {
|
|
74
|
+
ctx.beginPath();
|
|
75
|
+
ctx.moveTo(-size / 2, -size / 2);
|
|
76
|
+
ctx.lineTo(size / 2, -size / 2);
|
|
77
|
+
ctx.lineTo(size / 2, size / 2);
|
|
78
|
+
ctx.lineTo(-size / 2, size / 2);
|
|
79
|
+
ctx.closePath();
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// Optional: Create a shape map for easier lookup
|
|
83
|
+
export const basicShapes = {
|
|
84
|
+
circle: drawCircle,
|
|
85
|
+
square: drawSquare,
|
|
86
|
+
triangle: drawTriangle,
|
|
87
|
+
hexagon: drawHexagon,
|
|
88
|
+
star: drawStar,
|
|
89
|
+
"jacked-star": drawJackedStar,
|
|
90
|
+
heart: drawHeart,
|
|
91
|
+
diamond: drawDiamond,
|
|
92
|
+
cube: drawCube
|
|
93
|
+
};
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { defaultShapeConfig } from "../../constants";
|
|
2
|
+
import { applyTransforms, createCirclePoints, restoreContext } from "./utils";
|
|
3
|
+
|
|
4
|
+
export const ShapeConfigs = {
|
|
5
|
+
platonic: {
|
|
6
|
+
tetrahedron: { vertices: 4, faces: 4 },
|
|
7
|
+
cube: { vertices: 8, faces: 6 },
|
|
8
|
+
octahedron: { vertices: 6, faces: 8 },
|
|
9
|
+
dodecahedron: { vertices: 20, faces: 12 },
|
|
10
|
+
icosahedron: { vertices: 12, faces: 20 },
|
|
11
|
+
},
|
|
12
|
+
fibonacci: {
|
|
13
|
+
iterations: 13,
|
|
14
|
+
growthFactor: 1.618034, // Golden ratio
|
|
15
|
+
},
|
|
16
|
+
goldenRatio: {
|
|
17
|
+
iterations: 8,
|
|
18
|
+
ratio: 1.618034,
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const drawPlatonicSolid = (ctx, size, type, config = {}) => {
|
|
23
|
+
const finalConfig = { ...defaultShapeConfig, ...config };
|
|
24
|
+
applyTransforms(ctx, size, finalConfig);
|
|
25
|
+
|
|
26
|
+
const {
|
|
27
|
+
vertices,
|
|
28
|
+
// faces
|
|
29
|
+
} = ShapeConfigs.platonic[type];
|
|
30
|
+
const radius = size / 2;
|
|
31
|
+
|
|
32
|
+
// Calculate vertices based on platonic solid type
|
|
33
|
+
const points = createCirclePoints(0, 0, radius, vertices);
|
|
34
|
+
|
|
35
|
+
ctx.beginPath();
|
|
36
|
+
// Draw edges between vertices
|
|
37
|
+
points.forEach((p1, i) => {
|
|
38
|
+
points.slice(i + 1).forEach((p2) => {
|
|
39
|
+
ctx.moveTo(p1.x, p1.y);
|
|
40
|
+
ctx.lineTo(p2.x, p2.y);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
if (finalConfig.fillStyle !== "transparent") {
|
|
45
|
+
ctx.fill();
|
|
46
|
+
}
|
|
47
|
+
ctx.stroke();
|
|
48
|
+
restoreContext(ctx);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const drawFibonacciSpiral = (ctx, size, config = {}) => {
|
|
52
|
+
const finalConfig = {
|
|
53
|
+
...defaultShapeConfig,
|
|
54
|
+
...ShapeConfigs.fibonacci,
|
|
55
|
+
...config,
|
|
56
|
+
};
|
|
57
|
+
applyTransforms(ctx, size, finalConfig);
|
|
58
|
+
|
|
59
|
+
let current = 1;
|
|
60
|
+
let previous = 1;
|
|
61
|
+
let scale = size / Math.pow(finalConfig.growthFactor, finalConfig.iterations);
|
|
62
|
+
|
|
63
|
+
ctx.beginPath();
|
|
64
|
+
for (let i = 0; i < finalConfig.iterations; i++) {
|
|
65
|
+
const radius = scale * current;
|
|
66
|
+
const centerX = radius / 2;
|
|
67
|
+
const centerY = radius / 2;
|
|
68
|
+
|
|
69
|
+
ctx.arc(centerX, centerY, radius, Math.PI, Math.PI * 1.5);
|
|
70
|
+
|
|
71
|
+
// Calculate next Fibonacci number
|
|
72
|
+
const next = current + previous;
|
|
73
|
+
previous = current;
|
|
74
|
+
current = next;
|
|
75
|
+
|
|
76
|
+
// Transform for next iteration
|
|
77
|
+
ctx.translate(radius, 0);
|
|
78
|
+
ctx.rotate(Math.PI / 2);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
ctx.stroke();
|
|
82
|
+
restoreContext(ctx);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export const drawIslamicPattern = (ctx, size, config = {}) => {
|
|
86
|
+
const finalConfig = { ...defaultShapeConfig, ...config };
|
|
87
|
+
applyTransforms(ctx, size, finalConfig);
|
|
88
|
+
|
|
89
|
+
const gridSize = 8;
|
|
90
|
+
const unit = size / gridSize;
|
|
91
|
+
|
|
92
|
+
ctx.beginPath();
|
|
93
|
+
// Create base grid
|
|
94
|
+
for (let i = 0; i <= gridSize; i++) {
|
|
95
|
+
for (let j = 0; j <= gridSize; j++) {
|
|
96
|
+
const x = (i - gridSize / 2) * unit;
|
|
97
|
+
const y = (j - gridSize / 2) * unit;
|
|
98
|
+
|
|
99
|
+
// Draw star pattern at each intersection
|
|
100
|
+
const radius = unit / 2;
|
|
101
|
+
for (let k = 0; k < 8; k++) {
|
|
102
|
+
const angle = (k / 8) * Math.PI * 2;
|
|
103
|
+
const x1 = x + Math.cos(angle) * radius;
|
|
104
|
+
const y1 = y + Math.sin(angle) * radius;
|
|
105
|
+
if (k === 0) {
|
|
106
|
+
ctx.moveTo(x1, y1);
|
|
107
|
+
} else {
|
|
108
|
+
ctx.lineTo(x1, y1);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
ctx.closePath();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (finalConfig.fillStyle !== "transparent") {
|
|
116
|
+
ctx.fill();
|
|
117
|
+
}
|
|
118
|
+
ctx.stroke();
|
|
119
|
+
restoreContext(ctx);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export const drawCelticKnot = (ctx, size, config = {}) => {
|
|
123
|
+
const finalConfig = { ...defaultShapeConfig, ...config };
|
|
124
|
+
applyTransforms(ctx, size, finalConfig);
|
|
125
|
+
|
|
126
|
+
const gridSize = 4;
|
|
127
|
+
const unit = size / gridSize;
|
|
128
|
+
|
|
129
|
+
const drawKnotSegment = (x, y, type) => {
|
|
130
|
+
ctx.beginPath();
|
|
131
|
+
switch (type) {
|
|
132
|
+
case "over":
|
|
133
|
+
ctx.moveTo(x, y);
|
|
134
|
+
ctx.bezierCurveTo(
|
|
135
|
+
x + unit / 2,
|
|
136
|
+
y,
|
|
137
|
+
x + unit / 2,
|
|
138
|
+
y + unit,
|
|
139
|
+
x + unit,
|
|
140
|
+
y + unit
|
|
141
|
+
);
|
|
142
|
+
break;
|
|
143
|
+
case "under":
|
|
144
|
+
ctx.moveTo(x, y + unit);
|
|
145
|
+
ctx.bezierCurveTo(x + unit / 2, y + unit, x + unit / 2, y, x + unit, y);
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
ctx.stroke();
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
// Create knot pattern
|
|
152
|
+
for (let i = 0; i < gridSize; i++) {
|
|
153
|
+
for (let j = 0; j < gridSize; j++) {
|
|
154
|
+
const x = (i - gridSize / 2) * unit;
|
|
155
|
+
const y = (j - gridSize / 2) * unit;
|
|
156
|
+
drawKnotSegment(x, y, (i + j) % 2 === 0 ? "over" : "under");
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
restoreContext(ctx);
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
export const drawMerkaba = (ctx, size, config = {}) => {
|
|
164
|
+
const finalConfig = { ...defaultShapeConfig, ...config };
|
|
165
|
+
applyTransforms(ctx, size, finalConfig);
|
|
166
|
+
|
|
167
|
+
const radius = size / 2;
|
|
168
|
+
|
|
169
|
+
// Draw two intersecting tetrahedra
|
|
170
|
+
ctx.beginPath();
|
|
171
|
+
// First tetrahedron
|
|
172
|
+
const points1 = createCirclePoints(0, 0, radius, 3);
|
|
173
|
+
points1.forEach((p1, i) => {
|
|
174
|
+
points1.slice(i + 1).forEach((p2) => {
|
|
175
|
+
ctx.moveTo(p1.x, p1.y);
|
|
176
|
+
ctx.lineTo(p2.x, p2.y);
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// Second tetrahedron (rotated)
|
|
181
|
+
ctx.rotate(Math.PI / 6);
|
|
182
|
+
const points2 = createCirclePoints(0, 0, radius, 3);
|
|
183
|
+
points2.forEach((p1, i) => {
|
|
184
|
+
points2.slice(i + 1).forEach((p2) => {
|
|
185
|
+
ctx.moveTo(p1.x, p1.y);
|
|
186
|
+
ctx.lineTo(p2.x, p2.y);
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
if (finalConfig.fillStyle !== "transparent") {
|
|
191
|
+
ctx.fill();
|
|
192
|
+
}
|
|
193
|
+
ctx.stroke();
|
|
194
|
+
restoreContext(ctx);
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
export const complexShapes = {
|
|
198
|
+
platonicSolid: (ctx, size, type = "tetrahedron", config) =>
|
|
199
|
+
drawPlatonicSolid(ctx, size, type, config),
|
|
200
|
+
fibonacciSpiral: (ctx, size, config) =>
|
|
201
|
+
drawFibonacciSpiral(ctx, size, config),
|
|
202
|
+
islamicPattern: (ctx, size, config) => drawIslamicPattern(ctx, size, config),
|
|
203
|
+
celticKnot: (ctx, size, config) => drawCelticKnot(ctx, size, config),
|
|
204
|
+
merkaba: (ctx, size, config) => drawMerkaba(ctx, size, config),
|
|
205
|
+
};
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { createCirclePoints } from "./utils";
|
|
2
|
+
|
|
3
|
+
export const drawFlowerOfLife = (ctx, size) => {
|
|
4
|
+
const radius = size / 6;
|
|
5
|
+
const centers = [
|
|
6
|
+
{ x: 0, y: 0 },
|
|
7
|
+
{ x: radius * Math.sqrt(3), y: 0 },
|
|
8
|
+
{ x: radius * Math.sqrt(3) / 2, y: 1.5 * radius },
|
|
9
|
+
{ x: -radius * Math.sqrt(3) / 2, y: 1.5 * radius },
|
|
10
|
+
{ x: -radius * Math.sqrt(3), y: 0 },
|
|
11
|
+
{ x: -radius * Math.sqrt(3) / 2, y: -1.5 * radius },
|
|
12
|
+
{ x: radius * Math.sqrt(3) / 2, y: -1.5 * radius }
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
ctx.beginPath();
|
|
16
|
+
centers.forEach(center => {
|
|
17
|
+
ctx.moveTo(center.x + radius, center.y);
|
|
18
|
+
ctx.arc(center.x, center.y, radius, 0, Math.PI * 2);
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const drawTreeOfLife = (ctx, size) => {
|
|
23
|
+
const radius = size / 12;
|
|
24
|
+
const spacing = radius * 2.5;
|
|
25
|
+
|
|
26
|
+
// Sephirot positions (traditional layout)
|
|
27
|
+
const positions = [
|
|
28
|
+
{ x: 0, y: -spacing * 2 }, // Kether
|
|
29
|
+
{ x: -spacing, y: -spacing }, // Chokmah
|
|
30
|
+
{ x: spacing, y: -spacing }, // Binah
|
|
31
|
+
{ x: -spacing, y: 0 }, // Chesed
|
|
32
|
+
{ x: spacing, y: 0 }, // Geburah
|
|
33
|
+
{ x: 0, y: 0 }, // Tiphereth
|
|
34
|
+
{ x: -spacing, y: spacing }, // Netzach
|
|
35
|
+
{ x: spacing, y: spacing }, // Hod
|
|
36
|
+
{ x: 0, y: spacing * 2 }, // Yesod
|
|
37
|
+
{ x: 0, y: spacing * 3 } // Malkuth
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
// Draw circles
|
|
41
|
+
ctx.beginPath();
|
|
42
|
+
positions.forEach(pos => {
|
|
43
|
+
ctx.moveTo(pos.x + radius, pos.y);
|
|
44
|
+
ctx.arc(pos.x, pos.y, radius, 0, Math.PI * 2);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Draw connecting lines
|
|
48
|
+
ctx.moveTo(positions[0].x, positions[0].y);
|
|
49
|
+
positions.forEach((pos, i) => {
|
|
50
|
+
if (i > 0) {
|
|
51
|
+
positions.slice(i + 1).forEach(nextPos => {
|
|
52
|
+
ctx.moveTo(pos.x, pos.y);
|
|
53
|
+
ctx.lineTo(nextPos.x, nextPos.y);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export const drawMetatronsCube = (ctx, size) => {
|
|
60
|
+
const radius = size / 3;
|
|
61
|
+
|
|
62
|
+
// Create 13 points - one center and 12 vertices of an icosahedron
|
|
63
|
+
|
|
64
|
+
// const phi = (1 + Math.sqrt(5)) / 2; // Golden ratio
|
|
65
|
+
|
|
66
|
+
const vertices = [
|
|
67
|
+
{ x: 0, y: 0 }, // Center point
|
|
68
|
+
...createCirclePoints(0, 0, radius, 6), // Inner hexagon
|
|
69
|
+
...createCirclePoints(0, 0, radius * 1.5, 6) // Outer hexagon
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
ctx.beginPath();
|
|
73
|
+
// Draw all connecting lines
|
|
74
|
+
vertices.forEach((v1, i) => {
|
|
75
|
+
vertices.slice(i + 1).forEach(v2 => {
|
|
76
|
+
ctx.moveTo(v1.x, v1.y);
|
|
77
|
+
ctx.lineTo(v2.x, v2.y);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export const drawSriYantra = (ctx, size) => {
|
|
83
|
+
const radius = size / 2;
|
|
84
|
+
ctx.beginPath();
|
|
85
|
+
|
|
86
|
+
// Draw outer triangles
|
|
87
|
+
for (let i = 0; i < 9; i++) {
|
|
88
|
+
const angle = (i / 9) * Math.PI * 2;
|
|
89
|
+
const x1 = Math.cos(angle) * radius;
|
|
90
|
+
const y1 = Math.sin(angle) * radius;
|
|
91
|
+
const x2 = Math.cos(angle + Math.PI / 9) * radius;
|
|
92
|
+
const y2 = Math.sin(angle + Math.PI / 9) * radius;
|
|
93
|
+
|
|
94
|
+
ctx.moveTo(0, 0);
|
|
95
|
+
ctx.lineTo(x1, y1);
|
|
96
|
+
ctx.lineTo(x2, y2);
|
|
97
|
+
ctx.lineTo(0, 0);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Draw inner triangles
|
|
101
|
+
const innerRadius = radius * 0.6;
|
|
102
|
+
for (let i = 0; i < 9; i++) {
|
|
103
|
+
const angle = (i / 9) * Math.PI * 2 + Math.PI / 18;
|
|
104
|
+
const x1 = Math.cos(angle) * innerRadius;
|
|
105
|
+
const y1 = Math.sin(angle) * innerRadius;
|
|
106
|
+
const x2 = Math.cos(angle + Math.PI / 9) * innerRadius;
|
|
107
|
+
const y2 = Math.sin(angle + Math.PI / 9) * innerRadius;
|
|
108
|
+
|
|
109
|
+
ctx.moveTo(0, 0);
|
|
110
|
+
ctx.lineTo(x1, y1);
|
|
111
|
+
ctx.lineTo(x2, y2);
|
|
112
|
+
ctx.lineTo(0, 0);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
export const drawSeedOfLife = (ctx, size) => {
|
|
117
|
+
const radius = size / 4;
|
|
118
|
+
const centers = [
|
|
119
|
+
{ x: 0, y: 0 },
|
|
120
|
+
...createCirclePoints(0, 0, radius * 2, 6)
|
|
121
|
+
];
|
|
122
|
+
|
|
123
|
+
ctx.beginPath();
|
|
124
|
+
centers.forEach(center => {
|
|
125
|
+
ctx.moveTo(center.x + radius, center.y);
|
|
126
|
+
ctx.arc(center.x, center.y, radius, 0, Math.PI * 2);
|
|
127
|
+
});
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
export const drawVesicaPiscis = (ctx, size) => {
|
|
131
|
+
const radius = size / 3;
|
|
132
|
+
ctx.beginPath();
|
|
133
|
+
|
|
134
|
+
// Draw two overlapping circles
|
|
135
|
+
ctx.arc(-radius/2, 0, radius, 0, Math.PI * 2);
|
|
136
|
+
ctx.arc(radius/2, 0, radius, 0, Math.PI * 2);
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
export const drawTorus = (ctx, size) => {
|
|
140
|
+
const outerRadius = size / 2;
|
|
141
|
+
const innerRadius = size / 4;
|
|
142
|
+
const steps = 36;
|
|
143
|
+
|
|
144
|
+
ctx.beginPath();
|
|
145
|
+
for (let i = 0; i < steps; i++) {
|
|
146
|
+
const angle1 = (i / steps) * Math.PI * 2;
|
|
147
|
+
// const angle2 = ((i + 1) / steps) * Math.PI * 2;
|
|
148
|
+
|
|
149
|
+
for (let j = 0; j < steps; j++) {
|
|
150
|
+
const phi1 = (j / steps) * Math.PI * 2;
|
|
151
|
+
const phi2 = ((j + 1) / steps) * Math.PI * 2;
|
|
152
|
+
|
|
153
|
+
const x1 = (outerRadius + innerRadius * Math.cos(phi1)) * Math.cos(angle1);
|
|
154
|
+
const y1 = (outerRadius + innerRadius * Math.cos(phi1)) * Math.sin(angle1);
|
|
155
|
+
const x2 = (outerRadius + innerRadius * Math.cos(phi2)) * Math.cos(angle1);
|
|
156
|
+
const y2 = (outerRadius + innerRadius * Math.cos(phi2)) * Math.sin(angle1);
|
|
157
|
+
|
|
158
|
+
ctx.moveTo(x1, y1);
|
|
159
|
+
ctx.lineTo(x2, y2);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// Add to the existing shapes map
|
|
165
|
+
export const sacredShapes = {
|
|
166
|
+
// flowerOfLife: drawFlowerOfLife,
|
|
167
|
+
// treeOfLife: drawTreeOfLife,
|
|
168
|
+
metatronsCube: drawMetatronsCube,
|
|
169
|
+
sriYantra: drawSriYantra,
|
|
170
|
+
seedOfLife: drawSeedOfLife,
|
|
171
|
+
vesicaPiscis: drawVesicaPiscis,
|
|
172
|
+
torus: drawTorus
|
|
173
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export const degToRad = (degrees) => (degrees * Math.PI) / 180;
|
|
2
|
+
|
|
3
|
+
export const applyTransforms = (ctx, size, config) => {
|
|
4
|
+
ctx.save();
|
|
5
|
+
ctx.translate(0, 0);
|
|
6
|
+
if (config.rotation) {
|
|
7
|
+
ctx.rotate(degToRad(config.rotation));
|
|
8
|
+
}
|
|
9
|
+
ctx.lineWidth = config.lineWidth;
|
|
10
|
+
ctx.strokeStyle = config.strokeStyle;
|
|
11
|
+
ctx.fillStyle = config.fillStyle;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const restoreContext = (ctx) => {
|
|
15
|
+
ctx.restore();
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// Animation configuration stub for future use
|
|
19
|
+
export const createAnimationConfig = (type) => ({
|
|
20
|
+
enabled: false,
|
|
21
|
+
duration: 1000,
|
|
22
|
+
easing: "linear",
|
|
23
|
+
type,
|
|
24
|
+
// Add more animation-specific properties as needed
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
export const createCirclePoints = (cx, cy, radius, segments) => {
|
|
28
|
+
const points = [];
|
|
29
|
+
for (let i = 0; i < segments; i++) {
|
|
30
|
+
const angle = (i / segments) * Math.PI * 2;
|
|
31
|
+
points.push({
|
|
32
|
+
x: cx + Math.cos(angle) * radius,
|
|
33
|
+
y: cy + Math.sin(angle) * radius,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return points;
|
|
37
|
+
};
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
export const PRESETS = {
|
|
2
|
+
// Standard sizes with different hashes
|
|
3
|
+
react: {
|
|
4
|
+
hash: "46192e59d42f741c761cbea79462a8b3815dd905",
|
|
5
|
+
width: 1024,
|
|
6
|
+
height: 1024,
|
|
7
|
+
},
|
|
8
|
+
angular: {
|
|
9
|
+
hash: "f31a6c3e94420f43c0cd323a5a6a99376ee59ff8",
|
|
10
|
+
width: 1024,
|
|
11
|
+
height: 1024,
|
|
12
|
+
gridSize: 6, // Higher density grid
|
|
13
|
+
},
|
|
14
|
+
// Wide format variations
|
|
15
|
+
banner: {
|
|
16
|
+
hash: "d847ffd4269b22c54d6e85ad3c1892a298e961fb",
|
|
17
|
+
width: 1920,
|
|
18
|
+
height: 480,
|
|
19
|
+
gridSize: 8, // More horizontal cells for wide format
|
|
20
|
+
shapesPerLayer: 40,
|
|
21
|
+
},
|
|
22
|
+
ultrawide: {
|
|
23
|
+
hash: "a3e126e537ed2cd11ddf3a96c37066e97c7afee6",
|
|
24
|
+
width: 3440,
|
|
25
|
+
height: 1440,
|
|
26
|
+
gridSize: 12, // Extra wide needs more cells
|
|
27
|
+
shapesPerLayer: 60,
|
|
28
|
+
},
|
|
29
|
+
// Social media sizes
|
|
30
|
+
"instagram-square": {
|
|
31
|
+
hash: "ff00ff00ff00ff00ff00ff00ff00ff00ff00ff0",
|
|
32
|
+
width: 1080,
|
|
33
|
+
height: 1080,
|
|
34
|
+
},
|
|
35
|
+
"instagram-story": {
|
|
36
|
+
hash: "abc123def456abc123def456abc123def456abc1",
|
|
37
|
+
width: 1080,
|
|
38
|
+
height: 1920,
|
|
39
|
+
gridSize: 6,
|
|
40
|
+
layers: 6,
|
|
41
|
+
},
|
|
42
|
+
"twitter-header": {
|
|
43
|
+
hash: "7777777777777777777777777777777777777777",
|
|
44
|
+
width: 1500,
|
|
45
|
+
height: 500,
|
|
46
|
+
gridSize: 8,
|
|
47
|
+
shapesPerLayer: 35,
|
|
48
|
+
},
|
|
49
|
+
"linkedin-banner": {
|
|
50
|
+
hash: "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
|
51
|
+
width: 1584,
|
|
52
|
+
height: 396,
|
|
53
|
+
gridSize: 8,
|
|
54
|
+
shapesPerLayer: 35,
|
|
55
|
+
},
|
|
56
|
+
// Mobile sizes
|
|
57
|
+
"phone-wallpaper": {
|
|
58
|
+
hash: "ffffffffffffffffffffffffffffffffaaaaaaaa",
|
|
59
|
+
width: 1170,
|
|
60
|
+
height: 2532, // iPhone 13 Pro size
|
|
61
|
+
gridSize: 5,
|
|
62
|
+
layers: 6,
|
|
63
|
+
},
|
|
64
|
+
"tablet-wallpaper": {
|
|
65
|
+
hash: "123456789abcdef0123456789abcdef012345678",
|
|
66
|
+
width: 2048,
|
|
67
|
+
height: 2732, // iPad Pro size
|
|
68
|
+
gridSize: 7,
|
|
69
|
+
layers: 6,
|
|
70
|
+
},
|
|
71
|
+
// Special configurations
|
|
72
|
+
minimal: {
|
|
73
|
+
hash: "000000000000000000000000000000000fffffff",
|
|
74
|
+
width: 1024,
|
|
75
|
+
height: 1024,
|
|
76
|
+
layers: 3,
|
|
77
|
+
baseOpacity: 0.8,
|
|
78
|
+
shapesPerLayer: 15,
|
|
79
|
+
},
|
|
80
|
+
complex: {
|
|
81
|
+
hash: "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef",
|
|
82
|
+
width: 2048,
|
|
83
|
+
height: 2048,
|
|
84
|
+
gridSize: 8,
|
|
85
|
+
layers: 7,
|
|
86
|
+
shapesPerLayer: 50,
|
|
87
|
+
minShapeSize: 30,
|
|
88
|
+
maxShapeSize: 250,
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
// Basic configuration that applies to all shapes
|
|
93
|
+
export const defaultShapeConfig = {
|
|
94
|
+
strokeStyle: "#000000",
|
|
95
|
+
fillStyle: "transparent",
|
|
96
|
+
lineWidth: 1,
|
|
97
|
+
rotation: 0,
|
|
98
|
+
iterations: 1,
|
|
99
|
+
animate: false, // For future use
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// Base configuration types that can be extended per shape
|
|
103
|
+
export const ShapeConfigTypes = {
|
|
104
|
+
BASIC: "basic",
|
|
105
|
+
DETAILED: "detailed",
|
|
106
|
+
ANIMATED: "animated",
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// Golden ratio and other important proportions
|
|
110
|
+
export const Proportions = {
|
|
111
|
+
GOLDEN_RATIO: 1.618034,
|
|
112
|
+
SQUARE_ROOT_2: Math.sqrt(2),
|
|
113
|
+
SQUARE_ROOT_3: Math.sqrt(3),
|
|
114
|
+
SQUARE_ROOT_5: Math.sqrt(5),
|
|
115
|
+
PI: Math.PI,
|
|
116
|
+
PHI: (1 + Math.sqrt(5)) / 2,
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
// Helper for creating common sacred geometry combinations
|
|
120
|
+
export const PatternPresets = {
|
|
121
|
+
flowerOfLifeMandala: (size) => [
|
|
122
|
+
// { type: "flowerOfLife", config: { size } },
|
|
123
|
+
{ type: "merkaba", config: { size: size * 0.8 } },
|
|
124
|
+
{ type: "sriYantra", config: { size: size * 0.5 } },
|
|
125
|
+
],
|
|
126
|
+
|
|
127
|
+
platonicProgression: (size) => [
|
|
128
|
+
{ type: "platonicSolid", config: { size, type: "tetrahedron" } },
|
|
129
|
+
{ type: "platonicSolid", config: { size: size * 0.8, type: "cube" } },
|
|
130
|
+
{ type: "platonicSolid", config: { size: size * 0.6, type: "octahedron" } },
|
|
131
|
+
],
|
|
132
|
+
|
|
133
|
+
cosmicTree: (size) => [
|
|
134
|
+
// { type: "treeOfLife", config: { size } },
|
|
135
|
+
{ type: "fibonacciSpiral", config: { size: size * 0.9 } },
|
|
136
|
+
{ type: "metatronsCube", config: { size: size * 0.7 } },
|
|
137
|
+
],
|
|
138
|
+
};
|
package/src/lib/utils.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { shapes } from "./canvas/shapes";
|
|
2
|
+
|
|
3
|
+
export function gitHashToSeed(gitHash) {
|
|
4
|
+
return parseInt(gitHash.slice(0, 8), 16);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function getRandomFromHash(hash, index, min, max) {
|
|
8
|
+
const hexPair = hash.substr((index * 2) % hash.length, 2);
|
|
9
|
+
const decimal = parseInt(hexPair, 16);
|
|
10
|
+
return min + (decimal / 255) * (max - min);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Golden ratio and other important proportions
|
|
14
|
+
export const Proportions = {
|
|
15
|
+
GOLDEN_RATIO: 1.618034,
|
|
16
|
+
SQUARE_ROOT_2: Math.sqrt(2),
|
|
17
|
+
SQUARE_ROOT_3: Math.sqrt(3),
|
|
18
|
+
SQUARE_ROOT_5: Math.sqrt(5),
|
|
19
|
+
PI: Math.PI,
|
|
20
|
+
PHI: (1 + Math.sqrt(5)) / 2,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// Pattern combination utilities
|
|
24
|
+
export class PatternCombiner {
|
|
25
|
+
static getProportionalSize(baseSize, proportion) {
|
|
26
|
+
return baseSize * proportion;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
static centerPattern(ctx, width, height) {
|
|
30
|
+
ctx.translate(width / 2, height / 2);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Combines sacred geometry patterns with proper proportions
|
|
34
|
+
static layerPatterns(ctx, patterns, config) {
|
|
35
|
+
const {
|
|
36
|
+
baseSize,
|
|
37
|
+
baseOpacity = 0.6,
|
|
38
|
+
opacityReduction = 0.1,
|
|
39
|
+
rotationOffset = 0,
|
|
40
|
+
proportionType = "GOLDEN_RATIO",
|
|
41
|
+
} = config;
|
|
42
|
+
|
|
43
|
+
patterns.forEach((pattern, index) => {
|
|
44
|
+
const size = this.getProportionalSize(
|
|
45
|
+
baseSize,
|
|
46
|
+
Math.pow(Proportions[proportionType], index)
|
|
47
|
+
);
|
|
48
|
+
const opacity = Math.max(0.1, baseOpacity - index * opacityReduction);
|
|
49
|
+
const rotation = rotationOffset * index;
|
|
50
|
+
|
|
51
|
+
ctx.save();
|
|
52
|
+
ctx.globalAlpha = opacity;
|
|
53
|
+
ctx.rotate((rotation * Math.PI) / 180);
|
|
54
|
+
|
|
55
|
+
shapes[pattern.type](ctx, size, pattern.config);
|
|
56
|
+
ctx.restore();
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|