forked from ernestas-poskus/interactive-programming-python
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathclass_ball.py
More file actions
137 lines (111 loc) · 4.16 KB
/
class_ball.py
File metadata and controls
137 lines (111 loc) · 4.16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# ball physics code for generic 2D domain
# the functions inside() and normal() encode the shape of the ennvironment
import simplegui
import random
import math
# Canvas size
width = 600
height = 400
# Ball traits
radius = 20
color = "White"
# math helper function
def dot(v, w):
return v[0] * w[0] + v[1] * w[1]
class RectangularDomain:
def __init__(self, width, height):
self.width = width
self.height = height
self.border = 2
# return if bounding circle is inside the domain
def inside(self, center, radius):
in_width = ((radius + self.border) < center[0] <
(self.width - self.border - radius))
in_height = ((radius + self.border) < center[1] <
(self.height - self.border - radius))
return in_width and in_height
# return a unit normal to the domain boundary point nearest center
def normal(self, center):
left_dist = center[0]
right_dist = self.width - center[0]
top_dist = center[1]
bottom_dist = self.height - center[1]
if left_dist < min(right_dist, top_dist, bottom_dist):
return (1, 0)
elif right_dist < min(left_dist, top_dist, bottom_dist):
return (-1, 0)
elif top_dist < min(bottom_dist, left_dist, right_dist):
return (0, 1)
else:
return (0, -1)
# return random location
def random_pos(self, radius):
x = random.randrange(radius, self.width - radius - self.border)
y = random.randrange(radius, self.height - radius - self.border)
return [x, y]
# Draw boundary of domain
def draw(self, canvas):
canvas.draw_polygon([[0, 0], [self.width, 0],
[self.width, self.height], [0, self.height]],
self.border*2, "Red")
class CircularDomain:
def __init__(self, center, radius):
self.center = center
self.radius = radius
self.border = 2
# return if bounding circle is inside the domain
def inside(self, center, radius):
dx = center[0] - self.center[0]
dy = center[1] - self.center[1]
dr = math.sqrt(dx ** 2 + dy ** 2)
return dr < (self.radius - radius - self.border)
# return a unit normal to the domain boundary point nearest center
def normal(self, center):
dx = center[0] - self.center[0]
dy = center[1] - self.center[1]
dr = math.sqrt(dx ** 2 + dy ** 2)
return [dx / dr, dy / dr]
# return random location
def random_pos(self, radius):
r = random.random() * (self.radius - radius - self.border)
theta = random.random() * 2 * math.pi
x = r * math.cos(theta) + self.center[0]
y = r * math.sin(theta) + self.center[1]
return [x, y]
# Draw boundary of domain
def draw(self, canvas):
canvas.draw_circle(self.center, self.radius, self.border*2, "Red")
class Ball:
def __init__(self, radius, color, domain):
self.radius = radius
self.color = color
self.domain = domain
self.pos = self.domain.random_pos(self.radius)
self.vel = [random.random() + .1, random.random() + .1]
# bounce
def reflect(self):
norm = self.domain.normal(self.pos)
norm_length = dot(self.vel, norm)
self.vel[0] = self.vel[0] - 2 * norm_length * norm[0]
self.vel[1] = self.vel[1] - 2 * norm_length * norm[1]
# update ball position
def update(self):
self.pos[0] += self.vel[0]
self.pos[1] += self.vel[1]
if not self.domain.inside(self.pos, self.radius):
self.reflect()
# draw
def draw(self, canvas):
canvas.draw_circle(self.pos, self.radius, 1,
self.color, self.color)
# generic update code for ball physics
def draw(canvas):
ball.update()
field.draw(canvas)
ball.draw(canvas)
field = RectangularDomain(width, height)
# field = CircularDomain([width/2, height/2], 180)
ball = Ball(radius, color, field)
frame = simplegui.create_frame("Ball physics", width, height)
frame.set_draw_handler(draw)
frame.start()