1 """
2 This module contains the core part of the sprinbots, which is their nodes
3 and springs. Here are implemented all the physics behaviour and interations
4 among those parts.
5 """
6
7 from math import sqrt, pi, sin, log
8 from vector import Vector
9
10
11
12
13
14
15
16 ANGLE_STEP = 0.025
17
18
19 GRAVITY = (0,0.3)
20
21
22 AIR_RESISTANCE = 0.01
23
24
25 ELASTICITY = 0.6
26
27
28 VISCOSITY = 0.05
29
30
31 NODE_WEIGHT = 1.26
32
33
34 RADIUS = 8
35
36
37 MAX_NORMAL = 400.0
38
39
40 UP = 1
41
42
43 DOWN = 2
44
45
46 LEFT = 3
47
48
49 RIGHT = 4
50
52 """
53 A node is a mass circle which acts gravity, air resistance. It has a
54 position and a velocity and can be connected by springs
55 """
56
57 - def __init__(self, pos=(0,0), vel=(0,0), acc=(0,0)):
58 """
59 Creates a node with a start position, velocity and acceleration
60 """
61 self.pos = Vector(pos[0], pos[1])
62 self.vel = Vector(vel[0], vel[1])
63 self.acc = Vector(acc[0], acc[1])
64
66 """
67 Refreshes node: changes position based on velocity and velocity based
68 on acceleration. Also applies gravity and air resistance
69 """
70 self.pos = self.pos + self.vel
71 self.vel = self.vel + (self.acc * elast)
72 self.pos = self.pos + (self.acc * (1.0 - elast))
73 self.acc = Vector(0,0)
74
75 self.vel = self.vel + Vector(grav[0], grav[1])
76 self.vel = self.vel * (1.0 - atr)
77
78 - def colideWall(self, limit, side, atr_normal=0.6, atr_surface=0.5, min_vel=0.99, radius=RADIUS):
79 """
80 Colides this node with a wall, if possible. Applies surface friction and changes velocity
81 """
82 global UP, DOWN, LEFT, RIGHT
83 colision_strenght = 0
84
85 if side == LEFT and self.pos.x < limit + radius:
86 self.pos.x = limit + radius
87 self.vel.x = self.vel.x * - atr_normal if abs(self.vel.x * - atr_normal) > min_vel else 0.0
88 self.vel.y *= atr_surface
89 colision_strenght = abs(self.vel.x)
90 elif side == RIGHT and self.pos.x > limit - radius:
91 self.pos.x = limit - radius
92 self.vel.x = self.vel.x * - atr_normal if abs(self.vel.x * - atr_normal) > min_vel else 0.0
93 self.vel.y *= atr_surface
94 colision_strenght = abs(self.vel.x)
95
96 if side == UP and self.pos.y < limit + radius:
97 self.pos.y = limit + radius
98 self.vel.x *= atr_surface
99 self.vel.y = self.vel.y * - atr_normal if abs(self.vel.y * - atr_normal) > min_vel else 0.0
100 colision_strenght = abs(self.vel.y)
101 elif side == DOWN and self.pos.y > limit - radius:
102 self.pos.y = limit - radius
103 self.vel.x *= atr_surface
104 self.vel.y = self.vel.y * - atr_normal if abs(self.vel.y * - atr_normal) > min_vel else 0.0
105 colision_strenght = abs(self.vel.y)
106 elif side not in [UP, LEFT, RIGHT, DOWN]:
107 raise ValueError("side must be UP, LEFT, RIGHT or DOWN.")
108
109 return colision_strenght
110
112 """
113 Colides this node with another one, if possible
114 """
115
116 if (self is other):
117 return
118
119
120 distancia = (self.pos - other.pos).length()
121
122
123 if distancia != 0 and distancia <= radius*2:
124 colisao = (self.pos - other.pos)
125
126
127 trans_a = self.vel.projection(colisao)
128 trans_b = other.vel.projection(colisao)
129
130
131 r = (colisao.unit() * radius * 2) - colisao
132 self.pos += r / 2.0
133 other.pos -= r / 2.0
134
135
136 self.vel += trans_b - trans_a
137 other.vel += trans_a - trans_b
138
140 return "<Node pos=%dx%d, vel=%.2fx%.2f>" % (self.pos.x, self.pos.y, self.vel.x, self.vel.y)
141
142
143
145 """
146 Spring: An object that connect nodes, and can only exist if connecting two
147 diferent nodes. A spring has a normal lenght state and if pulled or pushed
148 it applies a force on the nodes it connects to restabilish it's state.
149 Springs can also move like muscles by changing it normal lenght by a sine
150 wave frequency.
151 """
152
153 - def __init__(self, a, b, amplitude=0, offset=pi, normal=None):
154 """
155 Creates a spring connecting nodes A and B, with an start amplitude, offset and
156 normal lenght.
157 """
158 self.a = a
159 self.b = b
160 self.normal = min(sqrt(sum((a.pos-b.pos)**2)) if normal is None else normal, MAX_NORMAL)
161 self.amplitude = amplitude
162 self.offset = offset
163
164 - def refresh(self, elast=0.5, ang=None, visc=0):
165 """
166 Refreshes a spring by appling forces on surrounding nodes and creating
167 buoyancy forces if there are viscosity on the enviroment.
168 """
169 ampl = 0 if ang is None else self.amplitude
170 if ang is None:
171 ang = 0
172
173 delta = (self.a.pos + self.a.vel) - (self.b.pos + self.b.vel)
174 dist = sqrt(sum(delta**2))
175 N = self.normal + (sin(ang+self.offset) * ampl * self.normal)
176
177 if dist != 0:
178 self.a.acc += (delta/dist * (N-dist) * (1.0-elast) * 0.5)
179 self.b.acc -= (delta/dist * (N-dist) * (1.0-elast) * 0.5)
180
181
182 if visc > 0:
183 vnormal = (self.a.pos - self.b.pos).perpendicular()
184 if vnormal.length() > 0:
185 vnodes = self.a.vel + self.b.vel
186 vproj = -vnodes.projection(vnormal)
187 accel = vproj * log((self.a.pos - self.b.pos).length(), 10) * visc
188
189 self.a.acc += accel
190 self.b.acc += accel
191
193 return "<Spring normal=%.2f, amplitude=%.2f>" % (self.normal, self.amplitude)
194