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, v_label, v_label_pos, v_color, v_pos): self.v = v # Vector self.v_label = v_label # Label for the vector self.v_color = v_color # Vector colour # Position of vector i.e. where its tail is. self.v_pos = v_pos # Axis of the cone; same as the vector's self.cone_axis = 0.1 * vp.norm(self.v) self.rod_radius = 0.02 # Absolute radius of the rod self.cone_radius = 0.06 # Absolute radius of the rod # Reduce the size of the rod by the axial size of the cone self.rod = vp.cylinder(pos=v_pos, axis=self.v - self.cone_axis, radius=self.rod_radius, color=v_color) # Place the base of the cone at the end of rod # which has been reduced by the cone's axial length self.cone = vp.cone(pos=self.v - self.cone_axis + v_pos, axis=self.cone_axis, radius=self.cone_radius, color=v_color) # Note where the tip of the cone is, # which will define the starting point of # of the axis line self.cone_tip = self.v + v_pos + 0.1 * vp.norm(self.v) self.axis_text = vp.label(text=v_label, pos=v_pos + v_label_pos * (self.cone_tip - v_pos), color=v_color, xoffset=3, yoffset=3, box=False)
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 calc_forces(particles, struct_springs): # Process gravity and drag forces for particle in chain.from_iterable(particles): if particle.locked: continue # Gravity particle.force = vp.vector(0, GRAVITY_ACC * particle.mass, 0) # Viscous drag - page 17. Surface without rotation calculation. # https://pl.wikipedia.org/wiki/Op%C3%B3r_aero(hydro)dynamiczny#Formu%C5%82y_na_wielko%C5%9B%C4%87_oporu_aero(hydro)dynamicznego drag_vector = -vp.norm(particle.vel) particle.force += drag_vector * DRAG_COEFFICIENT * AIR_DENSITY * 0.5 \ * particle.vel.mag2 * particle.surface # Wind force particle.force += wind_force() # Process spring forces - page 82 for spring in struct_springs: l = spring.particle1.pos - spring.particle2.pos relative_vel = spring.particle1.vel - spring.particle2.vel f1 = -(spring.k * (l.mag - spring.length) + spring.d * (vp.dot(relative_vel, l) / l.mag)) * l.norm() f2 = -f1 if not spring.particle1.locked: spring.particle1.force += f1 if not spring.particle2.locked: spring.particle2.force += f2
def check_for_collisions(particles): collisions = [] for particle in chain.from_iterable(particles): if particle.locked: continue # Check for collisions with ground if (particle.pos.y <= COLLISION_TOLERANCE) and (particle.vel.y < VELOCITY_TOLERANCE): collisions.append( Collision(particle=particle, normal=vp.norm(vp.vector(0, 1, 0)))) # Check for collisions with flag pole # Center of mass of a pole is at the same height as particle. So distance (vector) between two # mass centers will be also vector perpendicular to the edge of collision - page 248 dist = particle.pos - vp.vector(0, particle.pos.y, 0) n = dist.norm() relative_vel_n = vp.dot(particle.vel, n) if dist.mag <= (FLAG_POLE_RADIUS + COLLISION_TOLERANCE) and \ (0 < particle.pos.y < FLAG_POLE_HEIGHT) and (relative_vel_n < VELOCITY_TOLERANCE): collisions.append(Collision(particle=particle, normal=n)) return collisions
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 move(self): """Moves particle towards (0, 0, 0). Also moves randomly perpendicular to this trajectory. """ self.steps += 1 centre_vector = vp.norm(vp.vector(0, 0, 0) - self.pos) perp_vector = vp.rotate(centre_vector, angle=math.pi / 2) self.pos += centre_speed * centre_vector self.pos += 0.2 * random.uniform(-1, 1) * perp_vector
def forward(self, distance=1): self.position = self.position + distance * norm(self.heading) if self.isDrawing: #We add the point with its radius and color to the current curve keys = ['pos', 'color', 'radius'] values = [self.position, self.currentColor, self.width] new_pos = dict(zip(keys, values)) self.c.append(new_pos) if self.polygon: #We store the point to use it later on to generate the polygon, see "endPolygon()" self.listOfVectors.append(self.position)
def penetration_by_node(body1, body2): for pt1 in body1.vertices: penetration = True for pt2, pt2_next in zip(body2.vertices, body2.vertices[1:]+body2.vertices[:1]): edge = pt2_next - pt2 p = pt1 - pt2 mag_proj_on_edge = vp.dot(p, vp.norm(edge)) if mag_proj_on_edge < 0: penetration = False break if penetration: collision_normal = vp.norm(body1.pos - body2.pos) relative_vel = body1.vel - body2.vel return Collision(body1, body2, pt1, pt1, relative_vel, collision_normal) return None
def updateAxis(self): if not self.colorbar: return forward = vp.norm(self.scene.forward) screenUp = vp.norm(self.scene.up) right = vp.norm(vp.cross(forward, screenUp)) up = vp.norm(vp.cross(right, forward)) dx = 0.8 x = vp.vector(dx, 0.0, 0.0) y = vp.vector(0.0, dx, 0.0) z = vp.vector(0.0, 0.0, dx) self.xAx.axis = vp.vector(x.dot(right), x.dot(up), 0.0) self.yAx.axis = vp.vector(y.dot(right), y.dot(up), 0.0) self.zAx.axis = vp.vector(z.dot(right), z.dot(up), 0.0) self.axisLength.text = "{:.2f} <i>u</i>m".format( dx * 1e6 * self.scene.range * self.colorbar.width / self.scene.width)
def touches(self, other): """If incoming particle touches stationary particle, returns True and places incoming particle on edge of stationary one. Else, returns False. """ dist = other.pos - self.pos sum_radii = self.radius + other.radius if vp.mag(dist) <= sum_radii: # Puts incoming particle on very edge of stationary particle. other.pos = self.pos + sum_radii * vp.norm(dist) return True else: return False
def init_vel(centr_body, position): ''' Calculates velocity of an orbiting object with its initial position ''' if position != centr_body.position: radius = vp.mag(position - centr_body.position) # equate gravitational force to centripetal force gives vel_mag = np.sqrt(G*centr_body.mass/radius) # velocity vector is normal to the vector towards central object and the vector normal to xz-plane vec1 = vp.vector(centr_body.position - position) vec2 = vp.vector(0,1,0) vel_vec = vp.cross(vec1,vec2) velocity = vel_mag * vp.norm(vel_vec) + centr_body.velocity # taking centr_body motion into account else: velocity = vp.vector(0,0,0) # avoid division by 0 for sun's case return velocity
def integrate(dt, roller, alpha): slope_dir = vp.norm(vp.rotate(vp.vector(1, 0, 0), angle=-alpha)) # Rolling with sliding (movie - 13:21:00) # a = g(sin(a) - u*cos(a)) - page 104 # roller.acc = GRAVITY_ACC * (math.sin(alpha) - roller.friction_coeff * math.cos(alpha)) * slope_dir # Rolling without sliding - (movie - 10:24:00) roller.acc = (roller.mass * GRAVITY_ACC * math.sin(alpha)) / ( roller.mass + roller.moment / roller.radius**2) * slope_dir roller.vel += roller.acc * dt roller.pos += roller.vel * dt roller.ang_vel += ((roller.force * roller.radius) / roller.moment) * dt angle_diff = roller.ang_vel * dt # Minus, because function (vp.rotate) rotate counter-clockwise roller.rotate(angle=-angle_diff)
def collision_node_node(body1, body2): for pt1, pt2 in it.product(body1.vertices, body2.vertices): if not close_points(pt1, pt2): continue collision_pt1 = pt1 - body1.pos collision_pt2 = pt1 - body2.pos collision_normal = vp.norm(body1.pos - body2.pos) 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 reset(): global theta, thetadot, psi, psidot, phi, phidot theta = 0.3*vp.pi # initial polar angle of shaft (from vertical) thetadot = 0 # initial rate of change of polar angle psi = 0 # initial spin angle psidot = 30 # initial rate of change of spin angle (spin ang. velocity) phi = -vp.pi/2 # initial azimuthal angle phidot = 0 # initial rate of change of azimuthal angle if pureprecession: # Set to True if you want pure precession, without nutation a = (1-I3/I1)*vp.sin(theta)*vp.cos(theta) b = -(I3/I1)*psidot*vp.sin(theta) c = M*g*r*vp.sin(theta)/I1 phidot = (-b+vp.sqrt(b**2-4*a*c))/(2*a) gyro.axis = gyro.length * \ vp.vector(vp.sin(theta)*vp.sin(phi), vp.cos(theta), vp.sin(theta)*vp.cos(phi)) A = vp.norm(gyro.axis) gyro.pos = 0.5*Lshaft*A tip.pos = Lshaft*A tip.clear_trail()
def create_bodies(): ramp = vp.box(pos=vp.vector(0, -0.25, 0), length=5, width=2.5, height=0.5, alpha=vp.radians(15)) # Minus, because function (vp.rotate) rotate counter-clockwise ramp.rotate(angle=-ramp.alpha, axis=vp.vector(0, 0, 1)) roller = vp.cylinder(pos=vp.vector( -1 * math.cos(ramp.alpha) * 0.5 * ramp.length, math.sin(ramp.alpha) * 0.5 * ramp.length + 0.5, -1), axis=vp.vector(0, 0, 2), radius=0.5, texture=texture(), vel=vp.vector(0, 0, 0), ang_vel=0, mass=1, friction_coeff=0.15) slope_dir = vp.norm(vp.rotate(vp.vector(1, 0, 0), angle=-ramp.alpha)) arrow = vp.arrow(pos=vp.vector(0, 2, 1), axis=slope_dir) return roller, ramp, arrow
def moveView(self, event): camAxis = self.scene.camera.axis camDist = vp.mag(self.scene.center - self.scene.camera.pos) dtheta = self.sensitivity up = self.scene.up if event.key in ["up", "k", "K"]: self.scene.camera.pos -= up.norm() * dtheta * camDist return if event.key in ["down", "j", "J"]: self.scene.camera.pos += up.norm() * dtheta * camDist return if event.key in ["right", "l", "L"]: self.scene.camera.pos += vp.norm( up.cross(camAxis)) * dtheta * camDist return if event.key in ["left", "h", "H"]: self.scene.camera.pos -= vp.norm( up.cross(camAxis)) * dtheta * camDist return if event.key in [".", ">"]: # Get closer, by ratio ctr = self.scene.center self.scene.camera.pos = ctr - camAxis / (1 + dtheta) self.scene.camera.axis = ctr - self.scene.camera.pos return if event.key in [",", "<"]: # Get further ctr = self.scene.center self.scene.camera.pos = ctr - camAxis * (1 + dtheta) self.scene.camera.axis = ctr - self.scene.camera.pos return if event.key == "p": # pitch: Rotate camera around ctr-horiz axis self.scene.forward = vp.rotate(self.scene.forward, angle=dtheta, axis=vp.cross( self.scene.forward, self.scene.up)) return if event.key == "P": self.scene.forward = vp.rotate(self.scene.forward, angle=-dtheta, axis=vp.cross( self.scene.forward, self.scene.up)) return if event.key == "y": # yaw: Rotate camera around ctr - up axis. self.scene.forward = vp.rotate(self.scene.forward, angle=dtheta, axis=self.scene.up) return return if event.key == "Y": self.scene.forward = vp.rotate(self.scene.forward, angle=-dtheta, axis=self.scene.up) return if event.key == "r": # Roll, that is, change the 'up' vector self.scene.camera.rotate(angle=dtheta, axis=camAxis, origin=self.scene.camera.pos) return if event.key == "R": self.scene.camera.rotate(angle=-dtheta, axis=camAxis, origin=self.scene.camera.pos) return if event.key == "d": # Diameter scaling down for dbl in self.drawables_: dbl.diaScale *= 1.0 - self.sensitivity * 4 dbl.updateDiameter() return if event.key == "D": for dbl in self.drawables_: dbl.diaScale *= 1.0 + self.sensitivity * 4 dbl.updateDiameter() return if event.key == "s": # Scale down sleep time, make it faster. self.sleep *= 1 - self.sensitivity return if event.key == "S": # Scale up sleep time, make it slower. self.sleep *= 1 + self.sensitivity return if event.key == "a": # autoscale to fill view. self.doAutoscale() return if event.key == "g": self.hideAxis = not self.hideAxis # show/hide the axis here. if event.key == "t": # Turn on/off twisting/autorotate self.doRotation = not self.doRotation if event.key == "?": # Print out help for these commands self.printMoogulHelp()
def __handle_keyboard_inputs(self): """ Pans amount dependent on distance between camera and focus point. Closer = smaller pan amount A = move left (pan) D = move right (pan) W = move up (pan) S = move down (pan) <- = rotate left along camera axes (rotate) -> = rotate right along camera axes (rotate) ^ = rotate up along camera axes (rotate) V = rotate down along camera axes (rotate) Q = roll left (rotate) E = roll right (rotate) ctrl + LMB = rotate (Default Vpython) """ # If camera lock, just skip the function if self.__camera_lock: return # Constants pan_amount = 0.02 # units rot_amount = 1.0 # deg # Current settings cam_distance = self.scene.camera.axis.mag cam_pos = vector(self.scene.camera.pos) cam_focus = vector(self.scene.center) # Weird manipulation to get correct vector directions. (scene.camera.up always defaults to world up) cam_axis = (vector(self.scene.camera.axis)) # X cam_side_axis = self.scene.camera.up.cross(cam_axis) # Y cam_up = cam_axis.cross(cam_side_axis) # Z cam_up.mag = cam_axis.mag # Get a list of keys keys = keysdown() # Userpan uses ctrl, so skip this check to avoid changing camera pose while shift is held if 'shift' in keys: return ################################################################################################################ # PANNING # Check if the keys are pressed, update vectors as required # Changing camera position updates the scene center to follow same changes if 'w' in keys: cam_pos = cam_pos + cam_up * pan_amount if 's' in keys: cam_pos = cam_pos - cam_up * pan_amount if 'a' in keys: cam_pos = cam_pos + cam_side_axis * pan_amount if 'd' in keys: cam_pos = cam_pos - cam_side_axis * pan_amount # Update camera position before rotation (to keep pan and rotate separate) self.scene.camera.pos = cam_pos ################################################################################################################ # Camera Roll # If only one rotation key is pressed if 'q' in keys and 'e' not in keys: # Rotate camera up cam_up = cam_up.rotate(angle=-radians(rot_amount), axis=cam_axis) # Set magnitude as it went to inf cam_up.mag = cam_axis.mag # Set self.scene.up = cam_up # If only one rotation key is pressed if 'e' in keys and 'q' not in keys: # Rotate camera up cam_up = cam_up.rotate(angle=radians(rot_amount), axis=cam_axis) # Set magnitude as it went to inf cam_up.mag = cam_axis.mag # Set self.scene.up = cam_up ################################################################################################################ # CAMERA ROTATION d = cam_distance move_dist = sqrt(d ** 2 + d ** 2 - 2 * d * d * cos(radians(rot_amount))) # SAS Cosine # If only left not right key if 'left' in keys and 'right' not in keys: # Calculate distance to translate cam_pos = cam_pos + norm(cam_side_axis) * move_dist # Calculate new camera axis cam_axis = -(cam_pos - cam_focus) if 'right' in keys and 'left' not in keys: cam_pos = cam_pos - norm(cam_side_axis) * move_dist cam_axis = -(cam_pos - cam_focus) if 'up' in keys and 'down' not in keys: cam_pos = cam_pos + norm(cam_up) * move_dist cam_axis = -(cam_pos - cam_focus) if 'down' in keys and 'up' not in keys: cam_pos = cam_pos - norm(cam_up) * move_dist cam_axis = -(cam_pos - cam_focus) # Update camera position and axis self.scene.camera.pos = cam_pos self.scene.camera.axis = cam_axis
if __name__ == '__main__': portName = 'COM4' port = serial_connect(portName, 115200) vp.scene.range = 1.25 ax = vp.arrow(shaftwidth=0.1, color=vp.color.white) ay = vp.arrow(shaftwidth=0.1, color=vp.color.green) az = vp.arrow(shaftwidth=0.1, color=vp.color.cyan) ax.pos = vp.vector(0, 0, 0) ay.pos = vp.vector(0, 0, 0) az.pos = vp.vector(0, 0, 0) ax.axis = vp.vector(1, 0, 0) ay.axis = vp.vector(0, 0, -1) az.axis = vp.vector(0, 1, 0) while True: while port.inWaiting() == 0: pass read_string = port.readline().decode("utf-8") data_array = read_string.split(',') roll = (float(data_array[4]) * pi / 180) pitch = (float(data_array[5]) * pi / 180) yaw = (float(data_array[6]) * pi / 180) ax.axis = vp.norm(vp.vector(1, -pitch, yaw)) ay.axis = vp.norm(vp.vector(yaw, roll, -1)) az.axis = vp.norm(vp.vector(pitch, 1, roll)) # az.axis = vp.norm(vp.vector(pitch, -roll, 1))
t = 0 Nsteps = 20 # number of calculational steps between graphics updates while True: vp.rate(200) for step in range(Nsteps): # multiple calculation steps for accuracy # Calculate accelerations of the Lagrangian coordinates: atheta = vp.sin(theta)*vp.cos(theta)*phidot**2+(M*g*r*vp.sin(theta) - I3*(psidot+phidot*vp.cos(theta))*phidot*vp.sin(theta))/I1 aphi = (I3/I1)*(psidot+phidot*vp.cos(theta))*thetadot / \ vp.sin(theta)-2*vp.cos(theta)*thetadot*phidot/vp.sin(theta) apsi = phidot*thetadot*vp.sin(theta)-aphi*vp.cos(theta) # Update velocities of the Lagrangian coordinates: thetadot += atheta*dt phidot += aphi*dt psidot += apsi*dt # Update Lagrangian coordinates: theta += thetadot*dt phi += phidot*dt psi += psidot*dt gyro.axis = gyro.length * \ vp.vector(vp.sin(theta)*vp.sin(phi), vp.cos(theta), vp.sin(theta)*vp.cos(phi)) # Display approximate rotation of rotor and shaft: gyro.rotate(angle=psidot*dt*Nsteps) A = vp.norm(gyro.axis) gyro.pos = 0.5*Lshaft*A tip.pos = Lshaft*A t = t+dt*Nsteps
def start(self): """open the vpython window and start the simulation""" # set up important boolean values from options testing = self.options.testing central_centered = self.options.central_centered show_pointers = self.options.pointers # set up canvas, bodies and pointers scene = vp.canvas(title="Simulation zum Zweikörperproblem", height=self.options.canvas.height, width=self.options.canvas.width) central = Body(sim=self, name="central", mass=self.values.central.mass, velocity=vp.vector(self.values.central.velocity.x, self.values.central.velocity.y, self.values.central.velocity.z), radius=self.values.central.radius, make_trail=True, color=vp.vector(self.options.colors.bodies.x / 255, self.options.colors.bodies.y / 255, self.options.colors.bodies.z / 255)) sat = Body(sim=self, name="sat", mass=self.values.sat.mass, velocity=vp.vector(self.values.sat.velocity.x, self.values.sat.velocity.y, self.values.sat.velocity.z), pos=vp.vector( self.values.distance + self.values.central.radius + self.values.sat.radius, 0, 0), radius=self.values.sat.radius, make_trail=True, color=vp.vector(self.options.colors.bodies.x / 255, self.options.colors.bodies.y / 255, self.options.colors.bodies.z / 255)) central_ptr = vp.arrow() sat_ptr = vp.arrow() if show_pointers: central_ptr = vp.arrow(axis=vp.vector( 0, -((self.values.distance + central.radius + sat.radius) / 2), 0), color=vp.vector( self.options.colors.pointers.x, self.options.colors.pointers.y, self.options.colors.pointers.z)) central_ptr.pos = (central.pos - central_ptr.axis) + vp.vector( 0, self.values.central.radius, 0) sat_ptr = vp.arrow(axis=vp.vector( 0, -((self.values.distance + central.radius + sat.radius) / 2), 0), color=vp.vector(self.options.colors.pointers.x, self.options.colors.pointers.y, self.options.colors.pointers.z)) sat_ptr.pos = sat.pos - sat_ptr.axis + vp.vector( 0, self.values.sat.radius, 0) # set up buttons pause_sim = vp.button(text="Pause", bind=self.pause) vp.button(text="Stop", bind=lambda: os.kill(os.getpid(), signal.SIGINT)) vp.button(text="Restart", bind=self.restart) scene.append_to_caption("\n") # set up sliders for changing the radius of the two bodies central_slider = vp.slider( max=((self.values.distance + self.values.central.radius) / self.values.central.radius), min=1, value=1, top=12, bottom=12, bind=lambda: self.adjust_radius(slider=central_slider, sphere=central)) vp.button(text="Reset", bind=lambda: self.reset_slider(central_slider)) scene.append_to_caption("\n") sat_slider = vp.slider( max=((self.values.distance + self.values.sat.radius) / self.values.sat.radius), min=1, value=1, top=12, bottom=12, bind=lambda: self.adjust_radius(slider=sat_slider, sphere=sat)) vp.button(text="Reset", bind=lambda: self.reset_slider(sat_slider)) # set up time variables t = 0 t_max = self.options.rate * self.options.sim_time # main simulation loop if central_centered: scene.camera.follow(central) while t <= t_max: # weird behaviour of those two vp.rate(self.options.rate) # vp.sleep(1/self.options["update_rate"]) if pause_sim.text == "Pause": # physical calculations r = sat.pos - central.pos fv = (6.67430e-11 * central.mass * sat.mass) / (vp.mag(r)**2) central.force = fv * vp.norm(r) sat.force = fv * vp.norm(-r) central.calculate(self.options.delta_t) sat.calculate(self.options.delta_t) if show_pointers: # move pointers central_ptr.pos = central.pos - central_ptr.axis + \ vp.vector(0, central.radius, 0) sat_ptr.pos = sat.pos - sat_ptr.axis + vp.vector( 0, sat.radius, 0) if self.options.sim_time > 0: t += 1 if bool(self.options.restart): self.restart()
print(f'mag2(v1) = {vp.mag2(v1)} (Magnitude ** 2)') print(f'norm(v1) = {vp.norm(v1)} (Unit Vector in the direction of v1)') print(f' hat(v1) = {vp.hat(v1)} (Unit Vector in the direction of v1)') print() print(f' dot(v1, v2) = {vp.dot(v1, v2)}') print(f'cross(v1, v2) = {vp.cross(v1, v2)}') print() print( f'diff_angle(v1, v2) = {vp.diff_angle(v1, v2)} (Angle between v1 and v2 in radians)' ) print() print(f'proj(v1, v2) = {v1.proj(v2)} (Vector projection of v1 along v2)') print(f'comp(v1, v2) = {v1.comp(v2)} (Scalar projection of v1 onto v2)') print(f'v1.equals(v2) = {v1.equals(v2)}') # To normalise a vector i.e. set its length to 1: v3 = vp.vector(2, 3, 5) norm_v3 = vp.norm(v3) print(f'norm(v3) = {norm_v3}') print(f'Magnitude(norm(v3)) = {vp.mag(norm_v3)}') # Change the direction of v1 without changing its magnitude v2.hat = v1 # Changes the direction of v2 to that of v1 # but not its magnitude # Rotating a Vector # v2 = vp.rotate(v1, angle=a, axis=vp.vector(x, y, z)) # Angle in radians, The default axis is (0, 0, 1) for a rotation in the xy plane around the z axis # Equivalent v1.rotate(v1, angle=a, axis=vector(x, y, z))
def gravi_force(obj): difference = earth.pos - obj.pos acceleration = (const_g/(vp.mag(difference)**2))*vp.norm(difference) return acceleration
# Creating array to store all particles that have stopped moving. stationary = [] stationary.append(Particle(pos=vp.vector(0, 0, 0), radius=rad)) # Creating array to store all currently moving particles particles = [] for i in range(num_inbound): # Angle from positive x from which particle goes towards origin # angle = 2 * math.pi / num_inbound * i particles.append( Particle( radius=rad, # angle=angle, # pos=start_dist * vp.vector(math.cos(angle), math.sin(angle), 0)) pos=start_dist * vp.norm(vp.vector.random()))) max_steps = start_dist / centre_speed + 100 while True: # Making copy of particles array to avoid issues while iterating copy = particles # To ensure program remains fast as more particles are added if len(stationary) > 30: stationary = stationary[-20:] for particle in particles: particle.move() # If particle hasn't collided, then recreate it if particle.steps > max_steps: print("Particle took too long to collide.") copy.remove(particle)
theta_i = rand.choice(theta_part) p_0 = r_i * (vp.vector(np.cos(theta_i), np.sin(theta_i), 0)) part_i = vp.sphere(pos=p_0, radius=0.3, color=vp.vector(1, 0, 1)) #pa vels vel_part = np.linspace(cond_ini["v_min"], cond_ini["v_max"], 100) vel_i = vp.vector(rand.choice(vel_part), rand.choice(vel_part), 0) part_i.vel = vel_i part_i.carga = cond_ini["carga_libre"] part_i.momento = cond_ini["masa_libre"] * part_i.vel part_i.masa = cond_ini["masa_libre"] #guardamso en lista: Particulas_libres.append(part_i) #dinámica while t < t_max: vp.rate(100) for i in Particulas_libres: F_tot_i = vp.vector(0, 0, 0) for j in Particulas_libres: if i != j: mag = (ka * i.carga * j.carga) / (vp.mag(i.pos - j.pos)**2) r_un_j_i = vp.norm(i.pos - j.pos) F_tot_i += mag * r_un_j_i for k in Particulas_anillo: mag = (ka * i.carga * k.carga) / (vp.mag(i.pos - k.pos)**2) r_un_k_i = vp.norm(i.pos - k.pos) F_tot_i += mag * r_un_k_i #dinamica: i.momento = i.momento + dt * F_tot_i i.pos = i.pos + (dt / i.masa) * i.momento t += dt
def __init__(self, v, axis_label, axis_color, arrow_pos): # If we are drawing the axis-triad arrows # then set the vec_u 'unit vector' accordingley. # This ignores v in the argument list. if axis_label == 'x': self.vec_u = vec_i # Arrow.vec_i elif axis_label == 'y': self.vec_u = vec_j # Arrow.vec_j elif axis_label == 'z': self.vec_u = vec_k # Arrow.vec_k else: # Not drawing the axis triad # so just set tvec_u to the vector itself self.vec_u = v if axis_label in 'xyz': self.rod_radius = vp.mag(self.vec_u) * 0.01 self.cone_radius = vp.mag(self.vec_u) * 0.03 else: self.rod_radius = 0.02 self.cone_radius = 0.06 # Reduced Rod length = Rod length - Cone's axial length # Arrow length = Reduced Rod + Cone Axial Length # Use a fixed size of axis of the cone self.cone_axis = 0.1 * vp.norm(self.vec_u) # Reduce the size of the rod by the axial size of the cone self.rod = vp.cylinder(pos=arrow_pos, axis=self.vec_u - self.cone_axis, radius=self.rod_radius, color=axis_color) # Place the base of the cone at the end of rod # which has been reduced by the cone's axial length self.cone = vp.cone(pos=self.vec_u - self.cone_axis + arrow_pos, axis=self.cone_axis, radius=self.cone_radius, color=axis_color) # Note where the tip of the cone is, # which will define the starting point of # of the axis line self.cone_tip = self.vec_u + arrow_pos + 0.1 * vp.norm(self.vec_u) if axis_label in 'xyz': self.axis_text = vp.label(text=axis_label, pos=self.cone.pos + 0.1 * self.vec_u, color=axis_color, xoffset=3, yoffset=3, box=False) else: self.axis_text = vp.label(text=axis_label, pos=arrow_pos + 0.5 * self.vec_u, color=axis_color, xoffset=3, yoffset=3, box=False) self.axis_text.height = 0.6 * self.axis_text.height
def wind_force(): f = vp.norm( vp.vector(random.randrange(10, 25), 0, random.randrange( -15, 12))) * random.randrange(WIND_FACTOR) return f
def applyTropism(self, tropismVec, tropismStrength): correction = tropismStrength * cross(norm(self.heading), tropismVec) self.heading += self.heading.rotate(mag(correction), correction)
dx = vp.dot(rrel, vrel.hat) # rrel.mag*cos(theta) dy = vp.cross(rrel, vrel.hat).mag # rrel.mag*sin(theta) # alpha is the angle of the triangle composed of rrel, path of atom j, and a line # from the center of atom i to the center of atom j where atome j hits atom i: alpha = vp.asin(dy / (2 * Ratom)) # distance traveled into the atom from first contact d = (2 * Ratom) * vp.cos(alpha) - dx # time spent moving from first contact to position inside atom deltat = d / vrel.mag posi = posi - vi * deltat # back up to contact configuration posj = posj - vj * deltat mtot = 2 * mass pcmi = p[i] - ptot * mass / mtot # transform momenta to cm frame pcmj = p[j] - ptot * mass / mtot rrel = vp.norm(rrel) pcmi = pcmi - 2 * pcmi.dot(rrel) * rrel # bounce in cm frame pcmj = pcmj - 2 * pcmj.dot(rrel) * rrel p[i] = pcmi + ptot * mass / mtot # transform momenta back to lab frame p[j] = pcmj + ptot * mass / mtot apos[i] = posi + (p[i] / mass) * deltat # move forward deltat in time apos[j] = posj + (p[j] / mass) * deltat interchange(vi.mag, p[i].mag / mass) interchange(vj.mag, p[j].mag / mass) for i in range(Natoms): loc = apos[i] if abs(loc.x) > L / 2: if loc.x < 0: p[i].x = abs(p[i].x) else: