bouncingballs.py 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. from pypdevs.DEVS import AtomicDEVS, CoupledDEVS
  2. from pypdevs.simulator import Simulator
  3. from pypdevs.infinity import INFINITY
  4. import random
  5. import Tkinter as tk
  6. import math
  7. class Ball(AtomicDEVS):
  8. def __init__(self, ball_id, canvas):
  9. AtomicDEVS.__init__(self, "Ball[%i]" % ball_id)
  10. self.collision_detect = self.addInPort("colision_detect")
  11. self.position_out = self.addOutPort("position_out")
  12. self.ball_spawner_comm = self.addOutPort("ball_spawner")
  13. self.ball_id = ball_id
  14. self.canvas = canvas
  15. self.r = random.uniform(5.0, 20.0)
  16. self.vel = {'x': random.random() * 5.0 - 1.0, 'y': random.random() * 5.0 - 1.0}
  17. self.initialized = False
  18. self.event = None
  19. self.curr_state = "bouncing"
  20. self.pos = None
  21. self.time_passed = 0
  22. self.collision_detected = False
  23. x = random.uniform(0, self.canvas.canvasx(self.canvas.winfo_width()) - (self.r * 2 + 1))
  24. y = random.uniform(0, self.canvas.canvasy(self.canvas.winfo_height()) - (self.r * 2 + 1))
  25. self.id = self.canvas.create_oval(x, y, x + (self.r * 2), y + (self.r * 2), fill="red")
  26. ''' TODO: not legal '''
  27. self.canvas.tag_bind(self.id, "<Button>", self.buttonPress)
  28. self.initialized = True
  29. self.pos = (x, y)
  30. self.remaining = 0.03
  31. def buttonPress(self, event):
  32. self.event = event
  33. def timeAdvance(self):
  34. if self.curr_state in ("bouncing", "spawning", "selected"):
  35. return self.remaining
  36. elif self.curr_state == "to_delete":
  37. return INFINITY
  38. def outputFnc(self):
  39. output = {}
  40. if self.pos and self.curr_state in ("bouncing", "spawning"):
  41. output[self.position_out] = [(self.ball_id, self.pos, self.r)]
  42. if self.collision_detected and self.state != "spawning":
  43. output[self.ball_spawner_comm] = ["spawn_ball"]
  44. if self.curr_state == "selected":
  45. output[self.position_out] = [("delete", self.ball_id)]
  46. return output
  47. def intTransition(self):
  48. if self.initialized:
  49. if random.random() <= 0.008: #self.event and self.event.num == 1 or random.random() <= 0.008:
  50. self.canvas.itemconfig(self.id, fill="yellow")
  51. self.curr_state = "selected"
  52. self.event = None
  53. self.remaining = 2.5
  54. elif self.curr_state == "selected":
  55. self.curr_state = "to_delete"
  56. else:
  57. if self.time_passed < 1 and self.curr_state in ("bouncing", "spawning"):
  58. self.time_passed += 0.03
  59. if self.time_passed >= 1:
  60. self.canvas.itemconfig(self.id, fill="black")
  61. self.curr_state = "bouncing"
  62. self.time_passed = 0
  63. pos = self.canvas.coords(self.id)
  64. x = self.canvas.canvasx(pos[0])
  65. y = self.canvas.canvasy(pos[1])
  66. if x <= 0 or x + (self.r * 2) >= self.canvas.canvasx(self.canvas.winfo_width()):
  67. self.vel['x'] = -self.vel['x']
  68. if y <= 0 or y + (self.r * 2) >= self.canvas.canvasy(self.canvas.winfo_height()):
  69. self.vel['y'] = -self.vel['y']
  70. self.canvas.move(self.id, self.vel['x'], self.vel['y']);
  71. self.pos = self.canvas.coords(self.id)
  72. if self.collision_detected:
  73. self.curr_state = "spawning"
  74. self.canvas.itemconfig(self.id, fill="blue")
  75. self.remaining = 0.03
  76. self.collision_detected = False
  77. def extTransition(self, inputs):
  78. self.remaining -= self.elapsed
  79. if self.collision_detect in inputs and self.curr_state == "bouncing":
  80. self.collision_detected = True
  81. def modelTransition(self, state):
  82. if self.curr_state == "to_delete":
  83. state['to_delete'] = self
  84. return True
  85. class BallSpawner(AtomicDEVS):
  86. def __init__(self, canvas):
  87. AtomicDEVS.__init__(self, "BallSpawner")
  88. self.request = self.addInPort("request")
  89. self.canvas = canvas
  90. self.id_ctr = 0
  91. self.new_ball = None
  92. def timeAdvance(self):
  93. return 0.5
  94. def intTransition(self):
  95. self.new_ball = Ball(self.id_ctr, self.canvas)
  96. self.id_ctr += 1
  97. def extTransition(self, inputs):
  98. if self.request in inputs:
  99. self.new_ball = Ball(self.id_ctr, self.canvas)
  100. self.id_ctr += 1
  101. def modelTransition(self, state):
  102. if self.new_ball:
  103. state['new_ball'] = self.new_ball
  104. return True
  105. return False
  106. class PositionManager(AtomicDEVS):
  107. def __init__(self):
  108. AtomicDEVS.__init__(self, "PositionManager")
  109. self.balls_in = self.addInPort("balls_in")
  110. self.balls_out = {}
  111. self.collisions = set([])
  112. self.positions = {}
  113. self.distances = {}
  114. self.curr_time = 0.0
  115. def timeAdvance(self):
  116. return 0 if self.collisions else INFINITY
  117. def extTransition(self, inputs):
  118. self.curr_time += self.elapsed
  119. if self.balls_in in inputs:
  120. input_bag = inputs[self.balls_in]
  121. for i in input_bag:
  122. if i[0] == "delete":
  123. if i[1] in self.positions:
  124. del self.positions[i[1]]
  125. else:
  126. self.positions[i[0]] = (i[1], i[2])
  127. for k1, v1 in self.positions.iteritems():
  128. for k2, v2 in self.positions.iteritems():
  129. if k1 != k2:
  130. dx = v1[0][0] - v2[0][0]
  131. dy = v1[0][1] - v2[0][1]
  132. distance = math.sqrt(dx**2 + dy**2)
  133. if distance < v1[1] + v2[1]:
  134. self.collisions.add(k1)
  135. self.collisions.add(k2)
  136. self.distances[(k1, k2)] = distance
  137. def intTransition(self):
  138. self.curr_time += self.timeAdvance()
  139. self.collisions = set([])
  140. def outputFnc(self):
  141. return {self.balls_out[ball_id]: ["collision_detected"] for ball_id in self.collisions}
  142. class Window(CoupledDEVS, tk.Toplevel):
  143. def __init__(self):
  144. tk.Toplevel.__init__(self)
  145. CoupledDEVS.__init__(self, "Window")
  146. self.title('BouncingBalls')
  147. self.geometry('{}x{}'.format(800, 600))
  148. CANVAS_SIZE_TUPLE = (0, 0, self.winfo_screenwidth(), self.winfo_screenheight())
  149. self.canvas = tk.Canvas(self, relief=tk.RIDGE, scrollregion=CANVAS_SIZE_TUPLE)
  150. self.canvas.pack(expand = True, fill=tk.BOTH)
  151. self.ball_spawner = self.addSubModel(BallSpawner(self.canvas))
  152. self.position_manager = self.addSubModel(PositionManager())
  153. self.INTERRUPT = self.addInPort("INTERRUPT")
  154. self.connectPorts(self.INTERRUPT, self.ball_spawner.request)
  155. self.attributes("-topmost", True)
  156. def modelTransition(self, state):
  157. if 'new_ball' in state:
  158. new_ball = state['new_ball']
  159. self.addSubModel(new_ball)
  160. pos_mgr_port = self.position_manager.addOutPort("balls_out[%s]" % new_ball.ball_id)
  161. ''' TODO: can I just change the state like that here? '''
  162. self.position_manager.balls_out[new_ball.ball_id] = pos_mgr_port
  163. self.connectPorts(new_ball.position_out, self.position_manager.balls_in)
  164. self.connectPorts(pos_mgr_port, new_ball.collision_detect)
  165. self.connectPorts(new_ball.ball_spawner_comm, self.ball_spawner.request)
  166. del state['new_ball']
  167. if 'to_delete' in state:
  168. self.removeSubModel(state['to_delete'])
  169. state['to_delete'].canvas.delete(state['to_delete'].id)
  170. del state['to_delete']
  171. return False
  172. if __name__ == '__main__':
  173. random.seed(1337)
  174. ''' Tkinter initialization '''
  175. root = tk.Tk()
  176. root.withdraw()
  177. ''' instantiate DEVS model '''
  178. model = Window()
  179. ''' simulator setup '''
  180. sim = Simulator(model)
  181. sim.setRealTime(True)
  182. sim.setRealTimeInputFile(None)
  183. sim.setRealTimePorts({})
  184. sim.setRealTimePlatformTk(root)
  185. sim.setDSDEVS(True)
  186. sim.setTerminationTime(30)
  187. ''' run simulation + visualization '''
  188. sim.simulate()
  189. root.mainloop()