closr
Browse files
src/lib/components/MonsterGenerator/MonsterGenerator.svelte
CHANGED
@@ -28,7 +28,10 @@ Create a Pokémon-style monster that transforms the object into an imaginative c
|
|
28 |
|
29 |
Guidelines:
|
30 |
- Take the object's key visual elements (colors, shapes, materials) and incorporate them into a creature design
|
31 |
-
- Add
|
|
|
|
|
|
|
32 |
- The object should be recognizable but creatively interpreted
|
33 |
|
34 |
Rarity assessment: Common objects = weaker monsters. Unique/rare objects = stronger monsters.
|
@@ -42,7 +45,7 @@ Include:
|
|
42 |
const IMAGE_GENERATION_PROMPT = (concept: string) => `Convert this monster concept into a clear and succinct description of its appearance:
|
43 |
"${concept}"
|
44 |
|
45 |
-
Include all of its visual details, format the description as a single long sentence.`;
|
46 |
|
47 |
const MONSTER_STATS_PROMPT = (concept: string) => `Convert the following monster concept into a JSON object with stats:
|
48 |
|
|
|
28 |
|
29 |
Guidelines:
|
30 |
- Take the object's key visual elements (colors, shapes, materials) and incorporate them into a creature design
|
31 |
+
- Add eyes (can be glowing, mechanical, multiple, etc.) positioned where they make sense
|
32 |
+
- Include limbs (legs, arms, wings, tentacles) that grow from or replace parts of the object
|
33 |
+
- Add a mouth, beak, or feeding apparatus if appropriate
|
34 |
+
- Add creature elements like tail, fins, claws, or horns where fitting
|
35 |
- The object should be recognizable but creatively interpreted
|
36 |
|
37 |
Rarity assessment: Common objects = weaker monsters. Unique/rare objects = stronger monsters.
|
|
|
45 |
const IMAGE_GENERATION_PROMPT = (concept: string) => `Convert this monster concept into a clear and succinct description of its appearance:
|
46 |
"${concept}"
|
47 |
|
48 |
+
Include all of its visual details, most importantly include its creature features, format the description as a single long sentence.`;
|
49 |
|
50 |
const MONSTER_STATS_PROMPT = (concept: string) => `Convert the following monster concept into a JSON object with stats:
|
51 |
|
src/lib/utils/imageProcessing.ts
CHANGED
@@ -1,5 +1,6 @@
|
|
1 |
/**
|
2 |
* Converts an image URL to a base64 data URL with white background made transparent
|
|
|
3 |
*/
|
4 |
export async function makeWhiteTransparent(imageUrl: string): Promise<string> {
|
5 |
return new Promise((resolve, reject) => {
|
@@ -24,23 +25,99 @@ export async function makeWhiteTransparent(imageUrl: string): Promise<string> {
|
|
24 |
// Get image data
|
25 |
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
26 |
const data = imageData.data;
|
|
|
|
|
27 |
|
28 |
-
//
|
29 |
-
const
|
30 |
|
31 |
-
//
|
32 |
-
|
|
|
|
|
|
|
|
|
|
|
33 |
const r = data[i];
|
34 |
const g = data[i + 1];
|
35 |
const b = data[i + 2];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
36 |
|
37 |
-
// Check
|
38 |
-
|
39 |
-
|
40 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
41 |
|
42 |
-
|
43 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
44 |
}
|
45 |
}
|
46 |
|
|
|
1 |
/**
|
2 |
* Converts an image URL to a base64 data URL with white background made transparent
|
3 |
+
* Uses flood-fill from edges to only remove background white, preserving internal white
|
4 |
*/
|
5 |
export async function makeWhiteTransparent(imageUrl: string): Promise<string> {
|
6 |
return new Promise((resolve, reject) => {
|
|
|
25 |
// Get image data
|
26 |
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
27 |
const data = imageData.data;
|
28 |
+
const width = canvas.width;
|
29 |
+
const height = canvas.height;
|
30 |
|
31 |
+
// Create a mask to track which pixels to make transparent
|
32 |
+
const mask = new Uint8Array(width * height);
|
33 |
|
34 |
+
// Define white threshold with some tolerance
|
35 |
+
const whiteThreshold = 240;
|
36 |
+
const tolerance = 20;
|
37 |
+
|
38 |
+
// Helper function to check if a pixel is white-ish
|
39 |
+
const isWhite = (index: number): boolean => {
|
40 |
+
const i = index * 4;
|
41 |
const r = data[i];
|
42 |
const g = data[i + 1];
|
43 |
const b = data[i + 2];
|
44 |
+
return r > whiteThreshold && g > whiteThreshold && b > whiteThreshold;
|
45 |
+
};
|
46 |
+
|
47 |
+
// Helper function to check if colors are similar
|
48 |
+
const colorSimilar = (i1: number, i2: number): boolean => {
|
49 |
+
const idx1 = i1 * 4;
|
50 |
+
const idx2 = i2 * 4;
|
51 |
+
return Math.abs(data[idx1] - data[idx2]) < tolerance &&
|
52 |
+
Math.abs(data[idx1 + 1] - data[idx2 + 1]) < tolerance &&
|
53 |
+
Math.abs(data[idx1 + 2] - data[idx2 + 2]) < tolerance;
|
54 |
+
};
|
55 |
+
|
56 |
+
// Flood fill from edges
|
57 |
+
const queue: number[] = [];
|
58 |
+
|
59 |
+
// Add all edge pixels that are white to the queue
|
60 |
+
// Top and bottom edges
|
61 |
+
for (let x = 0; x < width; x++) {
|
62 |
+
if (isWhite(x)) {
|
63 |
+
queue.push(x);
|
64 |
+
mask[x] = 1;
|
65 |
+
}
|
66 |
+
const bottomIdx = (height - 1) * width + x;
|
67 |
+
if (isWhite(bottomIdx)) {
|
68 |
+
queue.push(bottomIdx);
|
69 |
+
mask[bottomIdx] = 1;
|
70 |
+
}
|
71 |
+
}
|
72 |
+
|
73 |
+
// Left and right edges
|
74 |
+
for (let y = 1; y < height - 1; y++) {
|
75 |
+
const leftIdx = y * width;
|
76 |
+
if (isWhite(leftIdx)) {
|
77 |
+
queue.push(leftIdx);
|
78 |
+
mask[leftIdx] = 1;
|
79 |
+
}
|
80 |
+
const rightIdx = y * width + width - 1;
|
81 |
+
if (isWhite(rightIdx)) {
|
82 |
+
queue.push(rightIdx);
|
83 |
+
mask[rightIdx] = 1;
|
84 |
+
}
|
85 |
+
}
|
86 |
+
|
87 |
+
// Flood fill
|
88 |
+
while (queue.length > 0) {
|
89 |
+
const idx = queue.pop()!;
|
90 |
+
const x = idx % width;
|
91 |
+
const y = Math.floor(idx / width);
|
92 |
|
93 |
+
// Check 4 neighbors
|
94 |
+
const neighbors = [
|
95 |
+
{ dx: -1, dy: 0 }, // left
|
96 |
+
{ dx: 1, dy: 0 }, // right
|
97 |
+
{ dx: 0, dy: -1 }, // up
|
98 |
+
{ dx: 0, dy: 1 } // down
|
99 |
+
];
|
100 |
+
|
101 |
+
for (const { dx, dy } of neighbors) {
|
102 |
+
const nx = x + dx;
|
103 |
+
const ny = y + dy;
|
104 |
|
105 |
+
if (nx >= 0 && nx < width && ny >= 0 && ny < height) {
|
106 |
+
const nIdx = ny * width + nx;
|
107 |
+
|
108 |
+
// If not already marked and color is similar to current pixel
|
109 |
+
if (!mask[nIdx] && isWhite(nIdx) && colorSimilar(idx, nIdx)) {
|
110 |
+
mask[nIdx] = 1;
|
111 |
+
queue.push(nIdx);
|
112 |
+
}
|
113 |
+
}
|
114 |
+
}
|
115 |
+
}
|
116 |
+
|
117 |
+
// Apply transparency based on mask
|
118 |
+
for (let i = 0; i < mask.length; i++) {
|
119 |
+
if (mask[i]) {
|
120 |
+
data[i * 4 + 3] = 0; // Set alpha to 0
|
121 |
}
|
122 |
}
|
123 |
|