1
0

preview.py 3.2 KB

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