Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # tk_softbody.py
- # still has an instability issues?
- import tkinter as tk
- import math
- WIDTH, HEIGHT = 600, 600
- softbodies = []
- dragged_body = None
- drag_offset_x = 0
- drag_offset_y = 0
- def create_softbody(x, y, radius, points_count):
- body_points = []
- body_springs = []
- for i in range(points_count):
- angle = 2 * math.pi * i / points_count
- px = x + radius * math.cos(angle)
- py = y + radius * math.sin(angle)
- body_points.append({'x': px, 'y': py, 'oldx': px, 'oldy': py, 'pinned': False})
- for i in range(points_count):
- p1 = body_points[i]
- p2 = body_points[(i + 1) % points_count]
- body_springs.append({'p1': p1, 'p2': p2, 'length': math.hypot(p2['x']-p1['x'], p2['y']-p1['y'])})
- for i in range(points_count // 2):
- p1 = body_points[i]
- p2 = body_points[i + points_count // 2]
- body_springs.append({'p1': p1, 'p2': p2, 'length': math.hypot(p2['x']-p1['x'], p2['y']-p1['y'])})
- return {
- 'points': body_points,
- 'springs': body_springs,
- 'radius': radius,
- 'flip_count': 0,
- 'last_area': 0,
- 'original_points': [{'x': px, 'y': py} for px, py in [(p['x'], p['y']) for p in body_points]]
- }
- def calculate_area(points):
- area = 0.0
- n = len(points)
- for i in range(n):
- x_i = points[i]['x']
- y_i = points[i]['y']
- x_j = points[(i+1)%n]['x']
- y_j = points[(i+1)%n]['y']
- area += (x_i * y_j) - (x_j * y_i)
- return abs(area) / 2.0
- def reset_body(body):
- cx = sum(p['x'] for p in body['points']) / len(body['points'])
- cy = sum(p['y'] for p in body['points']) / len(body['points'])
- for i, point in enumerate(body['points']):
- original = body['original_points'][i]
- point['x'] = cx + (original['x'] - body['original_points'][0]['x'])
- point['y'] = cy + (original['y'] - body['original_points'][0]['y'])
- point['oldx'] = point['x']
- point['oldy'] = point['y']
- body['flip_count'] = 0
- body['last_area'] = calculate_area(body['points'])
- def update_physics():
- for body in softbodies:
- current_area = calculate_area(body['points'])
- if body['last_area'] > 0 and current_area < body['last_area'] * 0.3: # Significant area reduction
- body['flip_count'] += 1
- else:
- body['flip_count'] = max(0, body['flip_count'] - 0.1)
- body['last_area'] = current_area
- if body['flip_count'] >= 2:
- reset_body(body)
- continue
- for point in body['points']:
- if not point['pinned']:
- vx = (point['x'] - point['oldx']) * 0.99
- vy = (point['y'] - point['oldy']) * 0.99
- point['oldx'] = point['x']
- point['oldy'] = point['y']
- point['x'] += vx
- point['y'] += vy
- point['y'] += 0.5
- if point['x'] < 5: point['x'], point['oldx'] = 5, point['x'] + vx*0.5
- if point['x'] > WIDTH-5: point['x'], point['oldx'] = WIDTH-5, point['x'] + vx*0.5
- if point['y'] < 5: point['y'], point['oldy'] = 5, point['y'] + vy*0.5
- if point['y'] > HEIGHT-5: point['y'], point['oldy'] = HEIGHT-5, point['y'] + vy*0.5
- for _ in range(3):
- for body in softbodies:
- for spring in body['springs']:
- p1 = spring['p1']
- p2 = spring['p2']
- dx = p2['x'] - p1['x']
- dy = p2['y'] - p1['y']
- distance = math.hypot(dx, dy)
- diff = 0
- if distance:
- diff = (spring['length'] - distance) / distance
- if not p1['pinned']:
- p1['x'] -= dx * diff * 0.5
- p1['y'] -= dy * diff * 0.5
- if not p2['pinned']:
- p2['x'] += dx * diff * 0.5
- p2['y'] += dy * diff * 0.5
- for i, body1 in enumerate(softbodies):
- for j, body2 in enumerate(softbodies):
- if i != j:
- for point in body1['points']:
- for other_point in body2['points']:
- dx = other_point['x'] - point['x']
- dy = other_point['y'] - point['y']
- distance = math.hypot(dx, dy)
- if distance < 10:
- if distance == 0: distance = 0.001
- force = (10 - distance) / distance * 0.5
- point['x'] -= dx * force
- point['y'] -= dy * force
- def draw():
- canvas.delete("all")
- for body in softbodies:
- for spring in body['springs']:
- p1 = spring['p1']
- p2 = spring['p2']
- canvas.create_line(p1['x'], p1['y'], p2['x'], p2['y'], fill="#d0d0ff", width=2)
- for point in body['points']:
- color = "red" if point['pinned'] else "#4444ff"
- canvas.create_oval(point['x']-3, point['y']-3, point['x']+3, point['y']+3, fill=color, outline="")
- def physics_loop():
- update_physics()
- draw()
- root.after(1, physics_loop)
- def on_click(event):
- global dragged_body, drag_offset_x, drag_offset_y
- for body in softbodies:
- cx = sum(p['x'] for p in body['points']) / len(body['points'])
- cy = sum(p['y'] for p in body['points']) / len(body['points'])
- if math.hypot(event.x - cx, event.y - cy) < body['radius']:
- dragged_body = body
- drag_offset_x = event.x - cx
- drag_offset_y = event.y - cy
- for point in body['points']:
- point['pinned'] = True
- return
- def on_drag(event):
- global dragged_body, drag_offset_x, drag_offset_y
- if dragged_body:
- cx = sum(p['x'] for p in dragged_body['points']) / len(dragged_body['points'])
- cy = sum(p['y'] for p in dragged_body['points']) / len(dragged_body['points'])
- new_cx = event.x - drag_offset_x
- new_cy = event.y - drag_offset_y
- dx = new_cx - cx
- dy = new_cy - cy
- for point in dragged_body['points']:
- point['x'] += dx
- point['y'] += dy
- point['oldx'] = point['x']
- point['oldy'] = point['y']
- def on_release(event):
- global dragged_body
- if dragged_body:
- for point in dragged_body['points']:
- point['pinned'] = False
- dragged_body = None
- root = tk.Tk()
- canvas = tk.Canvas(root, width=WIDTH, height=HEIGHT, bg='white')
- canvas.pack()
- softbodies = [
- create_softbody(200, 200, 50, 12),
- create_softbody(400, 400, 70, 16)
- ]
- canvas.bind("<Button-1>", on_click)
- canvas.bind("<B1-Motion>", on_drag)
- canvas.bind("<ButtonRelease-1>", on_release)
- physics_loop()
- root.mainloop()
Advertisement
Add Comment
Please, Sign In to add comment