dingbatch 0.1.0 → 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/dist/boomerang-s5naDBuf.d.ts +39 -0
- package/dist/chunk-5A7FSZ3T.js +1626 -0
- package/dist/chunk-5A7FSZ3T.js.map +1 -0
- package/dist/index.d.ts +229 -29
- package/dist/index.js +65 -3
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-IAFZ3FNR.js +0 -531
- package/dist/chunk-IAFZ3FNR.js.map +0 -1
|
@@ -0,0 +1,1626 @@
|
|
|
1
|
+
// src/generators/utils/svg.ts
|
|
2
|
+
function wrapPath(path, width, height) {
|
|
3
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${width} ${height}" width="${width}" height="${height}"><path d="${path}" fill="currentColor"/></svg>`;
|
|
4
|
+
}
|
|
5
|
+
function addSvgFields(result) {
|
|
6
|
+
const viewBox = `0 0 ${result.width} ${result.height}`;
|
|
7
|
+
const svg = wrapPath(result.path, result.width, result.height);
|
|
8
|
+
return { ...result, viewBox, svg };
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// src/presets/boomerang.ts
|
|
12
|
+
var crescent = {
|
|
13
|
+
style: "crescent",
|
|
14
|
+
armLength: 50,
|
|
15
|
+
bendAngle: 160,
|
|
16
|
+
thickness: 35,
|
|
17
|
+
taper: 0.5,
|
|
18
|
+
tipRoundness: 0.2,
|
|
19
|
+
armCurvature: 0,
|
|
20
|
+
bendSharpness: 0
|
|
21
|
+
};
|
|
22
|
+
var fatMoon = {
|
|
23
|
+
style: "crescent",
|
|
24
|
+
armLength: 45,
|
|
25
|
+
bendAngle: 180,
|
|
26
|
+
thickness: 50,
|
|
27
|
+
taper: 0.3,
|
|
28
|
+
tipRoundness: 0.6,
|
|
29
|
+
armCurvature: 0,
|
|
30
|
+
bendSharpness: 0
|
|
31
|
+
};
|
|
32
|
+
var sharpChevron = {
|
|
33
|
+
style: "chevron",
|
|
34
|
+
armLength: 55,
|
|
35
|
+
bendAngle: 60,
|
|
36
|
+
thickness: 40,
|
|
37
|
+
taper: 1,
|
|
38
|
+
tipRoundness: 0,
|
|
39
|
+
armCurvature: 0,
|
|
40
|
+
bendSharpness: 0
|
|
41
|
+
};
|
|
42
|
+
var thinArc = {
|
|
43
|
+
style: "crescent",
|
|
44
|
+
armLength: 60,
|
|
45
|
+
bendAngle: 140,
|
|
46
|
+
thickness: 20,
|
|
47
|
+
taper: 0.7,
|
|
48
|
+
tipRoundness: 0.1,
|
|
49
|
+
armCurvature: 0,
|
|
50
|
+
bendSharpness: 0
|
|
51
|
+
};
|
|
52
|
+
var horseshoe = {
|
|
53
|
+
style: "horseshoe",
|
|
54
|
+
armLength: 80,
|
|
55
|
+
bendAngle: 50,
|
|
56
|
+
thickness: 18,
|
|
57
|
+
taper: 0.2,
|
|
58
|
+
tipRoundness: 0.3,
|
|
59
|
+
armCurvature: 0.4,
|
|
60
|
+
bendSharpness: 0
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// src/generators/curved/boomerang.ts
|
|
64
|
+
function boomerangBase(params) {
|
|
65
|
+
const style = params.style || "classic";
|
|
66
|
+
switch (style) {
|
|
67
|
+
case "crescent":
|
|
68
|
+
return generateCrescent(params);
|
|
69
|
+
case "horseshoe":
|
|
70
|
+
return generateHorseshoe(params);
|
|
71
|
+
case "chevron":
|
|
72
|
+
return generateChevron(params);
|
|
73
|
+
case "classic":
|
|
74
|
+
default:
|
|
75
|
+
return generateClassic(params);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
var boomerang = Object.assign(boomerangBase, {
|
|
79
|
+
crescent: (overrides) => boomerangBase({ ...crescent, ...overrides }),
|
|
80
|
+
fatMoon: (overrides) => boomerangBase({ ...fatMoon, ...overrides }),
|
|
81
|
+
sharpChevron: (overrides) => boomerangBase({ ...sharpChevron, ...overrides }),
|
|
82
|
+
thinArc: (overrides) => boomerangBase({ ...thinArc, ...overrides }),
|
|
83
|
+
horseshoe: (overrides) => boomerangBase({ ...horseshoe, ...overrides })
|
|
84
|
+
});
|
|
85
|
+
function generateClassic(params) {
|
|
86
|
+
const {
|
|
87
|
+
armLength,
|
|
88
|
+
bendAngle,
|
|
89
|
+
armCurvature,
|
|
90
|
+
bendSharpness,
|
|
91
|
+
thickness,
|
|
92
|
+
taper,
|
|
93
|
+
tipRoundness,
|
|
94
|
+
armBalance = 1,
|
|
95
|
+
thicknessBalance = 0,
|
|
96
|
+
rotation = 0
|
|
97
|
+
} = params;
|
|
98
|
+
const clampedBendAngle = Math.max(30, Math.min(180, bendAngle));
|
|
99
|
+
const clampedArmCurvature = Math.max(0, Math.min(1, armCurvature));
|
|
100
|
+
const clampedBendSharpness = Math.max(0, Math.min(1, bendSharpness));
|
|
101
|
+
const clampedTaper = Math.max(0, Math.min(1, taper));
|
|
102
|
+
const clampedTipRoundness = Math.max(0, Math.min(1, tipRoundness));
|
|
103
|
+
const clampedArmBalance = Math.max(0.5, Math.min(2, armBalance));
|
|
104
|
+
const clampedThicknessBalance = Math.max(-1, Math.min(1, thicknessBalance));
|
|
105
|
+
const arm1Length = armLength * (2 / (1 + clampedArmBalance));
|
|
106
|
+
const arm2Length = armLength * (2 * clampedArmBalance / (1 + clampedArmBalance));
|
|
107
|
+
const halfAngle = clampedBendAngle / 2 * Math.PI / 180;
|
|
108
|
+
const arm1Dir = { x: -Math.sin(halfAngle), y: -Math.cos(halfAngle) };
|
|
109
|
+
const arm2Dir = { x: Math.sin(halfAngle), y: -Math.cos(halfAngle) };
|
|
110
|
+
const tip1 = { x: arm1Dir.x * arm1Length, y: arm1Dir.y * arm1Length };
|
|
111
|
+
const tip2 = { x: arm2Dir.x * arm2Length, y: arm2Dir.y * arm2Length };
|
|
112
|
+
const bendPoint = { x: 0, y: 0 };
|
|
113
|
+
const numSamples = 20;
|
|
114
|
+
const arm1Spine = generateArmSpine(tip1, bendPoint, arm1Dir, arm1Length, clampedArmCurvature, numSamples);
|
|
115
|
+
const arm2Spine = generateArmSpine(bendPoint, tip2, arm2Dir, arm2Length, clampedArmCurvature, numSamples);
|
|
116
|
+
const minThickness = thickness * clampedTaper;
|
|
117
|
+
function getThicknessAlongArm(t, isArm1) {
|
|
118
|
+
const tipFade = Math.sin(t * Math.PI / 2);
|
|
119
|
+
const baseThickness = minThickness + (thickness - minThickness) * tipFade;
|
|
120
|
+
const balanceFactor = isArm1 ? 1 - clampedThicknessBalance * 0.3 : 1 + clampedThicknessBalance * 0.3;
|
|
121
|
+
return baseThickness * balanceFactor;
|
|
122
|
+
}
|
|
123
|
+
const arm1Outer = [];
|
|
124
|
+
const arm1Inner = [];
|
|
125
|
+
const arm2Outer = [];
|
|
126
|
+
const arm2Inner = [];
|
|
127
|
+
for (let i = 0; i < arm1Spine.length; i++) {
|
|
128
|
+
const t = i / (arm1Spine.length - 1);
|
|
129
|
+
const point = arm1Spine[i];
|
|
130
|
+
const tangent = getSpineTangent(arm1Spine, i);
|
|
131
|
+
const normal = { x: -tangent.y, y: tangent.x };
|
|
132
|
+
const halfThick = getThicknessAlongArm(t, true) / 2;
|
|
133
|
+
arm1Outer.push({ x: point.x + normal.x * halfThick, y: point.y + normal.y * halfThick });
|
|
134
|
+
arm1Inner.push({ x: point.x - normal.x * halfThick, y: point.y - normal.y * halfThick });
|
|
135
|
+
}
|
|
136
|
+
for (let i = 0; i < arm2Spine.length; i++) {
|
|
137
|
+
const t = 1 - i / (arm2Spine.length - 1);
|
|
138
|
+
const point = arm2Spine[i];
|
|
139
|
+
const tangent = getSpineTangent(arm2Spine, i);
|
|
140
|
+
const normal = { x: -tangent.y, y: tangent.x };
|
|
141
|
+
const halfThick = getThicknessAlongArm(t, false) / 2;
|
|
142
|
+
arm2Outer.push({ x: point.x + normal.x * halfThick, y: point.y + normal.y * halfThick });
|
|
143
|
+
arm2Inner.push({ x: point.x - normal.x * halfThick, y: point.y - normal.y * halfThick });
|
|
144
|
+
}
|
|
145
|
+
const pathParts = [];
|
|
146
|
+
pathParts.push(`M ${arm1Outer[0].x.toFixed(2)},${arm1Outer[0].y.toFixed(2)}`);
|
|
147
|
+
const tip1Curve = clampedTipRoundness * thickness * 0.3;
|
|
148
|
+
pathParts.push(`Q ${(tip1.x + arm1Dir.x * tip1Curve).toFixed(2)},${(tip1.y + arm1Dir.y * tip1Curve).toFixed(2)} ${arm1Inner[0].x.toFixed(2)},${arm1Inner[0].y.toFixed(2)}`);
|
|
149
|
+
pathParts.push(smoothCurveThroughPoints(arm1Inner));
|
|
150
|
+
const bendInner2 = arm2Inner[0];
|
|
151
|
+
if (clampedBendSharpness < 0.3) {
|
|
152
|
+
pathParts.push(`L ${bendInner2.x.toFixed(2)},${bendInner2.y.toFixed(2)}`);
|
|
153
|
+
} else {
|
|
154
|
+
const bendInnerOffset = thickness * 0.2 * clampedBendSharpness;
|
|
155
|
+
const bendInnerCtrl = { x: 0, y: bendInnerOffset };
|
|
156
|
+
pathParts.push(`Q ${bendInnerCtrl.x.toFixed(2)},${bendInnerCtrl.y.toFixed(2)} ${bendInner2.x.toFixed(2)},${bendInner2.y.toFixed(2)}`);
|
|
157
|
+
}
|
|
158
|
+
pathParts.push(smoothCurveThroughPoints(arm2Inner));
|
|
159
|
+
const tip2Curve = clampedTipRoundness * thickness * 0.3;
|
|
160
|
+
pathParts.push(`Q ${(tip2.x + arm2Dir.x * tip2Curve).toFixed(2)},${(tip2.y + arm2Dir.y * tip2Curve).toFixed(2)} ${arm2Outer[arm2Outer.length - 1].x.toFixed(2)},${arm2Outer[arm2Outer.length - 1].y.toFixed(2)}`);
|
|
161
|
+
const arm2OuterReversed = [...arm2Outer].reverse();
|
|
162
|
+
pathParts.push(smoothCurveThroughPoints(arm2OuterReversed));
|
|
163
|
+
const bendOuter1 = arm1Outer[arm1Outer.length - 1];
|
|
164
|
+
if (clampedBendSharpness < 0.3) {
|
|
165
|
+
pathParts.push(`L ${bendOuter1.x.toFixed(2)},${bendOuter1.y.toFixed(2)}`);
|
|
166
|
+
} else {
|
|
167
|
+
const bendOuterOffset = -thickness * 0.4 * clampedBendSharpness;
|
|
168
|
+
const bendOuterCtrl = { x: 0, y: bendOuterOffset };
|
|
169
|
+
pathParts.push(`Q ${bendOuterCtrl.x.toFixed(2)},${bendOuterCtrl.y.toFixed(2)} ${bendOuter1.x.toFixed(2)},${bendOuter1.y.toFixed(2)}`);
|
|
170
|
+
}
|
|
171
|
+
const arm1OuterReversed = [...arm1Outer].reverse();
|
|
172
|
+
pathParts.push(smoothCurveThroughPoints(arm1OuterReversed));
|
|
173
|
+
pathParts.push("Z");
|
|
174
|
+
let path = pathParts.join(" ");
|
|
175
|
+
const allPoints = [...arm1Outer, ...arm1Inner, ...arm2Outer, ...arm2Inner];
|
|
176
|
+
let minX = Math.min(...allPoints.map((p) => p.x));
|
|
177
|
+
let maxX = Math.max(...allPoints.map((p) => p.x));
|
|
178
|
+
let minY = Math.min(...allPoints.map((p) => p.y));
|
|
179
|
+
let maxY = Math.max(...allPoints.map((p) => p.y));
|
|
180
|
+
const padding = thickness * 0.3 * clampedTipRoundness;
|
|
181
|
+
minX -= padding;
|
|
182
|
+
maxX += padding;
|
|
183
|
+
minY -= padding;
|
|
184
|
+
const width = maxX - minX;
|
|
185
|
+
const height = maxY - minY;
|
|
186
|
+
path = normalizePath(path, minX, minY);
|
|
187
|
+
if (rotation !== 0) {
|
|
188
|
+
path = rotatePath(path, rotation, width, height);
|
|
189
|
+
}
|
|
190
|
+
return addSvgFields({ path, width, height, centerX: width / 2, centerY: height / 2 });
|
|
191
|
+
}
|
|
192
|
+
function generateCrescent(params) {
|
|
193
|
+
const {
|
|
194
|
+
armLength,
|
|
195
|
+
// Used as arc radius
|
|
196
|
+
bendAngle,
|
|
197
|
+
// Used as arc span (degrees, 30-300)
|
|
198
|
+
thickness,
|
|
199
|
+
taper,
|
|
200
|
+
tipRoundness,
|
|
201
|
+
armBalance = 1,
|
|
202
|
+
// Asymmetric arc thickness along length
|
|
203
|
+
rotation = 0
|
|
204
|
+
} = params;
|
|
205
|
+
const radius = Math.max(20, armLength);
|
|
206
|
+
const arcSpan = Math.max(30, Math.min(300, bendAngle)) * Math.PI / 180;
|
|
207
|
+
const clampedTaper = Math.max(0, Math.min(1, taper));
|
|
208
|
+
const clampedTipRoundness = Math.max(0, Math.min(1, tipRoundness));
|
|
209
|
+
const clampedArmBalance = Math.max(0.5, Math.min(2, armBalance));
|
|
210
|
+
const numSamples = 30;
|
|
211
|
+
const startAngle = -arcSpan / 2;
|
|
212
|
+
const endAngle = arcSpan / 2;
|
|
213
|
+
const spine = [];
|
|
214
|
+
for (let i = 0; i <= numSamples; i++) {
|
|
215
|
+
const t = i / numSamples;
|
|
216
|
+
const angle = startAngle + (endAngle - startAngle) * t;
|
|
217
|
+
spine.push({
|
|
218
|
+
x: Math.sin(angle) * radius,
|
|
219
|
+
y: -Math.cos(angle) * radius + radius
|
|
220
|
+
// Shift so bottom of arc is at y=0
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
const minThickness = thickness * clampedTaper;
|
|
224
|
+
function getThicknessAtT(t) {
|
|
225
|
+
const balanceShift = (t - 0.5) * (clampedArmBalance - 1) * 0.5;
|
|
226
|
+
const adjustedT = Math.max(0, Math.min(1, t + balanceShift));
|
|
227
|
+
const adjustedDist = Math.min(adjustedT, 1 - adjustedT) * 2;
|
|
228
|
+
const adjustedFactor = Math.sin(adjustedDist * Math.PI / 2);
|
|
229
|
+
return minThickness + (thickness - minThickness) * adjustedFactor;
|
|
230
|
+
}
|
|
231
|
+
const outer = [];
|
|
232
|
+
const inner = [];
|
|
233
|
+
for (let i = 0; i < spine.length; i++) {
|
|
234
|
+
const t = i / (spine.length - 1);
|
|
235
|
+
const point = spine[i];
|
|
236
|
+
const tangent = getSpineTangent(spine, i);
|
|
237
|
+
const normal = { x: -tangent.y, y: tangent.x };
|
|
238
|
+
const halfThick = getThicknessAtT(t) / 2;
|
|
239
|
+
outer.push({ x: point.x + normal.x * halfThick, y: point.y + normal.y * halfThick });
|
|
240
|
+
inner.push({ x: point.x - normal.x * halfThick, y: point.y - normal.y * halfThick });
|
|
241
|
+
}
|
|
242
|
+
const pathParts = [];
|
|
243
|
+
pathParts.push(`M ${outer[0].x.toFixed(2)},${outer[0].y.toFixed(2)}`);
|
|
244
|
+
const startTipCurve = clampedTipRoundness * thickness * 0.4;
|
|
245
|
+
const startTangent = getSpineTangent(spine, 0);
|
|
246
|
+
pathParts.push(`Q ${(spine[0].x - startTangent.x * startTipCurve).toFixed(2)},${(spine[0].y - startTangent.y * startTipCurve).toFixed(2)} ${inner[0].x.toFixed(2)},${inner[0].y.toFixed(2)}`);
|
|
247
|
+
pathParts.push(smoothCurveThroughPoints(inner));
|
|
248
|
+
const endTipCurve = clampedTipRoundness * thickness * 0.4;
|
|
249
|
+
const endTangent = getSpineTangent(spine, spine.length - 1);
|
|
250
|
+
pathParts.push(`Q ${(spine[spine.length - 1].x + endTangent.x * endTipCurve).toFixed(2)},${(spine[spine.length - 1].y + endTangent.y * endTipCurve).toFixed(2)} ${outer[outer.length - 1].x.toFixed(2)},${outer[outer.length - 1].y.toFixed(2)}`);
|
|
251
|
+
const outerReversed = [...outer].reverse();
|
|
252
|
+
pathParts.push(smoothCurveThroughPoints(outerReversed));
|
|
253
|
+
pathParts.push("Z");
|
|
254
|
+
let path = pathParts.join(" ");
|
|
255
|
+
const allPoints = [...outer, ...inner];
|
|
256
|
+
let minX = Math.min(...allPoints.map((p) => p.x));
|
|
257
|
+
let maxX = Math.max(...allPoints.map((p) => p.x));
|
|
258
|
+
let minY = Math.min(...allPoints.map((p) => p.y));
|
|
259
|
+
let maxY = Math.max(...allPoints.map((p) => p.y));
|
|
260
|
+
const padding = thickness * 0.3 * clampedTipRoundness;
|
|
261
|
+
minX -= padding;
|
|
262
|
+
maxX += padding;
|
|
263
|
+
minY -= padding;
|
|
264
|
+
maxY += padding;
|
|
265
|
+
const width = maxX - minX;
|
|
266
|
+
const height = maxY - minY;
|
|
267
|
+
path = normalizePath(path, minX, minY);
|
|
268
|
+
if (rotation !== 0) {
|
|
269
|
+
path = rotatePath(path, rotation, width, height);
|
|
270
|
+
}
|
|
271
|
+
return addSvgFields({ path, width, height, centerX: width / 2, centerY: height / 2 });
|
|
272
|
+
}
|
|
273
|
+
function generateHorseshoe(params) {
|
|
274
|
+
const {
|
|
275
|
+
armLength,
|
|
276
|
+
// Length of the vertical arms
|
|
277
|
+
bendAngle,
|
|
278
|
+
// Spacing between arms (width at top)
|
|
279
|
+
armCurvature,
|
|
280
|
+
// How curved the bottom connection is
|
|
281
|
+
thickness,
|
|
282
|
+
taper,
|
|
283
|
+
tipRoundness,
|
|
284
|
+
armBalance = 1,
|
|
285
|
+
// Asymmetric arm lengths
|
|
286
|
+
rotation = 0
|
|
287
|
+
} = params;
|
|
288
|
+
const spacing = Math.max(20, bendAngle);
|
|
289
|
+
const clampedArmCurvature = Math.max(0, Math.min(1, armCurvature));
|
|
290
|
+
const clampedTaper = Math.max(0, Math.min(1, taper));
|
|
291
|
+
const clampedTipRoundness = Math.max(0, Math.min(1, tipRoundness));
|
|
292
|
+
const clampedArmBalance = Math.max(0.5, Math.min(2, armBalance));
|
|
293
|
+
const arm1Len = armLength * (2 / (1 + clampedArmBalance));
|
|
294
|
+
const arm2Len = armLength * (2 * clampedArmBalance / (1 + clampedArmBalance));
|
|
295
|
+
const halfSpacing = spacing / 2;
|
|
296
|
+
const halfThick = thickness / 2;
|
|
297
|
+
const tipThick = thickness * clampedTaper / 2;
|
|
298
|
+
const leftOuterX = -halfSpacing - halfThick;
|
|
299
|
+
const leftInnerX = -halfSpacing + halfThick;
|
|
300
|
+
const rightInnerX = halfSpacing - halfThick;
|
|
301
|
+
const rightOuterX = halfSpacing + halfThick;
|
|
302
|
+
const leftTopY = 0;
|
|
303
|
+
const leftBottomY = arm1Len;
|
|
304
|
+
const rightBottomY = arm2Len;
|
|
305
|
+
const rightTopY = 0;
|
|
306
|
+
const bottomY = Math.max(arm1Len, arm2Len);
|
|
307
|
+
const curveDepth = halfSpacing * clampedArmCurvature;
|
|
308
|
+
const pathParts = [];
|
|
309
|
+
const leftTipOuterX = -halfSpacing - tipThick;
|
|
310
|
+
const leftTipInnerX = -halfSpacing + tipThick;
|
|
311
|
+
const rightTipInnerX = halfSpacing - tipThick;
|
|
312
|
+
const rightTipOuterX = halfSpacing + tipThick;
|
|
313
|
+
pathParts.push(`M ${leftTipOuterX.toFixed(2)},${leftTopY.toFixed(2)}`);
|
|
314
|
+
if (clampedTipRoundness > 0) {
|
|
315
|
+
const tipCurve = clampedTipRoundness * thickness * 0.3;
|
|
316
|
+
pathParts.push(`Q ${(-halfSpacing).toFixed(2)},${(leftTopY - tipCurve).toFixed(2)} ${leftTipInnerX.toFixed(2)},${leftTopY.toFixed(2)}`);
|
|
317
|
+
} else {
|
|
318
|
+
pathParts.push(`L ${leftTipInnerX.toFixed(2)},${leftTopY.toFixed(2)}`);
|
|
319
|
+
}
|
|
320
|
+
pathParts.push(`L ${leftInnerX.toFixed(2)},${leftBottomY.toFixed(2)}`);
|
|
321
|
+
if (clampedArmCurvature > 0.1) {
|
|
322
|
+
const innerCtrlY = bottomY + curveDepth;
|
|
323
|
+
pathParts.push(`Q ${0},${innerCtrlY.toFixed(2)} ${rightInnerX.toFixed(2)},${rightBottomY.toFixed(2)}`);
|
|
324
|
+
} else {
|
|
325
|
+
pathParts.push(`L ${rightInnerX.toFixed(2)},${rightBottomY.toFixed(2)}`);
|
|
326
|
+
}
|
|
327
|
+
pathParts.push(`L ${rightTipInnerX.toFixed(2)},${rightTopY.toFixed(2)}`);
|
|
328
|
+
if (clampedTipRoundness > 0) {
|
|
329
|
+
const tipCurve = clampedTipRoundness * thickness * 0.3;
|
|
330
|
+
pathParts.push(`Q ${halfSpacing.toFixed(2)},${(rightTopY - tipCurve).toFixed(2)} ${rightTipOuterX.toFixed(2)},${rightTopY.toFixed(2)}`);
|
|
331
|
+
} else {
|
|
332
|
+
pathParts.push(`L ${rightTipOuterX.toFixed(2)},${rightTopY.toFixed(2)}`);
|
|
333
|
+
}
|
|
334
|
+
pathParts.push(`L ${rightOuterX.toFixed(2)},${rightBottomY.toFixed(2)}`);
|
|
335
|
+
if (clampedArmCurvature > 0.1) {
|
|
336
|
+
const outerCtrlY = bottomY + curveDepth + thickness;
|
|
337
|
+
pathParts.push(`Q ${0},${outerCtrlY.toFixed(2)} ${leftOuterX.toFixed(2)},${leftBottomY.toFixed(2)}`);
|
|
338
|
+
} else {
|
|
339
|
+
pathParts.push(`L ${leftOuterX.toFixed(2)},${leftBottomY.toFixed(2)}`);
|
|
340
|
+
}
|
|
341
|
+
pathParts.push(`L ${leftTipOuterX.toFixed(2)},${leftTopY.toFixed(2)}`);
|
|
342
|
+
pathParts.push("Z");
|
|
343
|
+
let path = pathParts.join(" ");
|
|
344
|
+
const padding = thickness * 0.3;
|
|
345
|
+
let minX = leftOuterX - padding;
|
|
346
|
+
let maxX = rightOuterX + padding;
|
|
347
|
+
let minY = -padding - clampedTipRoundness * thickness * 0.3;
|
|
348
|
+
let maxY = bottomY + curveDepth + thickness + padding;
|
|
349
|
+
const width = maxX - minX;
|
|
350
|
+
const height = maxY - minY;
|
|
351
|
+
path = normalizePath(path, minX, minY);
|
|
352
|
+
if (rotation !== 0) {
|
|
353
|
+
path = rotatePath(path, rotation, width, height);
|
|
354
|
+
}
|
|
355
|
+
return addSvgFields({ path, width, height, centerX: width / 2, centerY: height / 2 });
|
|
356
|
+
}
|
|
357
|
+
function generateChevron(params) {
|
|
358
|
+
const {
|
|
359
|
+
armLength,
|
|
360
|
+
// Length of the arms
|
|
361
|
+
bendAngle,
|
|
362
|
+
// Angle of the V (smaller = sharper point)
|
|
363
|
+
thickness,
|
|
364
|
+
// Controls arm width (proportional)
|
|
365
|
+
taper = 0,
|
|
366
|
+
// 0 = V with cutout, 1 = solid triangle (no cutout)
|
|
367
|
+
tipRoundness = 0,
|
|
368
|
+
// Roundness at the tip
|
|
369
|
+
armBalance = 1,
|
|
370
|
+
// Asymmetric arm lengths
|
|
371
|
+
rotation = 0
|
|
372
|
+
} = params;
|
|
373
|
+
const clampedBendAngle = Math.max(20, Math.min(160, bendAngle));
|
|
374
|
+
const clampedTaper = Math.max(0, Math.min(1, taper));
|
|
375
|
+
const clampedTipRoundness = Math.max(0, Math.min(1, tipRoundness));
|
|
376
|
+
const clampedArmBalance = Math.max(0.5, Math.min(2, armBalance));
|
|
377
|
+
const armWidth = Math.max(armLength * 0.15, thickness * 0.5);
|
|
378
|
+
const cutoutDepth = armLength * 0.6 * (1 - clampedTaper);
|
|
379
|
+
const arm1Len = armLength * (2 / (1 + clampedArmBalance));
|
|
380
|
+
const arm2Len = armLength * (2 * clampedArmBalance / (1 + clampedArmBalance));
|
|
381
|
+
const halfAngle = clampedBendAngle / 2 * Math.PI / 180;
|
|
382
|
+
const tip = { x: 0, y: 0 };
|
|
383
|
+
const arm1End = { x: -Math.sin(halfAngle) * arm1Len, y: -Math.cos(halfAngle) * arm1Len };
|
|
384
|
+
const arm2End = { x: Math.sin(halfAngle) * arm2Len, y: -Math.cos(halfAngle) * arm2Len };
|
|
385
|
+
const arm1Perp = { x: -Math.cos(halfAngle), y: Math.sin(halfAngle) };
|
|
386
|
+
const arm2Perp = { x: Math.cos(halfAngle), y: Math.sin(halfAngle) };
|
|
387
|
+
const arm1OuterCorner = {
|
|
388
|
+
x: arm1End.x + arm1Perp.x * armWidth / 2,
|
|
389
|
+
y: arm1End.y + arm1Perp.y * armWidth / 2
|
|
390
|
+
};
|
|
391
|
+
const arm1InnerCorner = {
|
|
392
|
+
x: arm1End.x - arm1Perp.x * armWidth / 2,
|
|
393
|
+
y: arm1End.y - arm1Perp.y * armWidth / 2
|
|
394
|
+
};
|
|
395
|
+
const arm2OuterCorner = {
|
|
396
|
+
x: arm2End.x + arm2Perp.x * armWidth / 2,
|
|
397
|
+
y: arm2End.y + arm2Perp.y * armWidth / 2
|
|
398
|
+
};
|
|
399
|
+
const arm2InnerCorner = {
|
|
400
|
+
x: arm2End.x - arm2Perp.x * armWidth / 2,
|
|
401
|
+
y: arm2End.y - arm2Perp.y * armWidth / 2
|
|
402
|
+
};
|
|
403
|
+
const innerTip = {
|
|
404
|
+
x: 0,
|
|
405
|
+
y: -cutoutDepth
|
|
406
|
+
};
|
|
407
|
+
const pathParts = [];
|
|
408
|
+
if (clampedTipRoundness > 0) {
|
|
409
|
+
const roundAmount = clampedTipRoundness * armWidth * 0.3;
|
|
410
|
+
const tipStart = {
|
|
411
|
+
x: tip.x + arm1Perp.x * roundAmount,
|
|
412
|
+
y: tip.y + arm1Perp.y * roundAmount - roundAmount * 0.5
|
|
413
|
+
};
|
|
414
|
+
pathParts.push(`M ${tipStart.x.toFixed(2)},${tipStart.y.toFixed(2)}`);
|
|
415
|
+
const tipEnd = {
|
|
416
|
+
x: tip.x + arm2Perp.x * roundAmount,
|
|
417
|
+
y: tip.y + arm2Perp.y * roundAmount - roundAmount * 0.5
|
|
418
|
+
};
|
|
419
|
+
pathParts.push(`Q ${tip.x.toFixed(2)},${tip.y.toFixed(2)} ${tipEnd.x.toFixed(2)},${tipEnd.y.toFixed(2)}`);
|
|
420
|
+
} else {
|
|
421
|
+
pathParts.push(`M ${tip.x.toFixed(2)},${tip.y.toFixed(2)}`);
|
|
422
|
+
}
|
|
423
|
+
pathParts.push(`L ${arm2OuterCorner.x.toFixed(2)},${arm2OuterCorner.y.toFixed(2)}`);
|
|
424
|
+
pathParts.push(`L ${arm2InnerCorner.x.toFixed(2)},${arm2InnerCorner.y.toFixed(2)}`);
|
|
425
|
+
if (cutoutDepth > 5 && clampedTaper < 0.9) {
|
|
426
|
+
pathParts.push(`L ${innerTip.x.toFixed(2)},${innerTip.y.toFixed(2)}`);
|
|
427
|
+
}
|
|
428
|
+
pathParts.push(`L ${arm1InnerCorner.x.toFixed(2)},${arm1InnerCorner.y.toFixed(2)}`);
|
|
429
|
+
pathParts.push(`L ${arm1OuterCorner.x.toFixed(2)},${arm1OuterCorner.y.toFixed(2)}`);
|
|
430
|
+
pathParts.push("Z");
|
|
431
|
+
let path = pathParts.join(" ");
|
|
432
|
+
const allPoints = [tip, arm1End, arm2End, arm1OuterCorner, arm1InnerCorner, arm2OuterCorner, arm2InnerCorner];
|
|
433
|
+
if (cutoutDepth > 5 && clampedTaper < 0.9) {
|
|
434
|
+
allPoints.push(innerTip);
|
|
435
|
+
}
|
|
436
|
+
let minX = Math.min(...allPoints.map((p) => p.x));
|
|
437
|
+
let maxX = Math.max(...allPoints.map((p) => p.x));
|
|
438
|
+
let minY = Math.min(...allPoints.map((p) => p.y));
|
|
439
|
+
let maxY = Math.max(...allPoints.map((p) => p.y));
|
|
440
|
+
const padding = armWidth * 0.2;
|
|
441
|
+
minX -= padding;
|
|
442
|
+
maxX += padding;
|
|
443
|
+
minY -= padding;
|
|
444
|
+
maxY += padding;
|
|
445
|
+
const width = maxX - minX;
|
|
446
|
+
const height = maxY - minY;
|
|
447
|
+
path = normalizePath(path, minX, minY);
|
|
448
|
+
if (rotation !== 0) {
|
|
449
|
+
path = rotatePath(path, rotation, width, height);
|
|
450
|
+
}
|
|
451
|
+
return addSvgFields({ path, width, height, centerX: width / 2, centerY: height / 2 });
|
|
452
|
+
}
|
|
453
|
+
function generateArmSpine(start, end, direction, length, curvature, numSamples) {
|
|
454
|
+
const points = [];
|
|
455
|
+
const perpDir = { x: direction.y, y: -direction.x };
|
|
456
|
+
const bowAmount = length * 0.4 * curvature;
|
|
457
|
+
const midPoint = { x: (start.x + end.x) / 2, y: (start.y + end.y) / 2 };
|
|
458
|
+
const controlPoint = {
|
|
459
|
+
x: midPoint.x + perpDir.x * bowAmount,
|
|
460
|
+
y: midPoint.y + perpDir.y * bowAmount
|
|
461
|
+
};
|
|
462
|
+
for (let i = 0; i <= numSamples; i++) {
|
|
463
|
+
const t = i / numSamples;
|
|
464
|
+
if (curvature < 0.1) {
|
|
465
|
+
points.push({
|
|
466
|
+
x: start.x + (end.x - start.x) * t,
|
|
467
|
+
y: start.y + (end.y - start.y) * t
|
|
468
|
+
});
|
|
469
|
+
} else {
|
|
470
|
+
const mt = 1 - t;
|
|
471
|
+
points.push({
|
|
472
|
+
x: mt * mt * start.x + 2 * mt * t * controlPoint.x + t * t * end.x,
|
|
473
|
+
y: mt * mt * start.y + 2 * mt * t * controlPoint.y + t * t * end.y
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
return points;
|
|
478
|
+
}
|
|
479
|
+
function getSpineTangent(spine, index) {
|
|
480
|
+
const prev = spine[Math.max(0, index - 1)];
|
|
481
|
+
const next = spine[Math.min(spine.length - 1, index + 1)];
|
|
482
|
+
const dx = next.x - prev.x;
|
|
483
|
+
const dy = next.y - prev.y;
|
|
484
|
+
const len = Math.sqrt(dx * dx + dy * dy);
|
|
485
|
+
return len > 0 ? { x: dx / len, y: dy / len } : { x: 1, y: 0 };
|
|
486
|
+
}
|
|
487
|
+
function smoothCurveThroughPoints(points) {
|
|
488
|
+
if (points.length < 2) return "";
|
|
489
|
+
const parts = [];
|
|
490
|
+
const tension = 0.3;
|
|
491
|
+
for (let i = 0; i < points.length - 1; i++) {
|
|
492
|
+
const p0 = points[Math.max(0, i - 1)];
|
|
493
|
+
const p1 = points[i];
|
|
494
|
+
const p2 = points[i + 1];
|
|
495
|
+
const p3 = points[Math.min(points.length - 1, i + 2)];
|
|
496
|
+
const cp1x = p1.x + (p2.x - p0.x) * tension;
|
|
497
|
+
const cp1y = p1.y + (p2.y - p0.y) * tension;
|
|
498
|
+
const cp2x = p2.x - (p3.x - p1.x) * tension;
|
|
499
|
+
const cp2y = p2.y - (p3.y - p1.y) * tension;
|
|
500
|
+
parts.push(`C ${cp1x.toFixed(2)},${cp1y.toFixed(2)} ${cp2x.toFixed(2)},${cp2y.toFixed(2)} ${p2.x.toFixed(2)},${p2.y.toFixed(2)}`);
|
|
501
|
+
}
|
|
502
|
+
return parts.join(" ");
|
|
503
|
+
}
|
|
504
|
+
function normalizePath(path, minX, minY) {
|
|
505
|
+
return path.replace(
|
|
506
|
+
/([-\d.]+),([-\d.]+)/g,
|
|
507
|
+
(_, x, y) => `${(parseFloat(x) - minX).toFixed(2)},${(parseFloat(y) - minY).toFixed(2)}`
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
function rotatePath(path, rotation, width, height) {
|
|
511
|
+
const rad = rotation * Math.PI / 180;
|
|
512
|
+
const cos = Math.cos(rad);
|
|
513
|
+
const sin = Math.sin(rad);
|
|
514
|
+
const cx = width / 2;
|
|
515
|
+
const cy = height / 2;
|
|
516
|
+
return path.replace(
|
|
517
|
+
/([-\d.]+),([-\d.]+)/g,
|
|
518
|
+
(_, xStr, yStr) => {
|
|
519
|
+
const x = parseFloat(xStr) - cx;
|
|
520
|
+
const y = parseFloat(yStr) - cy;
|
|
521
|
+
const rx = x * cos - y * sin + cx;
|
|
522
|
+
const ry = x * sin + y * cos + cy;
|
|
523
|
+
return `${rx.toFixed(2)},${ry.toFixed(2)}`;
|
|
524
|
+
}
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// src/generators/curved/blob.ts
|
|
529
|
+
function seededRandom(seed) {
|
|
530
|
+
let s = seed;
|
|
531
|
+
return () => {
|
|
532
|
+
s = (s * 9301 + 49297) % 233280;
|
|
533
|
+
return s / 233280;
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
function blob(params) {
|
|
537
|
+
const { radius, pointCount, irregularity, smoothing, seed } = params;
|
|
538
|
+
const random = seededRandom(seed);
|
|
539
|
+
const points = [];
|
|
540
|
+
const angleStep = Math.PI * 2 / pointCount;
|
|
541
|
+
for (let i = 0; i < pointCount; i++) {
|
|
542
|
+
const angle = i * angleStep;
|
|
543
|
+
const variation = 1 + (random() - 0.5) * irregularity;
|
|
544
|
+
const r = radius * variation;
|
|
545
|
+
points.push({
|
|
546
|
+
x: Math.cos(angle) * r + radius,
|
|
547
|
+
y: Math.sin(angle) * r + radius
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
const path = buildSmoothPath(points, smoothing);
|
|
551
|
+
return addSvgFields({
|
|
552
|
+
path,
|
|
553
|
+
width: radius * 2,
|
|
554
|
+
height: radius * 2,
|
|
555
|
+
centerX: radius,
|
|
556
|
+
centerY: radius
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
function buildSmoothPath(points, smoothing) {
|
|
560
|
+
if (points.length < 3) return "";
|
|
561
|
+
const parts = [];
|
|
562
|
+
parts.push(`M ${points[0].x.toFixed(2)},${points[0].y.toFixed(2)}`);
|
|
563
|
+
for (let i = 0; i < points.length; i++) {
|
|
564
|
+
const p0 = points[(i - 1 + points.length) % points.length];
|
|
565
|
+
const p1 = points[i];
|
|
566
|
+
const p2 = points[(i + 1) % points.length];
|
|
567
|
+
const p3 = points[(i + 2) % points.length];
|
|
568
|
+
const cp1x = p1.x + (p2.x - p0.x) * smoothing * 0.25;
|
|
569
|
+
const cp1y = p1.y + (p2.y - p0.y) * smoothing * 0.25;
|
|
570
|
+
const cp2x = p2.x - (p3.x - p1.x) * smoothing * 0.25;
|
|
571
|
+
const cp2y = p2.y - (p3.y - p1.y) * smoothing * 0.25;
|
|
572
|
+
parts.push(`C ${cp1x.toFixed(2)},${cp1y.toFixed(2)} ${cp2x.toFixed(2)},${cp2y.toFixed(2)} ${p2.x.toFixed(2)},${p2.y.toFixed(2)}`);
|
|
573
|
+
}
|
|
574
|
+
parts.push("Z");
|
|
575
|
+
return parts.join(" ");
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// src/generators/utils/path.ts
|
|
579
|
+
function degToRad(degrees) {
|
|
580
|
+
return degrees * Math.PI / 180;
|
|
581
|
+
}
|
|
582
|
+
function pointsToPath(points, closed = true) {
|
|
583
|
+
if (points.length === 0) return "";
|
|
584
|
+
const [first, ...rest] = points;
|
|
585
|
+
let path = `M ${first.x},${first.y}`;
|
|
586
|
+
for (const point of rest) {
|
|
587
|
+
path += ` L ${point.x},${point.y}`;
|
|
588
|
+
}
|
|
589
|
+
if (closed) {
|
|
590
|
+
path += " Z";
|
|
591
|
+
}
|
|
592
|
+
return path;
|
|
593
|
+
}
|
|
594
|
+
function circlePath(cx, cy, r) {
|
|
595
|
+
return `M ${cx - r},${cy} A ${r},${r} 0 1,0 ${cx + r},${cy} A ${r},${r} 0 1,0 ${cx - r},${cy}`;
|
|
596
|
+
}
|
|
597
|
+
function ellipsePath(cx, cy, rx, ry) {
|
|
598
|
+
return `M ${cx - rx},${cy} A ${rx},${ry} 0 1,0 ${cx + rx},${cy} A ${rx},${ry} 0 1,0 ${cx - rx},${cy}`;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// src/generators/curved/circle.ts
|
|
602
|
+
function circle(params) {
|
|
603
|
+
const { radius } = params;
|
|
604
|
+
return addSvgFields({
|
|
605
|
+
path: circlePath(radius, radius, radius),
|
|
606
|
+
width: radius * 2,
|
|
607
|
+
height: radius * 2,
|
|
608
|
+
centerX: radius,
|
|
609
|
+
centerY: radius
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// src/generators/curved/kidney.ts
|
|
614
|
+
function kidney(params) {
|
|
615
|
+
const { width, height, concavityDepth, concavityPosition } = params;
|
|
616
|
+
const hw = width / 2;
|
|
617
|
+
const hh = height / 2;
|
|
618
|
+
const concaveX = width * concavityDepth;
|
|
619
|
+
const concaveY = height * concavityPosition;
|
|
620
|
+
const path = [
|
|
621
|
+
`M ${hw},0`,
|
|
622
|
+
// Top right curve
|
|
623
|
+
`C ${width * 0.8},0 ${width},${height * 0.2} ${width},${hh}`,
|
|
624
|
+
// Bottom right curve
|
|
625
|
+
`C ${width},${height * 0.8} ${width * 0.8},${height} ${hw},${height}`,
|
|
626
|
+
// Bottom left curve (going to concave point)
|
|
627
|
+
`C ${width * 0.2},${height} 0,${height * 0.8} ${concaveX},${concaveY + hh * 0.3}`,
|
|
628
|
+
// The concave bite (curves inward)
|
|
629
|
+
`Q ${concaveX + width * 0.1},${concaveY} ${concaveX},${concaveY - hh * 0.3}`,
|
|
630
|
+
// Top left curve (from concave back to top)
|
|
631
|
+
`C 0,${height * 0.2} ${width * 0.2},0 ${hw},0`,
|
|
632
|
+
"Z"
|
|
633
|
+
].join(" ");
|
|
634
|
+
return addSvgFields({
|
|
635
|
+
path,
|
|
636
|
+
width,
|
|
637
|
+
height,
|
|
638
|
+
centerX: hw,
|
|
639
|
+
centerY: hh
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// src/generators/curved/moon.ts
|
|
644
|
+
function moon(params) {
|
|
645
|
+
const { outerRadius, innerRadius, innerOffset } = params;
|
|
646
|
+
const size = outerRadius * 2;
|
|
647
|
+
const center = outerRadius;
|
|
648
|
+
const offset = outerRadius * innerOffset;
|
|
649
|
+
const d = offset;
|
|
650
|
+
if (d >= outerRadius + innerRadius || d === 0) {
|
|
651
|
+
const path2 = `M ${center + outerRadius},${center} A ${outerRadius},${outerRadius} 0 1,1 ${center - outerRadius},${center} A ${outerRadius},${outerRadius} 0 1,1 ${center + outerRadius},${center} Z`;
|
|
652
|
+
return addSvgFields({ path: path2, width: size, height: size, centerX: center, centerY: center });
|
|
653
|
+
}
|
|
654
|
+
const r1 = outerRadius;
|
|
655
|
+
const r2 = innerRadius;
|
|
656
|
+
const x = (d * d + r1 * r1 - r2 * r2) / (2 * d);
|
|
657
|
+
const y = Math.sqrt(Math.max(0, r1 * r1 - x * x));
|
|
658
|
+
const ix1 = center + x;
|
|
659
|
+
const iy1 = center - y;
|
|
660
|
+
const iy2 = center + y;
|
|
661
|
+
const path = [
|
|
662
|
+
`M ${ix1.toFixed(2)},${iy1.toFixed(2)}`,
|
|
663
|
+
// Outer arc from top to bottom (going left/counter-clockwise)
|
|
664
|
+
`A ${r1},${r1} 0 1,0 ${ix1.toFixed(2)},${iy2.toFixed(2)}`,
|
|
665
|
+
// Inner arc from bottom to top (going right/clockwise) - note: arc from inner circle
|
|
666
|
+
`A ${r2},${r2} 0 0,0 ${ix1.toFixed(2)},${iy1.toFixed(2)}`,
|
|
667
|
+
"Z"
|
|
668
|
+
].join(" ");
|
|
669
|
+
return addSvgFields({
|
|
670
|
+
path,
|
|
671
|
+
width: size,
|
|
672
|
+
height: size,
|
|
673
|
+
centerX: center,
|
|
674
|
+
centerY: center
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
// src/generators/curved/oval.ts
|
|
679
|
+
function oval(params) {
|
|
680
|
+
const { width, height } = params;
|
|
681
|
+
const rx = width / 2;
|
|
682
|
+
const ry = height / 2;
|
|
683
|
+
return addSvgFields({
|
|
684
|
+
path: ellipsePath(rx, ry, rx, ry),
|
|
685
|
+
width,
|
|
686
|
+
height,
|
|
687
|
+
centerX: rx,
|
|
688
|
+
centerY: ry
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
// src/generators/curved/semicircle.ts
|
|
693
|
+
function semicircle(params) {
|
|
694
|
+
const { radius, arcAngle, rotation, closed } = params;
|
|
695
|
+
const startAngle = degToRad(rotation - arcAngle / 2);
|
|
696
|
+
const endAngle = degToRad(rotation + arcAngle / 2);
|
|
697
|
+
const startX = Math.cos(startAngle) * radius + radius;
|
|
698
|
+
const startY = Math.sin(startAngle) * radius + radius;
|
|
699
|
+
const endX = Math.cos(endAngle) * radius + radius;
|
|
700
|
+
const endY = Math.sin(endAngle) * radius + radius;
|
|
701
|
+
const largeArc = arcAngle > 180 ? 1 : 0;
|
|
702
|
+
let path;
|
|
703
|
+
if (closed) {
|
|
704
|
+
path = `M ${radius},${radius} L ${startX},${startY} A ${radius},${radius} 0 ${largeArc},1 ${endX},${endY} Z`;
|
|
705
|
+
} else {
|
|
706
|
+
path = `M ${startX},${startY} A ${radius},${radius} 0 ${largeArc},1 ${endX},${endY} Z`;
|
|
707
|
+
}
|
|
708
|
+
return addSvgFields({
|
|
709
|
+
path,
|
|
710
|
+
width: radius * 2,
|
|
711
|
+
height: radius * 2,
|
|
712
|
+
centerX: radius,
|
|
713
|
+
centerY: radius
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
// src/generators/angular/arrow.ts
|
|
718
|
+
function arrow(params) {
|
|
719
|
+
const { length, headWidth, headLength, tailWidth, direction } = params;
|
|
720
|
+
let path;
|
|
721
|
+
let width;
|
|
722
|
+
let height;
|
|
723
|
+
if (tailWidth <= 0) {
|
|
724
|
+
path = buildTriangleArrow(headWidth, headLength, direction);
|
|
725
|
+
if (direction === "up" || direction === "down") {
|
|
726
|
+
width = headWidth;
|
|
727
|
+
height = headLength;
|
|
728
|
+
} else {
|
|
729
|
+
width = headLength;
|
|
730
|
+
height = headWidth;
|
|
731
|
+
}
|
|
732
|
+
} else {
|
|
733
|
+
path = buildFullArrow(length, headWidth, headLength, tailWidth, direction);
|
|
734
|
+
if (direction === "up" || direction === "down") {
|
|
735
|
+
width = headWidth;
|
|
736
|
+
height = length;
|
|
737
|
+
} else {
|
|
738
|
+
width = length;
|
|
739
|
+
height = headWidth;
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
return addSvgFields({
|
|
743
|
+
path,
|
|
744
|
+
width,
|
|
745
|
+
height,
|
|
746
|
+
centerX: width / 2,
|
|
747
|
+
centerY: height / 2
|
|
748
|
+
});
|
|
749
|
+
}
|
|
750
|
+
function buildTriangleArrow(headW, headL, dir) {
|
|
751
|
+
const half = headW / 2;
|
|
752
|
+
switch (dir) {
|
|
753
|
+
case "up":
|
|
754
|
+
return `M ${half},0 L ${headW},${headL} L 0,${headL} Z`;
|
|
755
|
+
case "down":
|
|
756
|
+
return `M 0,0 L ${headW},0 L ${half},${headL} Z`;
|
|
757
|
+
case "left":
|
|
758
|
+
return `M 0,${half} L ${headL},0 L ${headL},${headW} Z`;
|
|
759
|
+
case "right":
|
|
760
|
+
return `M 0,0 L ${headL},${half} L 0,${headW} Z`;
|
|
761
|
+
default:
|
|
762
|
+
return buildTriangleArrow(headW, headL, "right");
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
function buildFullArrow(len, headW, headL, tailW, dir) {
|
|
766
|
+
const halfHead = headW / 2;
|
|
767
|
+
const halfTail = tailW / 2;
|
|
768
|
+
const tailLen = len - headL;
|
|
769
|
+
switch (dir) {
|
|
770
|
+
case "right":
|
|
771
|
+
return `M 0,${halfHead - halfTail} L ${tailLen},${halfHead - halfTail} L ${tailLen},0 L ${len},${halfHead} L ${tailLen},${headW} L ${tailLen},${halfHead + halfTail} L 0,${halfHead + halfTail} Z`;
|
|
772
|
+
case "left":
|
|
773
|
+
return `M ${len},${halfHead - halfTail} L ${headL},${halfHead - halfTail} L ${headL},0 L 0,${halfHead} L ${headL},${headW} L ${headL},${halfHead + halfTail} L ${len},${halfHead + halfTail} Z`;
|
|
774
|
+
case "up":
|
|
775
|
+
return `M ${halfHead - halfTail},${len} L ${halfHead - halfTail},${headL} L 0,${headL} L ${halfHead},0 L ${headW},${headL} L ${halfHead + halfTail},${headL} L ${halfHead + halfTail},${len} Z`;
|
|
776
|
+
case "down":
|
|
777
|
+
return `M ${halfHead - halfTail},0 L ${halfHead + halfTail},0 L ${halfHead + halfTail},${tailLen} L ${headW},${tailLen} L ${halfHead},${len} L 0,${tailLen} L ${halfHead - halfTail},${tailLen} Z`;
|
|
778
|
+
default:
|
|
779
|
+
return buildFullArrow(len, headW, headL, tailW, "right");
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
// src/generators/angular/bowtie.ts
|
|
784
|
+
function bowtie(params) {
|
|
785
|
+
const { width, height, pinchAmount } = params;
|
|
786
|
+
const halfW = width / 2;
|
|
787
|
+
const halfH = height / 2;
|
|
788
|
+
const pinchGap = halfH * (1 - pinchAmount);
|
|
789
|
+
const path = [
|
|
790
|
+
`M 0,${halfH}`,
|
|
791
|
+
// Left point (middle)
|
|
792
|
+
`L ${halfW},${halfH - pinchGap}`,
|
|
793
|
+
// Top of pinch
|
|
794
|
+
`L ${width},0`,
|
|
795
|
+
// Top right corner
|
|
796
|
+
`L ${width},${height}`,
|
|
797
|
+
// Bottom right corner
|
|
798
|
+
`L ${halfW},${halfH + pinchGap}`,
|
|
799
|
+
// Bottom of pinch
|
|
800
|
+
`L 0,${halfH}`,
|
|
801
|
+
// Back to left (close)
|
|
802
|
+
"Z"
|
|
803
|
+
].join(" ");
|
|
804
|
+
return addSvgFields({
|
|
805
|
+
path,
|
|
806
|
+
width,
|
|
807
|
+
height,
|
|
808
|
+
centerX: halfW,
|
|
809
|
+
centerY: halfH
|
|
810
|
+
});
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
// src/generators/angular/chevron.ts
|
|
814
|
+
function chevron(params) {
|
|
815
|
+
const { width, height, thickness, direction } = params;
|
|
816
|
+
let path;
|
|
817
|
+
if (thickness <= 0) {
|
|
818
|
+
path = buildSimpleChevron(width, height, direction);
|
|
819
|
+
} else {
|
|
820
|
+
path = buildThickChevron(width, height, thickness, direction);
|
|
821
|
+
}
|
|
822
|
+
return addSvgFields({
|
|
823
|
+
path,
|
|
824
|
+
width,
|
|
825
|
+
height,
|
|
826
|
+
centerX: width / 2,
|
|
827
|
+
centerY: height / 2
|
|
828
|
+
});
|
|
829
|
+
}
|
|
830
|
+
function buildSimpleChevron(w, h, dir) {
|
|
831
|
+
switch (dir) {
|
|
832
|
+
case "up":
|
|
833
|
+
return `M 0,${h} L ${w / 2},0 L ${w},${h}`;
|
|
834
|
+
case "down":
|
|
835
|
+
return `M 0,0 L ${w / 2},${h} L ${w},0`;
|
|
836
|
+
case "left":
|
|
837
|
+
return `M ${w},0 L 0,${h / 2} L ${w},${h}`;
|
|
838
|
+
case "right":
|
|
839
|
+
return `M 0,0 L ${w},${h / 2} L 0,${h}`;
|
|
840
|
+
default:
|
|
841
|
+
return `M 0,${h} L ${w / 2},0 L ${w},${h}`;
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
function buildThickChevron(w, h, t, dir) {
|
|
845
|
+
const halfW = w / 2;
|
|
846
|
+
switch (dir) {
|
|
847
|
+
case "up":
|
|
848
|
+
return `M 0,${h} L ${halfW},${t} L ${w},${h} L ${w - t},${h} L ${halfW},${t * 2} L ${t},${h} Z`;
|
|
849
|
+
case "down":
|
|
850
|
+
return `M 0,0 L ${t},0 L ${halfW},${h - t * 2} L ${w - t},0 L ${w},0 L ${halfW},${h} Z`;
|
|
851
|
+
case "left":
|
|
852
|
+
return `M ${w},0 L ${w},${t} L ${t * 2},${h / 2} L ${w},${h - t} L ${w},${h} L 0,${h / 2} Z`;
|
|
853
|
+
case "right":
|
|
854
|
+
return `M 0,0 L ${w},${h / 2} L 0,${h} L 0,${h - t} L ${w - t * 2},${h / 2} L 0,${t} Z`;
|
|
855
|
+
default:
|
|
856
|
+
return buildThickChevron(w, h, t, "up");
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
// src/generators/angular/cross.ts
|
|
861
|
+
function cross(params) {
|
|
862
|
+
const { size, armWidth, armRatio } = params;
|
|
863
|
+
const halfSize = size / 2;
|
|
864
|
+
const halfArm = armWidth / 2;
|
|
865
|
+
const armLength = halfSize * armRatio;
|
|
866
|
+
const path = [
|
|
867
|
+
// Top arm
|
|
868
|
+
`M ${halfSize - halfArm},${halfSize - armLength}`,
|
|
869
|
+
`L ${halfSize + halfArm},${halfSize - armLength}`,
|
|
870
|
+
// Top right corner to right arm
|
|
871
|
+
`L ${halfSize + halfArm},${halfSize - halfArm}`,
|
|
872
|
+
`L ${halfSize + armLength},${halfSize - halfArm}`,
|
|
873
|
+
// Right arm
|
|
874
|
+
`L ${halfSize + armLength},${halfSize + halfArm}`,
|
|
875
|
+
// Bottom right corner
|
|
876
|
+
`L ${halfSize + halfArm},${halfSize + halfArm}`,
|
|
877
|
+
// Bottom arm
|
|
878
|
+
`L ${halfSize + halfArm},${halfSize + armLength}`,
|
|
879
|
+
`L ${halfSize - halfArm},${halfSize + armLength}`,
|
|
880
|
+
// Bottom left corner
|
|
881
|
+
`L ${halfSize - halfArm},${halfSize + halfArm}`,
|
|
882
|
+
// Left arm
|
|
883
|
+
`L ${halfSize - armLength},${halfSize + halfArm}`,
|
|
884
|
+
`L ${halfSize - armLength},${halfSize - halfArm}`,
|
|
885
|
+
// Top left corner back to start
|
|
886
|
+
`L ${halfSize - halfArm},${halfSize - halfArm}`,
|
|
887
|
+
"Z"
|
|
888
|
+
].join(" ");
|
|
889
|
+
return addSvgFields({
|
|
890
|
+
path,
|
|
891
|
+
width: size,
|
|
892
|
+
height: size,
|
|
893
|
+
centerX: halfSize,
|
|
894
|
+
centerY: halfSize
|
|
895
|
+
});
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
// src/generators/angular/cursor.ts
|
|
899
|
+
function cursor(params) {
|
|
900
|
+
const { width, height } = params;
|
|
901
|
+
const path = [
|
|
902
|
+
`M 0,0`,
|
|
903
|
+
// Top point
|
|
904
|
+
`L ${width},${height * 0.65}`,
|
|
905
|
+
// Right side
|
|
906
|
+
`L ${width * 0.5},${height * 0.55}`,
|
|
907
|
+
// Notch point (inward)
|
|
908
|
+
`L ${width * 0.35},${height}`,
|
|
909
|
+
// Bottom right of stem
|
|
910
|
+
`L 0,${height * 0.75}`,
|
|
911
|
+
// Left edge
|
|
912
|
+
"Z"
|
|
913
|
+
].join(" ");
|
|
914
|
+
return addSvgFields({
|
|
915
|
+
path,
|
|
916
|
+
width,
|
|
917
|
+
height,
|
|
918
|
+
centerX: width / 3,
|
|
919
|
+
centerY: height / 2
|
|
920
|
+
});
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
// src/generators/angular/diamond.ts
|
|
924
|
+
function diamond(params) {
|
|
925
|
+
const { width, height, bulge, asymmetry = 0 } = params;
|
|
926
|
+
const b = Math.max(0, Math.min(1, bulge));
|
|
927
|
+
const asym = Math.max(-0.5, Math.min(0.5, asymmetry));
|
|
928
|
+
const cx = width / 2;
|
|
929
|
+
const cy = height / 2;
|
|
930
|
+
const top = { x: cx, y: 0 };
|
|
931
|
+
const bottom = { x: cx, y: height };
|
|
932
|
+
const left = { x: 0, y: cy };
|
|
933
|
+
const right = { x: width, y: cy };
|
|
934
|
+
const verticalFactor = 0.4 + b * 0.15;
|
|
935
|
+
const vOffset = cy * verticalFactor;
|
|
936
|
+
const horizontalBulge = b * 0.35;
|
|
937
|
+
const leftBulge = horizontalBulge * (1 - asym);
|
|
938
|
+
const rightBulge = horizontalBulge * (1 + asym);
|
|
939
|
+
const tr1 = { x: cx + cx * 0.2, y: vOffset };
|
|
940
|
+
const tr2 = { x: width + width * rightBulge, y: cy - vOffset };
|
|
941
|
+
const rb1 = { x: width + width * rightBulge, y: cy + vOffset };
|
|
942
|
+
const rb2 = { x: cx + cx * 0.2, y: height - vOffset };
|
|
943
|
+
const bl1 = { x: cx - cx * 0.2, y: height - vOffset };
|
|
944
|
+
const bl2 = { x: -(width * leftBulge), y: cy + vOffset };
|
|
945
|
+
const lt1 = { x: -(width * leftBulge), y: cy - vOffset };
|
|
946
|
+
const lt2 = { x: cx - cx * 0.2, y: vOffset };
|
|
947
|
+
const path = [
|
|
948
|
+
`M ${top.x},${top.y}`,
|
|
949
|
+
// Top to right (upper-right curve)
|
|
950
|
+
`C ${tr1.x},${tr1.y} ${tr2.x},${tr2.y} ${right.x},${right.y}`,
|
|
951
|
+
// Right to bottom (lower-right curve)
|
|
952
|
+
`C ${rb1.x},${rb1.y} ${rb2.x},${rb2.y} ${bottom.x},${bottom.y}`,
|
|
953
|
+
// Bottom to left (lower-left curve)
|
|
954
|
+
`C ${bl1.x},${bl1.y} ${bl2.x},${bl2.y} ${left.x},${left.y}`,
|
|
955
|
+
// Left to top (upper-left curve)
|
|
956
|
+
`C ${lt1.x},${lt1.y} ${lt2.x},${lt2.y} ${top.x},${top.y}`,
|
|
957
|
+
"Z"
|
|
958
|
+
].join(" ");
|
|
959
|
+
return addSvgFields({
|
|
960
|
+
path,
|
|
961
|
+
width,
|
|
962
|
+
height,
|
|
963
|
+
centerX: cx,
|
|
964
|
+
centerY: cy
|
|
965
|
+
});
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
// src/generators/angular/polygon.ts
|
|
969
|
+
function polygon(params) {
|
|
970
|
+
const { sides, radius, rotation } = params;
|
|
971
|
+
const vertices = [];
|
|
972
|
+
const angleStep = 360 / sides;
|
|
973
|
+
const rotationOffset = rotation - 90;
|
|
974
|
+
for (let i = 0; i < sides; i++) {
|
|
975
|
+
const angle = degToRad(i * angleStep + rotationOffset);
|
|
976
|
+
vertices.push({
|
|
977
|
+
x: Math.cos(angle) * radius + radius,
|
|
978
|
+
y: Math.sin(angle) * radius + radius
|
|
979
|
+
});
|
|
980
|
+
}
|
|
981
|
+
return addSvgFields({
|
|
982
|
+
path: pointsToPath(vertices, true),
|
|
983
|
+
width: radius * 2,
|
|
984
|
+
height: radius * 2,
|
|
985
|
+
centerX: radius,
|
|
986
|
+
centerY: radius
|
|
987
|
+
});
|
|
988
|
+
}
|
|
989
|
+
function hexagon(params) {
|
|
990
|
+
return polygon({ sides: 6, ...params });
|
|
991
|
+
}
|
|
992
|
+
function pentagon(params) {
|
|
993
|
+
return polygon({ sides: 5, ...params });
|
|
994
|
+
}
|
|
995
|
+
function octagon(params) {
|
|
996
|
+
return polygon({ sides: 8, ...params });
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
// src/generators/angular/rectangle.ts
|
|
1000
|
+
function rectangle(params) {
|
|
1001
|
+
const { width, height, cornerRadius } = params;
|
|
1002
|
+
const r = Math.min(cornerRadius, width / 2, height / 2);
|
|
1003
|
+
let path;
|
|
1004
|
+
if (r === 0) {
|
|
1005
|
+
path = `M 0,0 L ${width},0 L ${width},${height} L 0,${height} Z`;
|
|
1006
|
+
} else {
|
|
1007
|
+
path = `M ${r},0 L ${width - r},0 A ${r},${r} 0 0,1 ${width},${r} L ${width},${height - r} A ${r},${r} 0 0,1 ${width - r},${height} L ${r},${height} A ${r},${r} 0 0,1 0,${height - r} L 0,${r} A ${r},${r} 0 0,1 ${r},0 Z`;
|
|
1008
|
+
}
|
|
1009
|
+
return addSvgFields({
|
|
1010
|
+
path,
|
|
1011
|
+
width,
|
|
1012
|
+
height,
|
|
1013
|
+
centerX: width / 2,
|
|
1014
|
+
centerY: height / 2
|
|
1015
|
+
});
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
// src/generators/angular/swallowtail.ts
|
|
1019
|
+
function swallowtail(params) {
|
|
1020
|
+
const { width, height, notchDepth, notchCurvature } = params;
|
|
1021
|
+
const notchX = width * (1 - notchDepth);
|
|
1022
|
+
const halfHeight = height / 2;
|
|
1023
|
+
let path;
|
|
1024
|
+
if (notchCurvature < 0.2) {
|
|
1025
|
+
path = `M 0,0 L ${width},0 L ${notchX},${halfHeight} L ${width},${height} L 0,${height} Z`;
|
|
1026
|
+
} else {
|
|
1027
|
+
const cpX = notchX + (width - notchX) * notchCurvature * 0.5;
|
|
1028
|
+
path = `M 0,0 L ${width},0 Q ${cpX},${halfHeight} ${width},${height} L 0,${height} Z`;
|
|
1029
|
+
}
|
|
1030
|
+
return addSvgFields({
|
|
1031
|
+
path,
|
|
1032
|
+
width,
|
|
1033
|
+
height,
|
|
1034
|
+
centerX: width / 2,
|
|
1035
|
+
centerY: halfHeight
|
|
1036
|
+
});
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
// src/generators/angular/triangle.ts
|
|
1040
|
+
function triangle(params) {
|
|
1041
|
+
const { width, height, skew } = params;
|
|
1042
|
+
const halfWidth = width / 2;
|
|
1043
|
+
const apexX = halfWidth + skew * halfWidth;
|
|
1044
|
+
const path = `M 0,${height} L ${width},${height} L ${apexX},0 Z`;
|
|
1045
|
+
return addSvgFields({
|
|
1046
|
+
path,
|
|
1047
|
+
width,
|
|
1048
|
+
height,
|
|
1049
|
+
centerX: width / 2,
|
|
1050
|
+
centerY: height / 2
|
|
1051
|
+
});
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
// src/generators/radial/atomic.ts
|
|
1055
|
+
function atomic(params) {
|
|
1056
|
+
const { orbitCount, orbitRadius, orbitEccentricity, nucleusSize, orbitThickness } = params;
|
|
1057
|
+
const size = orbitRadius * 2;
|
|
1058
|
+
const center = orbitRadius;
|
|
1059
|
+
const a = orbitRadius;
|
|
1060
|
+
const b = orbitRadius * (1 - orbitEccentricity);
|
|
1061
|
+
const parts = [];
|
|
1062
|
+
if (nucleusSize > 0) {
|
|
1063
|
+
parts.push(`M ${center + nucleusSize},${center} A ${nucleusSize},${nucleusSize} 0 1,1 ${center - nucleusSize},${center} A ${nucleusSize},${nucleusSize} 0 1,1 ${center + nucleusSize},${center}`);
|
|
1064
|
+
}
|
|
1065
|
+
const angleStep = 180 / orbitCount;
|
|
1066
|
+
for (let i = 0; i < orbitCount; i++) {
|
|
1067
|
+
const rotation = i * angleStep;
|
|
1068
|
+
const rotRad = degToRad(rotation);
|
|
1069
|
+
const innerA = Math.max(1, a - orbitThickness);
|
|
1070
|
+
const innerB = Math.max(1, b - orbitThickness);
|
|
1071
|
+
const outerPath = createRotatedEllipse(center, center, a, b, rotRad);
|
|
1072
|
+
if (orbitThickness > 0) {
|
|
1073
|
+
const innerPath = createRotatedEllipse(center, center, innerA, innerB, rotRad, true);
|
|
1074
|
+
parts.push(outerPath + " " + innerPath);
|
|
1075
|
+
} else {
|
|
1076
|
+
parts.push(outerPath);
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
return addSvgFields({
|
|
1080
|
+
path: parts.join(" "),
|
|
1081
|
+
width: size,
|
|
1082
|
+
height: size,
|
|
1083
|
+
centerX: center,
|
|
1084
|
+
centerY: center
|
|
1085
|
+
});
|
|
1086
|
+
}
|
|
1087
|
+
function createRotatedEllipse(cx, cy, a, b, rotation, reverse = false) {
|
|
1088
|
+
const cos = Math.cos(rotation);
|
|
1089
|
+
const sin = Math.sin(rotation);
|
|
1090
|
+
const points = [];
|
|
1091
|
+
const segments = 36;
|
|
1092
|
+
for (let i = 0; i < segments; i++) {
|
|
1093
|
+
const t = i / segments * 2 * Math.PI;
|
|
1094
|
+
const ex = a * Math.cos(t);
|
|
1095
|
+
const ey = b * Math.sin(t);
|
|
1096
|
+
const rx = ex * cos - ey * sin;
|
|
1097
|
+
const ry = ex * sin + ey * cos;
|
|
1098
|
+
points.push({ x: cx + rx, y: cy + ry });
|
|
1099
|
+
}
|
|
1100
|
+
if (reverse) {
|
|
1101
|
+
points.reverse();
|
|
1102
|
+
}
|
|
1103
|
+
let path = `M ${points[0].x.toFixed(2)},${points[0].y.toFixed(2)}`;
|
|
1104
|
+
for (let i = 1; i < points.length; i++) {
|
|
1105
|
+
path += ` L ${points[i].x.toFixed(2)},${points[i].y.toFixed(2)}`;
|
|
1106
|
+
}
|
|
1107
|
+
path += " Z";
|
|
1108
|
+
return path;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
// src/generators/radial/flower.ts
|
|
1112
|
+
function flower(params) {
|
|
1113
|
+
const { petalCount, petalLength, petalWidth, centerSize, petalPointedness } = params;
|
|
1114
|
+
const size = (centerSize + petalLength) * 2;
|
|
1115
|
+
const center = size / 2;
|
|
1116
|
+
const parts = [];
|
|
1117
|
+
const angleStep = 360 / petalCount;
|
|
1118
|
+
for (let i = 0; i < petalCount; i++) {
|
|
1119
|
+
const angle = i * angleStep - 90;
|
|
1120
|
+
const angleRad = degToRad(angle);
|
|
1121
|
+
const halfWidth = petalWidth / 2;
|
|
1122
|
+
const baseAngle1 = degToRad(angle - 15);
|
|
1123
|
+
const baseAngle2 = degToRad(angle + 15);
|
|
1124
|
+
const base1X = center + Math.cos(baseAngle1) * centerSize;
|
|
1125
|
+
const base1Y = center + Math.sin(baseAngle1) * centerSize;
|
|
1126
|
+
const base2X = center + Math.cos(baseAngle2) * centerSize;
|
|
1127
|
+
const base2Y = center + Math.sin(baseAngle2) * centerSize;
|
|
1128
|
+
const tipX = center + Math.cos(angleRad) * (centerSize + petalLength);
|
|
1129
|
+
const tipY = center + Math.sin(angleRad) * (centerSize + petalLength);
|
|
1130
|
+
const perpAngle = angleRad + Math.PI / 2;
|
|
1131
|
+
const midRadius = centerSize + petalLength * 0.5;
|
|
1132
|
+
const cp1X = center + Math.cos(angleRad) * midRadius - Math.cos(perpAngle) * halfWidth;
|
|
1133
|
+
const cp1Y = center + Math.sin(angleRad) * midRadius - Math.sin(perpAngle) * halfWidth;
|
|
1134
|
+
const cp2X = center + Math.cos(angleRad) * midRadius + Math.cos(perpAngle) * halfWidth;
|
|
1135
|
+
const cp2Y = center + Math.sin(angleRad) * midRadius + Math.sin(perpAngle) * halfWidth;
|
|
1136
|
+
if (petalPointedness > 0.5) {
|
|
1137
|
+
parts.push(`M ${base1X.toFixed(2)},${base1Y.toFixed(2)}`);
|
|
1138
|
+
parts.push(`Q ${cp1X.toFixed(2)},${cp1Y.toFixed(2)} ${tipX.toFixed(2)},${tipY.toFixed(2)}`);
|
|
1139
|
+
parts.push(`Q ${cp2X.toFixed(2)},${cp2Y.toFixed(2)} ${base2X.toFixed(2)},${base2Y.toFixed(2)}`);
|
|
1140
|
+
parts.push("Z");
|
|
1141
|
+
} else {
|
|
1142
|
+
parts.push(`M ${base1X.toFixed(2)},${base1Y.toFixed(2)}`);
|
|
1143
|
+
parts.push(`C ${cp1X.toFixed(2)},${cp1Y.toFixed(2)} ${tipX.toFixed(2)},${tipY - halfWidth * 0.5} ${tipX.toFixed(2)},${tipY.toFixed(2)}`);
|
|
1144
|
+
parts.push(`C ${tipX.toFixed(2)},${tipY + halfWidth * 0.5} ${cp2X.toFixed(2)},${cp2Y.toFixed(2)} ${base2X.toFixed(2)},${base2Y.toFixed(2)}`);
|
|
1145
|
+
parts.push("Z");
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
if (centerSize > 0) {
|
|
1149
|
+
parts.push(`M ${center + centerSize},${center}`);
|
|
1150
|
+
parts.push(`A ${centerSize},${centerSize} 0 1,1 ${center - centerSize},${center}`);
|
|
1151
|
+
parts.push(`A ${centerSize},${centerSize} 0 1,1 ${center + centerSize},${center}`);
|
|
1152
|
+
}
|
|
1153
|
+
return addSvgFields({
|
|
1154
|
+
path: parts.join(" "),
|
|
1155
|
+
width: size,
|
|
1156
|
+
height: size,
|
|
1157
|
+
centerX: center,
|
|
1158
|
+
centerY: center
|
|
1159
|
+
});
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
// src/generators/radial/spiral.ts
|
|
1163
|
+
function spiral(params) {
|
|
1164
|
+
const { turns, startRadius, expansionRate, thickness, direction } = params;
|
|
1165
|
+
const totalAngle = turns * 360;
|
|
1166
|
+
const steps = Math.ceil(totalAngle / 5);
|
|
1167
|
+
const endRadius = startRadius + turns * expansionRate;
|
|
1168
|
+
const size = (endRadius + thickness) * 2;
|
|
1169
|
+
const center = size / 2;
|
|
1170
|
+
const points = [];
|
|
1171
|
+
for (let i = 0; i <= steps; i++) {
|
|
1172
|
+
const angle = i / steps * totalAngle * direction;
|
|
1173
|
+
const progress = i / steps;
|
|
1174
|
+
const radius = startRadius + progress * turns * expansionRate;
|
|
1175
|
+
const rad = degToRad(angle - 90);
|
|
1176
|
+
points.push({
|
|
1177
|
+
x: center + Math.cos(rad) * radius,
|
|
1178
|
+
y: center + Math.sin(rad) * radius
|
|
1179
|
+
});
|
|
1180
|
+
}
|
|
1181
|
+
let path;
|
|
1182
|
+
if (thickness <= 0) {
|
|
1183
|
+
path = points.map(
|
|
1184
|
+
(p, i) => (i === 0 ? "M" : "L") + ` ${p.x.toFixed(2)},${p.y.toFixed(2)}`
|
|
1185
|
+
).join(" ");
|
|
1186
|
+
} else {
|
|
1187
|
+
const innerPoints = [];
|
|
1188
|
+
const outerPoints = [];
|
|
1189
|
+
for (let i = 0; i <= steps; i++) {
|
|
1190
|
+
const angle = i / steps * totalAngle * direction;
|
|
1191
|
+
const progress = i / steps;
|
|
1192
|
+
const radius = startRadius + progress * turns * expansionRate;
|
|
1193
|
+
const halfThick = thickness / 2;
|
|
1194
|
+
const rad = degToRad(angle - 90);
|
|
1195
|
+
innerPoints.push({
|
|
1196
|
+
x: center + Math.cos(rad) * (radius - halfThick),
|
|
1197
|
+
y: center + Math.sin(rad) * (radius - halfThick)
|
|
1198
|
+
});
|
|
1199
|
+
outerPoints.push({
|
|
1200
|
+
x: center + Math.cos(rad) * (radius + halfThick),
|
|
1201
|
+
y: center + Math.sin(rad) * (radius + halfThick)
|
|
1202
|
+
});
|
|
1203
|
+
}
|
|
1204
|
+
const outerPath = outerPoints.map(
|
|
1205
|
+
(p, i) => (i === 0 ? "M" : "L") + ` ${p.x.toFixed(2)},${p.y.toFixed(2)}`
|
|
1206
|
+
).join(" ");
|
|
1207
|
+
const innerPath = innerPoints.reverse().map(
|
|
1208
|
+
(p) => `L ${p.x.toFixed(2)},${p.y.toFixed(2)}`
|
|
1209
|
+
).join(" ");
|
|
1210
|
+
path = outerPath + " " + innerPath + " Z";
|
|
1211
|
+
}
|
|
1212
|
+
return addSvgFields({
|
|
1213
|
+
path,
|
|
1214
|
+
width: size,
|
|
1215
|
+
height: size,
|
|
1216
|
+
centerX: center,
|
|
1217
|
+
centerY: center
|
|
1218
|
+
});
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
// src/generators/radial/star.ts
|
|
1222
|
+
function star(params) {
|
|
1223
|
+
const { points, rotation } = params;
|
|
1224
|
+
const maxRadius = Math.max(params.innerRadius, params.outerRadius);
|
|
1225
|
+
const minRadius = Math.min(params.innerRadius, params.outerRadius);
|
|
1226
|
+
const outerRadius = maxRadius;
|
|
1227
|
+
const innerRadius = minRadius;
|
|
1228
|
+
const vertices = [];
|
|
1229
|
+
const totalVertices = points * 2;
|
|
1230
|
+
const angleStep = 360 / totalVertices;
|
|
1231
|
+
const rotationRad = degToRad(rotation - 90);
|
|
1232
|
+
for (let i = 0; i < totalVertices; i++) {
|
|
1233
|
+
const angle = degToRad(i * angleStep) + rotationRad;
|
|
1234
|
+
const radius = i % 2 === 0 ? outerRadius : innerRadius;
|
|
1235
|
+
vertices.push({
|
|
1236
|
+
x: Math.cos(angle) * radius,
|
|
1237
|
+
y: Math.sin(angle) * radius
|
|
1238
|
+
});
|
|
1239
|
+
}
|
|
1240
|
+
const offset = outerRadius;
|
|
1241
|
+
const offsetVertices = vertices.map((v) => ({
|
|
1242
|
+
x: v.x + offset,
|
|
1243
|
+
y: v.y + offset
|
|
1244
|
+
}));
|
|
1245
|
+
return addSvgFields({
|
|
1246
|
+
path: pointsToPath(offsetVertices, true),
|
|
1247
|
+
width: outerRadius * 2,
|
|
1248
|
+
height: outerRadius * 2,
|
|
1249
|
+
centerX: offset,
|
|
1250
|
+
centerY: offset
|
|
1251
|
+
});
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
// src/generators/radial/starburst.ts
|
|
1255
|
+
function starburst(params) {
|
|
1256
|
+
const { rayCount, rayThickness, taper } = params;
|
|
1257
|
+
const outerRadius = Math.max(params.innerRadius, params.outerRadius);
|
|
1258
|
+
const innerRadius = Math.min(params.innerRadius, params.outerRadius);
|
|
1259
|
+
let fullPath = "";
|
|
1260
|
+
const angleStep = 360 / rayCount;
|
|
1261
|
+
const effectiveRadius = outerRadius + rayThickness / 2;
|
|
1262
|
+
const offset = effectiveRadius;
|
|
1263
|
+
for (let i = 0; i < rayCount; i++) {
|
|
1264
|
+
const angle = degToRad(i * angleStep - 90);
|
|
1265
|
+
const halfThicknessInner = rayThickness / 2;
|
|
1266
|
+
const halfThicknessOuter = halfThicknessInner * (1 - taper);
|
|
1267
|
+
const perpAngle = angle + Math.PI / 2;
|
|
1268
|
+
const innerLeft = {
|
|
1269
|
+
x: Math.cos(angle) * innerRadius + Math.cos(perpAngle) * halfThicknessInner + offset,
|
|
1270
|
+
y: Math.sin(angle) * innerRadius + Math.sin(perpAngle) * halfThicknessInner + offset
|
|
1271
|
+
};
|
|
1272
|
+
const innerRight = {
|
|
1273
|
+
x: Math.cos(angle) * innerRadius - Math.cos(perpAngle) * halfThicknessInner + offset,
|
|
1274
|
+
y: Math.sin(angle) * innerRadius - Math.sin(perpAngle) * halfThicknessInner + offset
|
|
1275
|
+
};
|
|
1276
|
+
const outerLeft = {
|
|
1277
|
+
x: Math.cos(angle) * outerRadius + Math.cos(perpAngle) * halfThicknessOuter + offset,
|
|
1278
|
+
y: Math.sin(angle) * outerRadius + Math.sin(perpAngle) * halfThicknessOuter + offset
|
|
1279
|
+
};
|
|
1280
|
+
const outerRight = {
|
|
1281
|
+
x: Math.cos(angle) * outerRadius - Math.cos(perpAngle) * halfThicknessOuter + offset,
|
|
1282
|
+
y: Math.sin(angle) * outerRadius - Math.sin(perpAngle) * halfThicknessOuter + offset
|
|
1283
|
+
};
|
|
1284
|
+
fullPath += pointsToPath([innerLeft, outerLeft, outerRight, innerRight], true) + " ";
|
|
1285
|
+
}
|
|
1286
|
+
return addSvgFields({
|
|
1287
|
+
path: fullPath.trim(),
|
|
1288
|
+
width: effectiveRadius * 2,
|
|
1289
|
+
height: effectiveRadius * 2,
|
|
1290
|
+
centerX: offset,
|
|
1291
|
+
centerY: offset
|
|
1292
|
+
});
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
// src/generators/radial/sun.ts
|
|
1296
|
+
function sun(params) {
|
|
1297
|
+
const { discRadius, rayCount, rayLength, rayWidth, rayTaper } = params;
|
|
1298
|
+
const totalRadius = discRadius + rayLength;
|
|
1299
|
+
const effectiveRadius = totalRadius + rayWidth / 2;
|
|
1300
|
+
const size = effectiveRadius * 2;
|
|
1301
|
+
const center = size / 2;
|
|
1302
|
+
const parts = [];
|
|
1303
|
+
const angleStep = 360 / rayCount;
|
|
1304
|
+
for (let i = 0; i < rayCount; i++) {
|
|
1305
|
+
const angle = i * angleStep - 90;
|
|
1306
|
+
const angleRad = degToRad(angle);
|
|
1307
|
+
const halfBase = rayWidth / 2;
|
|
1308
|
+
const halfTip = halfBase * (1 - rayTaper);
|
|
1309
|
+
const perpRad = angleRad + Math.PI / 2;
|
|
1310
|
+
const baseX = center + Math.cos(angleRad) * discRadius;
|
|
1311
|
+
const baseY = center + Math.sin(angleRad) * discRadius;
|
|
1312
|
+
const tipX = center + Math.cos(angleRad) * totalRadius;
|
|
1313
|
+
const tipY = center + Math.sin(angleRad) * totalRadius;
|
|
1314
|
+
const baseLeftX = baseX + Math.cos(perpRad) * halfBase;
|
|
1315
|
+
const baseLeftY = baseY + Math.sin(perpRad) * halfBase;
|
|
1316
|
+
const baseRightX = baseX - Math.cos(perpRad) * halfBase;
|
|
1317
|
+
const baseRightY = baseY - Math.sin(perpRad) * halfBase;
|
|
1318
|
+
const tipLeftX = tipX + Math.cos(perpRad) * halfTip;
|
|
1319
|
+
const tipLeftY = tipY + Math.sin(perpRad) * halfTip;
|
|
1320
|
+
const tipRightX = tipX - Math.cos(perpRad) * halfTip;
|
|
1321
|
+
const tipRightY = tipY - Math.sin(perpRad) * halfTip;
|
|
1322
|
+
if (rayTaper >= 0.95) {
|
|
1323
|
+
parts.push(`M ${baseLeftX.toFixed(2)},${baseLeftY.toFixed(2)} L ${tipX.toFixed(2)},${tipY.toFixed(2)} L ${baseRightX.toFixed(2)},${baseRightY.toFixed(2)} Z`);
|
|
1324
|
+
} else {
|
|
1325
|
+
parts.push(`M ${baseLeftX.toFixed(2)},${baseLeftY.toFixed(2)} L ${tipLeftX.toFixed(2)},${tipLeftY.toFixed(2)} L ${tipRightX.toFixed(2)},${tipRightY.toFixed(2)} L ${baseRightX.toFixed(2)},${baseRightY.toFixed(2)} Z`);
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
parts.push(`M ${center + discRadius},${center} A ${discRadius},${discRadius} 0 1,1 ${center - discRadius},${center} A ${discRadius},${discRadius} 0 1,1 ${center + discRadius},${center}`);
|
|
1329
|
+
return addSvgFields({
|
|
1330
|
+
path: parts.join(" "),
|
|
1331
|
+
width: size,
|
|
1332
|
+
height: size,
|
|
1333
|
+
centerX: center,
|
|
1334
|
+
centerY: center
|
|
1335
|
+
});
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
// src/generators/silhouettes/bird.ts
|
|
1339
|
+
var TRACED_PATHS = {
|
|
1340
|
+
// From 0022_004.svg - Bird in flight with spread wings (166x104 viewBox)
|
|
1341
|
+
flying: {
|
|
1342
|
+
path: `M0 0 C3.62 -0.06 7.25 -0.09 10.88 -0.13 C11.89 -0.14 12.9 -0.16 13.95 -0.18 C27.1 -0.26 39.18 2.37 49.25 11.44 C54.47 17.05 57.66 24.18 61 31 C63.61 29.69 63.7 28.62 64.75 25.94 C69.27 15.5 76.68 8 87.25 3.63 C92.1 1.8 96.68 0.88 101.87 0.87 C102.8 0.87 103.74 0.86 104.71 0.86 C106.21 0.86 106.21 0.86 107.73 0.87 C109.29 0.87 109.29 0.87 110.87 0.86 C113.05 0.86 115.22 0.87 117.4 0.87 C120.75 0.88 124.1 0.87 127.45 0.86 C129.57 0.86 131.68 0.87 133.8 0.87 C135.31 0.86 135.31 0.86 136.85 0.86 C138.24 0.87 138.24 0.87 139.65 0.87 C140.88 0.87 140.88 0.87 142.13 0.87 C144 1 144 1 145 2 C145.11 13.16 140.33 22.51 132.81 30.56 C129 34.28 124.75 36.66 120 39 C119.25 39.38 118.5 39.75 117.73 40.14 C117.16 40.42 116.59 40.71 116 41 C118.44 43.87 120.87 45.52 124.19 47.19 C134.06 52.52 140.24 60.7 143.74 71.23 C144.73 74.83 145.34 78.27 145 82 C143.44 83.56 141.94 83.15 139.78 83.18 C138.88 83.19 137.98 83.2 137.05 83.22 C135.58 83.23 135.58 83.23 134.08 83.24 C133.07 83.25 132.06 83.25 131.01 83.26 C128.86 83.28 126.71 83.29 124.56 83.3 C121.31 83.31 118.05 83.36 114.8 83.4 C98.5 83.52 85.63 82.43 73 71 C70.73 68.48 68.83 65.86 67 63 C66.59 62.39 66.18 61.79 65.75 61.16 C64.82 59.79 63.91 58.4 63 57 C62.01 57.5 62.01 57.5 61 58 C61.01 58.72 61.01 59.44 61.02 60.19 C61.04 63.44 61.05 66.69 61.06 69.94 C61.07 71.07 61.08 72.21 61.09 73.38 C61.09 74.46 61.09 75.54 61.1 76.65 C61.1 77.65 61.11 78.65 61.11 79.68 C61 82 61 82 60 83 C49.72 83.86 40.78 79.2 32.94 72.88 C24.73 65.46 20.88 55.86 20 45 C19.95 42.8 19.91 40.61 19.89 38.41 C19.88 37.21 19.86 36.02 19.85 34.78 C19.82 32.31 19.82 29.83 19.83 27.35 C19.69 19.45 18.66 13.79 13 8 C8.7 5.09 3.96 3.49 -1 2 C-0.67 1.34 -0.34 0.68 0 0 Z`,
|
|
1343
|
+
viewBox: { width: 166, height: 104 },
|
|
1344
|
+
translate: { x: 11, y: 10 }
|
|
1345
|
+
},
|
|
1346
|
+
// From 0044_006.svg - Bird perched/sitting (165x181 viewBox)
|
|
1347
|
+
perched: {
|
|
1348
|
+
path: `M0 0 C-1.43 4.39 -3.59 7.07 -6.73 10.42 C-7.22 10.94 -7.7 11.45 -8.19 11.98 C-14.39 18.56 -20.83 24.9 -27.25 31.25 C-37.95 41.82 -37.95 41.82 -48 53 C-49.8 55.01 -51.62 57.01 -53.44 59 C-54.3 59.95 -55.17 60.9 -56.06 61.88 C-56.7 62.58 -57.34 63.28 -58 64 C-57.67 64.66 -57.34 65.32 -57 66 C-53 66.09 -53 66.09 -49 66 C-48.67 65.67 -48.34 65.34 -48 65 C-40.54 64.64 -40.54 64.64 -37 67 C-35.33 66.35 -33.66 65.68 -32 65 C-29.16 64.73 -26.47 64.57 -23.63 64.56 C-22.86 64.56 -22.09 64.55 -21.31 64.55 C-17.21 64.64 -14.31 64.74 -11.06 67.5 C-6.76 71.01 -3.33 70.42 2 70 C5.46 70 7.74 70.83 11 72 C11.97 72.27 11.97 72.27 12.96 72.55 C18.52 74.28 21.97 76.99 25 82 C25.88 85.44 25.88 85.44 26 88 C25.01 88 24.02 88 23 88 C19.17 88.21 16.78 88.36 13.78 90.82 C10.66 96.39 11.84 102.53 12.16 108.75 C12.38 116.09 11.59 122.47 8 129 C6 130.98 3.94 132.66 1.71 134.38 C-0.36 136.07 -0.36 136.07 -1.13 139.75 C-1.28 143.15 -1.28 143.15 1 146 C3.54 147.09 6.02 148.04 8.63 148.94 C14.3 150.97 19.69 153.12 25 156 C23 159 23 159 20.64 159.51 C19.18 159.6 19.18 159.6 17.69 159.69 C16.74 159.75 15.8 159.82 14.82 159.89 C4.07 160.21 -6.42 159.78 -17.13 158.69 C-17.8 158.62 -18.48 158.55 -19.18 158.48 C-50.54 155.31 -92.47 150.85 -115 126 C-119.26 119.07 -120 112.02 -118.42 104.09 C-114.56 89.96 -106.68 77.84 -97 67 C-96.34 66.23 -95.68 65.46 -95 64.67 C-83.8 51.86 -71.76 40 -58 30 C-56.98 29.26 -56.98 29.26 -55.95 28.5 C-11.57 -3.77 -11.57 -3.77 0 0 Z`,
|
|
1349
|
+
viewBox: { width: 165, height: 181 },
|
|
1350
|
+
translate: { x: 130, y: 11 }
|
|
1351
|
+
},
|
|
1352
|
+
// From 0044_014.svg - Simple stylized bird (83x83 viewBox)
|
|
1353
|
+
simple: {
|
|
1354
|
+
path: `M0 0 C4.89 3.46 6.97 7.09 8.56 12.81 C7.9 13.14 7.24 13.47 6.56 13.81 C5.98 13.61 5.41 13.4 4.81 13.19 C1.38 12.61 -0.54 14.05 -3.44 15.81 C-5.72 19.23 -5.65 20.3 -5.53 24.29 C-5.5 25.92 -5.5 25.92 -5.47 27.57 C-5.41 29.84 -5.34 32.1 -5.27 34.37 C-5.11 43.64 -6.53 50.34 -12.44 57.81 C-16.22 61.15 -19.9 61.33 -24.76 61.15 C-35.86 59.74 -44.59 52.73 -51.29 44.11 C-56.17 37.61 -56.17 37.61 -57.44 33.81 C-53.5 33.81 -52.44 35.39 -49.44 37.81 C-47.51 38.96 -47.51 38.96 -45.44 38.81 C-40.21 36.2 -39.13 31.96 -37.39 26.75 C-37.08 25.78 -36.76 24.81 -36.44 23.81 C-33.08 14.3 -29.39 5.9 -20.37 0.69 C-14.06 -2.17 -6.48 -2.24 0 0 Z`,
|
|
1355
|
+
viewBox: { width: 83, height: 83 },
|
|
1356
|
+
translate: { x: 65.4375, y: 11.1875 }
|
|
1357
|
+
}
|
|
1358
|
+
};
|
|
1359
|
+
function bird(params) {
|
|
1360
|
+
const { size, style } = params;
|
|
1361
|
+
const traced = TRACED_PATHS[style];
|
|
1362
|
+
const maxDimension = Math.max(traced.viewBox.width, traced.viewBox.height);
|
|
1363
|
+
const scale = size / maxDimension;
|
|
1364
|
+
const actualWidth = traced.viewBox.width * scale;
|
|
1365
|
+
const actualHeight = traced.viewBox.height * scale;
|
|
1366
|
+
const normalizedPath = normalizeAndScalePath(traced.path, traced.translate, scale);
|
|
1367
|
+
return addSvgFields({
|
|
1368
|
+
path: normalizedPath,
|
|
1369
|
+
width: actualWidth,
|
|
1370
|
+
height: actualHeight,
|
|
1371
|
+
centerX: actualWidth / 2,
|
|
1372
|
+
centerY: actualHeight / 2
|
|
1373
|
+
});
|
|
1374
|
+
}
|
|
1375
|
+
function normalizeAndScalePath(path, translate, scale) {
|
|
1376
|
+
return path.replace(
|
|
1377
|
+
/(-?\d+\.?\d*)\s+(-?\d+\.?\d*)/g,
|
|
1378
|
+
(_, x, y) => {
|
|
1379
|
+
const newX = (parseFloat(x) + translate.x) * scale;
|
|
1380
|
+
const newY = (parseFloat(y) + translate.y) * scale;
|
|
1381
|
+
return `${newX.toFixed(2)} ${newY.toFixed(2)}`;
|
|
1382
|
+
}
|
|
1383
|
+
);
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
// src/generators/silhouettes/cat.ts
|
|
1387
|
+
var TRACED_PATHS2 = {
|
|
1388
|
+
// From 0026_007.svg - Standing cat silhouette (109x216 viewBox)
|
|
1389
|
+
sitting: {
|
|
1390
|
+
path: `M0 0 C0.99 0.66 1.98 1.32 3 2 C2.88 2.81 2.76 3.62 2.64 4.45 C1.59 11 1.59 11 3.94 16.88 C4.62 17.58 5.3 18.28 6 19 C6.69 21.94 6.69 21.94 6 25 C2.35 29.67 -2.21 31.74 -7.78 33.34 C-10.98 34.29 -13.95 35.64 -17 37 C-16.52 51.05 -13.13 63.42 -8 76.38 C-3.31 88.21 0.25 100.43 0.63 113.25 C0.88 115.42 0.88 115.42 4 117 C5.04 113.88 5.14 111.68 5.16 108.4 C5.17 106.68 5.17 106.68 5.19 104.92 C5.2 103.68 5.2 102.43 5.21 101.15 C5.23 99.19 5.25 97.23 5.27 95.28 C5.33 90.45 5.37 85.62 5.4 80.8 C5.42 78.09 5.44 75.39 5.48 72.68 C5.51 70.14 5.53 67.59 5.53 65.05 C5.65 56.73 6.34 49.12 10.94 42 C13.49 39.52 15.54 38.1 19.15 37.85 C24.98 38.41 28.14 39.47 32 44 C33.01 46.92 33.05 48.81 33 52 C31.81 54.06 31.81 54.06 30 55 C28 55.06 28 55.06 26 54 C24.25 50.94 24.25 50.94 23 48 C19.07 48.57 19.07 48.57 16 51 C15.68 54.01 15.48 56.92 15.38 59.94 C15.34 60.87 15.3 61.8 15.26 62.75 C14.86 73.64 14.92 84.52 14.98 95.41 C15.07 112.58 15.13 129.77 14.79 146.94 C14.76 148.45 14.74 149.97 14.73 151.48 C14.59 164.94 12.21 176.71 3 187 C-3.63 192.93 -14.68 197.14 -23.5 197.5 C-29.91 196.58 -33.61 194.01 -37.56 189.06 C-54.37 165.01 -53.77 127.39 -48.98 99.77 C-47.06 89.86 -43.71 80.61 -39.88 71.31 C-35.96 61.8 -32.76 51.02 -32.94 40.63 C-32.96 39.43 -32.98 38.23 -33 37 C-38.07 34.75 -43.18 32.88 -48.45 31.16 C-52.4 29.36 -54.53 27.24 -56.06 23.19 C-55.99 19.62 -55.64 19.3 -53.44 16.75 C-51.5 14.38 -50.96 13.51 -51.18 10.4 C-51.59 8.63 -52.06 6.88 -52.58 5.14 C-52.79 4.08 -52.79 4.08 -53 3 C-52.34 2.34 -51.68 1.68 -51 1 C-47.62 1.33 -46.03 2.74 -43.61 5.01 C-37.98 9.36 -32.06 8.95 -25.19 8.88 C-23.98 8.91 -22.77 8.95 -21.53 9 C-12.18 8.99 -6.9 6.21 0 0 Z`,
|
|
1391
|
+
viewBox: { width: 109, height: 216 },
|
|
1392
|
+
translate: { x: 66, y: 9 }
|
|
1393
|
+
},
|
|
1394
|
+
// From 0042_002.svg - Cat in profile/walking pose (191x425 viewBox)
|
|
1395
|
+
walking: {
|
|
1396
|
+
path: `M0 0 C10.29 7.63 13.75 19.98 17.54 31.72 C19.71 38.31 19.71 38.31 24.78 42.72 C26.37 43.35 26.37 43.35 28 44 C25.14 63.94 6.86 83.15 -8.39 94.83 C-13.4 98.45 -18.48 101.42 -24.07 104.05 C-25.96 104.85 -25.96 104.85 -27 106 C-27.35 115.76 -23.74 124.93 -20.63 134 C-20.23 135.18 -20.23 135.18 -19.82 136.39 C-15.15 150.18 -9.94 163.69 -4.23 177.1 C-3.07 179.84 -1.92 182.6 -0.78 185.36 C2.18 192.5 5.23 199.58 8.56 206.56 C22.5 236.07 30.23 268.33 30.1 300.98 C30.1 302.4 30.09 303.82 30.09 305.24 C30.09 308.94 30.08 312.65 30.07 316.35 C30.06 320.15 30.05 323.94 30.05 327.74 C30.04 335.16 30.02 342.58 30 350 C30.99 350 31.98 350 33 350 C37.81 337.57 42.15 325.32 44.59 312.2 C44.89 310.6 45.2 309.01 45.53 307.41 C52.34 273.03 40.61 236.94 27.92 205.3 C12.07 165.59 12.07 165.59 15 156 C16.51 153.98 17.47 153.17 19.88 152.42 C31.37 151.17 41.43 154.96 52 159 C52 159.99 52 160.98 52 162 C45.25 162.14 38.69 161.76 31.98 161.02 C28.67 161 26.8 161.2 24 163 C22.32 167.81 23.4 171.25 25.06 175.94 C25.29 176.6 25.53 177.26 25.77 177.94 C27.49 182.79 29.4 187.57 31.35 192.34 C53.2 246.42 67.82 301.72 44 358 C43.59 359.04 43.18 360.07 42.75 361.14 C37.54 374.09 31.19 386.75 18 393 C16.93 393.59 16.93 393.59 15.84 394.2 C-9.5 407.94 -43.12 406.72 -70.25 399.19 C-80.09 395.81 -89.3 389.36 -94.08 379.85 C-95.24 376.26 -95.33 372.91 -95.3 369.19 C-95.3 368.41 -95.29 367.63 -95.29 366.83 C-95.12 357.19 -93.87 347.67 -92.6 138.12 C-92.19 335.01 -91.79 331.89 -91.4 328.77 C-87.11 295.21 -87.11 295.21 -82.44 279.88 C-82.1 278.74 -81.75 277.6 -81.4 276.43 C-72.57 247.97 -58.92 221.89 -40.31 198.61 C-31.53 187.6 -25.93 177.42 -26.77 163 C-27.39 157.55 -29.44 152.37 -31.23 147.23 C-32.64 143.14 -33.92 139.01 -35.19 134.88 C-35.45 134.03 -35.7 133.19 -35.97 132.32 C-37.82 126.06 -38.97 119.96 -39.68 113.46 C-39.74 111.06 -39.74 111.06 -41 110 C-46.91 109.46 -52.68 109.66 -57.69 113.19 C-59.23 115.17 -59.23 115.17 -60.5 118.5 C-62.52 123.21 -62.52 123.21 -65 125 C-65.56 125.08 -66.13 125.17 -66.71 125.25 C-70.15 126.37 -72.56 128.72 -75.25 131.06 C-75.8 131.53 -76.36 132.01 -76.93 132.49 C-78.29 133.66 -79.65 134.83 -81 136 C-81.99 135.01 -81.99 135.01 -83 134 C-81.02 131.69 -79.04 129.38 -77 127 C-77.66 126.01 -78.32 125.02 -79 124 C-74.27 119.62 -69.73 115.98 -64 113 C-64.33 112.34 -64.66 111.68 -65 111 C-67.39 110.59 -69.71 110.29 -72.13 110.06 C-77.9 109.42 -83.61 108.62 -89.31 107.5 C-90.53 107.26 -91.74 107.03 -92.99 106.78 C-96 106 -96 106 -99 104 C-98.77 103.41 -98.55 102.81 -98.32 102.2 C-97.12 98.21 -96.38 95.33 -98.18 91.45 C-99.4 89.66 -100.65 87.88 -101.92 86.11 C-107.93 77.12 -112.45 65.7 -114 55 C-112.43 53.43 -110.91 53.81 -108.73 53.79 C-102.42 53.85 -96.46 55.1 -90.31 56.44 C-86.21 57.3 -82.11 58.16 -78 59 C-77.27 59.16 -76.55 59.32 -75.8 59.49 C-73.05 60.02 -71.49 60.29 -69.02 58.85 C-68.48 58.26 -67.94 57.67 -67.38 57.06 C-56.55 46.69 -41.79 40.39 -27.49 36.28 C-22.82 35.01 -22.82 35.01 -19.27 31.92 C-18.85 30.96 -18.43 29.99 -18 29 C-17.19 27.48 -16.37 25.96 -15.55 24.45 C-14.15 21.86 -12.76 19.26 -11.38 16.66 C-8.11 10.62 -4.37 5.29 0 0 Z`,
|
|
1397
|
+
viewBox: { width: 191, height: 425 },
|
|
1398
|
+
translate: { x: 124, y: 10 }
|
|
1399
|
+
},
|
|
1400
|
+
// From 0055_023.svg - Cat stretching/arched (185x253 viewBox)
|
|
1401
|
+
stretching: {
|
|
1402
|
+
path: `M0 0 C2.13 1.13 2.13 1.13 4 3 C6.45 11.19 4.54 20.53 2.27 28.57 C1.92 31.71 2.76 32.85 4.5 35.44 C6.3 38.45 6.99 39.67 7.06 43.25 C5.86 46.37 4.09 48.42 2 51 C2.33 51.99 2.66 52.98 3 54 C3.93 54.31 4.86 54.62 5.81 54.94 C9 56 9 56 10.94 58.06 C13.76 60.72 16.23 60.64 20 61 C20.33 61.33 20.66 61.66 21 62 C22.83 62.25 24.67 62.47 26.5 62.69 C35.09 64.07 41.4 68.28 46.78 75.09 C51.31 82.18 52.09 90.02 50.31 98.13 C48.09 102.99 45.69 106.78 41.31 109.94 C36.73 111.41 32.75 112.05 28 111 C23.76 108.36 20.76 105.14 18 101 C15.5 100.58 15.5 100.58 13 101 C12.34 101.66 11.68 102.32 11 103 C11.38 108.48 12.54 113.64 13.94 118.94 C14.64 121.62 15.33 124.31 16 127 C16.18 127.72 16.36 128.44 16.55 129.18 C20.51 145.81 20.84 165.17 11.69 180.15 C7.45 186.15 2.1 190.94 -4 195 C-4.85 198.03 -4.85 198.03 -5.38 201.56 C-7.2 210.73 -10.22 217.46 -17.57 223.43 C-32.9 233.32 -55.4 233.2 -72.81 229.81 C-82 227.43 -90.19 223.76 -96 216 C-101.96 205.42 -102.48 193.3 -99.75 181.57 C-95.52 166.1 -88.12 154.19 -80 141.5 C-69.62 125.26 -59.92 108.81 -59.81 88.94 C-59.8 87.75 -59.78 86.56 -59.77 85.34 C-60 82 -60.24 79.85 -62 77 C-64.51 75.88 -64.51 75.88 -67.5 75.44 C-85.46 71.58 -99.68 61.6 -111.38 47.61 C-113.18 44.7 -113.52 43.35 -113 40 C-111.71 37.97 -110.38 35.97 -109 34 C-107.78 31.22 -107.9 30.3 -108.88 27.38 C-111.66 19.03 -112.21 9.22 -109 1 C-103.32 0.46 -99.75 2.23 -94.88 4.81 C-94.17 5.17 -93.47 5.53 -92.75 5.89 C-89.88 7.37 -87.26 8.8 -84.7 10.77 C-82.94 12.31 -82.94 12.31 -79.94 11.88 C-76.76 11.26 -73.67 10.37 -70.56 9.5 C-65.98 8.22 -61.74 7.38 -57 7 C-56.35 6.95 -55.7 6.89 -55.04 6.84 C-46.71 6.48 -37.64 8.28 -29.86 11.16 C-25.74 12.37 -23.36 11.73 -19.64 9.73 C-18.85 9.22 -18.06 8.71 -17.25 8.19 C-11.75 4.77 -6.15 2.05 0 0 Z`,
|
|
1403
|
+
viewBox: { width: 185, height: 253 },
|
|
1404
|
+
translate: { x: 124, y: 10 }
|
|
1405
|
+
}
|
|
1406
|
+
};
|
|
1407
|
+
function cat(params) {
|
|
1408
|
+
const { size, style } = params;
|
|
1409
|
+
const traced = TRACED_PATHS2[style];
|
|
1410
|
+
const maxDimension = Math.max(traced.viewBox.width, traced.viewBox.height);
|
|
1411
|
+
const scale = size / maxDimension;
|
|
1412
|
+
const actualWidth = traced.viewBox.width * scale;
|
|
1413
|
+
const actualHeight = traced.viewBox.height * scale;
|
|
1414
|
+
const normalizedPath = normalizeAndScalePath2(traced.path, traced.translate, scale);
|
|
1415
|
+
return addSvgFields({
|
|
1416
|
+
path: normalizedPath,
|
|
1417
|
+
width: actualWidth,
|
|
1418
|
+
height: actualHeight,
|
|
1419
|
+
centerX: actualWidth / 2,
|
|
1420
|
+
centerY: actualHeight / 2
|
|
1421
|
+
});
|
|
1422
|
+
}
|
|
1423
|
+
function normalizeAndScalePath2(path, translate, scale) {
|
|
1424
|
+
return path.replace(
|
|
1425
|
+
/(-?\d+\.?\d*)\s+(-?\d+\.?\d*)/g,
|
|
1426
|
+
(_, x, y) => {
|
|
1427
|
+
const newX = (parseFloat(x) + translate.x) * scale;
|
|
1428
|
+
const newY = (parseFloat(y) + translate.y) * scale;
|
|
1429
|
+
return `${newX.toFixed(2)} ${newY.toFixed(2)}`;
|
|
1430
|
+
}
|
|
1431
|
+
);
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
// src/generators/silhouettes/dog.ts
|
|
1435
|
+
function dog(params) {
|
|
1436
|
+
const { size, style } = params;
|
|
1437
|
+
const scale = size / 100;
|
|
1438
|
+
let basePath;
|
|
1439
|
+
switch (style) {
|
|
1440
|
+
case "running":
|
|
1441
|
+
basePath = `M 10,50 C 5,45 5,35 15,30 L 25,20 C 30,15 35,20 35,25 L 40,30 C 50,25 60,25 70,30 L 80,25 C 85,22 90,28 85,35 L 90,40 C 95,45 95,55 90,60 L 95,75 L 85,75 L 80,60 L 70,65 L 65,80 L 55,80 L 60,65 L 40,65 L 35,80 L 25,80 L 30,65 L 20,60 L 10,75 L 5,70 L 15,55 C 10,55 5,55 10,50 Z M 25,35 C 27,35 28,37 28,39 C 28,41 27,42 25,42 C 23,42 22,41 22,39 C 22,37 23,35 25,35 Z`;
|
|
1442
|
+
break;
|
|
1443
|
+
case "sitting":
|
|
1444
|
+
basePath = `M 50,15 C 40,15 35,25 40,35 L 35,35 C 30,35 28,40 32,45 L 25,50 C 20,55 20,70 25,80 L 25,95 L 40,95 L 40,85 C 45,82 55,82 60,85 L 60,95 L 75,95 L 75,80 C 80,70 80,55 75,50 L 68,45 C 72,40 70,35 65,35 L 60,35 C 65,25 60,15 50,15 Z M 42,30 C 44,30 45,32 45,34 C 45,36 44,38 42,38 C 40,38 39,36 39,34 C 39,32 40,30 42,30 Z M 58,30 C 60,30 61,32 61,34 C 61,36 60,38 58,38 C 56,38 55,36 55,34 C 55,32 56,30 58,30 Z`;
|
|
1445
|
+
break;
|
|
1446
|
+
case "standing":
|
|
1447
|
+
default:
|
|
1448
|
+
basePath = `M 15,40 C 10,35 10,25 20,20 L 25,10 C 28,5 35,10 33,18 L 40,25 C 55,20 70,25 80,35 L 90,30 C 95,28 98,35 93,40 L 95,50 C 98,60 95,70 90,75 L 90,90 L 80,90 L 80,75 L 70,75 L 70,90 L 60,90 L 60,75 L 40,75 L 40,90 L 30,90 L 30,75 L 20,75 L 20,90 L 10,90 L 10,75 C 5,70 5,55 15,45 Z M 25,30 C 27,30 28,32 28,34 C 28,36 27,38 25,38 C 23,38 22,36 22,34 C 22,32 23,30 25,30 Z`;
|
|
1449
|
+
}
|
|
1450
|
+
const path = scalePath(basePath, scale);
|
|
1451
|
+
return addSvgFields({
|
|
1452
|
+
path,
|
|
1453
|
+
width: size,
|
|
1454
|
+
height: size,
|
|
1455
|
+
centerX: size / 2,
|
|
1456
|
+
centerY: size / 2
|
|
1457
|
+
});
|
|
1458
|
+
}
|
|
1459
|
+
function scalePath(path, scale) {
|
|
1460
|
+
return path.replace(/([\d.]+),([\d.]+)/g, (_, x, y) => {
|
|
1461
|
+
return `${(parseFloat(x) * scale).toFixed(2)},${(parseFloat(y) * scale).toFixed(2)}`;
|
|
1462
|
+
});
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
// src/generators/silhouettes/fish.ts
|
|
1466
|
+
var TRACED_PATHS3 = {
|
|
1467
|
+
// From 0057_017.svg - Tropical/detailed fish (169x134 viewBox) - main silhouette shape
|
|
1468
|
+
tropical: {
|
|
1469
|
+
path: `M0 0 C0.79 -0 1.58 -0.01 2.39 -0.01 C9.57 0.05 16.08 1.05 22.5 4.31 C23.3 4.47 24.11 4.63 24.93 4.79 C36.56 7.15 46.43 17.19 52.89 26.7 C54.87 29.86 56.74 33.03 58.5 36.31 C59.43 38.02 59.43 38.02 60.39 39.75 C63.19 45.43 65.13 51.46 67.13 57.46 C67.82 59.51 68.52 61.55 69.23 63.6 C69.67 64.91 70.11 66.23 70.55 67.54 C70.95 68.72 71.35 69.9 71.76 71.12 C72.51 74.34 72.44 76.18 71.5 79.31 C46.89 86.28 14.65 85.14 -8.13 72.9 C-12.51 70.2 -15.57 67.52 -18.14 63.02 C-19.54 61.06 -19.54 61.06 -22.38 59.59 C-26.31 56.72 -27.74 53.89 -29.75 49.56 C-30.09 48.88 -30.43 48.21 -30.77 47.51 C-35.11 38.53 -37.19 29.37 -34.31 19.63 C-31.37 11.81 -25.78 7.11 -18.5 3.31 C-12.27 0.68 -6.72 -0.14 0 0 Z`,
|
|
1470
|
+
viewBox: { width: 129, height: 104 },
|
|
1471
|
+
translate: { x: 45.5, y: 9.6875 }
|
|
1472
|
+
},
|
|
1473
|
+
// From 0005_004.svg - Simple fish shape (115x46 viewBox)
|
|
1474
|
+
simple: {
|
|
1475
|
+
path: `M0 0 C0.8 0.11 1.6 0.22 2.42 0.34 C4.93 0.69 7.43 1.06 9.94 1.44 C10.71 1.55 11.49 1.66 12.29 1.77 C14.21 2.08 16.11 2.53 18 3 C18.99 4.48 18.99 4.48 20 6 C19 7 19 7 16.23 7.03 C15.04 7 13.85 6.97 12.63 6.94 C6.18 6.88 0.27 7.57 -6 9 C-6 9.99 -6 10.98 -6 12 C-1.16 14.55 3.49 14.23 8.81 14.13 C10.14 14.11 10.14 14.11 11.49 14.1 C13.66 14.07 15.83 14.04 18 14 C18 14.99 18 15.98 18 17 C14.99 18.69 12.45 19.38 9.04 19.82 C8.08 19.94 7.12 20.06 6.13 20.19 C5.12 20.31 4.11 20.44 3.06 20.56 C2.02 20.69 0.97 20.82 -0.11 20.95 C-3.07 21.31 -6.03 21.66 -9 22 C-10.19 22.14 -11.37 22.27 -12.6 22.41 C-34.59 24.49 -58.75 25.32 -77 11 C-75.96 7.84 -74.96 6.98 -72.09 5.18 C-49.24 -6.03 -24.56 -3.65 0 0 Z`,
|
|
1476
|
+
viewBox: { width: 115, height: 46 },
|
|
1477
|
+
translate: { x: 86, y: 12 }
|
|
1478
|
+
},
|
|
1479
|
+
// From 0017_003.svg - Koi/curved fish shape (130x77 viewBox)
|
|
1480
|
+
koi: {
|
|
1481
|
+
path: `M0 0 C0.83 -0.02 1.65 -0.03 2.5 -0.05 C9.21 0.07 14.46 1.87 19.52 6.35 C24.99 13.78 26.51 22.44 25.5 31.56 C24.54 36.47 23.23 39.75 19.88 43.5 C8.35 50.54 -5.42 47.39 -17.98 45.3 C-23.15 44.44 -28.27 43.84 -33.5 43.56 C-34.64 43.47 -35.78 43.39 -36.96 43.3 C-44.03 42.97 -48.8 43.26 -54.46 47.7 C-57.93 50.87 -61.29 54.12 -64.5 57.56 C-65.16 56.9 -65.82 56.24 -66.5 55.56 C-62.54 49.62 -58.58 43.68 -54.5 37.56 C-56.86 32.84 -57.85 32.63 -62.6 30.77 C-63.81 30.28 -65.03 29.8 -66.27 29.3 C-67.56 28.8 -68.84 28.31 -70.13 27.81 C-71.42 27.3 -72.71 26.79 -74 26.28 C-77.16 25.03 -80.33 23.79 -83.5 22.56 C-83.5 21.57 -83.5 20.58 -83.5 19.56 C-82.84 19.58 -82.17 19.59 -81.49 19.61 C-63.01 19.99 -50.8 18.47 -34.48 9.7 C-23.13 3.65 -12.96 0.19 0 0 Z`,
|
|
1482
|
+
viewBox: { width: 130, height: 77 },
|
|
1483
|
+
translate: { x: 94.5, y: 10.4375 }
|
|
1484
|
+
}
|
|
1485
|
+
};
|
|
1486
|
+
function fish(params) {
|
|
1487
|
+
const { size, style } = params;
|
|
1488
|
+
const traced = TRACED_PATHS3[style];
|
|
1489
|
+
const maxDimension = Math.max(traced.viewBox.width, traced.viewBox.height);
|
|
1490
|
+
const scale = size / maxDimension;
|
|
1491
|
+
const actualWidth = traced.viewBox.width * scale;
|
|
1492
|
+
const actualHeight = traced.viewBox.height * scale;
|
|
1493
|
+
const normalizedPath = normalizeAndScalePath3(traced.path, traced.translate, scale);
|
|
1494
|
+
return addSvgFields({
|
|
1495
|
+
path: normalizedPath,
|
|
1496
|
+
width: actualWidth,
|
|
1497
|
+
height: actualHeight,
|
|
1498
|
+
centerX: actualWidth / 2,
|
|
1499
|
+
centerY: actualHeight / 2
|
|
1500
|
+
});
|
|
1501
|
+
}
|
|
1502
|
+
function normalizeAndScalePath3(path, translate, scale) {
|
|
1503
|
+
return path.replace(
|
|
1504
|
+
/(-?\d+\.?\d*)\s+(-?\d+\.?\d*)/g,
|
|
1505
|
+
(_, x, y) => {
|
|
1506
|
+
const newX = (parseFloat(x) + translate.x) * scale;
|
|
1507
|
+
const newY = (parseFloat(y) + translate.y) * scale;
|
|
1508
|
+
return `${newX.toFixed(2)} ${newY.toFixed(2)}`;
|
|
1509
|
+
}
|
|
1510
|
+
);
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
// src/generators/silhouettes/leaf.ts
|
|
1514
|
+
var TRACED_PATHS4 = {
|
|
1515
|
+
// From 0016_020.svg - Oval/simple leaf shape (61x136 viewBox)
|
|
1516
|
+
oval: {
|
|
1517
|
+
path: `M0 0 C3.88 3.57 6.56 7.18 9 11.88 C9.34 12.53 9.68 13.18 10.04 13.85 C21.05 35.48 24.35 62.76 17.32 86.18 C13.41 97.84 8.26 108.05 1 118 C-12.5 105.38 -18.61 82.92 -19.28 65.03 C-19.8 41.42 -14.64 21.9 -2 2 C-1.34 1.34 -0.68 0.68 0 0 Z`,
|
|
1518
|
+
viewBox: { width: 61, height: 136 },
|
|
1519
|
+
translate: { x: 30, y: 9 }
|
|
1520
|
+
},
|
|
1521
|
+
// From 0048_016.svg - Pointed/elongated leaf (92x205 viewBox)
|
|
1522
|
+
pointed: {
|
|
1523
|
+
path: `M0 0 C3.27 1.35 4.61 2.47 6.15 5.65 C6.51 6.4 6.87 7.14 7.25 7.92 C7.62 8.73 7.99 9.54 8.38 10.38 C8.77 11.22 9.16 12.06 9.56 12.93 C23.31 43.54 25.37 78.11 13.85 109.79 C12.16 114.18 10.33 118.5 8.44 122.81 C8.06 123.68 7.69 124.55 7.3 125.44 C6.93 126.25 6.56 127.06 6.18 127.89 C5.86 128.61 5.54 129.34 5.2 130.08 C3.79 132.34 2.2 133.53 0 135 C-1.98 132.69 -3.96 130.38 -6 128 C-9.51 131.51 -9.69 133.9 -10.31 138.56 C-12.83 154.7 -18.3 172.36 -29 185 C-32 184 -32 184 -33.07 182.43 C-33.39 181.75 -33.72 181.08 -34.05 180.38 C-34.42 179.62 -34.78 178.86 -35.16 178.07 C-35.54 177.24 -35.92 176.42 -36.31 175.56 C-36.71 174.71 -37.1 173.86 -37.51 172.98 C-44.81 156.92 -50.26 139.73 -51 122 C-51.03 121.37 -51.05 120.73 -51.08 120.08 C-52 95.22 -46.67 69.95 -31 50 C-27.17 51.45 -25.88 53.43 -24 57 C-23.01 56.5 -23.01 56.5 -22 56 C-21.43 54.19 -21.43 54.19 -21.11 51.98 C-20.96 51.15 -20.81 50.32 -20.66 49.46 C-20.5 48.57 -20.35 47.67 -20.19 46.75 C-18.02 35.21 -14.15 25.15 -9 14.63 C-8.68 13.97 -8.36 13.32 -8.03 12.64 C-5.73 8.1 -3.04 4.08 0 0 Z`,
|
|
1524
|
+
viewBox: { width: 92, height: 205 },
|
|
1525
|
+
translate: { x: 61, y: 10 }
|
|
1526
|
+
},
|
|
1527
|
+
// From 0035_033.svg - Broad/dual leaf shape (82x44 viewBox)
|
|
1528
|
+
broad: {
|
|
1529
|
+
path: `M0 0 C1.94 2.44 1.94 2.44 2.5 5.81 C1.63 11.44 -0.6 14.31 -5.06 17.75 C-9.66 20.34 -13.36 20.62 -18.63 20.69 C-20.33 20.73 -20.33 20.73 -22.07 20.77 C-25.06 20.44 -25.06 20.44 -26.96 18.92 C-27.33 18.43 -27.69 17.94 -28.06 17.44 C-27.01 15.18 -25.95 12.93 -24.88 10.69 C-24.58 10.05 -24.29 9.42 -23.99 8.77 C-21.7 4.04 -19.19 0.56 -14.03 -1.29 C-8.93 -1.99 -4.68 -2.39 0 0 Z`,
|
|
1530
|
+
viewBox: { width: 82, height: 44 },
|
|
1531
|
+
translate: { x: 69.0625, y: 12.5625 }
|
|
1532
|
+
}
|
|
1533
|
+
};
|
|
1534
|
+
function leaf(params) {
|
|
1535
|
+
const { size, style } = params;
|
|
1536
|
+
const traced = TRACED_PATHS4[style];
|
|
1537
|
+
const maxDimension = Math.max(traced.viewBox.width, traced.viewBox.height);
|
|
1538
|
+
const scale = size / maxDimension;
|
|
1539
|
+
const actualWidth = traced.viewBox.width * scale;
|
|
1540
|
+
const actualHeight = traced.viewBox.height * scale;
|
|
1541
|
+
const normalizedPath = normalizeAndScalePath4(traced.path, traced.translate, scale);
|
|
1542
|
+
return addSvgFields({
|
|
1543
|
+
path: normalizedPath,
|
|
1544
|
+
width: actualWidth,
|
|
1545
|
+
height: actualHeight,
|
|
1546
|
+
centerX: actualWidth / 2,
|
|
1547
|
+
centerY: actualHeight / 2
|
|
1548
|
+
});
|
|
1549
|
+
}
|
|
1550
|
+
function normalizeAndScalePath4(path, translate, scale) {
|
|
1551
|
+
return path.replace(
|
|
1552
|
+
/(-?\d+\.?\d*)\s+(-?\d+\.?\d*)/g,
|
|
1553
|
+
(_, x, y) => {
|
|
1554
|
+
const newX = (parseFloat(x) + translate.x) * scale;
|
|
1555
|
+
const newY = (parseFloat(y) + translate.y) * scale;
|
|
1556
|
+
return `${newX.toFixed(2)} ${newY.toFixed(2)}`;
|
|
1557
|
+
}
|
|
1558
|
+
);
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
// src/generators/silhouettes/rocket.ts
|
|
1562
|
+
function rocket(params) {
|
|
1563
|
+
const { size, style } = params;
|
|
1564
|
+
const scale = size / 100;
|
|
1565
|
+
let basePath;
|
|
1566
|
+
switch (style) {
|
|
1567
|
+
case "streamlined":
|
|
1568
|
+
basePath = `M 50,5 C 40,10 35,25 35,40 L 20,55 L 25,60 L 35,55 L 35,75 L 40,90 L 45,85 L 50,95 L 55,85 L 60,90 L 65,75 L 65,55 L 75,60 L 80,55 L 65,40 C 65,25 60,10 50,5 Z M 45,30 C 47,30 50,35 50,40 C 50,45 47,50 45,50 C 43,50 40,45 40,40 C 40,35 43,30 45,30 Z`;
|
|
1569
|
+
break;
|
|
1570
|
+
case "chunky":
|
|
1571
|
+
basePath = `M 50,5 C 35,10 30,30 30,50 L 15,60 L 15,75 L 30,70 L 30,80 L 35,95 L 45,85 L 50,95 L 55,85 L 65,95 L 70,80 L 70,70 L 85,75 L 85,60 L 70,50 C 70,30 65,10 50,5 Z M 40,35 L 60,35 L 60,55 L 40,55 Z`;
|
|
1572
|
+
break;
|
|
1573
|
+
case "classic":
|
|
1574
|
+
default:
|
|
1575
|
+
basePath = `M 50,5 C 40,8 38,20 38,35 L 20,50 L 22,60 L 38,52 L 38,75 L 42,95 L 50,80 L 58,95 L 62,75 L 62,52 L 78,60 L 80,50 L 62,35 C 62,20 60,8 50,5 Z M 44,30 C 48,30 52,35 52,42 C 52,49 48,55 44,55 C 40,55 36,49 36,42 C 36,35 40,30 44,30 Z M 50,60 L 45,70 L 50,68 L 55,70 Z`;
|
|
1576
|
+
}
|
|
1577
|
+
const path = scalePath2(basePath, scale);
|
|
1578
|
+
return addSvgFields({
|
|
1579
|
+
path,
|
|
1580
|
+
width: size,
|
|
1581
|
+
height: size,
|
|
1582
|
+
centerX: size / 2,
|
|
1583
|
+
centerY: size / 2
|
|
1584
|
+
});
|
|
1585
|
+
}
|
|
1586
|
+
function scalePath2(path, scale) {
|
|
1587
|
+
return path.replace(/([\d.]+),([\d.]+)/g, (_, x, y) => {
|
|
1588
|
+
return `${(parseFloat(x) * scale).toFixed(2)},${(parseFloat(y) * scale).toFixed(2)}`;
|
|
1589
|
+
});
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
export {
|
|
1593
|
+
boomerang,
|
|
1594
|
+
blob,
|
|
1595
|
+
circle,
|
|
1596
|
+
kidney,
|
|
1597
|
+
moon,
|
|
1598
|
+
oval,
|
|
1599
|
+
semicircle,
|
|
1600
|
+
arrow,
|
|
1601
|
+
bowtie,
|
|
1602
|
+
chevron,
|
|
1603
|
+
cross,
|
|
1604
|
+
cursor,
|
|
1605
|
+
diamond,
|
|
1606
|
+
polygon,
|
|
1607
|
+
hexagon,
|
|
1608
|
+
pentagon,
|
|
1609
|
+
octagon,
|
|
1610
|
+
rectangle,
|
|
1611
|
+
swallowtail,
|
|
1612
|
+
triangle,
|
|
1613
|
+
atomic,
|
|
1614
|
+
flower,
|
|
1615
|
+
spiral,
|
|
1616
|
+
star,
|
|
1617
|
+
starburst,
|
|
1618
|
+
sun,
|
|
1619
|
+
bird,
|
|
1620
|
+
cat,
|
|
1621
|
+
dog,
|
|
1622
|
+
fish,
|
|
1623
|
+
leaf,
|
|
1624
|
+
rocket
|
|
1625
|
+
};
|
|
1626
|
+
//# sourceMappingURL=chunk-5A7FSZ3T.js.map
|