class TestAim(RotateAndAim): def _get_starting_status(self): return BotStatus.AIMING def _get_starting_vector(self): return self.initial_turret def _get_pre_update_status(self): return BotStatus.AIMING def _get_actual_vector(self): return self.armour.turret def _continue(self, target_vector): angle = self.armour.turret.angle(target_vector) return angle != pytest.approx(0.0) def _act(self, angle): self.armour.aim(angle) @pytest.mark.parametrize("angle, target_vector", ( pytest.param(90, Vector2(0, -1), id="anti-clockwise"), pytest.param(-90, Vector2(0, 1), id="clockwise"), )) def test_aim(self, angle, target_vector): self._test("", angle, target_vector)
def resolve_collisions(self): """If hero touches, hero eats. Also reward gets updated.""" hero_radius = self.hero.radius to_remove = [] to_bounce = [] self.just_got_hit = False for obj in self.objects: collision_distance = hero_radius + obj.radius if self.squared_distance(self.hero.position, obj.position) <= (collision_distance ** 2): if obj.obj_type == 'gems': to_remove.append(obj) else: to_bounce.append(obj) for obj in to_remove: self.objects.remove(obj) self.objects_eaten[obj.obj_type] += 1 self.object_reward += self.settings["object_reward"][obj.obj_type] for obj in to_bounce: self.just_got_hit = True self.objects_eaten[obj.obj_type] += 1 self.object_reward += self.settings["object_reward"][obj.obj_type] * obj.radius # Do elastic collisions to work out new velocities of object and hero # Mass is assumed to be radius^2 (discs as opposed to spheres) # Elastic collision, so both energy and momentum is conserved dx = self.hero.position[0] - obj.position[0] dy = self.hero.position[1] - obj.position[1] angle = math.atan2(dy, dx) + 0.5 * math.pi h_mass = hero_radius ** 2 o_mass = obj.radius ** 2 h_x_vel = self.hero.speed[0] h_y_vel = self.hero.speed[1] o_x_vel = obj.speed[0] o_y_vel = obj.speed[1] h_angle = np.arctan2(h_y_vel, h_x_vel) o_angle = np.arctan2(o_y_vel, o_x_vel) h_speed = ((h_x_vel ** 2) + (h_y_vel ** 2)) ** 0.5 o_speed = ((o_x_vel ** 2) + (o_y_vel ** 2)) ** 0.5 total_mass = h_mass + o_mass vector1_h = np.array([h_angle, h_speed * (h_mass - o_mass) / total_mass]) vector2_h = np.array([angle, 2 * o_speed * o_mass / total_mass]) (h_angle, h_speed) = tuple(vector1_h + vector2_h) # Add the two vectors above vector1_o = np.array([o_angle, o_speed * (o_mass - h_mass) / total_mass]) vector2_o = np.array([angle + math.pi, 2 * h_speed * h_mass / total_mass]) (o_angle, o_speed) = tuple(vector1_o + vector2_o) # Add the two vectors above h_xv = h_speed * np.cos(h_angle) h_yv = h_speed * np.sin(h_angle) o_xv = o_speed * np.cos(o_angle) o_yv = o_speed * np.sin(o_angle) self.hero.speed = Vector2(float(h_xv), float(h_yv)) obj.speed = Vector2(float(o_xv), float(o_yv)) collision_distance = hero_radius + obj.radius x_displ = -np.sin(angle) y_displ = np.cos(angle) while self.squared_distance(self.hero.position, obj.position) <= (collision_distance ** 2): obj.translate(float(x_displ), float(y_displ))
def __init__(self, position, max_speed=0, angle=None, radius=None): """ Create a new omni-wheel object, specifying the position and either a direction vector directly or the angle in degrees clockwise from the position Y axis along with the radius of the wheel. :param euclid.Point2 position: The wheel's contact point with the surface, specified relative to the centre of the chassis. Units are millimetres. :param float max_speed: The maximum number of revolutions per second allowed for this wheel. When calculating the wheel speeds required for a given trajectory this value is used to scale back all motion if any wheel would have to move at an impossible speed. If not specified this defaults to None, indicating that no speed limit should be placed on this wheel. :param angle: The angle, specified in radians from the positive Y axis where positive values are clockwise from this axis when viewed from above, of the direction of travel of the wheel when driven with a positive speed. If this value is specified then radius must also be specified and dx,dy left as None. :param radius: The radius in millimetres of the wheel, measuring from the centre to the contact point with the surface, this may be hard to determine for some wheels based on their geometry, particularly for wheels with cylindrical rollers, as the radius will vary. For these cases it may be worth directly measuring the circumference of the entire assembly and calculating radius rather than measuring directly. This is used to determine the magnitude of the direction vector. If this is not None then the angle must also be specified, and dx,dy left as None. """ circumference = 2 * pi * radius self.vector = Vector2( sin(angle) * circumference, cos(angle) * circumference) super(OmniWheel, self).__init__(location=position, drive_vector=Vector2( sin(angle) * circumference, cos(angle) * circumference), maximum_rotations_per_second=max_speed, allows_slip=True)
def test(): chassis = HoloChassis(wheels=[ HoloChassis.OmniWheel(position=Point2(1, 0), angle=0, radius=60), HoloChassis.OmniWheel(position=Point2(-1, 0), angle=0, radius=60) ]) print chassis.get_wheel_speeds( Motion(translation=Vector2(0, 0), rotation=0.5)) print chassis.get_wheel_speeds(Motion(translation=Vector2(0, 0), rotation=0.5), origin=Point2(1, 0))
def __init__(self): self._tans = (smalltri(), smalltri(), square(), parallelogram(), bigtri(), bigtri()) for i in range(4): self._tans[i]._translate(Vector2(1 + 2 * i, 0)) for i in range(4, 7): self._tans[i]._translate(Vector2(1 + 3 * i, 2)) self._selected = None
def main(): colors = [BLACK, RED, GREEN, BLUE] speed = 100 # pixels per second number_of_circles = 10 gravity = Vector2(0, 50) # pixels per second per second # Initialize the display window screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) # Create a list of circles with random positions, radii, and velocities circles = [] for i in range(number_of_circles): radius = random.randint(10, 20) x = random.randint(radius, SCREEN_WIDTH - radius) y = random.randint(radius, SCREEN_HEIGHT - radius) position = Vector2(x, y) velocity = get_random_velocity(speed) color = random.choice(colors) circles.append(Circle(position, velocity, gravity, radius, color)) ### GAME LOOP ### clock = pygame.time.Clock() running = True while running: # break out of game loop if the window is closed for event in pygame.event.get(): if event.type == QUIT: running = False # get the time elapsed dt_ms = clock.tick(FPS_LIMIT) # milliseconds dt = dt_ms / 1000 # convert to seconds # make the background white screen.fill(WHITE) # move the circles and draw them for i, circle in enumerate(circles): circle.move(dt) # detect collisions and handle them for other_circle in circles[i + 1:]: if circle.surface_distance(other_circle, dt) <= 0: circle.collide(other_circle) for circle in circles: circle.draw(screen) # refresh the display window pygame.display.flip() pygame.quit()
def update_observation_lines(self): observable_distance = self.settings["observable_distance"] relevant_objects = [ obj for obj in self.objects if obj.position.distance(self.hero.position) < observable_distance and obj is not self.hero ] # objects sorted from closest to furthest relevant_objects.sort( key=lambda x: x.position.distance(self.hero.position)) for observation_line in self.observation_lines: # shift to hero position l = LineSegment2( self.hero.position + Vector2(*observation_line.p1), self.hero.position + Vector2(*observation_line.p2)) start_l, end_l = l.p1, l.p2 observed_object = None # if end of observation line is outside of walls, we see the wall. if end_l.x < 0.0 or end_l.x > 1.0 or end_l.y < 0.0 or end_l.y > 1.0: observed_object = "wall" for obj in relevant_objects: if l.distance(obj.position) < obj.radius: observed_object = obj break intersection = end_l if observed_object == "wall": # wall seen # best candidate is intersection between # l and a wall, that's # closest to the hero for wall in self.walls: candidate = l.intersect(wall) if candidate is not None: if (intersection is None or intersection.distance(self.hero.position) > candidate.distance(self.hero.position)): intersection = candidate elif observed_object is not None: # agent seen intersection_segment = obj.as_circle().intersect(l) if intersection_segment is not None: if (intersection_segment.p1.distance(self.hero.position) < intersection_segment.p2.distance( self.hero.position)): intersection = intersection_segment.pl else: intersection = intersection_segment.p2 self.line_end_where[observation_line] = intersection self.line_end_who[observation_line] = observed_object
def wall_collision_soon(a, dt): cur = a.position nex = a.position + a.speed * dt r = a.radius if cur.x < r or nex.x < r: return Vector2(-1, 0) if cur.x > 1 - r or nex.x > 1 - r: return Vector2(1, 0) if cur.y < r or nex.y < r: return Vector2(0, -1) if cur.y > 1 - r or nex.y > 1 - r: return Vector2(0, 1) return None
def __init__(self, settings): """Initiallize game simulator with settings""" self.settings = settings self.size = self.settings["world_size"] # make 4 walls self.walls = [ LineSegment2(Point2(0, 0), Point2(0, self.size[1])), LineSegment2(Point2(0, self.size[1]), Point2(self.size[0], self.size[1])), LineSegment2(Point2(self.size[0], self.size[1]), Point2(self.size[0], 0)), LineSegment2(Point2(self.size[0], 0), Point2(0, 0)) ] # make hero object self.hero = GameObject(Point2(*self.settings["hero_initial_position"]), Vector2(*self.settings["hero_initial_speed"]), "hero", self.settings) if not self.settings["hero_bounces_off_walls"]: self.hero.bounciness = 0.0 # now hero has no bounciness self.objects = [] # spawn 25 friends, 25 enemy for obj_type, number in settings["num_objects"].items(): # 2 times run for _ in range(number): self.spawn_object(obj_type) # generate 32 number of antennas self.observation_lines = self.generate_observation_lines() self.object_reward = 0 self.collected_rewards = [] # every observation_line sees one of objects or wall and # two numbers representing speed of the object (if applicable) self.eye_observation_size = len( self.settings["objects"] ) + 3 # 2+3 --> maybe [friend, enemy, wall, speed X, Y] # additionally there are two numbers representing agents own speed. self.observation_size = self.eye_observation_size * len( self.observation_lines ) + 2 # (5 * 32) + 2 ==> 2 is hero's own speed self.directions = [ Vector2(*d) for d in [[1, 0], [0, 1], [-1, 0], [0, -1]] ] # there are 4 directions. up down left right self.num_actions = len(self.directions) # so num_actions is 4 self.objects_eaten = defaultdict(lambda: 0)
def drive_at(self, x, y, speed, turn_speed=pi, min_distance=10, on_approach=None): """ Set and return a motion to get to a target specified relative to the robot's coordinate system. Note that the 'front' is added when the motion is applied to the robot, so this implicitly is relative to that, with positive y axis in the direction of the robot's front. :param x: X coordinate of the target in mm :param y: Y coordinate of the target in mm :param speed: Desired linear speed :param turn_speed: If a motion can't be calculated then turn on the spot instead, at turn_speed radians per second :param min_distance: If defined, and the target is closer, then stop :param on_approach: If defined, and min_distance is defined and satisfied, call this function when we hit min_distance :return: The :class:`approxeng.holochassis.chassis.Motion` that was applied. """ # Explicitly cast to floats in case we're not... x = float(x) y = float(y) speed = float(speed) turn_speed = float(turn_speed) min_distance = float(min_distance) if min_distance is not None and sqrt(x * x + y * y) < min_distance: motion = Motion(translation=Vector2(0, 0), rotation=0) if on_approach is not None: on_approach() else: if x == 0: # Straight ahead, avoid future division by zero! motion = Motion(translation=Vector2(0, speed), rotation=0) elif abs(y) < abs(x) or y <= 0: # Turn first without moving if x > 0: motion = Motion(translation=Vector2(0, 0), rotation=turn_speed) else: motion = Motion(translation=Vector2(0, 0), rotation=-turn_speed) else: radius = y * y / x # Angle is clockwise rotation angle = asin(x / y) arc_length = angle * radius print(x, y, angle, arc_length) motion = Motion(translation=Vector2(0, speed), rotation=angle * speed / arc_length) self.set_motion(motion) return motion
def align_to_robot(robot, corners, sodas, milks): robfront, robback = robot # calculate angle to rotate by robvec = Vector2(*robfront) - Vector2(*robback) theta = np.arctan2(robvec.y, robvec.x) - math.radians(90) theta = -theta # find upper-right and lower-left corners ca = corners[0] cb = corners[1] ur = max(ca[0], cb[0]), max(ca[1], cb[1]) ll = min(ca[0], cb[0]), min(ca[1], cb[1]) corners = [ur, ll] # rotate everything for i, soda in enumerate(sodas): v = Vector2(*soda) - Vector2(*robback) vrot = Vector3(v.x, v.y, 0).rotate_around(Vector3(0, 0, 1), theta) sodas[i] = vrot.x, vrot.y for i, milk in enumerate(milks): v = Vector2(*milk) - Vector2(*robback) vrot = Vector3(v.x, v.y, 0).rotate_around(Vector3(0, 0, 1), theta) milks[i] = vrot.x, vrot.y for i, corner in enumerate(corners): v = Vector2(*corner) - Vector2(*robback) vrot = Vector3(v.x, v.y, 0).rotate_around(Vector3(0, 0, 1), theta) corners[i] = vrot.x, vrot.y return corners, sodas, milks
def __init__(self, settings, gpsFile): """Initiallize game simulator with settings""" self.settings = settings self.GPS = gpsFile self.timeStep = 0 self.previousOffset = 0 self.deltaT = 0 self.size = self.settings["world_size"] self.walls = [ LineSegment2(Point2(0, 0), Point2(0, self.size[1])), LineSegment2(Point2(0, self.size[1]), Point2(self.size[0], self.size[1])), LineSegment2(Point2(self.size[0], self.size[1]), Point2(self.size[0], 0)), LineSegment2(Point2(self.size[0], 0), Point2(0, 0)) ] self.hero = GameObject(Point2(*self.settings["hero_initial_position"]), Vector2(*self.settings["hero_initial_speed"]), "hero", self.settings, 0) if not self.settings["hero_bounces_off_walls"]: self.hero.bounciness = 0.0 self.objects = [] count = 0 for obj_type, number in settings["num_objects"].items(): count = count + 2 for _ in range(number): self.spawn_object(obj_type, count) self.observation_lines = self.generate_observation_lines() self.object_reward = 0 self.collected_rewards = [] # every observation_line sees one of objects or wall and # two numbers representing speed of the object (if applicable) self.eye_observation_size = len(self.settings["objects"]) + 3 # additionally there are two numbers representing agents own speed and position. self.observation_size = self.eye_observation_size * len( self.observation_lines) + 2 + 2 self.directions = [ Vector2(*d) for d in [[1, 0], [0, 1], [-1, 0], [0, -1], [0.0, 0.0]] ] self.num_actions = len(self.directions) self.objects_eaten = defaultdict(lambda: 0)
def __init__(self, settings, record=False, seed=None): self.record = record self.fps = settings["fps"] self.frames_between_actions = settings["frames_between_actions"] self.max_frames = settings["max_frames"] self.obj_radius = settings["obj_radius"] if seed is not None: raise NotImplemented() self.sim = HeroSimulator(settings) self.num_frames = 1 self.actions = [Vector2(*a) for a in settings["action_acc"]] for obj_type, num_items in settings["num_objects"].items(): for _ in range(num_items): self.spawn_object(obj_type) self.rewards = settings["object_reward"] if self.record: self.recording = [self.sim.to_svg()] self.sim.collision_observer = lambda x, y: self.handle_collision(x, y) self.partial_reward = 0.0 self.metrics = {'score': 0.}
def get_position(self): """ Get the current location of the mouse cursor. @return: Mouse cursor location @rtype: list """ return Vector2(*pygame.mouse.get_pos())
def calculate_motion(self, speeds): """ Invert the motion to speed calculation to obtain the actual linear and angular velocity of the chassis given a vector of wheel speeds. If there are exactly three speeds (and therefore the chassis has three wheels), an optimised version of the algorithm is used which solves explicitly, otherwise if numpy is available it's used to calculate a unique solution. The three-wheel explicit algorithm is approximately 5 times faster than numpy's linear solver See http://docs.scipy.org/doc/numpy-1.10.1/reference/generated/numpy.linalg.solve.html for more details on the linear solver If this function is called on a list of speeds of length other than 3 and numpy is not available an empty Motion will be returned :param speeds: An array of wheel speeds, expressed as floats with units of radians per second, positive being towards the wheel vector. :return: A :class:`approxeng.chassis.Motion` object containing the calculated translation and rotation in the robot's coordinate space. """ if len(speeds) == 3: return self._calculate_motion_simple(speeds) elif NUMPY_AVAILABLE: return self._calculate_motion_numpy(speeds) else: return Motion(translation=Vector2(x=0, y=0), rotation=0)
def resolve_wall_colision(obj, direction, cor=1.0): wall_obj = GameObject(obj.position + direction * obj.radius, Vector2(0.0, 0.0), "wall", 0.0, mass=1000000.0) obj.speed = speed_after_collision(obj, wall_obj, cor=cor)
def makeBkObj(self): return BackGroundSprite().initialize(dict( objtype="background", movevector=Vector2.rect(100.0, -math.pi), drawfillfn=BackGroundSprite.DrawFill_Both, memorydc=g_rcs.File2OPedMDCList("background.gif", )[0] ))
def _scan_catches_tank_off_center(self): base_vector = Vector2(1., 1.) angle = 10 theta = math.pi / 180. * angle position_vector = base_vector.rotate(theta) * 100 position = self.ray.p + position_vector return position, angle, theta, "_assert_found_tank"
def get_regular_triangular_chassis(wheel_distance, wheel_radius, max_rotations_per_second): """ Build a HoloChassis object with three wheels, each identical in size and maximum speed. Each wheel is positioned at the corner of a regular triangle, and with direction perpendicular to the normal vector at that corner. :param wheel_distance: Distance in millimetres between the contact points of each pair of wheels (i.e. the length of each edge of the regular triangle) :param wheel_radius: Wheel radius in millimetres :param max_rotations_per_second: Maximum wheel speed in revolutions per second :return: An appropriately configured HoloChassis """ point = Point2(0, cos(radians(30)) * wheel_distance / 2.0) vector = Vector2(-2 * pi * wheel_radius, 0) # Pink wheel_a = HoloChassis.OmniWheel(position=point, vector=vector, max_speed=max_rotations_per_second) # Yellow wheel_b = HoloChassis.OmniWheel(position=rotate_point(point, pi * 2 / 3), vector=rotate_vector(vector, pi * 2 / 3), max_speed=max_rotations_per_second) # Green wheel_c = HoloChassis.OmniWheel(position=rotate_point(point, pi * 4 / 3), vector=rotate_vector(vector, pi * 4 / 3), max_speed=max_rotations_per_second) return HoloChassis(wheels=[wheel_a, wheel_b, wheel_c])
def in_plane(vec3): """Convert from 3D vectors in space to 2D vectors on the plane. This should be easier for AI strategy calculations. """ return Vector2(vec3.x, vec3.z)
def main(): speed = 70 #pixels per second number_of_circles = 150 add_color(number_of_circles) gravity = Vector2(0, 20) #pixel per seconds per seconds #initialize the display the windows screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) circles = [] for i in range(number_of_circles): radius = rand() x = random.randint(radius, SCREEN_WIDTH - radius) y = random.randint(radius, SCREEN_HEIGHT - radius) position = Vector2(x,y) velocity = get_random_velocity(speed) color = random.choice(colors) circles.append( Circle(position, velocity, gravity, radius, color)) ### GAME LOOP### clock = pygame.time.Clock() running = True while running: #break out of game loop if the window is closed for event in pygame.event.get(): if event.type == QUIT: running = False # get the time elpasef dt_ms = clock.tick(FPS_LIMIT) #miliseconds dt = dt_ms / 1000 # convert to seconds # make the background is white screen.fill(GOLD) #move the circles and draw them for i, circle in enumerate(circles): circle.move(dt) for other_circle in circles[i+1:]: if circle.surface_distance(other_circle,dt) <= 0: circle.collide(other_circle) for circle in circles: circle.move(dt) circle.draw(screen) pygame.display.flip()
def __init__(self, translation=None, x=None, y=None, rotation=0): """ Constructor :param euclid.Vector2 translation: Vector2 representing the translation component in robot coordinate space of the motion. Defaults to Vector2(0,0) :param float rotation: Rotation in radians per second. Defaults to 0. """ if translation is not None: self.translation = translation elif x is not None and y is not None: self.translation = Vector2(x, y) else: self.translation = Vector2(0, 0) self.rotation = rotation
def spawn_object(self, obj_type): speed = Vector2(random.gauss(0., 0.2), random.gauss(0., 0.2)) self.sim.add(GameObject(Point2(0., 0.), speed, obj_type, radius=self.obj_radius), ensure_noncolliding=True, randomize_position=True)
def __init__(self, name='SimulationThread', update_delay=0.01): super(Simulation, self).__init__(name=name) self.daemon = True self.motion = Motion(Vector2(0, 0), 0) self.pose = Pose(Point2(0, 0), 0) self.last_update_time = time() self.update_delay = update_delay self.after_simulation_callback = None self.running = True
def spawn_object(self, obj_type): """Spawn object of a given type and add it to the objects array""" radius = self.settings["object_radius"] position = np.random.uniform([radius, radius], np.array(self.size) - radius) position = Point2(float(position[0]), float(position[1])) max_speed = np.array(self.settings["maximum_speed"]) speed = np.random.uniform(-max_speed, max_speed).astype(float) speed = Vector2(float(speed[0]), float(speed[1])) self.objects.append(GameObject(position, speed, obj_type, self.settings))
def _bounce(self): left_margin = self.radius right_margin = SCREEN_WIDTH - self.radius top_margin = self.radius bottom_margin = SCREEN_HEIGHT - self.radius if self.position.x <= left_margin: self.position.x = 2 * left_margin - self.position.x self.velocity = self.velocity.reflect(Vector2(1,0)) elif self.position.x >= right_margin: self.position.x = 2 * right_margin - self.position.x self.velocity = self.velocity.reflect(Vector2(1,0)) if self.position.y <= top_margin: self.position.y = 2 * top_margin - self.position.y self.velocity = self.velocity.reflect(Vector2(0,1)) elif self.position.y >= bottom_margin: self.position.y = 2 * bottom_margin - self.position.y self.velocity = self.velocity.reflect(Vector2(0,1))
def relate_pages_to_machines(): """ Unpack the values from the settings file, create instances of Rectangle for the extents of both PAGES and MACHINES. :return: """ for k in settings.PAGES: v = settings.PAGES[k] v['extent'] = Rectangle(Vector2(v['width'], v['height']), Vector2(v['x'], v['y'])) v['name'] = k # unpack the machine details for v in settings.MACHINES: v['spec']['extent'] = Rectangle( Vector2(v['spec']['width'], v['spec']['height']), Vector2(0, 0)) return settings
def step(self, dt): """Simulate all the objects for a given amount of time. Also resolve collisions with the hero""" if self.settings["keep_hero_in_middle"]: for obj in self.objects: obj.step(dt, self.hero.speed) else: for obj in self.objects + [self.hero]: obj.step(dt, Vector2(0.0, 0.0)) self.resolve_collisions()
def update(self, dt): # handle input and move the car self.angle += (keyboard[key.LEFT] - keyboard[key.RIGHT]) * 150 * dt speed = self.properties.get('speed', 0) speed += (keyboard[key.UP] - keyboard[key.DOWN]) * 75 if speed > 300: speed = 300 if speed < -150: speed = -150 self.properties['speed'] = speed r = Matrix3.new_rotate(math.radians(self.get_angle())) v = dt * speed * (r * Vector2(0, 1)) self.x += v.x self.y += v.y
class Rectangle(): size = Vector2() position = Vector2() def __init__(self, size=Vector2(100, 100), position=Vector2(0, 0)): self.size = size self.position = position def contains(self, p): return self.position.x < p.x < (self.size.x + self.position.x) \ and self.position.y < p.y < (self.size.y + self.position.y) def __str__(self): return u"Rectangle size %s,%s and pos %s,%s" % (self.size.x, self.size.y, self.position.x, self.position.y) def height_to_width(self): if self.size.x != 0.0: return self.size.y / self.size.x else: return 1.0
def __init__(self, position, max_speed=0, angle=None, radius=None, vector=None): """ Create a new omni-wheel object, specifying the position and either a direction vector directly or the angle in degrees clockwise from the position Y axis along with the radius of the wheel. :param euclid.Point2 position: The wheel's contact point with the surface, specified relative to the centre of the chassis. Units are millimetres. :param float max_speed: The maximum number of revolutions per second allowed for this wheel. When calculating the wheel speeds required for a given trajectory this value is used to scale back all motion if any wheel would have to move at an impossible speed. If not specified this defaults to None, indicating that no speed limit should be placed on this wheel. :param angle: The angle, specified in radians from the positive Y axis where positive values are clockwise from this axis when viewed from above, of the direction of travel of the wheel when driven with a positive speed. If this value is specified then radius must also be specified and dx,dy left as None. :param radius: The radius in millimetres of the wheel, measuring from the centre to the contact point with the surface, this may be hard to determine for some wheels based on their geometry, particularly for wheels with cylindrical rollers, as the radius will vary. For these cases it may be worth directly measuring the circumference of the entire assembly and calculating radius rather than measuring directly. This is used to determine the magnitude of the direction vector. If this is not None then the angle must also be specified, and dx,dy left as None. :param euclid.Vector2 vector: 2 dimensional vector defining the translation of the wheel's contact point after a full revolution of the wheel. """ self.position = position self.max_speed = max_speed if angle is None and radius is None and vector is not None: # Specify wheel based on direct vector """ self.vector = vector elif angle is not None and radius is not None and vector is None: # Specify based on angle from positive Y axis and radius """ circumference = 2 * pi * radius self.vector = Vector2( sin(angle) * circumference, cos(angle) * circumference) else: raise ValueError( 'Must specify exactly one of angle and radius or translation vector' ) self.vector_magnitude_squared = self.vector.magnitude_squared() self.co_x = self.vector.x / self.vector_magnitude_squared self.co_y = self.vector.y / self.vector_magnitude_squared self.co_theta = (self.vector.x * self.position.y - self.vector.y * self.position.x) / self.vector_magnitude_squared
def makeFrontObj(self): o = ForegroundSprite().initialize( dict( objtype="cloud", pos=Vector2(random.random(), random.random()), movevector=Vector2.rect(0.01, math.pi), movelimit=0.1, group=self.dispgroup["frontgroup"], movefnargs={"accelvector": Vector2(1, 0)}, shapefnargs={}, ) ) o.initResource([random.choice(g_rcs.File2OPedMDCList("Clouds.png", slicearg=(1, 4, None, True)))]) return o