Advertisement
here2share

# tk_adjust_polygon.py

Jun 7th, 2025
237
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.29 KB | None | 0 0
  1. # tk_adjust_polygon.py
  2.  
  3. import tkinter as tk
  4. from tkinter import ttk
  5. from math import cos, sin, radians, degrees, atan2, hypot, pi
  6. import os
  7.  
  8. HANDLE_RADIUS = 5
  9. canvas_items = []
  10. handles = []
  11. polygon_points = []
  12. active_handle = 1
  13. selected_line = 1
  14. adjust_window = None
  15. halo = None
  16. last_updated = 'xy'
  17.  
  18. def clear_console():
  19.     os.system('cls' if os.name == 'nt' else 'clear')
  20.  
  21. def distance(p1, p2):
  22.     return hypot(p1[0] - p2[0], p1[1] - p2[1])
  23.  
  24. def update_polygon():
  25.     flat = [coord for pt in polygon_points for coord in pt]
  26.     canvas.coords(canvas_items[0], *flat)
  27.     for i, h in enumerate(handles):
  28.         x, y = polygon_points[i]
  29.         canvas.coords(h, x - HANDLE_RADIUS, y - HANDLE_RADIUS, x + HANDLE_RADIUS, y + HANDLE_RADIUS)
  30.         if halo and i == selected_line:
  31.             halo_radius = HANDLE_RADIUS + 4
  32.             canvas.coords(halo, x - halo_radius, y - halo_radius, x + halo_radius, y + halo_radius)
  33.     update_sliders_from_selected()
  34.  
  35. def on_press(event):
  36.     global active_handle, selected_line, halo
  37.     obj = canvas.find_withtag("current")[0]
  38.     for i, h in enumerate(handles):
  39.         if obj == h:
  40.             selected_line = i
  41.             active_handle = h
  42.             break
  43.  
  44.     for i, h in enumerate(handles):
  45.         if i == selected_line or i == (selected_line + 1) % len(polygon_points):
  46.             canvas.itemconfig(h, fill='lime')
  47.         else:
  48.             canvas.itemconfig(h, fill='red')
  49.  
  50.     if not halo:
  51.         halo = canvas.create_oval(0, 0, 0, 0, outline='green', width=2)
  52.     x, y = polygon_points[selected_line]
  53.     r = HANDLE_RADIUS + 4
  54.     canvas.coords(halo, x - r, y - r, x + r, y + r)
  55.     canvas.tag_lower(halo)
  56.  
  57. def on_drag(event):
  58.     x, y = event.x, event.y
  59.     polygon_points[selected_line] = (x, y)
  60.     update_polygon()
  61.  
  62. def on_release(event):
  63.     update_polygon()
  64.  
  65. def create_polygon(n, radius, center, length_override=0):
  66.     for item in canvas_items:
  67.         canvas.delete(item)
  68.     for h in handles:
  69.         canvas.delete(h)
  70.     canvas_items.clear()
  71.     handles.clear()
  72.     polygon_points.clear()
  73.     cx, cy = center
  74.     if length_override > 0:
  75.         angle_step = 2 * pi / n
  76.         polygon_points.append((cx + length_override, cy))
  77.         for i in range(1, n):
  78.             angle = angle_step * i
  79.             next_x = cx + cos(angle) * length_override
  80.             next_y = cy + sin(angle) * length_override
  81.             polygon_points.append((next_x, next_y))
  82.     else:
  83.         for i in range(n):
  84.             angle = 2 * pi * i / n
  85.             x = cx + radius * cos(angle)
  86.             y = cy + radius * sin(angle)
  87.             polygon_points.append((x, y))
  88.     poly = canvas.create_polygon(*[c for pt in polygon_points for c in pt], outline="black", fill="", width=2)
  89.     canvas_items.append(poly)
  90.     for i in range(n):
  91.         h = canvas.create_oval(0, 0, 0, 0, fill='red')
  92.         canvas.tag_bind(h, "<ButtonPress-1>", on_press)
  93.         canvas.tag_bind(h, "<B1-Motion>", on_drag)
  94.         canvas.tag_bind(h, "<ButtonRelease-1>", on_release)
  95.         handles.append(h)
  96.     update_polygon()
  97.  
  98. def toggle_adjust_window(event=None):
  99.     global adjust_window, angle_var, length_var, x_var, y_var
  100.  
  101.     if adjust_window and adjust_window.winfo_exists():
  102.         adjust_window.destroy()
  103.         adjust_window = None
  104.         return
  105.  
  106.     adjust_window = tk.Toplevel(root)
  107.     adjust_window.title("Adjust Line")
  108.  
  109.     def update_selected_from_sliders(val=None):
  110.         a = polygon_points[selected_line]
  111.         angle = radians(angle_var.get())
  112.         length = length_var.get()
  113.         dx = cos(angle) * length
  114.         dy = sin(angle) * length
  115.         b = (a[0] + dx, a[1] + dy)
  116.         polygon_points[(selected_line + 1) % len(polygon_points)] = b
  117.         update_polygon()
  118.  
  119.     def update_selected_from_xy(val=None):
  120.         polygon_points[selected_line] = (x_var.get(), y_var.get())
  121.         update_polygon()
  122.  
  123.     def reset_polygon():
  124.         create_polygon(reset_sides.get(), 290, (300, 300), length_override=reset_length.get())
  125.  
  126.     slider_specs = [
  127.         ("Angle (deg)", angle_var, update_selected_from_sliders, 0, 360, 0.1),
  128.         ("Length", length_var, update_selected_from_sliders, 0, 1000, 1),
  129.         ("X Position", x_var, update_selected_from_xy, -300, 900, 1),
  130.         ("Y Position", y_var, update_selected_from_xy, -300, 900, 1),
  131.     ]
  132.  
  133.     for label_text, variable, command, from_, to, resolution in slider_specs:
  134.         tk.Label(adjust_window, text=label_text, anchor='w').pack(fill='x', padx=5)
  135.         tk.Scale(adjust_window, from_=from_, to=to, resolution=resolution,
  136.                  variable=variable, orient='horizontal', length=1000,
  137.                  command=command).pack()
  138.  
  139.     ttk.Separator(adjust_window, orient='horizontal').pack(fill='x', padx=10, pady=5)
  140.     tk.Label(adjust_window, text="RESET POLYGON", fg='red').pack()
  141.  
  142.     reset_sides = tk.IntVar(value=12)
  143.     reset_length = tk.DoubleVar(value=0)
  144.  
  145.     tk.Label(adjust_window, text="Number of Sides", fg='red', anchor='w').pack(fill='x', padx=5)
  146.     tk.Scale(adjust_window, from_=3, to=120, variable=reset_sides,
  147.              orient='horizontal', length=1000).pack()
  148.  
  149.     tk.Label(adjust_window, text="Side Length (0 = radius mode)", fg='red', anchor='w').pack(fill='x', padx=5)
  150.     tk.Scale(adjust_window, from_=0, to=500, resolution=1, variable=reset_length,
  151.              orient='horizontal', length=1000).pack()
  152.  
  153.     tk.Button(adjust_window, text="Reset", bg='red', fg='white', command=reset_polygon).pack()
  154.  
  155. def update_sliders_from_selected():
  156.     start_point = polygon_points[selected_line]
  157.     end_point = polygon_points[(selected_line + 1) % len(polygon_points)]
  158.    
  159.     current_x, current_y = start_point
  160.     x_var.set(current_x)
  161.     y_var.set(current_y)
  162.    
  163.     dx = end_point[0] - start_point[0]
  164.     dy = end_point[1] - start_point[1]
  165.     current_angle = degrees(atan2(dy, dx)) % 360
  166.     angle_var.set(current_angle)
  167.    
  168.     current_length = distance(start_point, end_point)
  169.     length_var.set(current_length)
  170.  
  171. def on_spacebar(event=None):
  172.     clear_console()
  173.     min_x = min(x for x, y in polygon_points)
  174.     min_y = min(y for x, y in polygon_points)
  175.     shifted = [(int(x - min_x), int(y - min_y)) for x, y in polygon_points]
  176.     print(f"polygon = {shifted}")
  177.  
  178. def adj_angle(delta):
  179.     def _adjust(event=None):
  180.         if angle_var:
  181.             angle_var.set(angle_var.get() + delta)
  182.             update_selected_from_sliders()
  183.     return _adjust
  184.  
  185. root = tk.Tk()
  186. canvas = tk.Canvas(root, width=600, height=600, bg='white')
  187. canvas.pack()
  188.  
  189. angle_var = tk.DoubleVar()
  190. length_var = tk.DoubleVar()
  191. x_var = tk.DoubleVar()
  192. y_var = tk.DoubleVar()
  193.  
  194. create_polygon(12, 290, (300, 300))
  195.  
  196. canvas.bind_all("<z>", toggle_adjust_window)
  197. canvas.bind_all("<space>", on_spacebar)
  198.  
  199. incr = 0.01
  200. canvas.bind_all("<Right>", adj_angle(+incr))
  201. canvas.bind_all("<Left>", adj_angle(-incr))
  202.  
  203. root.mainloop()
  204.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement