diffsketcher / versatile_svg_generator.py
jree423's picture
Update: Add versatile SVG generator implementation
c158dc0 verified
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Versatile SVG Generator that creates different types of objects based on the prompt.
"""
import os
import io
import base64
import torch
import numpy as np
from PIL import Image
import cairosvg
import random
from pathlib import Path
import re
class VersatileSVGGenerator:
def __init__(self, model_dir):
"""Initialize the versatile SVG generator"""
self.model_dir = model_dir
self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"Initializing versatile SVG generator on device: {self.device}")
# Load CLIP model if available
try:
import clip
self.clip_model, _ = clip.load("ViT-B-32", device=self.device)
self.clip_available = True
print("CLIP model loaded successfully")
except Exception as e:
print(f"Error loading CLIP model: {e}")
self.clip_available = False
def generate_svg(self, prompt, num_paths=20, width=512, height=512):
"""Generate an SVG from a text prompt"""
print(f"Generating SVG for prompt: {prompt}")
# Use CLIP to encode the prompt if available
if self.clip_available:
try:
import clip
with torch.no_grad():
text = clip.tokenize([prompt]).to(self.device)
text_features = self.clip_model.encode_text(text)
text_features = text_features.cpu().numpy()[0]
# Normalize features
text_features = text_features / np.linalg.norm(text_features)
except Exception as e:
print(f"Error encoding prompt with CLIP: {e}")
text_features = np.random.randn(512) # Random features as fallback
else:
# Generate random features if CLIP is not available
text_features = np.random.randn(512)
# Determine what type of object to generate based on the prompt
object_type = self._determine_object_type(prompt)
# Generate SVG based on the object type
if object_type == "car":
svg_content = self._generate_car_svg(prompt, text_features, num_paths, width, height)
elif object_type == "landscape":
svg_content = self._generate_landscape_svg(prompt, text_features, num_paths, width, height)
elif object_type == "animal":
svg_content = self._generate_animal_svg(prompt, text_features, num_paths, width, height)
elif object_type == "building":
svg_content = self._generate_building_svg(prompt, text_features, num_paths, width, height)
elif object_type == "face":
svg_content = self._generate_face_svg(prompt, text_features, num_paths, width, height)
else:
svg_content = self._generate_abstract_svg(prompt, text_features, num_paths, width, height)
return svg_content
def _determine_object_type(self, prompt):
"""Determine what type of object to generate based on the prompt"""
prompt = prompt.lower()
# Check for car-related terms
car_terms = ["car", "vehicle", "truck", "suv", "sedan", "convertible", "sports car", "automobile"]
for term in car_terms:
if term in prompt:
return "car"
# Check for landscape-related terms
landscape_terms = ["landscape", "mountain", "forest", "beach", "ocean", "sea", "lake", "river", "sunset", "sunrise", "sky"]
for term in landscape_terms:
if term in prompt:
return "landscape"
# Check for animal-related terms
animal_terms = ["animal", "dog", "cat", "bird", "horse", "lion", "tiger", "elephant", "bear", "fish", "pet"]
for term in animal_terms:
if term in prompt:
return "animal"
# Check for building-related terms
building_terms = ["building", "house", "skyscraper", "tower", "castle", "mansion", "apartment", "office", "structure"]
for term in building_terms:
if term in prompt:
return "building"
# Check for face-related terms
face_terms = ["face", "portrait", "person", "man", "woman", "boy", "girl", "human", "head", "smile"]
for term in face_terms:
if term in prompt:
return "face"
# Default to abstract
return "abstract"
def _generate_car_svg(self, prompt, features, num_paths=20, width=512, height=512):
"""Generate a car-like SVG based on the prompt and features"""
# Start SVG
svg_content = f"""<svg width="{width}" height="{height}" xmlns="http://www.w3.org/2000/svg">
<rect width="100%" height="100%" fill="#f8f8f8"/>
"""
# Use the features to determine car properties
car_color_hue = int((features[0] + 1) * 180) % 360 # Map to 0-360 hue
car_size = 0.6 + 0.2 * features[1] # Size variation
car_style = int(abs(features[2] * 3)) % 3 # 0: sedan, 1: SUV, 2: sports car
# Calculate car dimensions
car_width = int(width * 0.7 * car_size)
car_height = int(height * 0.3 * car_size)
car_x = (width - car_width) // 2
car_y = height // 2
# Generate car body based on style
if car_style == 0: # Sedan
# Car body (rounded rectangle)
svg_content += f"""<rect x="{car_x}" y="{car_y}" width="{car_width}" height="{car_height}"
rx="20" ry="20" fill="hsl({car_color_hue}, 80%, 50%)" stroke="black" stroke-width="2" />"""
# Windshield
windshield_width = car_width * 0.7
windshield_height = car_height * 0.5
windshield_x = car_x + (car_width - windshield_width) // 2
windshield_y = car_y - windshield_height * 0.3
svg_content += f"""<rect x="{windshield_x}" y="{windshield_y}" width="{windshield_width}" height="{windshield_height}"
rx="10" ry="10" fill="#a8d8ff" stroke="black" stroke-width="1" />"""
# Wheels
wheel_radius = car_height * 0.4
wheel_y = car_y + car_height * 0.8
svg_content += f"""<circle cx="{car_x + car_width * 0.2}" cy="{wheel_y}" r="{wheel_radius}" fill="black" />"""
svg_content += f"""<circle cx="{car_x + car_width * 0.8}" cy="{wheel_y}" r="{wheel_radius}" fill="black" />"""
svg_content += f"""<circle cx="{car_x + car_width * 0.2}" cy="{wheel_y}" r="{wheel_radius * 0.6}" fill="#444" />"""
svg_content += f"""<circle cx="{car_x + car_width * 0.8}" cy="{wheel_y}" r="{wheel_radius * 0.6}" fill="#444" />"""
elif car_style == 1: # SUV
# Car body (taller rectangle)
svg_content += f"""<rect x="{car_x}" y="{car_y - car_height * 0.3}" width="{car_width}" height="{car_height * 1.3}"
rx="15" ry="15" fill="hsl({car_color_hue}, 80%, 50%)" stroke="black" stroke-width="2" />"""
# Windshield
windshield_width = car_width * 0.6
windshield_height = car_height * 0.6
windshield_x = car_x + (car_width - windshield_width) // 2
windshield_y = car_y - car_height * 0.2
svg_content += f"""<rect x="{windshield_x}" y="{windshield_y}" width="{windshield_width}" height="{windshield_height}"
rx="8" ry="8" fill="#a8d8ff" stroke="black" stroke-width="1" />"""
# Wheels (larger)
wheel_radius = car_height * 0.45
wheel_y = car_y + car_height * 0.7
svg_content += f"""<circle cx="{car_x + car_width * 0.2}" cy="{wheel_y}" r="{wheel_radius}" fill="black" />"""
svg_content += f"""<circle cx="{car_x + car_width * 0.8}" cy="{wheel_y}" r="{wheel_radius}" fill="black" />"""
svg_content += f"""<circle cx="{car_x + car_width * 0.2}" cy="{wheel_y}" r="{wheel_radius * 0.6}" fill="#444" />"""
svg_content += f"""<circle cx="{car_x + car_width * 0.8}" cy="{wheel_y}" r="{wheel_radius * 0.6}" fill="#444" />"""
else: # Sports car
# Car body (low, sleek shape)
svg_content += f"""<path d="M {car_x} {car_y + car_height * 0.5}
C {car_x + car_width * 0.1} {car_y - car_height * 0.2},
{car_x + car_width * 0.3} {car_y - car_height * 0.3},
{car_x + car_width * 0.5} {car_y - car_height * 0.2}
S {car_x + car_width * 0.9} {car_y},
{car_x + car_width} {car_y + car_height * 0.3}
L {car_x + car_width} {car_y + car_height * 0.7}
C {car_x + car_width * 0.9} {car_y + car_height},
{car_x + car_width * 0.1} {car_y + car_height},
{car_x} {car_y + car_height * 0.7} Z"
fill="hsl({car_color_hue}, 90%, 45%)" stroke="black" stroke-width="2" />"""
# Windshield
windshield_width = car_width * 0.4
windshield_x = car_x + car_width * 0.3
windshield_y = car_y - car_height * 0.1
svg_content += f"""<path d="M {windshield_x} {windshield_y}
C {windshield_x + windshield_width * 0.1} {windshield_y - car_height * 0.15},
{windshield_x + windshield_width * 0.9} {windshield_y - car_height * 0.15},
{windshield_x + windshield_width} {windshield_y} Z"
fill="#a8d8ff" stroke="black" stroke-width="1" />"""
# Wheels (low profile)
wheel_radius = car_height * 0.35
wheel_y = car_y + car_height * 0.7
svg_content += f"""<ellipse cx="{car_x + car_width * 0.2}" cy="{wheel_y}" rx="{wheel_radius * 1.2}" ry="{wheel_radius}" fill="black" />"""
svg_content += f"""<ellipse cx="{car_x + car_width * 0.8}" cy="{wheel_y}" rx="{wheel_radius * 1.2}" ry="{wheel_radius}" fill="black" />"""
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" />"""
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" />"""
# Add headlights
headlight_radius = car_width * 0.05
headlight_y = car_y + car_height * 0.3
svg_content += f"""<circle cx="{car_x + car_width * 0.1}" cy="{headlight_y}" r="{headlight_radius}" fill="yellow" stroke="black" stroke-width="1" />"""
svg_content += f"""<circle cx="{car_x + car_width * 0.9}" cy="{headlight_y}" r="{headlight_radius}" fill="yellow" stroke="black" stroke-width="1" />"""
# Add prompt as text
svg_content += f"""<text x="{width/2}" y="{height - 20}" font-family="Arial" font-size="12" text-anchor="middle">{prompt}</text>"""
# Close SVG
svg_content += "</svg>"
return svg_content
def _generate_landscape_svg(self, prompt, features, num_paths=20, width=512, height=512):
"""Generate a landscape SVG based on the prompt and features"""
# Start SVG
svg_content = f"""<svg width="{width}" height="{height}" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="skyGradient" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="#87CEEB" />
<stop offset="100%" stop-color="#E0F7FF" />
</linearGradient>
</defs>
<rect width="100%" height="100%" fill="url(#skyGradient)"/>
"""
# Use features to determine landscape properties
mountain_count = int(abs(features[0] * 5)) + 3
tree_count = int(abs(features[1] * 20)) + 5
has_sun = features[2] > 0
has_water = features[3] > 0
# Draw mountains
for i in range(mountain_count):
mountain_height = height * (0.3 + 0.2 * abs(features[i % len(features)]))
mountain_width = width * (0.2 + 0.1 * abs(features[(i+1) % len(features)]))
mountain_x = width * (i / mountain_count)
mountain_color = f"hsl({int(120 + features[i % len(features)] * 20)}, 30%, {30 + int(abs(features[i % len(features)] * 20))}%)"
svg_content += f"""<path d="M {mountain_x} {height}
L {mountain_x + mountain_width/2} {height - mountain_height}
L {mountain_x + mountain_width} {height} Z"
fill="{mountain_color}" stroke="none" />"""
# Draw sun if present
if has_sun:
sun_x = width * (0.1 + 0.8 * abs(features[4]))
sun_y = height * 0.2
sun_radius = width * 0.08
svg_content += f"""<circle cx="{sun_x}" cy="{sun_y}" r="{sun_radius}" fill="yellow" stroke="none">
<animate attributeName="r" values="{sun_radius};{sun_radius*1.05};{sun_radius}" dur="4s" repeatCount="indefinite" />
</circle>"""
# Draw water if present
if has_water:
water_height = height * 0.3
water_y = height - water_height
svg_content += f"""<rect x="0" y="{water_y}" width="{width}" height="{water_height}" fill="#4a86e8" opacity="0.7">
<animate attributeName="height" values="{water_height};{water_height*1.02};{water_height}" dur="3s" repeatCount="indefinite" />
</rect>"""
# Add waves
for i in range(5):
wave_y = water_y + i * water_height / 5
svg_content += f"""<path d="M 0 {wave_y}
Q {width/4} {wave_y-5}, {width/2} {wave_y}
T {width} {wave_y}"
fill="none" stroke="white" stroke-width="1" opacity="0.3" />"""
# Draw trees
for i in range(tree_count):
tree_x = width * (0.1 + 0.8 * (i / tree_count))
tree_y = height * 0.8
tree_height = height * (0.1 + 0.1 * abs(features[i % len(features)]))
tree_width = tree_height * 0.6
# Tree trunk
svg_content += f"""<rect x="{tree_x - tree_width/8}" y="{tree_y - tree_height/3}"
width="{tree_width/4}" height="{tree_height/3}"
fill="#8B4513" stroke="none" />"""
# Tree foliage
svg_content += f"""<path d="M {tree_x - tree_width/2} {tree_y - tree_height/3}
L {tree_x} {tree_y - tree_height}
L {tree_x + tree_width/2} {tree_y - tree_height/3} Z"
fill="#228B22" stroke="none" />
<path d="M {tree_x - tree_width/2} {tree_y - tree_height/2}
L {tree_x} {tree_y - tree_height * 1.1}
L {tree_x + tree_width/2} {tree_y - tree_height/2} Z"
fill="#228B22" stroke="none" />"""
# Add prompt as text
svg_content += f"""<text x="{width/2}" y="{height - 20}" font-family="Arial" font-size="12" text-anchor="middle" fill="black">{prompt}</text>"""
# Close SVG
svg_content += "</svg>"
return svg_content
def _generate_animal_svg(self, prompt, features, num_paths=20, width=512, height=512):
"""Generate an animal SVG based on the prompt and features"""
# Start SVG
svg_content = f"""<svg width="{width}" height="{height}" xmlns="http://www.w3.org/2000/svg">
<rect width="100%" height="100%" fill="#f8f8f8"/>
"""
# Determine animal type from prompt
animal_type = "generic"
if "dog" in prompt.lower() or "puppy" in prompt.lower():
animal_type = "dog"
elif "cat" in prompt.lower() or "kitten" in prompt.lower():
animal_type = "cat"
elif "bird" in prompt.lower():
animal_type = "bird"
elif "fish" in prompt.lower():
animal_type = "fish"
# Use features to determine animal properties
animal_color_hue = int((features[0] + 1) * 180) % 360 # Map to 0-360 hue
animal_size = 0.5 + 0.3 * features[1] # Size variation
# Calculate animal dimensions
animal_width = int(width * 0.6 * animal_size)
animal_height = int(height * 0.4 * animal_size)
animal_x = (width - animal_width) // 2
animal_y = height // 2
if animal_type == "dog":
# Dog body (oval)
svg_content += f"""<ellipse cx="{animal_x + animal_width * 0.5}" cy="{animal_y + animal_height * 0.5}"
rx="{animal_width * 0.5}" ry="{animal_height * 0.3}"
fill="hsl({animal_color_hue}, 70%, 60%)" stroke="black" stroke-width="2" />"""
# Dog head (circle)
head_radius = animal_width * 0.2
svg_content += f"""<circle cx="{animal_x + animal_width * 0.8}" cy="{animal_y + animal_height * 0.3}"
r="{head_radius}" fill="hsl({animal_color_hue}, 70%, 60%)" stroke="black" stroke-width="2" />"""
# Dog ears
svg_content += f"""<ellipse cx="{animal_x + animal_width * 0.75}" cy="{animal_y + animal_height * 0.15}"
rx="{head_radius * 0.5}" ry="{head_radius * 0.8}"
fill="hsl({animal_color_hue}, 70%, 50%)" stroke="black" stroke-width="1" />"""
svg_content += f"""<ellipse cx="{animal_x + animal_width * 0.85}" cy="{animal_y + animal_height * 0.15}"
rx="{head_radius * 0.5}" ry="{head_radius * 0.8}"
fill="hsl({animal_color_hue}, 70%, 50%)" stroke="black" stroke-width="1" />"""
# Dog eyes
svg_content += f"""<circle cx="{animal_x + animal_width * 0.75}" cy="{animal_y + animal_height * 0.25}"
r="{head_radius * 0.15}" fill="black" />"""
svg_content += f"""<circle cx="{animal_x + animal_width * 0.85}" cy="{animal_y + animal_height * 0.25}"
r="{head_radius * 0.15}" fill="black" />"""
# Dog nose
svg_content += f"""<ellipse cx="{animal_x + animal_width * 0.9}" cy="{animal_y + animal_height * 0.35}"
rx="{head_radius * 0.2}" ry="{head_radius * 0.15}"
fill="black" />"""
# Dog legs
leg_width = animal_width * 0.1
leg_height = animal_height * 0.4
svg_content += f"""<rect x="{animal_x + animal_width * 0.3}" y="{animal_y + animal_height * 0.6}"
width="{leg_width}" height="{leg_height}"
fill="hsl({animal_color_hue}, 70%, 55%)" stroke="black" stroke-width="1" />"""
svg_content += f"""<rect x="{animal_x + animal_width * 0.5}" y="{animal_y + animal_height * 0.6}"
width="{leg_width}" height="{leg_height}"
fill="hsl({animal_color_hue}, 70%, 55%)" stroke="black" stroke-width="1" />"""
# Dog tail
svg_content += f"""<path d="M {animal_x + animal_width * 0.1} {animal_y + animal_height * 0.4}
C {animal_x} {animal_y + animal_height * 0.2},
{animal_x - animal_width * 0.1} {animal_y + animal_height * 0.3},
{animal_x - animal_width * 0.05} {animal_y + animal_height * 0.5}"
fill="none" stroke="hsl({animal_color_hue}, 70%, 55%)" stroke-width="{leg_width}" stroke-linecap="round" />"""
elif animal_type == "cat":
# Cat body (oval)
svg_content += f"""<ellipse cx="{animal_x + animal_width * 0.5}" cy="{animal_y + animal_height * 0.5}"
rx="{animal_width * 0.4}" ry="{animal_height * 0.25}"
fill="hsl({animal_color_hue}, 70%, 60%)" stroke="black" stroke-width="2" />"""
# Cat head (circle)
head_radius = animal_width * 0.18
svg_content += f"""<circle cx="{animal_x + animal_width * 0.8}" cy="{animal_y + animal_height * 0.3}"
r="{head_radius}" fill="hsl({animal_color_hue}, 70%, 60%)" stroke="black" stroke-width="2" />"""
# Cat ears (triangles)
svg_content += f"""<path d="M {animal_x + animal_width * 0.75} {animal_y + animal_height * 0.2}
L {animal_x + animal_width * 0.7} {animal_y + animal_height * 0.05}
L {animal_x + animal_width * 0.65} {animal_y + animal_height * 0.2} Z"
fill="hsl({animal_color_hue}, 70%, 50%)" stroke="black" stroke-width="1" />"""
svg_content += f"""<path d="M {animal_x + animal_width * 0.85} {animal_y + animal_height * 0.2}
L {animal_x + animal_width * 0.9} {animal_y + animal_height * 0.05}
L {animal_x + animal_width * 0.95} {animal_y + animal_height * 0.2} Z"
fill="hsl({animal_color_hue}, 70%, 50%)" stroke="black" stroke-width="1" />"""
# Cat eyes (almond shaped)
svg_content += f"""<ellipse cx="{animal_x + animal_width * 0.75}" cy="{animal_y + animal_height * 0.25}"
rx="{head_radius * 0.15}" ry="{head_radius * 0.08}"
fill="black" />"""
svg_content += f"""<ellipse cx="{animal_x + animal_width * 0.85}" cy="{animal_y + animal_height * 0.25}"
rx="{head_radius * 0.15}" ry="{head_radius * 0.08}"
fill="black" />"""
# Cat nose
svg_content += f"""<path d="M {animal_x + animal_width * 0.8} {animal_y + animal_height * 0.3}
L {animal_x + animal_width * 0.78} {animal_y + animal_height * 0.33}
L {animal_x + animal_width * 0.82} {animal_y + animal_height * 0.33} Z"
fill="pink" stroke="black" stroke-width="0.5" />"""
# Cat whiskers
svg_content += f"""<path d="M {animal_x + animal_width * 0.78} {animal_y + animal_height * 0.32}
L {animal_x + animal_width * 0.65} {animal_y + animal_height * 0.3}"
fill="none" stroke="black" stroke-width="0.5" />"""
svg_content += f"""<path d="M {animal_x + animal_width * 0.78} {animal_y + animal_height * 0.34}
L {animal_x + animal_width * 0.65} {animal_y + animal_height * 0.35}"
fill="none" stroke="black" stroke-width="0.5" />"""
svg_content += f"""<path d="M {animal_x + animal_width * 0.82} {animal_y + animal_height * 0.32}
L {animal_x + animal_width * 0.95} {animal_y + animal_height * 0.3}"
fill="none" stroke="black" stroke-width="0.5" />"""
svg_content += f"""<path d="M {animal_x + animal_width * 0.82} {animal_y + animal_height * 0.34}
L {animal_x + animal_width * 0.95} {animal_y + animal_height * 0.35}"
fill="none" stroke="black" stroke-width="0.5" />"""
# Cat legs
leg_width = animal_width * 0.08
leg_height = animal_height * 0.3
svg_content += f"""<rect x="{animal_x + animal_width * 0.35}" y="{animal_y + animal_height * 0.6}"
width="{leg_width}" height="{leg_height}"
fill="hsl({animal_color_hue}, 70%, 55%)" stroke="black" stroke-width="1" />"""
svg_content += f"""<rect x="{animal_x + animal_width * 0.55}" y="{animal_y + animal_height * 0.6}"
width="{leg_width}" height="{leg_height}"
fill="hsl({animal_color_hue}, 70%, 55%)" stroke="black" stroke-width="1" />"""
# Cat tail
svg_content += f"""<path d="M {animal_x + animal_width * 0.1} {animal_y + animal_height * 0.4}
C {animal_x} {animal_y + animal_height * 0.2},
{animal_x - animal_width * 0.1} {animal_y + animal_height * 0.1},
{animal_x - animal_width * 0.15} {animal_y + animal_height * 0.3}"
fill="none" stroke="hsl({animal_color_hue}, 70%, 55%)" stroke-width="{leg_width}" stroke-linecap="round" />"""
elif animal_type == "bird":
# Bird body (oval)
svg_content += f"""<ellipse cx="{animal_x + animal_width * 0.5}" cy="{animal_y + animal_height * 0.5}"
rx="{animal_width * 0.3}" ry="{animal_height * 0.25}"
fill="hsl({animal_color_hue}, 90%, 60%)" stroke="black" stroke-width="2" />"""
# Bird head
head_radius = animal_width * 0.15
svg_content += f"""<circle cx="{animal_x + animal_width * 0.8}" cy="{animal_y + animal_height * 0.4}"
r="{head_radius}" fill="hsl({animal_color_hue}, 90%, 60%)" stroke="black" stroke-width="2" />"""
# Bird beak
svg_content += f"""<path d="M {animal_x + animal_width * 0.9} {animal_y + animal_height * 0.4}
L {animal_x + animal_width * 1.0} {animal_y + animal_height * 0.35}
L {animal_x + animal_width * 0.9} {animal_y + animal_height * 0.45} Z"
fill="orange" stroke="black" stroke-width="1" />"""
# Bird eye
svg_content += f"""<circle cx="{animal_x + animal_width * 0.85}" cy="{animal_y + animal_height * 0.35}"
r="{head_radius * 0.2}" fill="black" />"""
# Bird wings
svg_content += f"""<path d="M {animal_x + animal_width * 0.5} {animal_y + animal_height * 0.4}
C {animal_x + animal_width * 0.4} {animal_y + animal_height * 0.2},
{animal_x + animal_width * 0.3} {animal_y + animal_height * 0.1},
{animal_x + animal_width * 0.2} {animal_y + animal_height * 0.3}"
fill="hsl({animal_color_hue}, 90%, 50%)" stroke="black" stroke-width="1" />"""
# Bird tail
svg_content += f"""<path d="M {animal_x + animal_width * 0.2} {animal_y + animal_height * 0.5}
L {animal_x} {animal_y + animal_height * 0.4}
L {animal_x + animal_width * 0.1} {animal_y + animal_height * 0.5}
L {animal_x} {animal_y + animal_height * 0.6} Z"
fill="hsl({animal_color_hue}, 90%, 40%)" stroke="black" stroke-width="1" />"""
# Bird legs
leg_width = animal_width * 0.02
leg_height = animal_height * 0.2
svg_content += f"""<rect x="{animal_x + animal_width * 0.45}" y="{animal_y + animal_height * 0.75}"
width="{leg_width}" height="{leg_height}"
fill="orange" stroke="black" stroke-width="1" />"""
svg_content += f"""<rect x="{animal_x + animal_width * 0.55}" y="{animal_y + animal_height * 0.75}"
width="{leg_width}" height="{leg_height}"
fill="orange" stroke="black" stroke-width="1" />"""
elif animal_type == "fish":
# Fish body (oval)
svg_content += f"""<ellipse cx="{animal_x + animal_width * 0.5}" cy="{animal_y + animal_height * 0.5}"
rx="{animal_width * 0.4}" ry="{animal_height * 0.25}"
fill="hsl({animal_color_hue}, 90%, 60%)" stroke="black" stroke-width="2" />"""
# Fish tail
svg_content += f"""<path d="M {animal_x + animal_width * 0.1} {animal_y + animal_height * 0.3}
L {animal_x - animal_width * 0.1} {animal_y + animal_height * 0.5}
L {animal_x + animal_width * 0.1} {animal_y + animal_height * 0.7} Z"
fill="hsl({animal_color_hue}, 90%, 50%)" stroke="black" stroke-width="1" />"""
# Fish eye
svg_content += f"""<circle cx="{animal_x + animal_width * 0.7}" cy="{animal_y + animal_height * 0.4}"
r="{animal_width * 0.05}" fill="black" />"""
svg_content += f"""<circle cx="{animal_x + animal_width * 0.7}" cy="{animal_y + animal_height * 0.4}"
r="{animal_width * 0.02}" fill="white" />"""
# Fish fins
svg_content += f"""<path d="M {animal_x + animal_width * 0.5} {animal_y + animal_height * 0.25}
C {animal_x + animal_width * 0.6} {animal_y},
{animal_x + animal_width * 0.7} {animal_y},
{animal_x + animal_width * 0.8} {animal_y + animal_height * 0.25}"
fill="hsl({animal_color_hue}, 90%, 50%)" stroke="black" stroke-width="1" />"""
svg_content += f"""<path d="M {animal_x + animal_width * 0.5} {animal_y + animal_height * 0.75}
C {animal_x + animal_width * 0.6} {animal_y + animal_height},
{animal_x + animal_width * 0.7} {animal_y + animal_height},
{animal_x + animal_width * 0.8} {animal_y + animal_height * 0.75}"
fill="hsl({animal_color_hue}, 90%, 50%)" stroke="black" stroke-width="1" />"""
# Fish scales (simplified)
for i in range(5):
for j in range(3):
scale_x = animal_x + animal_width * (0.3 + i * 0.1)
scale_y = animal_y + animal_height * (0.4 + (j-1) * 0.1)
scale_radius = animal_width * 0.03
svg_content += f"""<circle cx="{scale_x}" cy="{scale_y}" r="{scale_radius}"
fill="none" stroke="hsl({animal_color_hue}, 90%, 40%)" stroke-width="0.5" />"""
# Water bubbles
for i in range(3):
bubble_x = animal_x + animal_width * (0.8 + i * 0.1)
bubble_y = animal_y + animal_height * (0.3 - i * 0.1)
bubble_radius = animal_width * (0.02 + i * 0.01)
svg_content += f"""<circle cx="{bubble_x}" cy="{bubble_y}" r="{bubble_radius}"
fill="white" fill-opacity="0.7" stroke="lightblue" stroke-width="0.5" />"""
else: # Generic animal
# Body (oval)
svg_content += f"""<ellipse cx="{animal_x + animal_width * 0.5}" cy="{animal_y + animal_height * 0.5}"
rx="{animal_width * 0.4}" ry="{animal_height * 0.25}"
fill="hsl({animal_color_hue}, 70%, 60%)" stroke="black" stroke-width="2" />"""
# Head (circle)
head_radius = animal_width * 0.2
svg_content += f"""<circle cx="{animal_x + animal_width * 0.8}" cy="{animal_y + animal_height * 0.4}"
r="{head_radius}" fill="hsl({animal_color_hue}, 70%, 60%)" stroke="black" stroke-width="2" />"""
# Eyes
svg_content += f"""<circle cx="{animal_x + animal_width * 0.75}" cy="{animal_y + animal_height * 0.35}"
r="{head_radius * 0.15}" fill="black" />"""
svg_content += f"""<circle cx="{animal_x + animal_width * 0.85}" cy="{animal_y + animal_height * 0.35}"
r="{head_radius * 0.15}" fill="black" />"""
# Legs
leg_width = animal_width * 0.08
leg_height = animal_height * 0.3
svg_content += f"""<rect x="{animal_x + animal_width * 0.3}" y="{animal_y + animal_height * 0.6}"
width="{leg_width}" height="{leg_height}"
fill="hsl({animal_color_hue}, 70%, 55%)" stroke="black" stroke-width="1" />"""
svg_content += f"""<rect x="{animal_x + animal_width * 0.5}" y="{animal_y + animal_height * 0.6}"
width="{leg_width}" height="{leg_height}"
fill="hsl({animal_color_hue}, 70%, 55%)" stroke="black" stroke-width="1" />"""
# Tail
svg_content += f"""<path d="M {animal_x + animal_width * 0.1} {animal_y + animal_height * 0.5}
C {animal_x} {animal_y + animal_height * 0.4},
{animal_x - animal_width * 0.1} {animal_y + animal_height * 0.3},
{animal_x - animal_width * 0.05} {animal_y + animal_height * 0.6}"
fill="none" stroke="hsl({animal_color_hue}, 70%, 55%)" stroke-width="{leg_width}" stroke-linecap="round" />"""
# Add prompt as text
svg_content += f"""<text x="{width/2}" y="{height - 20}" font-family="Arial" font-size="12" text-anchor="middle">{prompt}</text>"""
# Close SVG
svg_content += "</svg>"
return svg_content
def _generate_building_svg(self, prompt, features, num_paths=20, width=512, height=512):
"""Generate a building SVG based on the prompt and features"""
# Start SVG
svg_content = f"""<svg width="{width}" height="{height}" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="skyGradient" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="#87CEEB" />
<stop offset="100%" stop-color="#E0F7FF" />
</linearGradient>
</defs>
<rect width="100%" height="100%" fill="url(#skyGradient)"/>
"""
# Determine building type from prompt
building_type = "generic"
if "house" in prompt.lower():
building_type = "house"
elif "skyscraper" in prompt.lower() or "tower" in prompt.lower():
building_type = "skyscraper"
elif "castle" in prompt.lower():
building_type = "castle"
# Use features to determine building properties
building_color_hue = int((features[0] + 1) * 180) % 360 # Map to 0-360 hue
building_size = 0.5 + 0.3 * features[1] # Size variation
# Calculate building dimensions
building_width = int(width * 0.6 * building_size)
building_height = int(height * 0.7 * building_size)
building_x = (width - building_width) // 2
building_y = height - building_height
if building_type == "house":
# House base
svg_content += f"""<rect x="{building_x}" y="{building_y + building_height * 0.3}"
width="{building_width}" height="{building_height * 0.7}"
fill="hsl({building_color_hue}, 30%, 70%)" stroke="black" stroke-width="2" />"""
# House roof
svg_content += f"""<path d="M {building_x - building_width * 0.1} {building_y + building_height * 0.3}
L {building_x + building_width * 0.5} {building_y}
L {building_x + building_width * 1.1} {building_y + building_height * 0.3} Z"
fill="hsl({(building_color_hue + 30) % 360}, 50%, 40%)" stroke="black" stroke-width="2" />"""
# House door
door_width = building_width * 0.2
door_height = building_height * 0.4
door_x = building_x + (building_width - door_width) / 2
door_y = building_y + building_height - door_height
svg_content += f"""<rect x="{door_x}" y="{door_y}"
width="{door_width}" height="{door_height}"
fill="hsl({(building_color_hue + 60) % 360}, 30%, 40%)" stroke="black" stroke-width="1" />"""
# Door knob
svg_content += f"""<circle cx="{door_x + door_width * 0.8}" cy="{door_y + door_height * 0.5}"
r="{door_width * 0.1}" fill="gold" stroke="black" stroke-width="0.5" />"""
# Windows
window_width = building_width * 0.15
window_height = building_height * 0.15
window_spacing = building_width * 0.25
for i in range(2):
for j in range(2):
window_x = building_x + window_spacing + i * window_spacing
window_y = building_y + building_height * 0.4 + j * window_spacing
svg_content += f"""<rect x="{window_x}" y="{window_y}"
width="{window_width}" height="{window_height}"
fill="#a8d8ff" stroke="black" stroke-width="1" />"""
# Window crossbars
svg_content += f"""<path d="M {window_x} {window_y + window_height/2}
L {window_x + window_width} {window_y + window_height/2}"
fill="none" stroke="black" stroke-width="0.5" />"""
svg_content += f"""<path d="M {window_x + window_width/2} {window_y}
L {window_x + window_width/2} {window_y + window_height}"
fill="none" stroke="black" stroke-width="0.5" />"""
# Chimney
chimney_width = building_width * 0.1
chimney_height = building_height * 0.3
chimney_x = building_x + building_width * 0.7
chimney_y = building_y + building_height * 0.1 - chimney_height
svg_content += f"""<rect x="{chimney_x}" y="{chimney_y}"
width="{chimney_width}" height="{chimney_height}"
fill="hsl({(building_color_hue + 30) % 360}, 30%, 30%)" stroke="black" stroke-width="1" />"""
elif building_type == "skyscraper":
# Skyscraper base
svg_content += f"""<rect x="{building_x}" y="{building_y}"
width="{building_width}" height="{building_height}"
fill="hsl({building_color_hue}, 20%, 70%)" stroke="black" stroke-width="2" />"""
# Skyscraper top
top_width = building_width * 0.7
top_height = building_height * 0.1
top_x = building_x + (building_width - top_width) / 2
svg_content += f"""<rect x="{top_x}" y="{building_y - top_height}"
width="{top_width}" height="{top_height}"
fill="hsl({building_color_hue}, 20%, 50%)" stroke="black" stroke-width="1" />"""
# Antenna
antenna_width = building_width * 0.02
antenna_height = building_height * 0.15
antenna_x = building_x + building_width / 2 - antenna_width / 2
antenna_y = building_y - top_height - antenna_height
svg_content += f"""<rect x="{antenna_x}" y="{antenna_y}"
width="{antenna_width}" height="{antenna_height}"
fill="silver" stroke="black" stroke-width="0.5" />"""
# Windows (grid pattern)
window_width = building_width * 0.08
window_height = building_height * 0.05
window_spacing_x = building_width * 0.12
window_spacing_y = building_height * 0.08
for i in range(int(building_width / window_spacing_x) - 1):
for j in range(int(building_height / window_spacing_y) - 1):
window_x = building_x + window_spacing_x * (i + 0.5)
window_y = building_y + window_spacing_y * (j + 0.5)
# Randomize window lighting
window_color = "#a8d8ff"
if random.random() < 0.3: # 30% chance of lit window
window_color = "#ffff88"
svg_content += f"""<rect x="{window_x}" y="{window_y}"
width="{window_width}" height="{window_height}"
fill="{window_color}" stroke="black" stroke-width="0.5" />"""
# Entrance
entrance_width = building_width * 0.3
entrance_height = building_height * 0.1
entrance_x = building_x + (building_width - entrance_width) / 2
entrance_y = building_y + building_height - entrance_height
svg_content += f"""<rect x="{entrance_x}" y="{entrance_y}"
width="{entrance_width}" height="{entrance_height}"
fill="hsl({(building_color_hue + 60) % 360}, 30%, 40%)" stroke="black" stroke-width="1" />"""
elif building_type == "castle":
# Castle base
svg_content += f"""<rect x="{building_x}" y="{building_y + building_height * 0.2}"
width="{building_width}" height="{building_height * 0.8}"
fill="hsl({building_color_hue}, 15%, 60%)" stroke="black" stroke-width="2" />"""
# Castle towers
tower_width = building_width * 0.2
tower_height = building_height * 1.0
# Left tower
svg_content += f"""<rect x="{building_x - tower_width * 0.5}" y="{building_y}"
width="{tower_width}" height="{tower_height}"
fill="hsl({building_color_hue}, 15%, 50%)" stroke="black" stroke-width="2" />"""
# Right tower
svg_content += f"""<rect x="{building_x + building_width - tower_width * 0.5}" y="{building_y}"
width="{tower_width}" height="{tower_height}"
fill="hsl({building_color_hue}, 15%, 50%)" stroke="black" stroke-width="2" />"""
# Crenellations (castle top)
crenel_width = building_width * 0.05
crenel_height = building_height * 0.05
crenel_count = int(building_width / crenel_width)
for i in range(crenel_count):
if i % 2 == 0:
crenel_x = building_x + i * crenel_width
svg_content += f"""<rect x="{crenel_x}" y="{building_y + building_height * 0.15}"
width="{crenel_width}" height="{crenel_height}"
fill="hsl({building_color_hue}, 15%, 60%)" stroke="black" stroke-width="1" />"""
# Tower crenellations
tower_crenel_count = int(tower_width / crenel_width)
# Left tower crenellations
for i in range(tower_crenel_count):
if i % 2 == 0:
crenel_x = building_x - tower_width * 0.5 + i * crenel_width
svg_content += f"""<rect x="{crenel_x}" y="{building_y - crenel_height}"
width="{crenel_width}" height="{crenel_height}"
fill="hsl({building_color_hue}, 15%, 50%)" stroke="black" stroke-width="1" />"""
# Right tower crenellations
for i in range(tower_crenel_count):
if i % 2 == 0:
crenel_x = building_x + building_width - tower_width * 0.5 + i * crenel_width
svg_content += f"""<rect x="{crenel_x}" y="{building_y - crenel_height}"
width="{crenel_width}" height="{crenel_height}"
fill="hsl({building_color_hue}, 15%, 50%)" stroke="black" stroke-width="1" />"""
# Castle door (gate)
door_width = building_width * 0.25
door_height = building_height * 0.4
door_x = building_x + (building_width - door_width) / 2
door_y = building_y + building_height - door_height
# Gate arch
svg_content += f"""<path d="M {door_x} {door_y + door_height * 0.5}
A {door_width/2} {door_height/2} 0 0 1 {door_x + door_width} {door_y + door_height * 0.5}
L {door_x + door_width} {door_y + door_height}
L {door_x} {door_y + door_height} Z"
fill="hsl({(building_color_hue + 30) % 360}, 30%, 30%)" stroke="black" stroke-width="1" />"""
# Windows
window_width = building_width * 0.1
window_height = building_height * 0.15
window_spacing = building_width * 0.25
for i in range(3):
window_x = building_x + window_spacing * (i + 0.5)
window_y = building_y + building_height * 0.4
# Arched window
svg_content += f"""<path d="M {window_x} {window_y + window_height * 0.5}
A {window_width/2} {window_height/2} 0 0 1 {window_x + window_width} {window_y + window_height * 0.5}
L {window_x + window_width} {window_y + window_height}
L {window_x} {window_y + window_height} Z"
fill="#a8d8ff" stroke="black" stroke-width="1" />"""
# Tower windows (slits)
slit_width = tower_width * 0.1
slit_height = tower_height * 0.1
# Left tower slits
for i in range(3):
slit_x = building_x - tower_width * 0.5 + tower_width * 0.45
slit_y = building_y + tower_height * (0.2 + i * 0.2)
svg_content += f"""<rect x="{slit_x}" y="{slit_y}"
width="{slit_width}" height="{slit_height}"
fill="black" />"""
# Right tower slits
for i in range(3):
slit_x = building_x + building_width - tower_width * 0.5 + tower_width * 0.45
slit_y = building_y + tower_height * (0.2 + i * 0.2)
svg_content += f"""<rect x="{slit_x}" y="{slit_y}"
width="{slit_width}" height="{slit_height}"
fill="black" />"""
else: # Generic building
# Building base
svg_content += f"""<rect x="{building_x}" y="{building_y}"
width="{building_width}" height="{building_height}"
fill="hsl({building_color_hue}, 30%, 70%)" stroke="black" stroke-width="2" />"""
# Building roof
roof_height = building_height * 0.2
svg_content += f"""<path d="M {building_x - building_width * 0.05} {building_y}
L {building_x + building_width * 0.5} {building_y - roof_height}
L {building_x + building_width * 1.05} {building_y} Z"
fill="hsl({(building_color_hue + 30) % 360}, 50%, 40%)" stroke="black" stroke-width="2" />"""
# Building door
door_width = building_width * 0.2
door_height = building_height * 0.3
door_x = building_x + (building_width - door_width) / 2
door_y = building_y + building_height - door_height
svg_content += f"""<rect x="{door_x}" y="{door_y}"
width="{door_width}" height="{door_height}"
fill="hsl({(building_color_hue + 60) % 360}, 30%, 40%)" stroke="black" stroke-width="1" />"""
# Windows
window_width = building_width * 0.15
window_height = building_height * 0.15
window_spacing_x = building_width * 0.25
window_spacing_y = building_height * 0.25
for i in range(3):
for j in range(2):
window_x = building_x + window_spacing_x * (i + 0.5)
window_y = building_y + window_spacing_y * (j + 0.5)
svg_content += f"""<rect x="{window_x}" y="{window_y}"
width="{window_width}" height="{window_height}"
fill="#a8d8ff" stroke="black" stroke-width="1" />"""
# Add prompt as text
svg_content += f"""<text x="{width/2}" y="{height - 20}" font-family="Arial" font-size="12" text-anchor="middle">{prompt}</text>"""
# Close SVG
svg_content += "</svg>"
return svg_content
def _generate_face_svg(self, prompt, features, num_paths=20, width=512, height=512):
"""Generate a face SVG based on the prompt and features"""
# Start SVG
svg_content = f"""<svg width="{width}" height="{height}" xmlns="http://www.w3.org/2000/svg">
<rect width="100%" height="100%" fill="#f8f8f8"/>
"""
# Use features to determine face properties
face_color_hue = int((features[0] + 1) * 20) % 40 + 10 # Map to 10-50 hue (skin tones)
face_size = 0.5 + 0.2 * features[1] # Size variation
face_shape = int(abs(features[2] * 3)) % 3 # 0: round, 1: oval, 2: square
# Calculate face dimensions
face_width = int(width * 0.6 * face_size)
face_height = int(height * 0.7 * face_size)
face_x = (width - face_width) // 2
face_y = (height - face_height) // 2
# Draw face shape
if face_shape == 0: # Round
svg_content += f"""<circle cx="{width/2}" cy="{height/2}" r="{face_width/2}"
fill="hsl({face_color_hue}, 50%, 80%)" stroke="black" stroke-width="2" />"""
elif face_shape == 1: # Oval
svg_content += f"""<ellipse cx="{width/2}" cy="{height/2}" rx="{face_width/2}" ry="{face_height/2}"
fill="hsl({face_color_hue}, 50%, 80%)" stroke="black" stroke-width="2" />"""
else: # Square with rounded corners
svg_content += f"""<rect x="{face_x}" y="{face_y}" width="{face_width}" height="{face_height}"
rx="{face_width/10}" ry="{face_height/10}"
fill="hsl({face_color_hue}, 50%, 80%)" stroke="black" stroke-width="2" />"""
# Determine gender from prompt
is_female = any(term in prompt.lower() for term in ["woman", "girl", "female", "lady"])
# Draw eyes
eye_width = face_width * 0.15
eye_height = face_height * 0.08
eye_y = face_y + face_height * 0.35
left_eye_x = face_x + face_width * 0.3 - eye_width / 2
right_eye_x = face_x + face_width * 0.7 - eye_width / 2
# Eye whites
svg_content += f"""<ellipse cx="{left_eye_x + eye_width/2}" cy="{eye_y + eye_height/2}"
rx="{eye_width/2}" ry="{eye_height/2}" fill="white" stroke="black" stroke-width="1" />"""
svg_content += f"""<ellipse cx="{right_eye_x + eye_width/2}" cy="{eye_y + eye_height/2}"
rx="{eye_width/2}" ry="{eye_height/2}" fill="white" stroke="black" stroke-width="1" />"""
# Pupils
pupil_size = eye_width * 0.3
svg_content += f"""<circle cx="{left_eye_x + eye_width/2}" cy="{eye_y + eye_height/2}"
r="{pupil_size}" fill="black" />"""
svg_content += f"""<circle cx="{right_eye_x + eye_width/2}" cy="{eye_y + eye_height/2}"
r="{pupil_size}" fill="black" />"""
# Eyebrows
brow_width = eye_width * 1.2
brow_height = eye_height * 0.5
brow_y = eye_y - eye_height * 0.8
svg_content += f"""<path d="M {left_eye_x} {brow_y}
Q {left_eye_x + brow_width/2} {brow_y - brow_height}, {left_eye_x + brow_width} {brow_y}"
fill="none" stroke="black" stroke-width="2" />"""
svg_content += f"""<path d="M {right_eye_x} {brow_y}
Q {right_eye_x + brow_width/2} {brow_y - brow_height}, {right_eye_x + brow_width} {brow_y}"
fill="none" stroke="black" stroke-width="2" />"""
# Nose
nose_width = face_width * 0.1
nose_height = face_height * 0.15
nose_x = face_x + face_width / 2 - nose_width / 2
nose_y = face_y + face_height * 0.5 - nose_height / 2
svg_content += f"""<path d="M {nose_x + nose_width/2} {nose_y}
L {nose_x} {nose_y + nose_height}
L {nose_x + nose_width} {nose_y + nose_height}"
fill="none" stroke="black" stroke-width="1" />"""
# Mouth
mouth_width = face_width * 0.4
mouth_height = face_height * 0.05
mouth_x = face_x + face_width / 2 - mouth_width / 2
mouth_y = face_y + face_height * 0.7
# Smiling mouth
svg_content += f"""<path d="M {mouth_x} {mouth_y}
Q {mouth_x + mouth_width/2} {mouth_y + mouth_height}, {mouth_x + mouth_width} {mouth_y}"
fill="none" stroke="black" stroke-width="1.5" />"""
# Hair
hair_color_hue = int((features[3] + 1) * 180) % 360 # Map to 0-360 hue
if is_female:
# Long hair for female
svg_content += f"""<path d="M {face_x + face_width * 0.1} {face_y + face_height * 0.2}
C {face_x - face_width * 0.1} {face_y + face_height * 0.5},
{face_x - face_width * 0.2} {face_y + face_height},
{face_x + face_width * 0.1} {face_y + face_height * 1.1}
L {face_x + face_width * 0.9} {face_y + face_height * 1.1}
C {face_x + face_width * 1.2} {face_y + face_height},
{face_x + face_width * 1.1} {face_y + face_height * 0.5},
{face_x + face_width * 0.9} {face_y + face_height * 0.2} Z"
fill="hsl({hair_color_hue}, 70%, 40%)" stroke="black" stroke-width="1" />"""
# Hair on top of head
svg_content += f"""<path d="M {face_x + face_width * 0.1} {face_y + face_height * 0.2}
C {face_x + face_width * 0.3} {face_y - face_height * 0.1},
{face_x + face_width * 0.7} {face_y - face_height * 0.1},
{face_x + face_width * 0.9} {face_y + face_height * 0.2} Z"
fill="hsl({hair_color_hue}, 70%, 40%)" stroke="black" stroke-width="1" />"""
else:
# Short hair for male
svg_content += f"""<path d="M {face_x} {face_y + face_height * 0.3}
C {face_x + face_width * 0.1} {face_y},
{face_x + face_width * 0.9} {face_y},
{face_x + face_width} {face_y + face_height * 0.3} Z"
fill="hsl({hair_color_hue}, 70%, 40%)" stroke="black" stroke-width="1" />"""
# Hair sides
svg_content += f"""<path d="M {face_x} {face_y + face_height * 0.3}
L {face_x - face_width * 0.05} {face_y + face_height * 0.5}
L {face_x} {face_y + face_height * 0.5} Z"
fill="hsl({hair_color_hue}, 70%, 40%)" stroke="black" stroke-width="1" />"""
svg_content += f"""<path d="M {face_x + face_width} {face_y + face_height * 0.3}
L {face_x + face_width * 1.05} {face_y + face_height * 0.5}
L {face_x + face_width} {face_y + face_height * 0.5} Z"
fill="hsl({hair_color_hue}, 70%, 40%)" stroke="black" stroke-width="1" />"""
# Add ears
ear_width = face_width * 0.1
ear_height = face_height * 0.2
left_ear_x = face_x - ear_width / 2
right_ear_x = face_x + face_width - ear_width / 2
ear_y = face_y + face_height * 0.4
svg_content += f"""<ellipse cx="{left_ear_x}" cy="{ear_y}"
rx="{ear_width/2}" ry="{ear_height/2}"
fill="hsl({face_color_hue}, 50%, 75%)" stroke="black" stroke-width="1" />"""
svg_content += f"""<ellipse cx="{right_ear_x}" cy="{ear_y}"
rx="{ear_width/2}" ry="{ear_height/2}"
fill="hsl({face_color_hue}, 50%, 75%)" stroke="black" stroke-width="1" />"""
# Add prompt as text
svg_content += f"""<text x="{width/2}" y="{height - 20}" font-family="Arial" font-size="12" text-anchor="middle">{prompt}</text>"""
# Close SVG
svg_content += "</svg>"
return svg_content
def _generate_abstract_svg(self, prompt, features, num_paths=20, width=512, height=512):
"""Generate an abstract SVG based on the prompt and features"""
# Start SVG
svg_content = f"""<svg width="{width}" height="{height}" xmlns="http://www.w3.org/2000/svg">
<rect width="100%" height="100%" fill="#f8f8f8"/>
"""
# Use features to determine abstract properties
color_scheme = int(abs(features[0] * 5)) % 5 # 0-4 color schemes
shape_complexity = int(abs(features[1] * 10)) + 5 # 5-15 shapes
use_gradients = features[2] > 0
# Define color schemes
color_schemes = [
# Warm colors
[f"hsl({h}, 80%, 60%)" for h in range(0, 61, 15)],
# Cool colors
[f"hsl({h}, 80%, 60%)" for h in range(180, 241, 15)],
# Complementary
[f"hsl({h}, 80%, 60%)" for h in range(0, 361, 180)],
# Monochromatic
[f"hsl(210, 80%, {l}%)" for l in range(30, 91, 15)],
# Rainbow
[f"hsl({h}, 80%, 60%)" for h in range(0, 361, 60)]
]
colors = color_schemes[color_scheme]
# Add gradients if needed
if use_gradients:
svg_content += """<defs>"""
for i, color in enumerate(colors[:-1]):
svg_content += f"""
<linearGradient id="gradient{i}" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="{color}" />
<stop offset="100%" stop-color="{colors[i+1]}" />
</linearGradient>"""
svg_content += """</defs>"""
# Generate shapes based on prompt hash
prompt_hash = sum(ord(c) for c in prompt)
random.seed(prompt_hash)
for i in range(shape_complexity):
shape_type = i % 4 # 0: circle, 1: rectangle, 2: polygon, 3: path
x = random.randint(0, width)
y = random.randint(0, height)
size = random.randint(20, 150)
color_idx = i % len(colors)
fill = f"url(#gradient{color_idx})" if use_gradients and color_idx < len(colors) - 1 else colors[color_idx]
opacity = 0.3 + random.random() * 0.7
if shape_type == 0: # Circle
svg_content += f"""<circle cx="{x}" cy="{y}" r="{size/2}"
fill="{fill}" stroke="none" opacity="{opacity}" />"""
elif shape_type == 1: # Rectangle
svg_content += f"""<rect x="{x - size/2}" y="{y - size/2}" width="{size}" height="{size * 0.8}"
rx="{size/10}" ry="{size/10}"
fill="{fill}" stroke="none" opacity="{opacity}"
transform="rotate({random.randint(0, 90)}, {x}, {y})" />"""
elif shape_type == 2: # Polygon
points = []
sides = random.randint(3, 8)
for j in range(sides):
angle = j * 2 * 3.14159 / sides
px = x + size/2 * np.cos(angle)
py = y + size/2 * np.sin(angle)
points.append(f"{px},{py}")
svg_content += f"""<polygon points="{' '.join(points)}"
fill="{fill}" stroke="none" opacity="{opacity}" />"""
else: # Path (curved)
path = f"M {x} {y} "
control_points = random.randint(2, 5)
for j in range(control_points):
cx1 = x + random.randint(-size, size)
cy1 = y + random.randint(-size, size)
cx2 = x + random.randint(-size, size)
cy2 = y + random.randint(-size, size)
ex = x + random.randint(-size, size)
ey = y + random.randint(-size, size)
path += f"C {cx1} {cy1}, {cx2} {cy2}, {ex} {ey} "
svg_content += f"""<path d="{path}"
fill="none" stroke="{colors[color_idx]}" stroke-width="{random.randint(1, 10)}"
opacity="{opacity}" stroke-linecap="round" />"""
# Add text elements based on the prompt
words = re.findall(r'\b\w+\b', prompt)
for i, word in enumerate(words[:5]): # Use up to 5 words from the prompt
text_x = random.randint(width // 4, width * 3 // 4)
text_y = random.randint(height // 4, height * 3 // 4)
text_size = random.randint(10, 40)
text_color = colors[i % len(colors)]
text_opacity = 0.7 + random.random() * 0.3
text_rotation = random.randint(-45, 45)
svg_content += f"""<text x="{text_x}" y="{text_y}"
font-family="Arial" font-size="{text_size}" text-anchor="middle"
fill="{text_color}" opacity="{text_opacity}"
transform="rotate({text_rotation}, {text_x}, {text_y})">{word}</text>"""
# Add prompt as text
svg_content += f"""<text x="{width/2}" y="{height - 20}" font-family="Arial" font-size="12" text-anchor="middle">{prompt}</text>"""
# Close SVG
svg_content += "</svg>"
return svg_content
def svg_to_png(self, svg_content):
"""Convert SVG content to PNG"""
try:
png_data = cairosvg.svg2png(bytestring=svg_content.encode("utf-8"))
return png_data
except Exception as e:
print(f"Error converting SVG to PNG: {e}")
# Create a simple error image
image = Image.new("RGB", (512, 512), color="#ff0000")
from PIL import ImageDraw
draw = ImageDraw.Draw(image)
draw.text((256, 256), f"Error: {str(e)}", fill="white", anchor="mm")
# Convert PIL Image to PNG data
buffer = io.BytesIO()
image.save(buffer, format="PNG")
return buffer.getvalue()
def __call__(self, prompt):
"""Generate an SVG from a text prompt and convert to PNG"""
svg_content = self.generate_svg(prompt)
png_data = self.svg_to_png(svg_content)
# Create a PIL Image from the PNG data
image = Image.open(io.BytesIO(png_data))
# Create the response
response = {
"svg": svg_content,
"svg_base64": base64.b64encode(svg_content.encode("utf-8")).decode("utf-8"),
"png_base64": base64.b64encode(png_data).decode("utf-8"),
"image": image
}
return response