Home>

### python - bounce processing in tkinter

The BOID model is implemented using Python.

Error message

I use Python standard tkinter, but the bounce process doesn't work well when there are multiple birds and converges to the lower right corner.

Applicable source code
``````import math
import random
from statistics import mean
from tkinter import *

class Coordinate ():
def __init __ (self):
self.x = 0
self.y = 0
def reset (self):
self.x = 0
self.y = 0

class Bird ():
"" "
Bird class
"" "
def __init __ (self, x, y, vx, vy, identifier, field_info, others, view):
"" "Bird Class Constructor
Agent for BOID
: param x: x coordinate
: param y: y coordinate
: param vx: amount of movement in the x direction
: param vy: amount of movement in y direction
: param id: ID for identification
: param field_info: (field_width, field_height, fish_radius, fish_speed)
: param others: other fish
: param view: Field of view (radius)
"" "
self.x = x
self.y = y
self.vx = vx
self.vy = vy
self.identifier = identifier
self.others = others
self.view = view
self.field_info = field_info
self.inverse_x = self.field_info -self.x
self.inverse_y = self.field_info -self.y
self.r1 = 2.0 # cohesion coefficient
self.r2 = 0.5 # separation coefficient
self.r3 = 1.0 # alignment coefficient
self.neighbors = [] # nearby agents
self.v1 = Coordinate ()
self.v2 = Coordinate ()
self.v3 = Coordinate ()
def set_others (self, others):
self.others = others
self.find_neighbors ()
# Joindef cohesion (self):
center_pull_coeff = 10
if len (self.neighbors) == 0:
return
self.v1.x = mean ([agent.x for agent in self.neighbors if not agent.identifier == self.identifier])
self.v1.y = mean ([agent.y for agent in self.neighbors if not agent.identifier == self.identifier])
self.v1.x = (self.v1.x-self.x)/center_pull_coeff
self.v1.y = (self.v1.y-self.y)/center_pull_coeff
# Withdrawal
def separation (self):
personal_space = 50
for agent in self.neighbors:
if self.identifier == agent.identifier:
continue
dist = self.calc_distance (agent, self)
if dist<= personal_space:
self.v2.x-= (agent.x-self.x)/dist
self.v2.y-= (agent.y-self.y)/dist
def _separation (self):
if len (self.neighbors) == 0:
return
self.v2.x = mean ((agent.x for agent in self.neighbors if not agent.identifier == self.identifier])-self.x
self.v2.y = mean ([agent.y for agent in self.neighbors if not agent.identifier == self.identifier])-self.y
# Distance calculation
def calc_distance (self, a, b):
return min (math.sqrt ((a.x-b.x) ** 2 + (a.y-b.y) ** 2),
math.sqrt ((a.inverse_x-b.x) ** 2 + (a.inverse_y-b.y) ** 2))
# Alignment
def alignment (self):
if len (self.neighbors) == 0:
return
self.v3.x = mean ([agent.x for agent in self.neighbors if not self.identifier == agent.identifier])
self.v3.y = mean ([agent.y for agent in self.neighbors if not self.identifier == agent.identifier])
self.v3.x = (self.v3.x-self.vx)/2
self.v3.y = (self.v3.y-self.vy)/2
def _collision_detection (self):
if self.x-self.field_info <= 0:
self.vx * = -1
self.x = self.field_info 
if self.x + self.field_info >= self.field_info :
self.vx * = -1
self.x = self.field_info -self.field_info 
if self.y-self.field_info <= 0:
self.vy * = -1
self.y = self.field_info 
if self.y + self.field_info >= self.field_info :
self.vy * = -1
self.y = self.field_info -self.field_info 
def step (self):
self.cohesion ()
self.separation ()
self.alignment ()
def find_neighbors (self):
self.neighbors = (neighbor for neighbor in self.others
if self.calc_distance (self, neighbor)<= self.view andnot self.identifier == neighbor.identifier]
def update (self):
self.vx + = self.r1 * self.v1.x + self.r2 * self.v2.x + self.r3 * self.v3.x
self.vy + = self.r1 * self.v1.y + self.r2 * self.v2.y + self.r3 * self.v3.y
distance = math.sqrt (self.vx ** 2 + self.vy ** 2)
if distance>self.field_info :
self.vx = (self.vx/distance) * self.field_info 
self.vy = (self.vy/distance) * self.field_info 
self.x + = int (self.vx)
self.y + = int (self.vy)
self._collision_detection ()
print (self.vx, self.vy)
self.inverse_x = self.field_info -self.x
self.inverse_y = self.field_info -self.y
def clear_movement (self):
self.v1.reset ()
self.v2.reset ()
self.v3.reset ()
def draw (self):
self.clear_movement ()
self.step ()
self.update ()
return self.x, self.y, self.vx, self.vy
root = Tk ()
WIDTH = 1000
HEIGHT = 1000
bird_num = 3
SPEED = 5
VIEW = 50
c = Canvas (root, width = WIDTH, height = HEIGHT)
c.pack ()

fish_list = (Bird (random.randint (0, WIDTH), random.randint (0, HEIGHT), random.randint (-SPEED, SPEED),
random.randint (-SPEED, SPEED), i, (WIDTH, HEIGHT, fish_radian, SPEED), [],

VIEW) for i in range (bird_num)]
[fish.set_others (fish_list) for fish in fish_list]

def animate ():
c.delete ("all")
for fish in fish_list:
fish.draw ()
c.create_line (fish.x, fish.y, fish.x + fish.vx * 3, fish.y + fish.vy * 3)
[fish.set_others (fish_list) for fish in fish_list]
root.after (20, animate)
animate ()
root.mainloop ()``````

I tried changing various conditional expressions.
In almost all conditions, it converged to the lower right of the window as well.

Supplemental information (FW/tool version etc.)

MacOS Catalina
Python 3.6.8

``````self.vx + = self.r1 * self.v1.x + self.r2 * self.v2.x + self.r3 * self.v3.x
self.vy + = self.r1 * self.v1.y + self.r2 * self.v2.y + self.r3 * self.v3.y``````

I think that self.vx and self.vy will change more and more quickly. Is this the intended behavior?

I tried refactoring to understand the source.
I tried to fix the problem part.

``````import random
from statistics import mean
from tkinter import *
class Field:
WIDTH = 300
HEIGHT = 200
class Coordinate ():
def __init __ (self):
self.reset ()
def reset (self):
self.x = 0
self.y = 0
class Bird ():
NUM = 3
SPEED = 5
VIEW = 50
@staticmethod
def setup ():
Bird.birds = [Bird () for _ in range (Bird.NUM)]
def __init __ (self):
self.x = random.randint (0, Field.WIDTH)
self.y = random.randint (0, Field.HEIGHT)
self.vx = random.randint (-self.SPEED, self.SPEED)
self.vy = random.randint (-self.SPEED, self.SPEED)
self.view = self.VIEW
self.inverse_x = Field.WIDTH-self.x
self.inverse_y = Field.HEIGHT-self.y
self.r1 = 2.0 # cohesion coefficient
self.r2 = 0.5 # separation coefficient
self.r3 = 1.0 # alignment coefficient
self.v1 = Coordinate ()
self.v2 = Coordinate ()
self.v3 = Coordinate ()
def neighbors (self):
return [agent for agent in self.birds if agent! = self]
# Join
def cohesion (self):neighbors = self.neighbors ()
if not neighbors:
return
center_pull_coeff = 10
self.v1.x = mean ([agent.x for agent in neighbors])
self.v1.y = mean ([agent.y for agent in neighbors])
self.v1.x = (self.v1.x-self.x)/center_pull_coeff
self.v1.y = (self.v1.y-self.y)/center_pull_coeff
# Withdrawal
def separation (self):
personal_space = 50
for agent in self.neighbors ():
dist = self.calc_distance (agent)
if dist and dist<= personal_space:
self.v2.x-= (agent.x-self.x)/dist
self.v2.y-= (agent.y-self.y)/dist
def _separation (self):
neighbors = self.neighbors ()
if not neighbors:
return
self.v2.x = mean ([agent.x for agent in neighbords])-self.x
self.v2.y = mean ([agent.y for agent in neighbords])-self.y
# Distance calculation
def calc_distance (self, target):
return ((target.x-self.x) ** 2 + (target.y-self.y) ** 2) ** 0.5
# Alignment
def alignment (self):
neighbors = self.neighbors ()
if not neighbors:
return
self.v3.x = mean ([agent.x for agent in neighbors])
self.v3.y = mean ([agent.y for agent in neighbors])
self.v3.x = (self.v3.x-self.vx)/2
self.v3.y = (self.v3.y-self.vy)/2
def _collision_detection (self):
self.vx * = -1
self.vx * = -1
self.vy * = -1
self.vy * = -1self.y = Field.HEIGHT-self.RADIAN
def step (self):
self.cohesion ()
self.separation ()
self.alignment ()
def update (self):
dx = self.r1 * self.v1.x + self.r2 * self.v2.x + self.r3 * self.v3.x
dy = self.r1 * self.v1.y + self.r2 * self.v2.y + self.r3 * self.v3.y
self.vx + = dx if self.vx>= 0 else -dx
self.vy + = dy if self.vy>= 0 else -dy
distance = (self.vx ** 2 + self.vy ** 2) ** 0.5
if distance>self.SPEED:
self.vx = (self.vx/distance) * self.SPEED
self.vy = (self.vy/distance) * self.SPEED
self.x + = int (self.vx)
self.y + = int (self.vy)
self._collision_detection ()
print (self.vx, self.vy)
self.inverse_x = Field.WIDTH-self.x
self.inverse_y = Field.HEIGHT-self.y
def clear_movement (self):
self.v1.reset ()
self.v2.reset ()
self.v3.reset ()
def draw (self, drawer):
self.clear_movement ()
self.step ()
self.update ()
drawer.create_line (self.x, self.y, self.x + self.vx * 3, self.y + self.vy * 3)
def main ():
Bird.setup ()
root = Tk ()
canvas = Canvas (root, width = Field.WIDTH, height = Field.HEIGHT)
canvas.pack ()
def animate ():
canvas.delete ("all")
for bird in Bird.birds:
bird.draw (canvas)
root.after (20, animate)
animate ()
root.mainloop ()
if __name__ == '__main__':
main ()``````