here2share

# tk_subtractive_blend_polygons.py

Nov 26th, 2025
77
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 3.59 KB | None | 0 0
  1. # tk_subtractive_blend_polygons.py
  2.  
  3. import tkinter as tk
  4. from PIL import Image, ImageDraw, ImageTk, ImageChops
  5. import math
  6.  
  7. sz = 600
  8.  
  9. def center_coords(coords_list, sz):
  10.     xs = [x for coords in coords_list for x, y in coords]
  11.     ys = [y for coords in coords_list for x, y in coords]
  12.     min_x, max_x = min(xs), max(xs)
  13.     min_y, max_y = min(ys), max(ys)
  14.     width = max_x - min_x
  15.     height = max_y - min_y
  16.     offset_x = (sz - width) / 2 - min_x
  17.     offset_y = (sz - height) / 2 - min_y
  18.     return [[(x + offset_x, y + offset_y) for x, y in coords] for coords in coords_list]
  19.  
  20. def srgb_from_linear(x):
  21.     if x <= 0.0031308:
  22.         return 12.92 * x
  23.     return 1.055 * (x ** (1/2.4)) - 0.055
  24.  
  25. def linear_from_srgb(x):
  26.     if x <= 0.04045:
  27.         return x / 12.92
  28.     return ((x + 0.055) / 1.055) ** 2.4
  29.  
  30. def channel_transmission(v_srgb, leak, gamma):
  31.     v_lin = linear_from_srgb(v_srgb)
  32.     t = leak + (1.0 - leak) * (v_lin ** gamma)
  33.     return max(0.0, min(1.0, t))
  34.  
  35. def subtractive_blend(c1, c2,
  36.                               leak1=(0.02, 0.20, 0.85),
  37.                               gamma1=(1.6, 1.4, 1.0),
  38.                               leak2=(0.85, 0.90, 0.02),
  39.                               gamma2=(1.0, 1.0, 1.6)):
  40.     r1, g1, b1 = [v/255.0 for v in c1[:3]]
  41.     r2, g2, b2 = [v/255.0 for v in c2[:3]]
  42.  
  43.     t1_r = channel_transmission(r1, leak1[0], gamma1[0])
  44.     t1_g = channel_transmission(g1, leak1[1], gamma1[1])
  45.     t1_b = channel_transmission(b1, leak1[2], gamma1[2])
  46.  
  47.     t2_r = channel_transmission(r2, leak2[0], gamma2[0])
  48.     t2_g = channel_transmission(g2, leak2[1], gamma2[1])
  49.     t2_b = channel_transmission(b2, leak2[2], gamma2[2])
  50.  
  51.     t_r = t1_r * t2_r
  52.     t_g = t1_g * t2_g
  53.     t_b = t1_b * t2_b
  54.  
  55.     R = int(round(255 * srgb_from_linear(t_r)))
  56.     G = int(round(255 * srgb_from_linear(t_g)))
  57.     B = int(round(255 * srgb_from_linear(t_b)))
  58.  
  59.     A = max(c1[3], c2[3])
  60.     return (R, G, B, A)
  61.  
  62. def composite_shapes(shapes, sz):
  63.     result = Image.new("RGBA", (sz, sz), (255, 255, 255, 255))
  64.     accumulated_mask = Image.new("L", (sz, sz), 0)
  65.     accumulated_color = None
  66.  
  67.     for coords, color in shapes:
  68.         layer = Image.new("RGBA", (sz, sz), (0, 0, 0, 0))
  69.         ImageDraw.Draw(layer).polygon(coords, fill=color)
  70.         mask = layer.split()[3]
  71.  
  72.         overlap = ImageChops.multiply(accumulated_mask, mask)
  73.         only_new = ImageChops.subtract(mask, overlap)
  74.  
  75.         if accumulated_color:
  76.             blended = subtractive_blend(accumulated_color, color)
  77.             result.paste(blended, mask=overlap)
  78.  
  79.         result.paste(color, mask=only_new)
  80.  
  81.         accumulated_mask = ImageChops.add(accumulated_mask, mask)
  82.         accumulated_color = color
  83.  
  84.     return result
  85.  
  86. # Example: two triangles
  87. blue_coords = [(0, 0), (500, 0), (250, 450)]
  88. cx = sum(x for x, y in blue_coords) / 3
  89. cy = sum(y for x, y in blue_coords) / 3
  90. theta = math.radians(30)
  91. yellow_coords = []
  92. for (x, y) in blue_coords:
  93.     x_new = cx + (x - cx) * math.cos(theta) - (y - cy) * math.sin(theta)
  94.     y_new = cy + (x - cx) * math.sin(theta) + (y - cy) * math.cos(theta)
  95.     yellow_coords.append((x_new, y_new))
  96.  
  97. blue_coords, yellow_coords = center_coords([blue_coords, yellow_coords], sz)
  98.  
  99. shapes = [
  100.     (blue_coords, (0, 0, 255, 255)),
  101.     (yellow_coords, (255, 255, 0, 255)),
  102. ]
  103.  
  104. result = composite_shapes(shapes, sz)
  105.  
  106. root = tk.Tk()
  107. root.geometry(f"{sz}x{sz}+0+0")
  108. canvas = tk.Canvas(root, width=sz, height=sz, bg="white", highlightthickness=0)
  109. canvas.pack()
  110. tk_img = ImageTk.PhotoImage(result)
  111. canvas.create_image(0, 0, anchor="nw", image=tk_img)
  112. root.mainloop()
Advertisement
Add Comment
Please, Sign In to add comment