| |
| |
|
|
| """ |
| 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 |