Skip to content

Instantly share code, notes, and snippets.

@chrishamant
Forked from fogleman/main.py
Created February 26, 2016 07:16
Show Gist options
  • Save chrishamant/477a773cc55b6a347678 to your computer and use it in GitHub Desktop.
Save chrishamant/477a773cc55b6a347678 to your computer and use it in GitHub Desktop.
Collision Avoidance
from collections import deque
from math import sin, cos, pi, atan2, hypot
import random
import time
import wx
SIZE = 600
COUNT = 64
SPEED = 100
FOLLOWERS = 4
COLORS = [
wx.RED,
]
class Bot(object):
def __init__(self, position, target):
self.position = position
self.target = target
self.speed = random.random() + 0.5
self.padding = random.random() * 8 + 16
self.history = deque(maxlen=64)
def get_position(self, offset):
px, py = self.position
tx, ty = self.target
angle = atan2(ty - py, tx - px)
return (px + cos(angle) * offset, py + sin(angle) * offset)
def update(self, bots):
px, py = self.position
tx, ty = self.target
angle = atan2(ty - py, tx - px)
dx = cos(angle)
dy = sin(angle)
for bot in bots:
if bot == self:
continue
x, y = bot.position
d = hypot(px - x, py - y) ** 2
p = bot.padding ** 2
angle = atan2(py - y, px - x)
dx += cos(angle) / d * p
dy += sin(angle) / d * p
angle = atan2(dy, dx)
magnitude = hypot(dx, dy)
return angle, magnitude
def set_position(self, position):
self.position = position
if not self.history:
self.history.append(self.position)
return
x, y = self.position
px, py = self.history[-1]
d = hypot(px - x, py - y)
if d >= 10:
self.history.append(self.position)
class Model(object):
def __init__(self, width, height, count):
self.width = width
self.height = height
self.bots = self.create_bots(count)
def create_bots(self, count):
result = []
for i in range(count):
position = self.select_point()
target = self.select_point()
bot = Bot(position, target)
result.append(bot)
return result
def select_point(self):
cx = self.width / 2.0
cy = self.height / 2.0
radius = min(self.width, self.height) * 0.4
angle = random.random() * 2 * pi
x = cx + cos(angle) * radius
y = cy + sin(angle) * radius
return (x, y)
def update(self, dt):
data = [bot.update(self.bots) for bot in self.bots]
for bot, (angle, magnitude) in zip(self.bots, data):
speed = min(1, 0.2 + magnitude * 0.8)
dx = cos(angle) * dt * SPEED * bot.speed * speed
dy = sin(angle) * dt * SPEED * bot.speed * speed
px, py = bot.position
tx, ty = bot.target
bot.set_position((px + dx, py + dy))
if hypot(px - tx, py - ty) < 10:
bot.target = self.select_point()
for bot in self.bots[-FOLLOWERS:]:
bot.target = self.bots[0].get_position(10)
class Panel(wx.Panel):
def __init__(self, parent):
super(Panel, self).__init__(parent)
self.model = Model(SIZE, SIZE, COUNT)
self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM)
self.Bind(wx.EVT_SIZE, self.on_size)
self.Bind(wx.EVT_PAINT, self.on_paint)
self.Bind(wx.EVT_LEFT_DOWN, self.on_left_down)
self.Bind(wx.EVT_RIGHT_DOWN, self.on_right_down)
self.timestamp = time.time()
self.on_timer()
def on_timer(self):
now = time.time()
dt = now - self.timestamp
self.timestamp = now
self.model.update(dt)
self.Refresh()
wx.CallLater(10, self.on_timer)
def on_left_down(self, event):
self.model.bots[0].target = event.GetPosition()
def on_right_down(self, event):
width, height = self.GetClientSize()
self.model = Model(width, height, COUNT)
def on_size(self, event):
width, height = self.GetClientSize()
self.model = Model(width, height, COUNT)
event.Skip()
self.Refresh()
def on_paint(self, event):
n = len(COLORS)
dc = wx.AutoBufferedPaintDC(self)
dc.SetBackground(wx.BLACK_BRUSH)
dc.Clear()
dc.SetPen(wx.BLACK_PEN)
for index, bot in enumerate(self.model.bots[:n]):
dc.SetBrush(wx.Brush(COLORS[index]))
for x, y in bot.history:
dc.DrawCircle(x, y, 3)
dc.SetBrush(wx.BLACK_BRUSH)
for index, bot in enumerate(self.model.bots[:n]):
dc.SetPen(wx.Pen(COLORS[index]))
x, y = bot.target
dc.DrawCircle(x, y, 6)
for index, bot in enumerate(self.model.bots):
dc.SetPen(wx.BLACK_PEN)
if index < n:
dc.SetBrush(wx.Brush(COLORS[index]))
elif index >= COUNT - FOLLOWERS:
dc.SetBrush(wx.BLACK_BRUSH)
dc.SetPen(wx.WHITE_PEN)
else:
dc.SetBrush(wx.WHITE_BRUSH)
x, y = bot.position
dc.DrawCircle(x, y, 6)
class Frame(wx.Frame):
def __init__(self):
super(Frame, self).__init__(None)
self.SetTitle('Motion')
self.SetClientSize((SIZE, SIZE))
Panel(self)
def main():
app = wx.App()
frame = Frame()
frame.Center()
frame.Show()
app.MainLoop()
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment