1
0

preview.py 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
  1. """Preview module for generating image previews of patterns."""
  2. import os
  3. import math
  4. import asyncio
  5. from io import BytesIO
  6. from PIL import Image, ImageDraw
  7. from modules.core.pattern_manager import parse_theta_rho_file, THETA_RHO_DIR
  8. async def generate_preview_image(pattern_file, format='WEBP'):
  9. """Generate a preview for a pattern file, optimized for a 300x300 view."""
  10. file_path = os.path.join(THETA_RHO_DIR, pattern_file)
  11. # Use asyncio.to_thread to prevent blocking the event loop
  12. coordinates = await asyncio.to_thread(parse_theta_rho_file, file_path)
  13. # Use 1000x1000 for high quality rendering
  14. RENDER_SIZE = 2048
  15. # Final display size
  16. DISPLAY_SIZE = 512
  17. if not coordinates:
  18. # Create an image with "No pattern data" text
  19. img = Image.new('RGBA', (DISPLAY_SIZE, DISPLAY_SIZE), (255, 255, 255, 0)) # Transparent background
  20. draw = ImageDraw.Draw(img)
  21. text = "No pattern data"
  22. try:
  23. bbox = draw.textbbox((0, 0), text)
  24. text_width = bbox[2] - bbox[0]
  25. text_height = bbox[3] - bbox[1]
  26. text_x = (DISPLAY_SIZE - text_width) / 2
  27. text_y = (DISPLAY_SIZE - text_height) / 2
  28. except:
  29. text_x = DISPLAY_SIZE / 4
  30. text_y = DISPLAY_SIZE / 2
  31. draw.text((text_x, text_y), text, fill="black")
  32. img_byte_arr = BytesIO()
  33. img.save(img_byte_arr, format=format)
  34. img_byte_arr.seek(0)
  35. return img_byte_arr.getvalue()
  36. # Image drawing parameters
  37. img = Image.new('RGBA', (RENDER_SIZE, RENDER_SIZE), (255, 255, 255, 0)) # Transparent background
  38. draw = ImageDraw.Draw(img)
  39. CENTER = RENDER_SIZE / 2.0
  40. SCALE_FACTOR = (RENDER_SIZE / 2.0) - 10.0
  41. LINE_COLOR = "black"
  42. STROKE_WIDTH = 2 # Increased stroke width for better visibility after scaling
  43. points_to_draw = []
  44. for theta, rho in coordinates:
  45. x = CENTER - rho * SCALE_FACTOR * math.cos(theta)
  46. y = CENTER - rho * SCALE_FACTOR * math.sin(theta)
  47. points_to_draw.append((x, y))
  48. if len(points_to_draw) > 1:
  49. draw.line(points_to_draw, fill=LINE_COLOR, width=STROKE_WIDTH, joint="curve")
  50. elif len(points_to_draw) == 1:
  51. r = 4 # Larger radius for single point to remain visible after scaling
  52. x, y = points_to_draw[0]
  53. draw.ellipse([(x-r, y-r), (x+r, y+r)], fill=LINE_COLOR)
  54. # Scale down to display size with high-quality resampling
  55. img = img.resize((DISPLAY_SIZE, DISPLAY_SIZE), Image.Resampling.LANCZOS)
  56. # Rotate the image 180 degrees
  57. img = img.rotate(180)
  58. img_byte_arr = BytesIO()
  59. img.save(img_byte_arr, format=format, lossless=False, alpha_quality=20, method=0)
  60. img_byte_arr.seek(0)
  61. return img_byte_arr.getvalue()