preview.py 2.5 KB

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