Update: Add versatile SVG generator implementation
Browse files- handler.py +11 -11
- 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
|
| 28 |
try:
|
| 29 |
-
from
|
| 30 |
except ImportError:
|
| 31 |
-
print("Warning:
|
| 32 |
-
|
| 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
|
| 42 |
-
if
|
| 43 |
try:
|
| 44 |
-
self.model =
|
| 45 |
self.use_model = True
|
| 46 |
-
print("
|
| 47 |
except Exception as e:
|
| 48 |
-
print(f"Error initializing
|
| 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
|
| 80 |
result = self.model(prompt)
|
| 81 |
image = result["image"]
|
| 82 |
except Exception as e:
|
| 83 |
-
print(f"Error using
|
| 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
|