here2share

# tk_softbody.py

Aug 29th, 2025
199
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.91 KB | None | 0 0
  1. # tk_softbody.py
  2. # still has an instability issues?
  3.  
  4. import tkinter as tk
  5. import math
  6.  
  7. WIDTH, HEIGHT = 600, 600
  8. softbodies = []
  9. dragged_body = None
  10. drag_offset_x = 0
  11. drag_offset_y = 0
  12.  
  13. def create_softbody(x, y, radius, points_count):
  14.     body_points = []
  15.     body_springs = []
  16.    
  17.     for i in range(points_count):
  18.         angle = 2 * math.pi * i / points_count
  19.         px = x + radius * math.cos(angle)
  20.         py = y + radius * math.sin(angle)
  21.         body_points.append({'x': px, 'y': py, 'oldx': px, 'oldy': py, 'pinned': False})
  22.    
  23.     for i in range(points_count):
  24.         p1 = body_points[i]
  25.         p2 = body_points[(i + 1) % points_count]
  26.         body_springs.append({'p1': p1, 'p2': p2, 'length': math.hypot(p2['x']-p1['x'], p2['y']-p1['y'])})
  27.    
  28.     for i in range(points_count // 2):
  29.         p1 = body_points[i]
  30.         p2 = body_points[i + points_count // 2]
  31.         body_springs.append({'p1': p1, 'p2': p2, 'length': math.hypot(p2['x']-p1['x'], p2['y']-p1['y'])})
  32.    
  33.     return {
  34.         'points': body_points,
  35.         'springs': body_springs,
  36.         'radius': radius,
  37.         'flip_count': 0,
  38.         'last_area': 0,
  39.         'original_points': [{'x': px, 'y': py} for px, py in [(p['x'], p['y']) for p in body_points]]
  40.     }
  41.  
  42. def calculate_area(points):
  43.     area = 0.0
  44.     n = len(points)
  45.     for i in range(n):
  46.         x_i = points[i]['x']
  47.         y_i = points[i]['y']
  48.         x_j = points[(i+1)%n]['x']
  49.         y_j = points[(i+1)%n]['y']
  50.         area += (x_i * y_j) - (x_j * y_i)
  51.     return abs(area) / 2.0
  52.  
  53. def reset_body(body):
  54.     cx = sum(p['x'] for p in body['points']) / len(body['points'])
  55.     cy = sum(p['y'] for p in body['points']) / len(body['points'])
  56.    
  57.     for i, point in enumerate(body['points']):
  58.         original = body['original_points'][i]
  59.         point['x'] = cx + (original['x'] - body['original_points'][0]['x'])
  60.         point['y'] = cy + (original['y'] - body['original_points'][0]['y'])
  61.         point['oldx'] = point['x']
  62.         point['oldy'] = point['y']
  63.    
  64.     body['flip_count'] = 0
  65.     body['last_area'] = calculate_area(body['points'])
  66.  
  67. def update_physics():
  68.     for body in softbodies:
  69.         current_area = calculate_area(body['points'])
  70.        
  71.         if body['last_area'] > 0 and current_area < body['last_area'] * 0.3:  # Significant area reduction
  72.             body['flip_count'] += 1
  73.         else:
  74.             body['flip_count'] = max(0, body['flip_count'] - 0.1)
  75.        
  76.         body['last_area'] = current_area
  77.        
  78.         if body['flip_count'] >= 2:
  79.             reset_body(body)
  80.             continue
  81.        
  82.         for point in body['points']:
  83.             if not point['pinned']:
  84.                 vx = (point['x'] - point['oldx']) * 0.99
  85.                 vy = (point['y'] - point['oldy']) * 0.99
  86.                 point['oldx'] = point['x']
  87.                 point['oldy'] = point['y']
  88.                 point['x'] += vx
  89.                 point['y'] += vy
  90.                 point['y'] += 0.5
  91.                
  92.                 if point['x'] < 5: point['x'], point['oldx'] = 5, point['x'] + vx*0.5
  93.                 if point['x'] > WIDTH-5: point['x'], point['oldx'] = WIDTH-5, point['x'] + vx*0.5
  94.                 if point['y'] < 5: point['y'], point['oldy'] = 5, point['y'] + vy*0.5
  95.                 if point['y'] > HEIGHT-5: point['y'], point['oldy'] = HEIGHT-5, point['y'] + vy*0.5
  96.    
  97.     for _ in range(3):
  98.         for body in softbodies:
  99.             for spring in body['springs']:
  100.                 p1 = spring['p1']
  101.                 p2 = spring['p2']
  102.                 dx = p2['x'] - p1['x']
  103.                 dy = p2['y'] - p1['y']
  104.                 distance = math.hypot(dx, dy)
  105.                 diff = 0
  106.                 if distance:
  107.                     diff = (spring['length'] - distance) / distance
  108.                 if not p1['pinned']:
  109.                     p1['x'] -= dx * diff * 0.5
  110.                     p1['y'] -= dy * diff * 0.5
  111.                 if not p2['pinned']:
  112.                     p2['x'] += dx * diff * 0.5
  113.                     p2['y'] += dy * diff * 0.5
  114.    
  115.     for i, body1 in enumerate(softbodies):
  116.         for j, body2 in enumerate(softbodies):
  117.             if i != j:
  118.                 for point in body1['points']:
  119.                     for other_point in body2['points']:
  120.                         dx = other_point['x'] - point['x']
  121.                         dy = other_point['y'] - point['y']
  122.                         distance = math.hypot(dx, dy)
  123.                         if distance < 10:
  124.                             if distance == 0: distance = 0.001
  125.                             force = (10 - distance) / distance * 0.5
  126.                             point['x'] -= dx * force
  127.                             point['y'] -= dy * force
  128.  
  129. def draw():
  130.     canvas.delete("all")
  131.     for body in softbodies:
  132.         for spring in body['springs']:
  133.             p1 = spring['p1']
  134.             p2 = spring['p2']
  135.             canvas.create_line(p1['x'], p1['y'], p2['x'], p2['y'], fill="#d0d0ff", width=2)
  136.        
  137.         for point in body['points']:
  138.             color = "red" if point['pinned'] else "#4444ff"
  139.             canvas.create_oval(point['x']-3, point['y']-3, point['x']+3, point['y']+3, fill=color, outline="")
  140.  
  141. def physics_loop():
  142.     update_physics()
  143.     draw()
  144.     root.after(1, physics_loop)
  145.  
  146. def on_click(event):
  147.     global dragged_body, drag_offset_x, drag_offset_y
  148.     for body in softbodies:
  149.         cx = sum(p['x'] for p in body['points']) / len(body['points'])
  150.         cy = sum(p['y'] for p in body['points']) / len(body['points'])
  151.         if math.hypot(event.x - cx, event.y - cy) < body['radius']:
  152.             dragged_body = body
  153.             drag_offset_x = event.x - cx
  154.             drag_offset_y = event.y - cy
  155.             for point in body['points']:
  156.                 point['pinned'] = True
  157.             return
  158.  
  159. def on_drag(event):
  160.     global dragged_body, drag_offset_x, drag_offset_y
  161.     if dragged_body:
  162.         cx = sum(p['x'] for p in dragged_body['points']) / len(dragged_body['points'])
  163.         cy = sum(p['y'] for p in dragged_body['points']) / len(dragged_body['points'])
  164.         new_cx = event.x - drag_offset_x
  165.         new_cy = event.y - drag_offset_y
  166.         dx = new_cx - cx
  167.         dy = new_cy - cy
  168.         for point in dragged_body['points']:
  169.             point['x'] += dx
  170.             point['y'] += dy
  171.             point['oldx'] = point['x']
  172.             point['oldy'] = point['y']
  173.  
  174. def on_release(event):
  175.     global dragged_body
  176.     if dragged_body:
  177.         for point in dragged_body['points']:
  178.             point['pinned'] = False
  179.         dragged_body = None
  180.  
  181. root = tk.Tk()
  182. canvas = tk.Canvas(root, width=WIDTH, height=HEIGHT, bg='white')
  183. canvas.pack()
  184.  
  185. softbodies = [
  186.     create_softbody(200, 200, 50, 12),
  187.     create_softbody(400, 400, 70, 16)
  188. ]
  189.  
  190. canvas.bind("<Button-1>", on_click)
  191. canvas.bind("<B1-Motion>", on_drag)
  192. canvas.bind("<ButtonRelease-1>", on_release)
  193.  
  194. physics_loop()
  195. root.mainloop()
Advertisement
Add Comment
Please, Sign In to add comment