def other_balls_collision(self, rack): for another_ball in rack: if another_ball.ball.visible: pos_a = self.ball.pos pos_b = another_ball.ball.pos vel_a = self.velocity vel_b = another_ball.velocity radius = self.ball.radius if self != another_ball and mag(pos_a - pos_b) < 2 * radius: a = mag(vel_a - vel_b)**2 b = dot(-2 * (pos_a - pos_b), (vel_a - vel_b)) c = mag(pos_a - pos_b)**2 - (2 * radius)**2 delta = b**2 - 4 * a * c if a != 0 and delta > 0: dtprim = (-b + sqrt(delta)) / (2 * a) pos_a = pos_a - vel_a * dtprim pos_b = pos_b - vel_b * dtprim tmp = (dot(vel_a - vel_b, (pos_a - pos_b) / mag(pos_a - pos_b))) * ( (pos_a - pos_b) / mag(pos_a - pos_b)) vel_a -= tmp vel_b += tmp pos_a += vel_a * dtprim pos_b += vel_b * dtprim self.ball.pos = pos_a self.set_velocity(vel_a) another_ball.pos = pos_b another_ball.set_velocity(vel_b)
def collision_node_edge(body1, body2): DISTANCE_TOLERANCE = 0.03 for pt1, (pt2, pt2_next) in it.product(body1.vertices, zip(body2.vertices, body2.vertices[1:]+body2.vertices[:1])): edge = pt2_next - pt2 edge_normal = vp.norm(edge) p = pt1 - pt2 proj_on_edge = edge_normal * vp.dot(p, edge_normal) if vp.mag(proj_on_edge + edge) <= edge.mag or proj_on_edge.mag > edge.mag: continue # Perpendicular distance to edge dist_to_edge = vp.mag(vp.cross(p, edge_normal)) if dist_to_edge > DISTANCE_TOLERANCE: continue collision_pt1 = pt1 - body1.pos collision_pt2 = pt1 - body2.pos collision_normal = vp.norm(vp.cross(vp.cross(edge_normal, p), edge_normal)) vel1 = body1.vel + vp.cross(body1.ang_vel, collision_pt1) vel2 = body2.vel + vp.cross(body2.ang_vel, collision_pt2) relative_vel = vel1 - vel2 if is_collision_course(relative_vel, collision_normal): return Collision(body1, body2, collision_pt1, collision_pt2, relative_vel, collision_normal) return None
def __init__(self, v, arrow_label, arrow_color, arrow_pos): if arrow_label == 'x': vec_u = Arrow.vec_i elif arrow_label == 'y': vec_u = Arrow.vec_j elif arrow_label == 'z': vec_u = Arrow.vec_k else: vec_u = vp.vector(v.x * Arrow.vec_i.x, v.y * Arrow.vec_j.y, v.z * Arrow.vec_k.z) self.rod_radius = vp.mag(vec_u) * 0.01 scaled_arrow_pos = vp.vector(arrow_pos.x * Arrow.vec_i.x, arrow_pos.y * Arrow.vec_i.y, arrow_pos.z * Arrow.vec_i.z) self.rod = vp.cylinder(pos=scaled_arrow_pos, axis=vec_u, radius=self.rod_radius, color=arrow_color) self.cone = vp.cone(pos=vec_u, axis=0.1 * vec_u, radius=vp.mag(vec_u) * 0.03, color=arrow_color) if arrow_label in 'xyz': self.axis_text = vp.text(text=arrow_label, pos=self.cone.pos + 0.1 * vec_u, color=arrow_color)
def move(self, dt): self.ball.pos = self.ball.pos + dt * self.velocity self.ball.rotate(angle=mag(self.velocity) * 0.0002 * pi, axis=self.rotation_axis) self.set_velocity(self.velocity - self.velocity * 0.005) if mag(self.velocity) < 1: self.set_velocity(vec(0, 0, 0))
def amplitude(self, delta): if hasattr(self, '_amp'): if vp.mag(delta) > vp.mag(self.amplitude): self._amp = delta #print the maxium distance from rest position, during the entire oscillation print("\rMeasured amplitude:", vp.mag(delta), end='') else: self._amp = delta
def evita_traslapes(pos_i): for i in pos_i: for j in pos_i: if j != i: if vp.mag(j.pos - i.pos) < 2 * r_p: lambda_1 = (2 * r_p - (vp.mag(i.pos - j.pos))) / (vp.mag(i.vel)) i.pos = i.pos + lambda_1 * i.vel
def mediciones(lista_de_particulas): suma_energias_cin = 0 suma_energias_pot = 0 for i in lista_de_particulas: suma_energias_cin = suma_energias_cin + (1 / 2) * ( vp.mag(i.vel)**2 ) #<-- agregar masa en general recuerda que la masa es de 1 y los radios todos son iguales, podriamos generalizar en un futuro suma_energias_pot = suma_energias_pot + vp.mag( campo_grav) * np.absolute( i.pos.y + long_rect) #<- aqui iría tambien en mgh return ((suma_energias_cin, suma_energias_pot))
def eval_gravitational_force(p1: vp.sphere, p2: vp.sphere): G = 1 r = p1.pos - p2.pos distance = vp.mag(r) rhat = r / distance force = -rhat * G * p1.mass * p2.mass / distance ** 2 return force
def escenario3_avance(dt): global particulas, t, n, conversion for i in range(len(particulas)): # Verifica si la particula esta en el punto de choque if round(particulas[i][0].posicion.x, 0) == 10 and particulas[i][1]: #genera el choque velocidad_nueva, l_nueva = choque(particulas[i][0]) #actualiza las propiedades particulas[i][0].cambiar_velocidad(velocidad_nueva) particulas[i][0].cambiar_longitud_onda(l_nueva) #evolucion temporal particulas[i][0].evolucion_temporal(dt) #la particula choco particulas[i][1] = False n += 1 else: particulas[i][0].evolucion_temporal(dt) #detiende la particula si se ha alejado mas de 10 if vp.mag(particulas[i][0].posicion - vp.vector(10, 0, 0)) >= 10.0: particulas[i][0].velocidad = vp.vector(0, 0, 0) #genera particulas nuevas cada cierto tiempo if t >= 2.5: particulas.append([ mod.Photon(vp.vector(4, 0, 0), vp.vector(0, 0, 0), 380, conversion), True ]) t = 0 t += dt
def create_line(pos1, pos2, scene): """ Create a line from position 1 to position 2. :param scene: The scene in which to draw the object :type scene: class:`vpython.canvas` :param pos1: 3D position of one end of the line. :type pos1: class:`vpython.vector` :param pos2: 3D position of the other end of the line. :type pos2: class:`vpython.vector` """ # Length of the line using the magnitude line_len = mag(pos2-pos1) # Position of the line is the midpoint (centre) between the ends position = (pos1 + pos2) / 2 # Axis direction of the line (to align the box (line) to intersect the two points) axis_dir = pos2 - pos1 # Return a box of thin width and height to resemble a line thickness = 0.01 return box(canvas=scene, pos=position, axis=axis_dir, length=line_len, width=thickness, height=thickness, color=color.black)
def mediciones(lista_de_particulas): suma_energias = 0 for i in lista_de_particulas: suma_energias = suma_energias + (1 / 2) * ( vp.mag(i.vel)**2 ) #<-- agregar masa en general recuerda que la masa es de 1 y los radios todos son iguales, podriamos generalizar en un futuro return (suma_energias)
def reDrawLines(): global fwd_line, mouse_line, cam_dist, ray, scene_size, linelen linelen = scene_size + vs.mag(cam_frame.axis.norm() * cam_dist + cam_frame.pos) reDrawLine(fwd_line, vs.vector(cam_dist, 0, 0), linelen, vs.vector(-1, 0, 0)) reDrawLine(mouse_line, vs.vector(cam_dist, 0, 0), linelen, ray)
def acceleration(A: vp.sphere, B: vp.sphere) -> vp.vec: """Returns the acceleration on A from B due to gravitational attraction. Note: returns a vp.vec object.""" denom = vp.mag(A.pos - B.pos)**3 if np.isclose(denom, 0): return vp.vec(1E20, 1E20, 1E20) return -G * B.mass * (A.pos - B.pos) / denom
def __init__(self, pos, mass, orbitalParentmass, orbitalParentpos, orbitalParentv): self.age = 0 self.mass = mass self.planet = sphere(pos=pos, radius=((3 * self.mass) / (4 * pi * 3000))**(1 / 3), make_trail=True, retain=5000) self.colorMe() self.G = 6.7e-11 r = orbitalParentpos.x - self.planet.pos.x rmag = abs(r) inner = self.G * orbitalParentmass / rmag self.v = vector( orbitalParentv.x, 0, orbitalParentv.z + (random.choice([-1, 1]) * math.sqrt(inner)) + random.randint(0, 100)) self.p = self.mass * self.v self.Vol = (4 * pi * (self.planet.radius**3)) / 3 self.name = (''.join( [random.choice(string.ascii_letters) for n in range(4)])).lower() self.name = self.name.capitalize() self.label = label(pos=self.planet.pos, height=16, yoffset=5, text=self.name + "\n" + str(int(mag(self.v))) + "m/s", line=False) print("New Planet " + self.name)
def update_grid(self): """ Update the grid axes and numbers if the camera position/rotation has changed. """ # Obtain the new camera settings new_camera_pos = vector(self.__scene.camera.pos) new_camera_axes = vector(self.__scene.camera.axis) old_camera_pos = vector(self.camera_pos) old_camera_axes = vector(self.camera_axes) # Update old positions self.camera_pos = new_camera_pos self.camera_axes = new_camera_axes distance_from_center = mag(self.__scene.center - self.__scene.camera.pos) new_scale = round(distance_from_center / 30.0, 1) if not new_scale == self.__scale: self.set_scale(new_scale) # If camera is different to previous: update if (not new_camera_axes.equals(old_camera_axes)) or ( not new_camera_pos.equals(old_camera_pos)): # Update grid self.__move_grid_objects() update_grid_numbers(self.__focal_point, self.grid_object[self.__labels_idx], self.__num_squares, self.__scale, self.__is_3d, self.__scene)
def force_grav(A,B): """ Renvoie un vecteur de la force exercé par A sur B """ d = mag(A.sph.pos - B.sph.pos) # distance entre les deux objets u = norm(A.sph.pos - B.sph.pos) # vecteur unitaire entre les deux objets F = u*((G* A.mass * B.mass)/(d**2)) # Calcul de F, le force gravitationnelle entre les deux objets return F
def create_line(pos1, pos2, scene, colour=None, thickness=0.01, opacity=1): # pragma nocover """ Create a line from position 1 to position 2. :param scene: The scene in which to draw the object :type scene: class:`vpython.canvas` :param pos1: 3D position of one end of the line. :type pos1: class:`vpython.vector` :param pos2: 3D position of the other end of the line. :type pos2: class:`vpython.vector` :param colour: RGB list to colour the line to :type colour: `list` :param thickness: Thickness of the line :type thickness: `float` :param opacity: Opacity of the line :type opacity: `float` :raises ValueError: RGB colour must be normalised between 0->1 :raises ValueError: Thickness must be greater than 0 :return: A box resembling a line :rtype: class:`vpython.box` """ # Set default colour # Stops a warning about mutable parameter if colour is None: colour = [0, 0, 0] if colour[0] > 1.0 or colour[1] > 1.0 or colour[2] > 1.0 or \ colour[0] < 0.0 or colour[1] < 0.0 or colour[2] < 0.0: raise ValueError("RGB values must be normalised between 0 and 1") if thickness < 0.0: raise ValueError("Thickness must be greater than 0") # Length of the line using the magnitude line_len = mag(pos2 - pos1) # Position of the line is the midpoint (centre) between the ends position = (pos1 + pos2) / 2 # Axis direction of the line (to align the box (line) to intersect the # two points) axis_dir = pos2 - pos1 # Return a box of thin width and height to resemble a line # thickness = 0.01 return box(canvas=scene, pos=position, axis=axis_dir, length=line_len, width=thickness, height=thickness, color=vector(colour[0], colour[1], colour[2]), opacity=opacity)
def update(self, dt): # forces axis, theta = _euler.euler2axangle(self.pqr.x, self.pqr.y, self.pqr.z) axis = _vp.vector(axis[0], axis[1], axis[2]) up = _vp.rotate(_vp.vector(0,1,0), theta, axis) a = _vp.vector(0, -_gravity, 0) a = a + (self.thrust1+self.thrust2+self.thrust3+self.thrust4)/self.mass * up + self.wind/self.mass a = a - (_lin_drag_coef * _vp.mag(self.xyz_dot)**2)/self.mass * self.xyz_dot self.xyz_dot = self.xyz_dot + a * dt # torques (ignoring propeller torques) cg = self.cgpos * up tpos1 = _vp.rotate(_vp.vector(1.3*_size,0,0), theta, axis) tpos2 = _vp.rotate(_vp.vector(0,0,1.3*_size), theta, axis) tpos3 = _vp.rotate(_vp.vector(-1.3*_size,0,0), theta, axis) tpos4 = _vp.rotate(_vp.vector(0,0,-1.3*_size), theta, axis) torque = _vp.cross(cg, _vp.vector(0, -_gravity, 0)) torque = torque + _vp.cross(tpos1, self.thrust1 * up) torque = torque + _vp.cross(tpos2, self.thrust2 * up) torque = torque + _vp.cross(tpos3, self.thrust3 * up) torque = torque + _vp.cross(tpos4, self.thrust4 * up) torque = torque - _rot_drag_coef * self.pqr_dot aa = torque/self.inertia if _vp.mag(aa) > 0: aai, aaj, aak = _euler.axangle2euler((aa.x, aa.y, aa.z), _vp.mag(aa)) aa = _vp.vector(aai, aaj, aak) self.pqr_dot = self.pqr_dot + aa * dt else: self.pqr_dot = _vp.vector(0,0,0) # ground interaction if self.xyz.y <= 0: self.xyz.y = 0 if self.xyz_dot.y <= 0: self.xyz_dot.x = self.xyz_dot.x * _ground_friction self.xyz_dot.y = 0 self.xyz_dot.z = self.xyz_dot.z * _ground_friction self.pqr_dot = self.pqr_dot * _ground_friction # energy update self.energy += _power_coef * (self.thrust1**1.5 + self.thrust2**1.5 + self.thrust3**1.5 + self.thrust4**1.5) * dt # time update self.xyz += self.xyz_dot * dt self.pqr += self.pqr_dot * dt # callback if self.updated is not None: self.updated(self) self.draw()
def urto_elastico(body0, body1): '''collisione elastica in 3 dimensioni''' vrel = body0.velocity - body1.velocity rrel = body0.pos - body1.pos distance = mag(rrel) ratio0 = 2 * body1.mass / (body0.mass + body1.mass) ratio1 = 2 * body0.mass / (body0.mass + body1.mass) body0.velocity += -ratio0 * dot(vrel, rrel) / distance**2 * rrel body1.velocity += -ratio1 * dot(-vrel, -rrel) / distance**2 * (-rrel)
def colision_part_2(obj_1, obj_2): if vp.mag(obj_1.pos - obj_2.pos) < 2 * r_p: delta_v_1 = obj_1.vel - obj_2.vel delta_p_1 = obj_1.pos - obj_2.pos delta_v_2 = obj_2.vel - obj_1.vel delta_p_2 = obj_2.pos - obj_1.pos coef_1 = (vp.dot(delta_v_1, delta_p_1)) / (vp.dot( delta_p_1, delta_p_1)) coef_2 = (vp.dot(delta_v_2, delta_p_2)) / (vp.dot( delta_p_2, delta_p_2)) obj_1.vel = obj_1.vel - coef_1 * (obj_1.pos - obj_2.pos) obj_2.vel = obj_2.vel - coef_2 * (obj_2.pos - obj_1.pos) lambda_1 = (2 * r_p - (vp.mag(obj_1.pos - obj_2.pos))) / (vp.mag(obj_1.vel)) obj_1.pos = obj_1.pos + lambda_1 * obj_1.vel
def check_collisions(self): for index, body in enumerate(self.bodies): for other_index, other_body in enumerate(self.bodies): if index != other_index: distance = mag(body.pos - other_body.pos) if distance <= (body.radius + other_body.radius): if [other_index, index] not in self.collided_couples: self.collided_couples.append([index, other_index]) return (self.collided_couples)
def init_acc(centr_body, position): ''' Calculates acceleration of an orbiting object due to the centr_body ''' # neglect forces from bodies other than the one it is orbiting if position != centr_body.position: radius_vec = position - centr_body.position acceleration = -G * centr_body.mass * radius_vec / vp.mag(radius_vec)**3 else: acceleration = vp.vector(0,0,0) # for sun return acceleration
def calculate_acceleration(self, other=None): if other is None: super().calculate_acceleration() else: r0 = self.body.pos - self.origin l0 = self.length dl0 = vp.mag(r0) - l0 self.dl = dl0 # useful to calculate potential energy r1 = other.body.pos - other.origin l1 = other.length dl1 = vp.mag(r1) - l1 if dl0 / l0 < -0.95 or dl1 / l1 < -0.95: raise ValueError('Spring too compressed') else: dr0 = r0 * (dl0 / vp.mag(r0)) dr1 = r1 * (dl1 / vp.mag(r1)) self.acceleration = -(self.k * dr0) / self.m + (other.k * dr1) / self.m
def __universal_gravitation(self, sphere_one, sphere_two): """ Uses Newton's Law of Universal Gravitation to find the force of gravity on the first sphere due to the second sphere. :param sphere_one: (Sphere) VPython sphere for first sphere. :param sphere_two: (Sphere) VPython sphere for second sphere. :return: (VPython vector) Force vector due to gravity on sphere_one from sphere_two. """ position_vector = sphere_one.pos - sphere_two.pos return -self._gravity*sphere_one.mass*sphere_two.mass*position_vector/mag(position_vector)**3
def choque( foton: object) -> list: #choque de un foton, cambia longitud de onda. v = vp.mag(foton.velocidad) theta = np.random.random() * np.pi * 2 phi = np.arccos(np.random.random() * 2 - 1) vx = v * np.sin(theta) * np.cos(phi) vy = v * np.sin(theta) * np.sin(phi) vz = v * np.cos(theta) wave_lenghtf = theta_a_wl(theta) #retorna la direción de la velocidad nueva y la longitud de onda nueva return vp.vector(vx, vy, vz), wave_lenghtf
def update(self, dt, solarObjects): self.age = self.age + dt Ftotal = vector(0, 0, 0) for solarObject in solarObjects: if solarObject.name == self.name: continue r = solarObject.planet.pos - self.planet.pos rmag = mag(r) if rmag < (self.planet.radius + solarObject.planet.radius): if self.mass > solarObject.mass: solarObject.planet.visible = 0 solarObject.planet.clear_trail() solarObject.label.visible = False self.Vol = self.Vol + solarObject.Vol self.planet.radius = ((self.Vol * 3) / (4 * pi))**(1 / 3) self.mass = self.mass + solarObject.mass self.v = (self.p + solarObject.p) / self.mass solarObjects.remove(solarObject) print(solarObject.name + " collided with " + self.name) else: self.planet.visible = 0 self.planet.clear_trail() self.label.visible = False solarObject.Vol = self.Vol + solarObject.Vol solarObject.planet.radius = ((solarObject.Vol * 3) / (4 * pi))**(1 / 3) solarObject.mass = self.mass + solarObject.mass solarObject.v = (self.p + solarObject.p) / solarObject.mass solarObjects.remove(self) print(self.name + " collided with " + solarObject.name) self.colorMe() Fmag = self.G * self.mass * solarObject.mass / (rmag**2) rhat = r / rmag Fnet = Fmag * rhat Ftotal = Ftotal + Fnet self.v = self.v + ((Ftotal / self.mass) * dt) self.p = self.v * self.mass self.planet.pos = self.planet.pos + (self.v * dt) self.label.pos = self.planet.pos self.label.text = self.name + "\n" + str(int(mag(self.v))) + "m/s" return solarObjects
def doAutoscale(self): if len(self.drawables_[0].activeDia) == 0: print("Warning: No values to display in Moogli view ", self.title) return cmin = self.drawables_[0].coordMin cmax = self.drawables_[0].coordMax diamax = max(self.drawables_[0].activeDia) v0 = vp.vector(cmin[0], cmin[1], cmin[2]) v1 = vp.vector(cmax[0], cmax[1], cmax[2]) #self.scene.camera.axis = self.scene.forward * vp.mag(v1 - v0) * 4 self.scene.center = (v0 + v1) / 2.0 self.scene.range = (diamax + vp.mag(v0 - v1)) / 1.5
def partially_inelastic_collision(self): # totally inelastic collision, but if the couple of bodies collides with others can breack for indexs in self.collided_couples: body0 = self.bodies[indexs[0]] body1 = self.bodies[indexs[1]] m0 = body0.mass m1 = body1.mass # grow body 1 cm_pos = (body0.pos * m0 + body1.pos * m1) / (m0 + m1) cm_velocity = (body0.velocity * m0 + body1.velocity * m1) / (m0 + m1) dr0 = body0.pos - cm_pos dv0 = body0.velocity - cm_velocity dr1 = body1.pos - cm_pos dv1 = body1.velocity - cm_velocity omega0 = cross(-dv0, dr0) / mag(dr0)**2 omega1 = cross(-dv1, dr1) / mag(dr1)**2 #assert omega0 == omega1 body0.velocity = cm_velocity + cross(omega0, dr0) body1.velocity = cm_velocity + cross(omega1, dr1) self.collided_couples.clear()
def calc_forces(dt, body1, body2): dist = vp.mag(body1.pos - body2.pos) if dist <= body1.radius or dist <= body2.radius: print('Impact. The end.') exit() grav_mag = (GRAVITY_ACC * body1.mass * body2.mass) / (dist**2) dir1 = vp.norm(body2.pos - body1.pos) dir2 = vp.norm(body1.pos - body2.pos) body1.force += dir1 * grav_mag body2.force += dir2 * grav_mag
def calculate_center_of_mass_and_energy(self): c_m = vector(0, 0, 0) v_m = vector(0, 0, 0) m_tot = 0 KE = 0 for index, body in enumerate(self.bodies): c_m += body.mass * body.pos v_m += body.mass * body.velocity m_tot += body.mass KE += 1 / 2 * body.mass * mag(body.velocity)**2 print('Kinetic Energy (J):', KE) #print('Center of Mass velocity:', mag(v_m/m_tot)) return c_m / m_tot, KE