jree423 commited on
Commit
c158dc0
·
verified ·
1 Parent(s): ee7ff50

Update: Add versatile SVG generator implementation

Browse files
Files changed (2) hide show
  1. handler.py +11 -11
  2. versatile_svg_generator.py +1160 -0
handler.py CHANGED
@@ -24,12 +24,12 @@ except ImportError:
24
  subprocess.check_call(["pip", "install", "git+https://github.com/openai/CLIP.git"])
25
  import clip
26
 
27
- # Import the simplified DiffSketcher
28
  try:
29
- from simplified_diffsketcher import SimplifiedDiffSketcher
30
  except ImportError:
31
- print("Warning: simplified_diffsketcher not found. Using placeholder.")
32
- SimplifiedDiffSketcher = None
33
 
34
  class EndpointHandler:
35
  def __init__(self, model_dir):
@@ -38,14 +38,14 @@ class EndpointHandler:
38
  self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
39
  print(f"Initializing model on device: {self.device}")
40
 
41
- # Initialize the simplified DiffSketcher if available
42
- if SimplifiedDiffSketcher is not None:
43
  try:
44
- self.model = SimplifiedDiffSketcher(model_dir)
45
  self.use_model = True
46
- print("Simplified DiffSketcher initialized successfully")
47
  except Exception as e:
48
- print(f"Error initializing simplified DiffSketcher: {e}")
49
  self.use_model = False
50
  else:
51
  self.use_model = False
@@ -76,11 +76,11 @@ class EndpointHandler:
76
  # Generate SVG using the model or placeholder
77
  if self.use_model:
78
  try:
79
- # Use the simplified DiffSketcher
80
  result = self.model(prompt)
81
  image = result["image"]
82
  except Exception as e:
83
- print(f"Error using simplified DiffSketcher: {e}")
84
  # Fall back to placeholder
85
  svg_content = self.generate_placeholder_svg(prompt)
86
  png_data = cairosvg.svg2png(bytestring=svg_content.encode("utf-8"))
 
24
  subprocess.check_call(["pip", "install", "git+https://github.com/openai/CLIP.git"])
25
  import clip
26
 
27
+ # Import the versatile SVG generator
28
  try:
29
+ from versatile_svg_generator import VersatileSVGGenerator
30
  except ImportError:
31
+ print("Warning: versatile_svg_generator not found. Using placeholder.")
32
+ VersatileSVGGenerator = None
33
 
34
  class EndpointHandler:
35
  def __init__(self, model_dir):
 
38
  self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
39
  print(f"Initializing model on device: {self.device}")
40
 
41
+ # Initialize the versatile SVG generator if available
42
+ if VersatileSVGGenerator is not None:
43
  try:
44
+ self.model = VersatileSVGGenerator(model_dir)
45
  self.use_model = True
46
+ print("Versatile SVG generator initialized successfully")
47
  except Exception as e:
48
+ print(f"Error initializing versatile SVG generator: {e}")
49
  self.use_model = False
50
  else:
51
  self.use_model = False
 
76
  # Generate SVG using the model or placeholder
77
  if self.use_model:
78
  try:
79
+ # Use the versatile SVG generator
80
  result = self.model(prompt)
81
  image = result["image"]
82
  except Exception as e:
83
+ print(f"Error using versatile SVG generator: {e}")
84
  # Fall back to placeholder
85
  svg_content = self.generate_placeholder_svg(prompt)
86
  png_data = cairosvg.svg2png(bytestring=svg_content.encode("utf-8"))
versatile_svg_generator.py ADDED
@@ -0,0 +1,1160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ Versatile SVG Generator that creates different types of objects based on the prompt.
6
+ """
7
+
8
+ import os
9
+ import io
10
+ import base64
11
+ import torch
12
+ import numpy as np
13
+ from PIL import Image
14
+ import cairosvg
15
+ import random
16
+ from pathlib import Path
17
+ import re
18
+
19
+ class VersatileSVGGenerator:
20
+ def __init__(self, model_dir):
21
+ """Initialize the versatile SVG generator"""
22
+ self.model_dir = model_dir
23
+ self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
24
+ print(f"Initializing versatile SVG generator on device: {self.device}")
25
+
26
+ # Load CLIP model if available
27
+ try:
28
+ import clip
29
+ self.clip_model, _ = clip.load("ViT-B-32", device=self.device)
30
+ self.clip_available = True
31
+ print("CLIP model loaded successfully")
32
+ except Exception as e:
33
+ print(f"Error loading CLIP model: {e}")
34
+ self.clip_available = False
35
+
36
+ def generate_svg(self, prompt, num_paths=20, width=512, height=512):
37
+ """Generate an SVG from a text prompt"""
38
+ print(f"Generating SVG for prompt: {prompt}")
39
+
40
+ # Use CLIP to encode the prompt if available
41
+ if self.clip_available:
42
+ try:
43
+ import clip
44
+ with torch.no_grad():
45
+ text = clip.tokenize([prompt]).to(self.device)
46
+ text_features = self.clip_model.encode_text(text)
47
+ text_features = text_features.cpu().numpy()[0]
48
+ # Normalize features
49
+ text_features = text_features / np.linalg.norm(text_features)
50
+ except Exception as e:
51
+ print(f"Error encoding prompt with CLIP: {e}")
52
+ text_features = np.random.randn(512) # Random features as fallback
53
+ else:
54
+ # Generate random features if CLIP is not available
55
+ text_features = np.random.randn(512)
56
+
57
+ # Determine what type of object to generate based on the prompt
58
+ object_type = self._determine_object_type(prompt)
59
+
60
+ # Generate SVG based on the object type
61
+ if object_type == "car":
62
+ svg_content = self._generate_car_svg(prompt, text_features, num_paths, width, height)
63
+ elif object_type == "landscape":
64
+ svg_content = self._generate_landscape_svg(prompt, text_features, num_paths, width, height)
65
+ elif object_type == "animal":
66
+ svg_content = self._generate_animal_svg(prompt, text_features, num_paths, width, height)
67
+ elif object_type == "building":
68
+ svg_content = self._generate_building_svg(prompt, text_features, num_paths, width, height)
69
+ elif object_type == "face":
70
+ svg_content = self._generate_face_svg(prompt, text_features, num_paths, width, height)
71
+ else:
72
+ svg_content = self._generate_abstract_svg(prompt, text_features, num_paths, width, height)
73
+
74
+ return svg_content
75
+
76
+ def _determine_object_type(self, prompt):
77
+ """Determine what type of object to generate based on the prompt"""
78
+ prompt = prompt.lower()
79
+
80
+ # Check for car-related terms
81
+ car_terms = ["car", "vehicle", "truck", "suv", "sedan", "convertible", "sports car", "automobile"]
82
+ for term in car_terms:
83
+ if term in prompt:
84
+ return "car"
85
+
86
+ # Check for landscape-related terms
87
+ landscape_terms = ["landscape", "mountain", "forest", "beach", "ocean", "sea", "lake", "river", "sunset", "sunrise", "sky"]
88
+ for term in landscape_terms:
89
+ if term in prompt:
90
+ return "landscape"
91
+
92
+ # Check for animal-related terms
93
+ animal_terms = ["animal", "dog", "cat", "bird", "horse", "lion", "tiger", "elephant", "bear", "fish", "pet"]
94
+ for term in animal_terms:
95
+ if term in prompt:
96
+ return "animal"
97
+
98
+ # Check for building-related terms
99
+ building_terms = ["building", "house", "skyscraper", "tower", "castle", "mansion", "apartment", "office", "structure"]
100
+ for term in building_terms:
101
+ if term in prompt:
102
+ return "building"
103
+
104
+ # Check for face-related terms
105
+ face_terms = ["face", "portrait", "person", "man", "woman", "boy", "girl", "human", "head", "smile"]
106
+ for term in face_terms:
107
+ if term in prompt:
108
+ return "face"
109
+
110
+ # Default to abstract
111
+ return "abstract"
112
+
113
+ def _generate_car_svg(self, prompt, features, num_paths=20, width=512, height=512):
114
+ """Generate a car-like SVG based on the prompt and features"""
115
+ # Start SVG
116
+ svg_content = f"""<svg width="{width}" height="{height}" xmlns="http://www.w3.org/2000/svg">
117
+ <rect width="100%" height="100%" fill="#f8f8f8"/>
118
+ """
119
+
120
+ # Use the features to determine car properties
121
+ car_color_hue = int((features[0] + 1) * 180) % 360 # Map to 0-360 hue
122
+ car_size = 0.6 + 0.2 * features[1] # Size variation
123
+ car_style = int(abs(features[2] * 3)) % 3 # 0: sedan, 1: SUV, 2: sports car
124
+
125
+ # Calculate car dimensions
126
+ car_width = int(width * 0.7 * car_size)
127
+ car_height = int(height * 0.3 * car_size)
128
+ car_x = (width - car_width) // 2
129
+ car_y = height // 2
130
+
131
+ # Generate car body based on style
132
+ if car_style == 0: # Sedan
133
+ # Car body (rounded rectangle)
134
+ svg_content += f"""<rect x="{car_x}" y="{car_y}" width="{car_width}" height="{car_height}"
135
+ rx="20" ry="20" fill="hsl({car_color_hue}, 80%, 50%)" stroke="black" stroke-width="2" />"""
136
+
137
+ # Windshield
138
+ windshield_width = car_width * 0.7
139
+ windshield_height = car_height * 0.5
140
+ windshield_x = car_x + (car_width - windshield_width) // 2
141
+ windshield_y = car_y - windshield_height * 0.3
142
+ svg_content += f"""<rect x="{windshield_x}" y="{windshield_y}" width="{windshield_width}" height="{windshield_height}"
143
+ rx="10" ry="10" fill="#a8d8ff" stroke="black" stroke-width="1" />"""
144
+
145
+ # Wheels
146
+ wheel_radius = car_height * 0.4
147
+ wheel_y = car_y + car_height * 0.8
148
+ svg_content += f"""<circle cx="{car_x + car_width * 0.2}" cy="{wheel_y}" r="{wheel_radius}" fill="black" />"""
149
+ svg_content += f"""<circle cx="{car_x + car_width * 0.8}" cy="{wheel_y}" r="{wheel_radius}" fill="black" />"""
150
+ svg_content += f"""<circle cx="{car_x + car_width * 0.2}" cy="{wheel_y}" r="{wheel_radius * 0.6}" fill="#444" />"""
151
+ svg_content += f"""<circle cx="{car_x + car_width * 0.8}" cy="{wheel_y}" r="{wheel_radius * 0.6}" fill="#444" />"""
152
+
153
+ elif car_style == 1: # SUV
154
+ # Car body (taller rectangle)
155
+ svg_content += f"""<rect x="{car_x}" y="{car_y - car_height * 0.3}" width="{car_width}" height="{car_height * 1.3}"
156
+ rx="15" ry="15" fill="hsl({car_color_hue}, 80%, 50%)" stroke="black" stroke-width="2" />"""
157
+
158
+ # Windshield
159
+ windshield_width = car_width * 0.6
160
+ windshield_height = car_height * 0.6
161
+ windshield_x = car_x + (car_width - windshield_width) // 2
162
+ windshield_y = car_y - car_height * 0.2
163
+ svg_content += f"""<rect x="{windshield_x}" y="{windshield_y}" width="{windshield_width}" height="{windshield_height}"
164
+ rx="8" ry="8" fill="#a8d8ff" stroke="black" stroke-width="1" />"""
165
+
166
+ # Wheels (larger)
167
+ wheel_radius = car_height * 0.45
168
+ wheel_y = car_y + car_height * 0.7
169
+ svg_content += f"""<circle cx="{car_x + car_width * 0.2}" cy="{wheel_y}" r="{wheel_radius}" fill="black" />"""
170
+ svg_content += f"""<circle cx="{car_x + car_width * 0.8}" cy="{wheel_y}" r="{wheel_radius}" fill="black" />"""
171
+ svg_content += f"""<circle cx="{car_x + car_width * 0.2}" cy="{wheel_y}" r="{wheel_radius * 0.6}" fill="#444" />"""
172
+ svg_content += f"""<circle cx="{car_x + car_width * 0.8}" cy="{wheel_y}" r="{wheel_radius * 0.6}" fill="#444" />"""
173
+
174
+ else: # Sports car
175
+ # Car body (low, sleek shape)
176
+ svg_content += f"""<path d="M {car_x} {car_y + car_height * 0.5}
177
+ C {car_x + car_width * 0.1} {car_y - car_height * 0.2},
178
+ {car_x + car_width * 0.3} {car_y - car_height * 0.3},
179
+ {car_x + car_width * 0.5} {car_y - car_height * 0.2}
180
+ S {car_x + car_width * 0.9} {car_y},
181
+ {car_x + car_width} {car_y + car_height * 0.3}
182
+ L {car_x + car_width} {car_y + car_height * 0.7}
183
+ C {car_x + car_width * 0.9} {car_y + car_height},
184
+ {car_x + car_width * 0.1} {car_y + car_height},
185
+ {car_x} {car_y + car_height * 0.7} Z"
186
+ fill="hsl({car_color_hue}, 90%, 45%)" stroke="black" stroke-width="2" />"""
187
+
188
+ # Windshield
189
+ windshield_width = car_width * 0.4
190
+ windshield_x = car_x + car_width * 0.3
191
+ windshield_y = car_y - car_height * 0.1
192
+ svg_content += f"""<path d="M {windshield_x} {windshield_y}
193
+ C {windshield_x + windshield_width * 0.1} {windshield_y - car_height * 0.15},
194
+ {windshield_x + windshield_width * 0.9} {windshield_y - car_height * 0.15},
195
+ {windshield_x + windshield_width} {windshield_y} Z"
196
+ fill="#a8d8ff" stroke="black" stroke-width="1" />"""
197
+
198
+ # Wheels (low profile)
199
+ wheel_radius = car_height * 0.35
200
+ wheel_y = car_y + car_height * 0.7
201
+ svg_content += f"""<ellipse cx="{car_x + car_width * 0.2}" cy="{wheel_y}" rx="{wheel_radius * 1.2}" ry="{wheel_radius}" fill="black" />"""
202
+ svg_content += f"""<ellipse cx="{car_x + car_width * 0.8}" cy="{wheel_y}" rx="{wheel_radius * 1.2}" ry="{wheel_radius}" fill="black" />"""
203
+ svg_content += f"""<ellipse cx="{car_x + car_width * 0.2}" cy="{wheel_y}" rx="{wheel_radius * 0.7}" ry="{wheel_radius * 0.6}" fill="#444" />"""
204
+ svg_content += f"""<ellipse cx="{car_x + car_width * 0.8}" cy="{wheel_y}" rx="{wheel_radius * 0.7}" ry="{wheel_radius * 0.6}" fill="#444" />"""
205
+
206
+ # Add headlights
207
+ headlight_radius = car_width * 0.05
208
+ headlight_y = car_y + car_height * 0.3
209
+ svg_content += f"""<circle cx="{car_x + car_width * 0.1}" cy="{headlight_y}" r="{headlight_radius}" fill="yellow" stroke="black" stroke-width="1" />"""
210
+ svg_content += f"""<circle cx="{car_x + car_width * 0.9}" cy="{headlight_y}" r="{headlight_radius}" fill="yellow" stroke="black" stroke-width="1" />"""
211
+
212
+ # Add prompt as text
213
+ svg_content += f"""<text x="{width/2}" y="{height - 20}" font-family="Arial" font-size="12" text-anchor="middle">{prompt}</text>"""
214
+
215
+ # Close SVG
216
+ svg_content += "</svg>"
217
+
218
+ return svg_content
219
+
220
+ def _generate_landscape_svg(self, prompt, features, num_paths=20, width=512, height=512):
221
+ """Generate a landscape SVG based on the prompt and features"""
222
+ # Start SVG
223
+ svg_content = f"""<svg width="{width}" height="{height}" xmlns="http://www.w3.org/2000/svg">
224
+ <defs>
225
+ <linearGradient id="skyGradient" x1="0%" y1="0%" x2="0%" y2="100%">
226
+ <stop offset="0%" stop-color="#87CEEB" />
227
+ <stop offset="100%" stop-color="#E0F7FF" />
228
+ </linearGradient>
229
+ </defs>
230
+ <rect width="100%" height="100%" fill="url(#skyGradient)"/>
231
+ """
232
+
233
+ # Use features to determine landscape properties
234
+ mountain_count = int(abs(features[0] * 5)) + 3
235
+ tree_count = int(abs(features[1] * 20)) + 5
236
+ has_sun = features[2] > 0
237
+ has_water = features[3] > 0
238
+
239
+ # Draw mountains
240
+ for i in range(mountain_count):
241
+ mountain_height = height * (0.3 + 0.2 * abs(features[i % len(features)]))
242
+ mountain_width = width * (0.2 + 0.1 * abs(features[(i+1) % len(features)]))
243
+ mountain_x = width * (i / mountain_count)
244
+ mountain_color = f"hsl({int(120 + features[i % len(features)] * 20)}, 30%, {30 + int(abs(features[i % len(features)] * 20))}%)"
245
+
246
+ svg_content += f"""<path d="M {mountain_x} {height}
247
+ L {mountain_x + mountain_width/2} {height - mountain_height}
248
+ L {mountain_x + mountain_width} {height} Z"
249
+ fill="{mountain_color}" stroke="none" />"""
250
+
251
+ # Draw sun if present
252
+ if has_sun:
253
+ sun_x = width * (0.1 + 0.8 * abs(features[4]))
254
+ sun_y = height * 0.2
255
+ sun_radius = width * 0.08
256
+ svg_content += f"""<circle cx="{sun_x}" cy="{sun_y}" r="{sun_radius}" fill="yellow" stroke="none">
257
+ <animate attributeName="r" values="{sun_radius};{sun_radius*1.05};{sun_radius}" dur="4s" repeatCount="indefinite" />
258
+ </circle>"""
259
+
260
+ # Draw water if present
261
+ if has_water:
262
+ water_height = height * 0.3
263
+ water_y = height - water_height
264
+ svg_content += f"""<rect x="0" y="{water_y}" width="{width}" height="{water_height}" fill="#4a86e8" opacity="0.7">
265
+ <animate attributeName="height" values="{water_height};{water_height*1.02};{water_height}" dur="3s" repeatCount="indefinite" />
266
+ </rect>"""
267
+
268
+ # Add waves
269
+ for i in range(5):
270
+ wave_y = water_y + i * water_height / 5
271
+ svg_content += f"""<path d="M 0 {wave_y}
272
+ Q {width/4} {wave_y-5}, {width/2} {wave_y}
273
+ T {width} {wave_y}"
274
+ fill="none" stroke="white" stroke-width="1" opacity="0.3" />"""
275
+
276
+ # Draw trees
277
+ for i in range(tree_count):
278
+ tree_x = width * (0.1 + 0.8 * (i / tree_count))
279
+ tree_y = height * 0.8
280
+ tree_height = height * (0.1 + 0.1 * abs(features[i % len(features)]))
281
+ tree_width = tree_height * 0.6
282
+
283
+ # Tree trunk
284
+ svg_content += f"""<rect x="{tree_x - tree_width/8}" y="{tree_y - tree_height/3}"
285
+ width="{tree_width/4}" height="{tree_height/3}"
286
+ fill="#8B4513" stroke="none" />"""
287
+
288
+ # Tree foliage
289
+ svg_content += f"""<path d="M {tree_x - tree_width/2} {tree_y - tree_height/3}
290
+ L {tree_x} {tree_y - tree_height}
291
+ L {tree_x + tree_width/2} {tree_y - tree_height/3} Z"
292
+ fill="#228B22" stroke="none" />
293
+
294
+ <path d="M {tree_x - tree_width/2} {tree_y - tree_height/2}
295
+ L {tree_x} {tree_y - tree_height * 1.1}
296
+ L {tree_x + tree_width/2} {tree_y - tree_height/2} Z"
297
+ fill="#228B22" stroke="none" />"""
298
+
299
+ # Add prompt as text
300
+ svg_content += f"""<text x="{width/2}" y="{height - 20}" font-family="Arial" font-size="12" text-anchor="middle" fill="black">{prompt}</text>"""
301
+
302
+ # Close SVG
303
+ svg_content += "</svg>"
304
+
305
+ return svg_content
306
+
307
+ def _generate_animal_svg(self, prompt, features, num_paths=20, width=512, height=512):
308
+ """Generate an animal SVG based on the prompt and features"""
309
+ # Start SVG
310
+ svg_content = f"""<svg width="{width}" height="{height}" xmlns="http://www.w3.org/2000/svg">
311
+ <rect width="100%" height="100%" fill="#f8f8f8"/>
312
+ """
313
+
314
+ # Determine animal type from prompt
315
+ animal_type = "generic"
316
+ if "dog" in prompt.lower() or "puppy" in prompt.lower():
317
+ animal_type = "dog"
318
+ elif "cat" in prompt.lower() or "kitten" in prompt.lower():
319
+ animal_type = "cat"
320
+ elif "bird" in prompt.lower():
321
+ animal_type = "bird"
322
+ elif "fish" in prompt.lower():
323
+ animal_type = "fish"
324
+
325
+ # Use features to determine animal properties
326
+ animal_color_hue = int((features[0] + 1) * 180) % 360 # Map to 0-360 hue
327
+ animal_size = 0.5 + 0.3 * features[1] # Size variation
328
+
329
+ # Calculate animal dimensions
330
+ animal_width = int(width * 0.6 * animal_size)
331
+ animal_height = int(height * 0.4 * animal_size)
332
+ animal_x = (width - animal_width) // 2
333
+ animal_y = height // 2
334
+
335
+ if animal_type == "dog":
336
+ # Dog body (oval)
337
+ svg_content += f"""<ellipse cx="{animal_x + animal_width * 0.5}" cy="{animal_y + animal_height * 0.5}"
338
+ rx="{animal_width * 0.5}" ry="{animal_height * 0.3}"
339
+ fill="hsl({animal_color_hue}, 70%, 60%)" stroke="black" stroke-width="2" />"""
340
+
341
+ # Dog head (circle)
342
+ head_radius = animal_width * 0.2
343
+ svg_content += f"""<circle cx="{animal_x + animal_width * 0.8}" cy="{animal_y + animal_height * 0.3}"
344
+ r="{head_radius}" fill="hsl({animal_color_hue}, 70%, 60%)" stroke="black" stroke-width="2" />"""
345
+
346
+ # Dog ears
347
+ svg_content += f"""<ellipse cx="{animal_x + animal_width * 0.75}" cy="{animal_y + animal_height * 0.15}"
348
+ rx="{head_radius * 0.5}" ry="{head_radius * 0.8}"
349
+ fill="hsl({animal_color_hue}, 70%, 50%)" stroke="black" stroke-width="1" />"""
350
+ svg_content += f"""<ellipse cx="{animal_x + animal_width * 0.85}" cy="{animal_y + animal_height * 0.15}"
351
+ rx="{head_radius * 0.5}" ry="{head_radius * 0.8}"
352
+ fill="hsl({animal_color_hue}, 70%, 50%)" stroke="black" stroke-width="1" />"""
353
+
354
+ # Dog eyes
355
+ svg_content += f"""<circle cx="{animal_x + animal_width * 0.75}" cy="{animal_y + animal_height * 0.25}"
356
+ r="{head_radius * 0.15}" fill="black" />"""
357
+ svg_content += f"""<circle cx="{animal_x + animal_width * 0.85}" cy="{animal_y + animal_height * 0.25}"
358
+ r="{head_radius * 0.15}" fill="black" />"""
359
+
360
+ # Dog nose
361
+ svg_content += f"""<ellipse cx="{animal_x + animal_width * 0.9}" cy="{animal_y + animal_height * 0.35}"
362
+ rx="{head_radius * 0.2}" ry="{head_radius * 0.15}"
363
+ fill="black" />"""
364
+
365
+ # Dog legs
366
+ leg_width = animal_width * 0.1
367
+ leg_height = animal_height * 0.4
368
+ svg_content += f"""<rect x="{animal_x + animal_width * 0.3}" y="{animal_y + animal_height * 0.6}"
369
+ width="{leg_width}" height="{leg_height}"
370
+ fill="hsl({animal_color_hue}, 70%, 55%)" stroke="black" stroke-width="1" />"""
371
+ svg_content += f"""<rect x="{animal_x + animal_width * 0.5}" y="{animal_y + animal_height * 0.6}"
372
+ width="{leg_width}" height="{leg_height}"
373
+ fill="hsl({animal_color_hue}, 70%, 55%)" stroke="black" stroke-width="1" />"""
374
+
375
+ # Dog tail
376
+ svg_content += f"""<path d="M {animal_x + animal_width * 0.1} {animal_y + animal_height * 0.4}
377
+ C {animal_x} {animal_y + animal_height * 0.2},
378
+ {animal_x - animal_width * 0.1} {animal_y + animal_height * 0.3},
379
+ {animal_x - animal_width * 0.05} {animal_y + animal_height * 0.5}"
380
+ fill="none" stroke="hsl({animal_color_hue}, 70%, 55%)" stroke-width="{leg_width}" stroke-linecap="round" />"""
381
+
382
+ elif animal_type == "cat":
383
+ # Cat body (oval)
384
+ svg_content += f"""<ellipse cx="{animal_x + animal_width * 0.5}" cy="{animal_y + animal_height * 0.5}"
385
+ rx="{animal_width * 0.4}" ry="{animal_height * 0.25}"
386
+ fill="hsl({animal_color_hue}, 70%, 60%)" stroke="black" stroke-width="2" />"""
387
+
388
+ # Cat head (circle)
389
+ head_radius = animal_width * 0.18
390
+ svg_content += f"""<circle cx="{animal_x + animal_width * 0.8}" cy="{animal_y + animal_height * 0.3}"
391
+ r="{head_radius}" fill="hsl({animal_color_hue}, 70%, 60%)" stroke="black" stroke-width="2" />"""
392
+
393
+ # Cat ears (triangles)
394
+ svg_content += f"""<path d="M {animal_x + animal_width * 0.75} {animal_y + animal_height * 0.2}
395
+ L {animal_x + animal_width * 0.7} {animal_y + animal_height * 0.05}
396
+ L {animal_x + animal_width * 0.65} {animal_y + animal_height * 0.2} Z"
397
+ fill="hsl({animal_color_hue}, 70%, 50%)" stroke="black" stroke-width="1" />"""
398
+ svg_content += f"""<path d="M {animal_x + animal_width * 0.85} {animal_y + animal_height * 0.2}
399
+ L {animal_x + animal_width * 0.9} {animal_y + animal_height * 0.05}
400
+ L {animal_x + animal_width * 0.95} {animal_y + animal_height * 0.2} Z"
401
+ fill="hsl({animal_color_hue}, 70%, 50%)" stroke="black" stroke-width="1" />"""
402
+
403
+ # Cat eyes (almond shaped)
404
+ svg_content += f"""<ellipse cx="{animal_x + animal_width * 0.75}" cy="{animal_y + animal_height * 0.25}"
405
+ rx="{head_radius * 0.15}" ry="{head_radius * 0.08}"
406
+ fill="black" />"""
407
+ svg_content += f"""<ellipse cx="{animal_x + animal_width * 0.85}" cy="{animal_y + animal_height * 0.25}"
408
+ rx="{head_radius * 0.15}" ry="{head_radius * 0.08}"
409
+ fill="black" />"""
410
+
411
+ # Cat nose
412
+ svg_content += f"""<path d="M {animal_x + animal_width * 0.8} {animal_y + animal_height * 0.3}
413
+ L {animal_x + animal_width * 0.78} {animal_y + animal_height * 0.33}
414
+ L {animal_x + animal_width * 0.82} {animal_y + animal_height * 0.33} Z"
415
+ fill="pink" stroke="black" stroke-width="0.5" />"""
416
+
417
+ # Cat whiskers
418
+ svg_content += f"""<path d="M {animal_x + animal_width * 0.78} {animal_y + animal_height * 0.32}
419
+ L {animal_x + animal_width * 0.65} {animal_y + animal_height * 0.3}"
420
+ fill="none" stroke="black" stroke-width="0.5" />"""
421
+ svg_content += f"""<path d="M {animal_x + animal_width * 0.78} {animal_y + animal_height * 0.34}
422
+ L {animal_x + animal_width * 0.65} {animal_y + animal_height * 0.35}"
423
+ fill="none" stroke="black" stroke-width="0.5" />"""
424
+ svg_content += f"""<path d="M {animal_x + animal_width * 0.82} {animal_y + animal_height * 0.32}
425
+ L {animal_x + animal_width * 0.95} {animal_y + animal_height * 0.3}"
426
+ fill="none" stroke="black" stroke-width="0.5" />"""
427
+ svg_content += f"""<path d="M {animal_x + animal_width * 0.82} {animal_y + animal_height * 0.34}
428
+ L {animal_x + animal_width * 0.95} {animal_y + animal_height * 0.35}"
429
+ fill="none" stroke="black" stroke-width="0.5" />"""
430
+
431
+ # Cat legs
432
+ leg_width = animal_width * 0.08
433
+ leg_height = animal_height * 0.3
434
+ svg_content += f"""<rect x="{animal_x + animal_width * 0.35}" y="{animal_y + animal_height * 0.6}"
435
+ width="{leg_width}" height="{leg_height}"
436
+ fill="hsl({animal_color_hue}, 70%, 55%)" stroke="black" stroke-width="1" />"""
437
+ svg_content += f"""<rect x="{animal_x + animal_width * 0.55}" y="{animal_y + animal_height * 0.6}"
438
+ width="{leg_width}" height="{leg_height}"
439
+ fill="hsl({animal_color_hue}, 70%, 55%)" stroke="black" stroke-width="1" />"""
440
+
441
+ # Cat tail
442
+ svg_content += f"""<path d="M {animal_x + animal_width * 0.1} {animal_y + animal_height * 0.4}
443
+ C {animal_x} {animal_y + animal_height * 0.2},
444
+ {animal_x - animal_width * 0.1} {animal_y + animal_height * 0.1},
445
+ {animal_x - animal_width * 0.15} {animal_y + animal_height * 0.3}"
446
+ fill="none" stroke="hsl({animal_color_hue}, 70%, 55%)" stroke-width="{leg_width}" stroke-linecap="round" />"""
447
+
448
+ elif animal_type == "bird":
449
+ # Bird body (oval)
450
+ svg_content += f"""<ellipse cx="{animal_x + animal_width * 0.5}" cy="{animal_y + animal_height * 0.5}"
451
+ rx="{animal_width * 0.3}" ry="{animal_height * 0.25}"
452
+ fill="hsl({animal_color_hue}, 90%, 60%)" stroke="black" stroke-width="2" />"""
453
+
454
+ # Bird head
455
+ head_radius = animal_width * 0.15
456
+ svg_content += f"""<circle cx="{animal_x + animal_width * 0.8}" cy="{animal_y + animal_height * 0.4}"
457
+ r="{head_radius}" fill="hsl({animal_color_hue}, 90%, 60%)" stroke="black" stroke-width="2" />"""
458
+
459
+ # Bird beak
460
+ svg_content += f"""<path d="M {animal_x + animal_width * 0.9} {animal_y + animal_height * 0.4}
461
+ L {animal_x + animal_width * 1.0} {animal_y + animal_height * 0.35}
462
+ L {animal_x + animal_width * 0.9} {animal_y + animal_height * 0.45} Z"
463
+ fill="orange" stroke="black" stroke-width="1" />"""
464
+
465
+ # Bird eye
466
+ svg_content += f"""<circle cx="{animal_x + animal_width * 0.85}" cy="{animal_y + animal_height * 0.35}"
467
+ r="{head_radius * 0.2}" fill="black" />"""
468
+
469
+ # Bird wings
470
+ svg_content += f"""<path d="M {animal_x + animal_width * 0.5} {animal_y + animal_height * 0.4}
471
+ C {animal_x + animal_width * 0.4} {animal_y + animal_height * 0.2},
472
+ {animal_x + animal_width * 0.3} {animal_y + animal_height * 0.1},
473
+ {animal_x + animal_width * 0.2} {animal_y + animal_height * 0.3}"
474
+ fill="hsl({animal_color_hue}, 90%, 50%)" stroke="black" stroke-width="1" />"""
475
+
476
+ # Bird tail
477
+ svg_content += f"""<path d="M {animal_x + animal_width * 0.2} {animal_y + animal_height * 0.5}
478
+ L {animal_x} {animal_y + animal_height * 0.4}
479
+ L {animal_x + animal_width * 0.1} {animal_y + animal_height * 0.5}
480
+ L {animal_x} {animal_y + animal_height * 0.6} Z"
481
+ fill="hsl({animal_color_hue}, 90%, 40%)" stroke="black" stroke-width="1" />"""
482
+
483
+ # Bird legs
484
+ leg_width = animal_width * 0.02
485
+ leg_height = animal_height * 0.2
486
+ svg_content += f"""<rect x="{animal_x + animal_width * 0.45}" y="{animal_y + animal_height * 0.75}"
487
+ width="{leg_width}" height="{leg_height}"
488
+ fill="orange" stroke="black" stroke-width="1" />"""
489
+ svg_content += f"""<rect x="{animal_x + animal_width * 0.55}" y="{animal_y + animal_height * 0.75}"
490
+ width="{leg_width}" height="{leg_height}"
491
+ fill="orange" stroke="black" stroke-width="1" />"""
492
+
493
+ elif animal_type == "fish":
494
+ # Fish body (oval)
495
+ svg_content += f"""<ellipse cx="{animal_x + animal_width * 0.5}" cy="{animal_y + animal_height * 0.5}"
496
+ rx="{animal_width * 0.4}" ry="{animal_height * 0.25}"
497
+ fill="hsl({animal_color_hue}, 90%, 60%)" stroke="black" stroke-width="2" />"""
498
+
499
+ # Fish tail
500
+ svg_content += f"""<path d="M {animal_x + animal_width * 0.1} {animal_y + animal_height * 0.3}
501
+ L {animal_x - animal_width * 0.1} {animal_y + animal_height * 0.5}
502
+ L {animal_x + animal_width * 0.1} {animal_y + animal_height * 0.7} Z"
503
+ fill="hsl({animal_color_hue}, 90%, 50%)" stroke="black" stroke-width="1" />"""
504
+
505
+ # Fish eye
506
+ svg_content += f"""<circle cx="{animal_x + animal_width * 0.7}" cy="{animal_y + animal_height * 0.4}"
507
+ r="{animal_width * 0.05}" fill="black" />"""
508
+ svg_content += f"""<circle cx="{animal_x + animal_width * 0.7}" cy="{animal_y + animal_height * 0.4}"
509
+ r="{animal_width * 0.02}" fill="white" />"""
510
+
511
+ # Fish fins
512
+ svg_content += f"""<path d="M {animal_x + animal_width * 0.5} {animal_y + animal_height * 0.25}
513
+ C {animal_x + animal_width * 0.6} {animal_y},
514
+ {animal_x + animal_width * 0.7} {animal_y},
515
+ {animal_x + animal_width * 0.8} {animal_y + animal_height * 0.25}"
516
+ fill="hsl({animal_color_hue}, 90%, 50%)" stroke="black" stroke-width="1" />"""
517
+
518
+ svg_content += f"""<path d="M {animal_x + animal_width * 0.5} {animal_y + animal_height * 0.75}
519
+ C {animal_x + animal_width * 0.6} {animal_y + animal_height},
520
+ {animal_x + animal_width * 0.7} {animal_y + animal_height},
521
+ {animal_x + animal_width * 0.8} {animal_y + animal_height * 0.75}"
522
+ fill="hsl({animal_color_hue}, 90%, 50%)" stroke="black" stroke-width="1" />"""
523
+
524
+ # Fish scales (simplified)
525
+ for i in range(5):
526
+ for j in range(3):
527
+ scale_x = animal_x + animal_width * (0.3 + i * 0.1)
528
+ scale_y = animal_y + animal_height * (0.4 + (j-1) * 0.1)
529
+ scale_radius = animal_width * 0.03
530
+ svg_content += f"""<circle cx="{scale_x}" cy="{scale_y}" r="{scale_radius}"
531
+ fill="none" stroke="hsl({animal_color_hue}, 90%, 40%)" stroke-width="0.5" />"""
532
+
533
+ # Water bubbles
534
+ for i in range(3):
535
+ bubble_x = animal_x + animal_width * (0.8 + i * 0.1)
536
+ bubble_y = animal_y + animal_height * (0.3 - i * 0.1)
537
+ bubble_radius = animal_width * (0.02 + i * 0.01)
538
+ svg_content += f"""<circle cx="{bubble_x}" cy="{bubble_y}" r="{bubble_radius}"
539
+ fill="white" fill-opacity="0.7" stroke="lightblue" stroke-width="0.5" />"""
540
+
541
+ else: # Generic animal
542
+ # Body (oval)
543
+ svg_content += f"""<ellipse cx="{animal_x + animal_width * 0.5}" cy="{animal_y + animal_height * 0.5}"
544
+ rx="{animal_width * 0.4}" ry="{animal_height * 0.25}"
545
+ fill="hsl({animal_color_hue}, 70%, 60%)" stroke="black" stroke-width="2" />"""
546
+
547
+ # Head (circle)
548
+ head_radius = animal_width * 0.2
549
+ svg_content += f"""<circle cx="{animal_x + animal_width * 0.8}" cy="{animal_y + animal_height * 0.4}"
550
+ r="{head_radius}" fill="hsl({animal_color_hue}, 70%, 60%)" stroke="black" stroke-width="2" />"""
551
+
552
+ # Eyes
553
+ svg_content += f"""<circle cx="{animal_x + animal_width * 0.75}" cy="{animal_y + animal_height * 0.35}"
554
+ r="{head_radius * 0.15}" fill="black" />"""
555
+ svg_content += f"""<circle cx="{animal_x + animal_width * 0.85}" cy="{animal_y + animal_height * 0.35}"
556
+ r="{head_radius * 0.15}" fill="black" />"""
557
+
558
+ # Legs
559
+ leg_width = animal_width * 0.08
560
+ leg_height = animal_height * 0.3
561
+ svg_content += f"""<rect x="{animal_x + animal_width * 0.3}" y="{animal_y + animal_height * 0.6}"
562
+ width="{leg_width}" height="{leg_height}"
563
+ fill="hsl({animal_color_hue}, 70%, 55%)" stroke="black" stroke-width="1" />"""
564
+ svg_content += f"""<rect x="{animal_x + animal_width * 0.5}" y="{animal_y + animal_height * 0.6}"
565
+ width="{leg_width}" height="{leg_height}"
566
+ fill="hsl({animal_color_hue}, 70%, 55%)" stroke="black" stroke-width="1" />"""
567
+
568
+ # Tail
569
+ svg_content += f"""<path d="M {animal_x + animal_width * 0.1} {animal_y + animal_height * 0.5}
570
+ C {animal_x} {animal_y + animal_height * 0.4},
571
+ {animal_x - animal_width * 0.1} {animal_y + animal_height * 0.3},
572
+ {animal_x - animal_width * 0.05} {animal_y + animal_height * 0.6}"
573
+ fill="none" stroke="hsl({animal_color_hue}, 70%, 55%)" stroke-width="{leg_width}" stroke-linecap="round" />"""
574
+
575
+ # Add prompt as text
576
+ svg_content += f"""<text x="{width/2}" y="{height - 20}" font-family="Arial" font-size="12" text-anchor="middle">{prompt}</text>"""
577
+
578
+ # Close SVG
579
+ svg_content += "</svg>"
580
+
581
+ return svg_content
582
+
583
+ def _generate_building_svg(self, prompt, features, num_paths=20, width=512, height=512):
584
+ """Generate a building SVG based on the prompt and features"""
585
+ # Start SVG
586
+ svg_content = f"""<svg width="{width}" height="{height}" xmlns="http://www.w3.org/2000/svg">
587
+ <defs>
588
+ <linearGradient id="skyGradient" x1="0%" y1="0%" x2="0%" y2="100%">
589
+ <stop offset="0%" stop-color="#87CEEB" />
590
+ <stop offset="100%" stop-color="#E0F7FF" />
591
+ </linearGradient>
592
+ </defs>
593
+ <rect width="100%" height="100%" fill="url(#skyGradient)"/>
594
+ """
595
+
596
+ # Determine building type from prompt
597
+ building_type = "generic"
598
+ if "house" in prompt.lower():
599
+ building_type = "house"
600
+ elif "skyscraper" in prompt.lower() or "tower" in prompt.lower():
601
+ building_type = "skyscraper"
602
+ elif "castle" in prompt.lower():
603
+ building_type = "castle"
604
+
605
+ # Use features to determine building properties
606
+ building_color_hue = int((features[0] + 1) * 180) % 360 # Map to 0-360 hue
607
+ building_size = 0.5 + 0.3 * features[1] # Size variation
608
+
609
+ # Calculate building dimensions
610
+ building_width = int(width * 0.6 * building_size)
611
+ building_height = int(height * 0.7 * building_size)
612
+ building_x = (width - building_width) // 2
613
+ building_y = height - building_height
614
+
615
+ if building_type == "house":
616
+ # House base
617
+ svg_content += f"""<rect x="{building_x}" y="{building_y + building_height * 0.3}"
618
+ width="{building_width}" height="{building_height * 0.7}"
619
+ fill="hsl({building_color_hue}, 30%, 70%)" stroke="black" stroke-width="2" />"""
620
+
621
+ # House roof
622
+ svg_content += f"""<path d="M {building_x - building_width * 0.1} {building_y + building_height * 0.3}
623
+ L {building_x + building_width * 0.5} {building_y}
624
+ L {building_x + building_width * 1.1} {building_y + building_height * 0.3} Z"
625
+ fill="hsl({(building_color_hue + 30) % 360}, 50%, 40%)" stroke="black" stroke-width="2" />"""
626
+
627
+ # House door
628
+ door_width = building_width * 0.2
629
+ door_height = building_height * 0.4
630
+ door_x = building_x + (building_width - door_width) / 2
631
+ door_y = building_y + building_height - door_height
632
+ svg_content += f"""<rect x="{door_x}" y="{door_y}"
633
+ width="{door_width}" height="{door_height}"
634
+ fill="hsl({(building_color_hue + 60) % 360}, 30%, 40%)" stroke="black" stroke-width="1" />"""
635
+
636
+ # Door knob
637
+ svg_content += f"""<circle cx="{door_x + door_width * 0.8}" cy="{door_y + door_height * 0.5}"
638
+ r="{door_width * 0.1}" fill="gold" stroke="black" stroke-width="0.5" />"""
639
+
640
+ # Windows
641
+ window_width = building_width * 0.15
642
+ window_height = building_height * 0.15
643
+ window_spacing = building_width * 0.25
644
+
645
+ for i in range(2):
646
+ for j in range(2):
647
+ window_x = building_x + window_spacing + i * window_spacing
648
+ window_y = building_y + building_height * 0.4 + j * window_spacing
649
+ svg_content += f"""<rect x="{window_x}" y="{window_y}"
650
+ width="{window_width}" height="{window_height}"
651
+ fill="#a8d8ff" stroke="black" stroke-width="1" />"""
652
+
653
+ # Window crossbars
654
+ svg_content += f"""<path d="M {window_x} {window_y + window_height/2}
655
+ L {window_x + window_width} {window_y + window_height/2}"
656
+ fill="none" stroke="black" stroke-width="0.5" />"""
657
+ svg_content += f"""<path d="M {window_x + window_width/2} {window_y}
658
+ L {window_x + window_width/2} {window_y + window_height}"
659
+ fill="none" stroke="black" stroke-width="0.5" />"""
660
+
661
+ # Chimney
662
+ chimney_width = building_width * 0.1
663
+ chimney_height = building_height * 0.3
664
+ chimney_x = building_x + building_width * 0.7
665
+ chimney_y = building_y + building_height * 0.1 - chimney_height
666
+ svg_content += f"""<rect x="{chimney_x}" y="{chimney_y}"
667
+ width="{chimney_width}" height="{chimney_height}"
668
+ fill="hsl({(building_color_hue + 30) % 360}, 30%, 30%)" stroke="black" stroke-width="1" />"""
669
+
670
+ elif building_type == "skyscraper":
671
+ # Skyscraper base
672
+ svg_content += f"""<rect x="{building_x}" y="{building_y}"
673
+ width="{building_width}" height="{building_height}"
674
+ fill="hsl({building_color_hue}, 20%, 70%)" stroke="black" stroke-width="2" />"""
675
+
676
+ # Skyscraper top
677
+ top_width = building_width * 0.7
678
+ top_height = building_height * 0.1
679
+ top_x = building_x + (building_width - top_width) / 2
680
+ svg_content += f"""<rect x="{top_x}" y="{building_y - top_height}"
681
+ width="{top_width}" height="{top_height}"
682
+ fill="hsl({building_color_hue}, 20%, 50%)" stroke="black" stroke-width="1" />"""
683
+
684
+ # Antenna
685
+ antenna_width = building_width * 0.02
686
+ antenna_height = building_height * 0.15
687
+ antenna_x = building_x + building_width / 2 - antenna_width / 2
688
+ antenna_y = building_y - top_height - antenna_height
689
+ svg_content += f"""<rect x="{antenna_x}" y="{antenna_y}"
690
+ width="{antenna_width}" height="{antenna_height}"
691
+ fill="silver" stroke="black" stroke-width="0.5" />"""
692
+
693
+ # Windows (grid pattern)
694
+ window_width = building_width * 0.08
695
+ window_height = building_height * 0.05
696
+ window_spacing_x = building_width * 0.12
697
+ window_spacing_y = building_height * 0.08
698
+
699
+ for i in range(int(building_width / window_spacing_x) - 1):
700
+ for j in range(int(building_height / window_spacing_y) - 1):
701
+ window_x = building_x + window_spacing_x * (i + 0.5)
702
+ window_y = building_y + window_spacing_y * (j + 0.5)
703
+
704
+ # Randomize window lighting
705
+ window_color = "#a8d8ff"
706
+ if random.random() < 0.3: # 30% chance of lit window
707
+ window_color = "#ffff88"
708
+
709
+ svg_content += f"""<rect x="{window_x}" y="{window_y}"
710
+ width="{window_width}" height="{window_height}"
711
+ fill="{window_color}" stroke="black" stroke-width="0.5" />"""
712
+
713
+ # Entrance
714
+ entrance_width = building_width * 0.3
715
+ entrance_height = building_height * 0.1
716
+ entrance_x = building_x + (building_width - entrance_width) / 2
717
+ entrance_y = building_y + building_height - entrance_height
718
+ svg_content += f"""<rect x="{entrance_x}" y="{entrance_y}"
719
+ width="{entrance_width}" height="{entrance_height}"
720
+ fill="hsl({(building_color_hue + 60) % 360}, 30%, 40%)" stroke="black" stroke-width="1" />"""
721
+
722
+ elif building_type == "castle":
723
+ # Castle base
724
+ svg_content += f"""<rect x="{building_x}" y="{building_y + building_height * 0.2}"
725
+ width="{building_width}" height="{building_height * 0.8}"
726
+ fill="hsl({building_color_hue}, 15%, 60%)" stroke="black" stroke-width="2" />"""
727
+
728
+ # Castle towers
729
+ tower_width = building_width * 0.2
730
+ tower_height = building_height * 1.0
731
+
732
+ # Left tower
733
+ svg_content += f"""<rect x="{building_x - tower_width * 0.5}" y="{building_y}"
734
+ width="{tower_width}" height="{tower_height}"
735
+ fill="hsl({building_color_hue}, 15%, 50%)" stroke="black" stroke-width="2" />"""
736
+
737
+ # Right tower
738
+ svg_content += f"""<rect x="{building_x + building_width - tower_width * 0.5}" y="{building_y}"
739
+ width="{tower_width}" height="{tower_height}"
740
+ fill="hsl({building_color_hue}, 15%, 50%)" stroke="black" stroke-width="2" />"""
741
+
742
+ # Crenellations (castle top)
743
+ crenel_width = building_width * 0.05
744
+ crenel_height = building_height * 0.05
745
+ crenel_count = int(building_width / crenel_width)
746
+
747
+ for i in range(crenel_count):
748
+ if i % 2 == 0:
749
+ crenel_x = building_x + i * crenel_width
750
+ svg_content += f"""<rect x="{crenel_x}" y="{building_y + building_height * 0.15}"
751
+ width="{crenel_width}" height="{crenel_height}"
752
+ fill="hsl({building_color_hue}, 15%, 60%)" stroke="black" stroke-width="1" />"""
753
+
754
+ # Tower crenellations
755
+ tower_crenel_count = int(tower_width / crenel_width)
756
+
757
+ # Left tower crenellations
758
+ for i in range(tower_crenel_count):
759
+ if i % 2 == 0:
760
+ crenel_x = building_x - tower_width * 0.5 + i * crenel_width
761
+ svg_content += f"""<rect x="{crenel_x}" y="{building_y - crenel_height}"
762
+ width="{crenel_width}" height="{crenel_height}"
763
+ fill="hsl({building_color_hue}, 15%, 50%)" stroke="black" stroke-width="1" />"""
764
+
765
+ # Right tower crenellations
766
+ for i in range(tower_crenel_count):
767
+ if i % 2 == 0:
768
+ crenel_x = building_x + building_width - tower_width * 0.5 + i * crenel_width
769
+ svg_content += f"""<rect x="{crenel_x}" y="{building_y - crenel_height}"
770
+ width="{crenel_width}" height="{crenel_height}"
771
+ fill="hsl({building_color_hue}, 15%, 50%)" stroke="black" stroke-width="1" />"""
772
+
773
+ # Castle door (gate)
774
+ door_width = building_width * 0.25
775
+ door_height = building_height * 0.4
776
+ door_x = building_x + (building_width - door_width) / 2
777
+ door_y = building_y + building_height - door_height
778
+
779
+ # Gate arch
780
+ svg_content += f"""<path d="M {door_x} {door_y + door_height * 0.5}
781
+ A {door_width/2} {door_height/2} 0 0 1 {door_x + door_width} {door_y + door_height * 0.5}
782
+ L {door_x + door_width} {door_y + door_height}
783
+ L {door_x} {door_y + door_height} Z"
784
+ fill="hsl({(building_color_hue + 30) % 360}, 30%, 30%)" stroke="black" stroke-width="1" />"""
785
+
786
+ # Windows
787
+ window_width = building_width * 0.1
788
+ window_height = building_height * 0.15
789
+ window_spacing = building_width * 0.25
790
+
791
+ for i in range(3):
792
+ window_x = building_x + window_spacing * (i + 0.5)
793
+ window_y = building_y + building_height * 0.4
794
+
795
+ # Arched window
796
+ svg_content += f"""<path d="M {window_x} {window_y + window_height * 0.5}
797
+ A {window_width/2} {window_height/2} 0 0 1 {window_x + window_width} {window_y + window_height * 0.5}
798
+ L {window_x + window_width} {window_y + window_height}
799
+ L {window_x} {window_y + window_height} Z"
800
+ fill="#a8d8ff" stroke="black" stroke-width="1" />"""
801
+
802
+ # Tower windows (slits)
803
+ slit_width = tower_width * 0.1
804
+ slit_height = tower_height * 0.1
805
+
806
+ # Left tower slits
807
+ for i in range(3):
808
+ slit_x = building_x - tower_width * 0.5 + tower_width * 0.45
809
+ slit_y = building_y + tower_height * (0.2 + i * 0.2)
810
+ svg_content += f"""<rect x="{slit_x}" y="{slit_y}"
811
+ width="{slit_width}" height="{slit_height}"
812
+ fill="black" />"""
813
+
814
+ # Right tower slits
815
+ for i in range(3):
816
+ slit_x = building_x + building_width - tower_width * 0.5 + tower_width * 0.45
817
+ slit_y = building_y + tower_height * (0.2 + i * 0.2)
818
+ svg_content += f"""<rect x="{slit_x}" y="{slit_y}"
819
+ width="{slit_width}" height="{slit_height}"
820
+ fill="black" />"""
821
+
822
+ else: # Generic building
823
+ # Building base
824
+ svg_content += f"""<rect x="{building_x}" y="{building_y}"
825
+ width="{building_width}" height="{building_height}"
826
+ fill="hsl({building_color_hue}, 30%, 70%)" stroke="black" stroke-width="2" />"""
827
+
828
+ # Building roof
829
+ roof_height = building_height * 0.2
830
+ svg_content += f"""<path d="M {building_x - building_width * 0.05} {building_y}
831
+ L {building_x + building_width * 0.5} {building_y - roof_height}
832
+ L {building_x + building_width * 1.05} {building_y} Z"
833
+ fill="hsl({(building_color_hue + 30) % 360}, 50%, 40%)" stroke="black" stroke-width="2" />"""
834
+
835
+ # Building door
836
+ door_width = building_width * 0.2
837
+ door_height = building_height * 0.3
838
+ door_x = building_x + (building_width - door_width) / 2
839
+ door_y = building_y + building_height - door_height
840
+ svg_content += f"""<rect x="{door_x}" y="{door_y}"
841
+ width="{door_width}" height="{door_height}"
842
+ fill="hsl({(building_color_hue + 60) % 360}, 30%, 40%)" stroke="black" stroke-width="1" />"""
843
+
844
+ # Windows
845
+ window_width = building_width * 0.15
846
+ window_height = building_height * 0.15
847
+ window_spacing_x = building_width * 0.25
848
+ window_spacing_y = building_height * 0.25
849
+
850
+ for i in range(3):
851
+ for j in range(2):
852
+ window_x = building_x + window_spacing_x * (i + 0.5)
853
+ window_y = building_y + window_spacing_y * (j + 0.5)
854
+ svg_content += f"""<rect x="{window_x}" y="{window_y}"
855
+ width="{window_width}" height="{window_height}"
856
+ fill="#a8d8ff" stroke="black" stroke-width="1" />"""
857
+
858
+ # Add prompt as text
859
+ svg_content += f"""<text x="{width/2}" y="{height - 20}" font-family="Arial" font-size="12" text-anchor="middle">{prompt}</text>"""
860
+
861
+ # Close SVG
862
+ svg_content += "</svg>"
863
+
864
+ return svg_content
865
+
866
+ def _generate_face_svg(self, prompt, features, num_paths=20, width=512, height=512):
867
+ """Generate a face SVG based on the prompt and features"""
868
+ # Start SVG
869
+ svg_content = f"""<svg width="{width}" height="{height}" xmlns="http://www.w3.org/2000/svg">
870
+ <rect width="100%" height="100%" fill="#f8f8f8"/>
871
+ """
872
+
873
+ # Use features to determine face properties
874
+ face_color_hue = int((features[0] + 1) * 20) % 40 + 10 # Map to 10-50 hue (skin tones)
875
+ face_size = 0.5 + 0.2 * features[1] # Size variation
876
+ face_shape = int(abs(features[2] * 3)) % 3 # 0: round, 1: oval, 2: square
877
+
878
+ # Calculate face dimensions
879
+ face_width = int(width * 0.6 * face_size)
880
+ face_height = int(height * 0.7 * face_size)
881
+ face_x = (width - face_width) // 2
882
+ face_y = (height - face_height) // 2
883
+
884
+ # Draw face shape
885
+ if face_shape == 0: # Round
886
+ svg_content += f"""<circle cx="{width/2}" cy="{height/2}" r="{face_width/2}"
887
+ fill="hsl({face_color_hue}, 50%, 80%)" stroke="black" stroke-width="2" />"""
888
+ elif face_shape == 1: # Oval
889
+ svg_content += f"""<ellipse cx="{width/2}" cy="{height/2}" rx="{face_width/2}" ry="{face_height/2}"
890
+ fill="hsl({face_color_hue}, 50%, 80%)" stroke="black" stroke-width="2" />"""
891
+ else: # Square with rounded corners
892
+ svg_content += f"""<rect x="{face_x}" y="{face_y}" width="{face_width}" height="{face_height}"
893
+ rx="{face_width/10}" ry="{face_height/10}"
894
+ fill="hsl({face_color_hue}, 50%, 80%)" stroke="black" stroke-width="2" />"""
895
+
896
+ # Determine gender from prompt
897
+ is_female = any(term in prompt.lower() for term in ["woman", "girl", "female", "lady"])
898
+
899
+ # Draw eyes
900
+ eye_width = face_width * 0.15
901
+ eye_height = face_height * 0.08
902
+ eye_y = face_y + face_height * 0.35
903
+ left_eye_x = face_x + face_width * 0.3 - eye_width / 2
904
+ right_eye_x = face_x + face_width * 0.7 - eye_width / 2
905
+
906
+ # Eye whites
907
+ svg_content += f"""<ellipse cx="{left_eye_x + eye_width/2}" cy="{eye_y + eye_height/2}"
908
+ rx="{eye_width/2}" ry="{eye_height/2}" fill="white" stroke="black" stroke-width="1" />"""
909
+ svg_content += f"""<ellipse cx="{right_eye_x + eye_width/2}" cy="{eye_y + eye_height/2}"
910
+ rx="{eye_width/2}" ry="{eye_height/2}" fill="white" stroke="black" stroke-width="1" />"""
911
+
912
+ # Pupils
913
+ pupil_size = eye_width * 0.3
914
+ svg_content += f"""<circle cx="{left_eye_x + eye_width/2}" cy="{eye_y + eye_height/2}"
915
+ r="{pupil_size}" fill="black" />"""
916
+ svg_content += f"""<circle cx="{right_eye_x + eye_width/2}" cy="{eye_y + eye_height/2}"
917
+ r="{pupil_size}" fill="black" />"""
918
+
919
+ # Eyebrows
920
+ brow_width = eye_width * 1.2
921
+ brow_height = eye_height * 0.5
922
+ brow_y = eye_y - eye_height * 0.8
923
+
924
+ svg_content += f"""<path d="M {left_eye_x} {brow_y}
925
+ Q {left_eye_x + brow_width/2} {brow_y - brow_height}, {left_eye_x + brow_width} {brow_y}"
926
+ fill="none" stroke="black" stroke-width="2" />"""
927
+ svg_content += f"""<path d="M {right_eye_x} {brow_y}
928
+ Q {right_eye_x + brow_width/2} {brow_y - brow_height}, {right_eye_x + brow_width} {brow_y}"
929
+ fill="none" stroke="black" stroke-width="2" />"""
930
+
931
+ # Nose
932
+ nose_width = face_width * 0.1
933
+ nose_height = face_height * 0.15
934
+ nose_x = face_x + face_width / 2 - nose_width / 2
935
+ nose_y = face_y + face_height * 0.5 - nose_height / 2
936
+
937
+ svg_content += f"""<path d="M {nose_x + nose_width/2} {nose_y}
938
+ L {nose_x} {nose_y + nose_height}
939
+ L {nose_x + nose_width} {nose_y + nose_height}"
940
+ fill="none" stroke="black" stroke-width="1" />"""
941
+
942
+ # Mouth
943
+ mouth_width = face_width * 0.4
944
+ mouth_height = face_height * 0.05
945
+ mouth_x = face_x + face_width / 2 - mouth_width / 2
946
+ mouth_y = face_y + face_height * 0.7
947
+
948
+ # Smiling mouth
949
+ svg_content += f"""<path d="M {mouth_x} {mouth_y}
950
+ Q {mouth_x + mouth_width/2} {mouth_y + mouth_height}, {mouth_x + mouth_width} {mouth_y}"
951
+ fill="none" stroke="black" stroke-width="1.5" />"""
952
+
953
+ # Hair
954
+ hair_color_hue = int((features[3] + 1) * 180) % 360 # Map to 0-360 hue
955
+
956
+ if is_female:
957
+ # Long hair for female
958
+ svg_content += f"""<path d="M {face_x + face_width * 0.1} {face_y + face_height * 0.2}
959
+ C {face_x - face_width * 0.1} {face_y + face_height * 0.5},
960
+ {face_x - face_width * 0.2} {face_y + face_height},
961
+ {face_x + face_width * 0.1} {face_y + face_height * 1.1}
962
+ L {face_x + face_width * 0.9} {face_y + face_height * 1.1}
963
+ C {face_x + face_width * 1.2} {face_y + face_height},
964
+ {face_x + face_width * 1.1} {face_y + face_height * 0.5},
965
+ {face_x + face_width * 0.9} {face_y + face_height * 0.2} Z"
966
+ fill="hsl({hair_color_hue}, 70%, 40%)" stroke="black" stroke-width="1" />"""
967
+
968
+ # Hair on top of head
969
+ svg_content += f"""<path d="M {face_x + face_width * 0.1} {face_y + face_height * 0.2}
970
+ C {face_x + face_width * 0.3} {face_y - face_height * 0.1},
971
+ {face_x + face_width * 0.7} {face_y - face_height * 0.1},
972
+ {face_x + face_width * 0.9} {face_y + face_height * 0.2} Z"
973
+ fill="hsl({hair_color_hue}, 70%, 40%)" stroke="black" stroke-width="1" />"""
974
+ else:
975
+ # Short hair for male
976
+ svg_content += f"""<path d="M {face_x} {face_y + face_height * 0.3}
977
+ C {face_x + face_width * 0.1} {face_y},
978
+ {face_x + face_width * 0.9} {face_y},
979
+ {face_x + face_width} {face_y + face_height * 0.3} Z"
980
+ fill="hsl({hair_color_hue}, 70%, 40%)" stroke="black" stroke-width="1" />"""
981
+
982
+ # Hair sides
983
+ svg_content += f"""<path d="M {face_x} {face_y + face_height * 0.3}
984
+ L {face_x - face_width * 0.05} {face_y + face_height * 0.5}
985
+ L {face_x} {face_y + face_height * 0.5} Z"
986
+ fill="hsl({hair_color_hue}, 70%, 40%)" stroke="black" stroke-width="1" />"""
987
+
988
+ svg_content += f"""<path d="M {face_x + face_width} {face_y + face_height * 0.3}
989
+ L {face_x + face_width * 1.05} {face_y + face_height * 0.5}
990
+ L {face_x + face_width} {face_y + face_height * 0.5} Z"
991
+ fill="hsl({hair_color_hue}, 70%, 40%)" stroke="black" stroke-width="1" />"""
992
+
993
+ # Add ears
994
+ ear_width = face_width * 0.1
995
+ ear_height = face_height * 0.2
996
+ left_ear_x = face_x - ear_width / 2
997
+ right_ear_x = face_x + face_width - ear_width / 2
998
+ ear_y = face_y + face_height * 0.4
999
+
1000
+ svg_content += f"""<ellipse cx="{left_ear_x}" cy="{ear_y}"
1001
+ rx="{ear_width/2}" ry="{ear_height/2}"
1002
+ fill="hsl({face_color_hue}, 50%, 75%)" stroke="black" stroke-width="1" />"""
1003
+
1004
+ svg_content += f"""<ellipse cx="{right_ear_x}" cy="{ear_y}"
1005
+ rx="{ear_width/2}" ry="{ear_height/2}"
1006
+ fill="hsl({face_color_hue}, 50%, 75%)" stroke="black" stroke-width="1" />"""
1007
+
1008
+ # Add prompt as text
1009
+ svg_content += f"""<text x="{width/2}" y="{height - 20}" font-family="Arial" font-size="12" text-anchor="middle">{prompt}</text>"""
1010
+
1011
+ # Close SVG
1012
+ svg_content += "</svg>"
1013
+
1014
+ return svg_content
1015
+
1016
+ def _generate_abstract_svg(self, prompt, features, num_paths=20, width=512, height=512):
1017
+ """Generate an abstract SVG based on the prompt and features"""
1018
+ # Start SVG
1019
+ svg_content = f"""<svg width="{width}" height="{height}" xmlns="http://www.w3.org/2000/svg">
1020
+ <rect width="100%" height="100%" fill="#f8f8f8"/>
1021
+ """
1022
+
1023
+ # Use features to determine abstract properties
1024
+ color_scheme = int(abs(features[0] * 5)) % 5 # 0-4 color schemes
1025
+ shape_complexity = int(abs(features[1] * 10)) + 5 # 5-15 shapes
1026
+ use_gradients = features[2] > 0
1027
+
1028
+ # Define color schemes
1029
+ color_schemes = [
1030
+ # Warm colors
1031
+ [f"hsl({h}, 80%, 60%)" for h in range(0, 61, 15)],
1032
+ # Cool colors
1033
+ [f"hsl({h}, 80%, 60%)" for h in range(180, 241, 15)],
1034
+ # Complementary
1035
+ [f"hsl({h}, 80%, 60%)" for h in range(0, 361, 180)],
1036
+ # Monochromatic
1037
+ [f"hsl(210, 80%, {l}%)" for l in range(30, 91, 15)],
1038
+ # Rainbow
1039
+ [f"hsl({h}, 80%, 60%)" for h in range(0, 361, 60)]
1040
+ ]
1041
+
1042
+ colors = color_schemes[color_scheme]
1043
+
1044
+ # Add gradients if needed
1045
+ if use_gradients:
1046
+ svg_content += """<defs>"""
1047
+ for i, color in enumerate(colors[:-1]):
1048
+ svg_content += f"""
1049
+ <linearGradient id="gradient{i}" x1="0%" y1="0%" x2="100%" y2="100%">
1050
+ <stop offset="0%" stop-color="{color}" />
1051
+ <stop offset="100%" stop-color="{colors[i+1]}" />
1052
+ </linearGradient>"""
1053
+ svg_content += """</defs>"""
1054
+
1055
+ # Generate shapes based on prompt hash
1056
+ prompt_hash = sum(ord(c) for c in prompt)
1057
+ random.seed(prompt_hash)
1058
+
1059
+ for i in range(shape_complexity):
1060
+ shape_type = i % 4 # 0: circle, 1: rectangle, 2: polygon, 3: path
1061
+ x = random.randint(0, width)
1062
+ y = random.randint(0, height)
1063
+ size = random.randint(20, 150)
1064
+ color_idx = i % len(colors)
1065
+ fill = f"url(#gradient{color_idx})" if use_gradients and color_idx < len(colors) - 1 else colors[color_idx]
1066
+ opacity = 0.3 + random.random() * 0.7
1067
+
1068
+ if shape_type == 0: # Circle
1069
+ svg_content += f"""<circle cx="{x}" cy="{y}" r="{size/2}"
1070
+ fill="{fill}" stroke="none" opacity="{opacity}" />"""
1071
+ elif shape_type == 1: # Rectangle
1072
+ svg_content += f"""<rect x="{x - size/2}" y="{y - size/2}" width="{size}" height="{size * 0.8}"
1073
+ rx="{size/10}" ry="{size/10}"
1074
+ fill="{fill}" stroke="none" opacity="{opacity}"
1075
+ transform="rotate({random.randint(0, 90)}, {x}, {y})" />"""
1076
+ elif shape_type == 2: # Polygon
1077
+ points = []
1078
+ sides = random.randint(3, 8)
1079
+ for j in range(sides):
1080
+ angle = j * 2 * 3.14159 / sides
1081
+ px = x + size/2 * np.cos(angle)
1082
+ py = y + size/2 * np.sin(angle)
1083
+ points.append(f"{px},{py}")
1084
+
1085
+ svg_content += f"""<polygon points="{' '.join(points)}"
1086
+ fill="{fill}" stroke="none" opacity="{opacity}" />"""
1087
+ else: # Path (curved)
1088
+ path = f"M {x} {y} "
1089
+ control_points = random.randint(2, 5)
1090
+ for j in range(control_points):
1091
+ cx1 = x + random.randint(-size, size)
1092
+ cy1 = y + random.randint(-size, size)
1093
+ cx2 = x + random.randint(-size, size)
1094
+ cy2 = y + random.randint(-size, size)
1095
+ ex = x + random.randint(-size, size)
1096
+ ey = y + random.randint(-size, size)
1097
+ path += f"C {cx1} {cy1}, {cx2} {cy2}, {ex} {ey} "
1098
+
1099
+ svg_content += f"""<path d="{path}"
1100
+ fill="none" stroke="{colors[color_idx]}" stroke-width="{random.randint(1, 10)}"
1101
+ opacity="{opacity}" stroke-linecap="round" />"""
1102
+
1103
+ # Add text elements based on the prompt
1104
+ words = re.findall(r'\b\w+\b', prompt)
1105
+ for i, word in enumerate(words[:5]): # Use up to 5 words from the prompt
1106
+ text_x = random.randint(width // 4, width * 3 // 4)
1107
+ text_y = random.randint(height // 4, height * 3 // 4)
1108
+ text_size = random.randint(10, 40)
1109
+ text_color = colors[i % len(colors)]
1110
+ text_opacity = 0.7 + random.random() * 0.3
1111
+ text_rotation = random.randint(-45, 45)
1112
+
1113
+ svg_content += f"""<text x="{text_x}" y="{text_y}"
1114
+ font-family="Arial" font-size="{text_size}" text-anchor="middle"
1115
+ fill="{text_color}" opacity="{text_opacity}"
1116
+ transform="rotate({text_rotation}, {text_x}, {text_y})">{word}</text>"""
1117
+
1118
+ # Add prompt as text
1119
+ svg_content += f"""<text x="{width/2}" y="{height - 20}" font-family="Arial" font-size="12" text-anchor="middle">{prompt}</text>"""
1120
+
1121
+ # Close SVG
1122
+ svg_content += "</svg>"
1123
+
1124
+ return svg_content
1125
+
1126
+ def svg_to_png(self, svg_content):
1127
+ """Convert SVG content to PNG"""
1128
+ try:
1129
+ png_data = cairosvg.svg2png(bytestring=svg_content.encode("utf-8"))
1130
+ return png_data
1131
+ except Exception as e:
1132
+ print(f"Error converting SVG to PNG: {e}")
1133
+ # Create a simple error image
1134
+ image = Image.new("RGB", (512, 512), color="#ff0000")
1135
+ from PIL import ImageDraw
1136
+ draw = ImageDraw.Draw(image)
1137
+ draw.text((256, 256), f"Error: {str(e)}", fill="white", anchor="mm")
1138
+
1139
+ # Convert PIL Image to PNG data
1140
+ buffer = io.BytesIO()
1141
+ image.save(buffer, format="PNG")
1142
+ return buffer.getvalue()
1143
+
1144
+ def __call__(self, prompt):
1145
+ """Generate an SVG from a text prompt and convert to PNG"""
1146
+ svg_content = self.generate_svg(prompt)
1147
+ png_data = self.svg_to_png(svg_content)
1148
+
1149
+ # Create a PIL Image from the PNG data
1150
+ image = Image.open(io.BytesIO(png_data))
1151
+
1152
+ # Create the response
1153
+ response = {
1154
+ "svg": svg_content,
1155
+ "svg_base64": base64.b64encode(svg_content.encode("utf-8")).decode("utf-8"),
1156
+ "png_base64": base64.b64encode(png_data).decode("utf-8"),
1157
+ "image": image
1158
+ }
1159
+
1160
+ return response