|
|
|
|
|
|
|
""" |
|
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}") |
|
|
|
|
|
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}") |
|
|
|
|
|
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] |
|
|
|
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) |
|
else: |
|
|
|
text_features = np.random.randn(512) |
|
|
|
|
|
object_type = self._determine_object_type(prompt) |
|
|
|
|
|
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() |
|
|
|
|
|
car_terms = ["car", "vehicle", "truck", "suv", "sedan", "convertible", "sports car", "automobile"] |
|
for term in car_terms: |
|
if term in prompt: |
|
return "car" |
|
|
|
|
|
landscape_terms = ["landscape", "mountain", "forest", "beach", "ocean", "sea", "lake", "river", "sunset", "sunrise", "sky"] |
|
for term in landscape_terms: |
|
if term in prompt: |
|
return "landscape" |
|
|
|
|
|
animal_terms = ["animal", "dog", "cat", "bird", "horse", "lion", "tiger", "elephant", "bear", "fish", "pet"] |
|
for term in animal_terms: |
|
if term in prompt: |
|
return "animal" |
|
|
|
|
|
building_terms = ["building", "house", "skyscraper", "tower", "castle", "mansion", "apartment", "office", "structure"] |
|
for term in building_terms: |
|
if term in prompt: |
|
return "building" |
|
|
|
|
|
face_terms = ["face", "portrait", "person", "man", "woman", "boy", "girl", "human", "head", "smile"] |
|
for term in face_terms: |
|
if term in prompt: |
|
return "face" |
|
|
|
|
|
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""" |
|
|
|
svg_content = f"""<svg width="{width}" height="{height}" xmlns="http://www.w3.org/2000/svg"> |
|
<rect width="100%" height="100%" fill="#f8f8f8"/> |
|
""" |
|
|
|
|
|
car_color_hue = int((features[0] + 1) * 180) % 360 |
|
car_size = 0.6 + 0.2 * features[1] |
|
car_style = int(abs(features[2] * 3)) % 3 |
|
|
|
|
|
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 |
|
|
|
|
|
if car_style == 0: |
|
|
|
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_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" />""" |
|
|
|
|
|
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: |
|
|
|
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_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" />""" |
|
|
|
|
|
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: |
|
|
|
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_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" />""" |
|
|
|
|
|
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" />""" |
|
|
|
|
|
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" />""" |
|
|
|
|
|
svg_content += f"""<text x="{width/2}" y="{height - 20}" font-family="Arial" font-size="12" text-anchor="middle">{prompt}</text>""" |
|
|
|
|
|
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""" |
|
|
|
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)"/> |
|
""" |
|
|
|
|
|
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 |
|
|
|
|
|
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" />""" |
|
|
|
|
|
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>""" |
|
|
|
|
|
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>""" |
|
|
|
|
|
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" />""" |
|
|
|
|
|
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 |
|
|
|
|
|
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" />""" |
|
|
|
|
|
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" />""" |
|
|
|
|
|
svg_content += f"""<text x="{width/2}" y="{height - 20}" font-family="Arial" font-size="12" text-anchor="middle" fill="black">{prompt}</text>""" |
|
|
|
|
|
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""" |
|
|
|
svg_content = f"""<svg width="{width}" height="{height}" xmlns="http://www.w3.org/2000/svg"> |
|
<rect width="100%" height="100%" fill="#f8f8f8"/> |
|
""" |
|
|
|
|
|
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" |
|
|
|
|
|
animal_color_hue = int((features[0] + 1) * 180) % 360 |
|
animal_size = 0.5 + 0.3 * features[1] |
|
|
|
|
|
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": |
|
|
|
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" />""" |
|
|
|
|
|
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" />""" |
|
|
|
|
|
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" />""" |
|
|
|
|
|
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" />""" |
|
|
|
|
|
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" />""" |
|
|
|
|
|
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" />""" |
|
|
|
|
|
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": |
|
|
|
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_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" />""" |
|
|
|
|
|
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" />""" |
|
|
|
|
|
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" />""" |
|
|
|
|
|
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" />""" |
|
|
|
|
|
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" />""" |
|
|
|
|
|
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" />""" |
|
|
|
|
|
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": |
|
|
|
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" />""" |
|
|
|
|
|
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" />""" |
|
|
|
|
|
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" />""" |
|
|
|
|
|
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" />""" |
|
|
|
|
|
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" />""" |
|
|
|
|
|
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" />""" |
|
|
|
|
|
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": |
|
|
|
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" />""" |
|
|
|
|
|
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" />""" |
|
|
|
|
|
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" />""" |
|
|
|
|
|
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" />""" |
|
|
|
|
|
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" />""" |
|
|
|
|
|
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: |
|
|
|
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_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" />""" |
|
|
|
|
|
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" />""" |
|
|
|
|
|
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" />""" |
|
|
|
|
|
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" />""" |
|
|
|
|
|
svg_content += f"""<text x="{width/2}" y="{height - 20}" font-family="Arial" font-size="12" text-anchor="middle">{prompt}</text>""" |
|
|
|
|
|
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""" |
|
|
|
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)"/> |
|
""" |
|
|
|
|
|
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" |
|
|
|
|
|
building_color_hue = int((features[0] + 1) * 180) % 360 |
|
building_size = 0.5 + 0.3 * features[1] |
|
|
|
|
|
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": |
|
|
|
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" />""" |
|
|
|
|
|
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" />""" |
|
|
|
|
|
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" />""" |
|
|
|
|
|
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" />""" |
|
|
|
|
|
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" />""" |
|
|
|
|
|
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_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": |
|
|
|
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" />""" |
|
|
|
|
|
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_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" />""" |
|
|
|
|
|
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) |
|
|
|
|
|
window_color = "#a8d8ff" |
|
if random.random() < 0.3: |
|
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_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": |
|
|
|
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" />""" |
|
|
|
|
|
tower_width = building_width * 0.2 |
|
tower_height = building_height * 1.0 |
|
|
|
|
|
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" />""" |
|
|
|
|
|
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" />""" |
|
|
|
|
|
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_crenel_count = int(tower_width / crenel_width) |
|
|
|
|
|
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" />""" |
|
|
|
|
|
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" />""" |
|
|
|
|
|
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 |
|
|
|
|
|
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" />""" |
|
|
|
|
|
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 |
|
|
|
|
|
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" />""" |
|
|
|
|
|
slit_width = tower_width * 0.1 |
|
slit_height = tower_height * 0.1 |
|
|
|
|
|
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" />""" |
|
|
|
|
|
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: |
|
|
|
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" />""" |
|
|
|
|
|
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" />""" |
|
|
|
|
|
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" />""" |
|
|
|
|
|
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" />""" |
|
|
|
|
|
svg_content += f"""<text x="{width/2}" y="{height - 20}" font-family="Arial" font-size="12" text-anchor="middle">{prompt}</text>""" |
|
|
|
|
|
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""" |
|
|
|
svg_content = f"""<svg width="{width}" height="{height}" xmlns="http://www.w3.org/2000/svg"> |
|
<rect width="100%" height="100%" fill="#f8f8f8"/> |
|
""" |
|
|
|
|
|
face_color_hue = int((features[0] + 1) * 20) % 40 + 10 |
|
face_size = 0.5 + 0.2 * features[1] |
|
face_shape = int(abs(features[2] * 3)) % 3 |
|
|
|
|
|
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 |
|
|
|
|
|
if face_shape == 0: |
|
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: |
|
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: |
|
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" />""" |
|
|
|
|
|
is_female = any(term in prompt.lower() for term in ["woman", "girl", "female", "lady"]) |
|
|
|
|
|
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 |
|
|
|
|
|
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" />""" |
|
|
|
|
|
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" />""" |
|
|
|
|
|
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_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_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 |
|
|
|
|
|
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_color_hue = int((features[3] + 1) * 180) % 360 |
|
|
|
if is_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" />""" |
|
|
|
|
|
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: |
|
|
|
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" />""" |
|
|
|
|
|
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" />""" |
|
|
|
|
|
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" />""" |
|
|
|
|
|
svg_content += f"""<text x="{width/2}" y="{height - 20}" font-family="Arial" font-size="12" text-anchor="middle">{prompt}</text>""" |
|
|
|
|
|
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""" |
|
|
|
svg_content = f"""<svg width="{width}" height="{height}" xmlns="http://www.w3.org/2000/svg"> |
|
<rect width="100%" height="100%" fill="#f8f8f8"/> |
|
""" |
|
|
|
|
|
color_scheme = int(abs(features[0] * 5)) % 5 |
|
shape_complexity = int(abs(features[1] * 10)) + 5 |
|
use_gradients = features[2] > 0 |
|
|
|
|
|
color_schemes = [ |
|
|
|
[f"hsl({h}, 80%, 60%)" for h in range(0, 61, 15)], |
|
|
|
[f"hsl({h}, 80%, 60%)" for h in range(180, 241, 15)], |
|
|
|
[f"hsl({h}, 80%, 60%)" for h in range(0, 361, 180)], |
|
|
|
[f"hsl(210, 80%, {l}%)" for l in range(30, 91, 15)], |
|
|
|
[f"hsl({h}, 80%, 60%)" for h in range(0, 361, 60)] |
|
] |
|
|
|
colors = color_schemes[color_scheme] |
|
|
|
|
|
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>""" |
|
|
|
|
|
prompt_hash = sum(ord(c) for c in prompt) |
|
random.seed(prompt_hash) |
|
|
|
for i in range(shape_complexity): |
|
shape_type = i % 4 |
|
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: |
|
svg_content += f"""<circle cx="{x}" cy="{y}" r="{size/2}" |
|
fill="{fill}" stroke="none" opacity="{opacity}" />""" |
|
elif shape_type == 1: |
|
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: |
|
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 = 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" />""" |
|
|
|
|
|
words = re.findall(r'\b\w+\b', prompt) |
|
for i, word in enumerate(words[:5]): |
|
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>""" |
|
|
|
|
|
svg_content += f"""<text x="{width/2}" y="{height - 20}" font-family="Arial" font-size="12" text-anchor="middle">{prompt}</text>""" |
|
|
|
|
|
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}") |
|
|
|
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") |
|
|
|
|
|
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) |
|
|
|
|
|
image = Image.open(io.BytesIO(png_data)) |
|
|
|
|
|
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 |