Package springbots :: Module gear
[hide private]

Source Code for Module springbots.gear

  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  # Constants 
 12  # 
 13   
 14  #: The rate which the angle changes, this parameter 
 15  #: affects the frequency of motion of springs 
 16  ANGLE_STEP = 0.025 
 17   
 18  #: Acceleration vector aplied every step of simulation on nodes 
 19  GRAVITY = (0,0.3) 
 20   
 21  #: % of node's velocity lost every step of simulation 
 22  AIR_RESISTANCE = 0.01 
 23   
 24  #: Parameter which determines the force of springs 
 25  ELASTICITY = 0.6 
 26   
 27  #: Causes buoyancy force on springs motion 
 28  VISCOSITY = 0.05 
 29   
 30  #: This parameter is not used on physics 
 31  NODE_WEIGHT = 1.26 
 32   
 33  #: Node's radius 
 34  RADIUS = 8 
 35   
 36  #: Maximum spring's normal 
 37  MAX_NORMAL = 400.0 
 38   
 39  #: Direction constant: UP 
 40  UP              = 1 
 41   
 42  #: Direction constant: DOWN 
 43  DOWN    = 2 
 44   
 45  #: Direction constant: LEFT 
 46  LEFT    = 3 
 47   
 48  #: Direction constant: RIGHT 
 49  RIGHT   = 4 
 50   
51 -class Node(object):
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
65 - def refresh(self, atr=AIR_RESISTANCE, grav=GRAVITY, elast=ELASTICITY):
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
111 - def colide(self, other, radius=RADIUS):
112 """ 113 Colides this node with another one, if possible 114 """ 115 # Nao pode colidir consigo proprio 116 if (self is other): 117 return 118 119 # calcula distancia entre os bots 120 distancia = (self.pos - other.pos).length() 121 122 # detecta colisao 123 if distancia != 0 and distancia <= radius*2: 124 colisao = (self.pos - other.pos) # vetor colisao(ligando os dois) 125 126 # projeta velocidades no vetor colisao 127 trans_a = self.vel.projection(colisao) 128 trans_b = other.vel.projection(colisao) 129 130 # corrige posicoes 131 r = (colisao.unit() * radius * 2) - colisao 132 self.pos += r / 2.0 133 other.pos -= r / 2.0 134 135 # troca velocidades 136 self.vel += trans_b - trans_a 137 other.vel += trans_a - trans_b
138
139 - def __repr__(self):
140 return "<Node pos=%dx%d, vel=%.2fx%.2f>" % (self.pos.x, self.pos.y, self.vel.x, self.vel.y)
141 142 143
144 -class Spring(object):
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 # Aplica empuxo de fluido 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
192 - def __repr__(self):
193 return "<Spring normal=%.2f, amplitude=%.2f>" % (self.normal, self.amplitude)
194