from PIL import Image, ImageDraw, ImageFont import os from utils.normalize_interpolate import normalize_interpolate from utils.path_utils import resolve_path def generate_image(prompt, frame_number, normalized_pos, output_folder="frames", ramp_start=(64, 64),ramp_end=(448, 448), img_size=(512, 512), ball_radius=15): """ Args: prompt (str): Text prompt (optional, for annotation) frame_number (int): Frame index ball_position_normalized (float): 0.0 (top) to 1.0 (bottom) along the ramp output_folder (str): Where to save images ramp_start: start of ramp in pixel coords ramp_end: end of ramp in pixel coords """ os.makedirs(output_folder, exist_ok=True) img_size = (512, 512) img = Image.new('RGB', img_size, color=(255, 255, 255)) d = ImageDraw.Draw(img) # Position of the ball x, y = normalize_interpolate(ramp_start, ramp_end, normalized_pos) # Draw the ramp line d.line([ramp_start, ramp_end], fill=(150, 150, 150), width=3) # Draw ball d.ellipse( [(x - ball_radius, y - ball_radius), (x + ball_radius, y + ball_radius)], fill=(255, 0, 0), outline=(0, 0, 0) ) try: font = ImageFont.truetype("arial.ttf", 20) except: font = ImageFont.load_default() d.text((10, 10), f"Frame {frame_number}: {prompt[:30]}", fill=(0, 0, 0), font=font) file_path = resolve_path(f"frame_{frame_number:03}.png", subdir=output_folder, write_mode=True) img.save(file_path) return file_path def draw_scene( entities, frame_number, prompt="", output_folder="frames", img_size=(512, 512), background_color=(255, 255, 255) ): """ Draws a general physics frame from a list of entities. Args: entities (List[Dict]): Each entity has a type, position, shape, etc. frame_number (int): Index of the frame prompt (str): Annotation label output_folder (str): Directory to save frames img_size (Tuple[int, int]): Image size background_color (Tuple[int, int, int]): RGB color """ img = Image.new('RGB', img_size, color=background_color) draw = ImageDraw.Draw(img) for entity in entities: etype = entity.get("type") pos = entity.get("position") # (x, y) in pixels radius = entity.get("radius", 5) color = entity.get("color", (255, 0, 0)) if etype == "particle": x, y = pos draw.ellipse( [(x - radius, y - radius), (x + radius, y + radius)], fill=color, outline=(0, 0, 0) ) elif etype == "line": draw.line(entity["points"], fill=color, width=entity.get("width", 2)) elif etype == "text": try: font = ImageFont.truetype("arial.ttf", entity.get("size", 16)) except: font = ImageFont.load_default() draw.text(pos, entity["text"], fill=color, font=font) # Future types: vector fields, heatmaps, force arrows, etc. path = resolve_path(f"frame_{frame_number:03}.png", subdir=output_folder, write_mode=True) img.save(path) return path