def scattered_obstacles(numobs, radius, sc_size): """Convenience function for randomly-scattered obstacles and their sprites. Args: numobs (positive int): Number of obstacles to generate. radius (positive float): Obstacle radius. obs_image (pygame.Surface): Obstacle image (shared among instances). sc_size (int, int): Width, height of the display area, in pixels. Returns: (list, list): First list contains the SimpleObstacle2d's; 2nd their sprites. Notes: By default, this uses the same sprite class as BasePointMass2d. """ sc_width, sc_height = sc_size yoffset = sc_height // (numobs + 2) yvals = list(range(yoffset, sc_height - yoffset, yoffset)) shuffle(yvals) obslist = list() for i in range(numobs): offset = (i + 1) / (numobs + 1) rany = yvals[i] new_pos = Point2d(offset * sc_width, rany) obs_image = obstacle_bumper(radius) obstacle = SimpleObstacle2d(new_pos, Point2d(0, 0), radius, obs_image) obslist.append(obstacle) obs_sprites = [obs.sprite for obs in obslist] return obslist, obs_sprites
def __init__(self, position, velocity, radius, mass, maxspeed, maxforce, spritedata=None): # Non-constant Point2d's need to be copied self.pos = copy.copy(position) self.vel = copy.copy(velocity) self.accumulated_force = Point2d(0, 0) # Additional values; these should be mostly constant self.radius = float(radius) self.mass = float(mass) self.maxspeed = float(maxspeed) self.maxforce = float(maxforce) # Normalized front vector in world coordinates; aligned with velocity. try: self.front = velocity.unit() except ZeroDivisionError: # If velocity is <0,0>, set facing to positive x-axis self.front = Point2d(1.0, 0.0) self.left = Point2d(-self.front[1], self.front[0]) # Optional sprite data if self.__class__._spriteclass and spritedata is not None: self.sprite = self.__class__._spriteclass(self, spritedata)
def test_vector_equals_magnitude_times_direction(self, x, y): vtest = Point2d(x, y) if vtest == Point2d(0, 0): self.assertRaises(ZeroDivisionError) elif isnan(x * x + y * y) or isinf(x * x + y * y): self.assertRaises(OverflowError) else: vscaled = vtest.norm() * vtest.unit() self.assertAlmostEqual(vtest.x, vscaled.x) self.assertAlmostEqual(vtest.y, vscaled.y)
def test_cauchy_schwarz_inequality(self, a, b, c, d): if isnan(a * a + b * b) or isinf(a * a + b * b): self.assertRaises(OverflowError) elif isnan(c * c + d * d) or isinf(c * c + d * d): self.assertRaises(OverflowError) else: vtest = Point2d(a, b) wtest = Point2d(c, d) if isnan(vtest * wtest) or isnan(vtest.norm() * wtest.norm()): self.assertRaises(OverflowError) else: self.assertLessEqual(abs(vtest * wtest), vtest.norm() * wtest.norm())
def move(self, delta_t=1.0, force_vector=None): """Updates rectilinear position, velocity, and acceleration. Args: delta_t (float): Time increment since last move. force_vector (Point2d, optional): Vector force to apply during this update. If force_vector is None (the default), we use the force accumulated by self.accumulate_force() since the last call to this method, and zero out the accumulated force. Otherwise, apply the force_vector given, and leave the accumulated force unaffected. In any case, the maximum force and resulting velocity are limited by maxforce and maxspeed attributes. """ # Update position using current velocity self.pos = self.pos + delta_t * self.vel # If no force_vector was given, use self-accumulated force. if force_vector is None: force_vector = copy.copy(self.accumulated_force) self.accumulated_force.zero() # Don't exceed our maximum force; compute acceleration force_vector.truncate(self.maxforce) accel = (delta_t / self.mass) * force_vector # Compute new velocity, but don't exceed maximum speed. self.vel = self.vel + accel self.vel.truncate(self.maxspeed) # Align heading to match our forward velocity. Note that # if velocity is very small, skip this to avoid jittering. if self.vel.sqnorm() > SPEED_EPSILON_SQ: self.front = self.vel.unit() self.left = Point2d(-self.front.y, self.front.x)
def test_rotations_are_orthogonal(self, x, y): from math import pi vtest = Point2d(x, y) vperp = vtest.rotated_by(pi / 2) vperp2 = vtest.rotated_by(-pi / 2) self.assertAlmostEqual(vtest * vperp, 0.0) self.assertAlmostEqual(vtest * vperp2, 0.0)
def test_length_of_unit_vectors(self, x, y): assume(x * x + y * y != 0) if isnan(x * x + y * y) or isinf(x * x + y * y): self.assertRaises(OverflowError) else: vtest = Point2d(x, y) u = vtest.unit() vtest.normalize() self.assertAlmostEqual(u.norm(), 1.0) self.assertAlmostEqual(vtest.norm(), 1.0)
def __init__(self, center, length, thick, f_normal, spritedata=None): # Positional data self.pos = Point2d(center[0], center[1]) self.front = f_normal.unit() self.left = self.front.left_normal() self.rsq = (length / 2)**2 # Structural data self.length = length self.thick = thick # Wall sprite if self.__class__._spriteclass and spritedata is not None: self.sprite = self.__class__._spriteclass(self, *spritedata)
def boundary_walls(sc_size, thick=10): """Convenience function to generate walls/sprites near the screen border. Args: sc_size (int, int): Width, height of the display area, in pixels. thick (int, optional): Thickness of walls, in pixels. Returns: (list, list): List of BaseWall2d, List of BaseWall2dSprite. """ sc_width, sc_height = sc_size wall_list = [ BaseWall2d((sc_width // 2, thick), sc_width, thick, Point2d(0, 1), WALL_DATA), BaseWall2d((sc_width // 2, sc_height - thick), sc_width, thick, Point2d(0, -1), WALL_DATA), BaseWall2d((thick, sc_height // 2), sc_height, thick, Point2d(1, 0), WALL_DATA), BaseWall2d((sc_width - thick, sc_height // 2), sc_height, thick, Point2d(-1, 0), WALL_DATA) ] wall_sprites = [wall.sprite for wall in wall_list] return wall_list, wall_sprites
def test_xy_coords(self, x, y): vtest = Point2d(x, y) self.assertEqual(vtest.x, x) self.assertEqual(vtest.y, y)
def test_negation_subtraction_inverses(self, x, y): from aiboids.point2d import ZERO_VECTOR vtest = Point2d(x, y) self.assertEqual(vtest + (-vtest), ZERO_VECTOR) self.assertEqual(vtest - vtest, ZERO_VECTOR)
def test_coordinate_addition(self, a, b, c, d): vtest = Point2d(a, b) + Point2d(c, d) self.assertEqual(vtest, Point2d(a + c, b + d))
sys.path.append(mypar) from aiboids.point2d import Point2d from aiboids.vehicle2d import SimpleVehicle2d from aiboids import pgrender if __name__ == "__main__": # Display set-up SCREEN_SIZE = (1024, 768) SCREEN, BGCOLOR = pgrender.setup(SCREEN_SIZE, 'FLOCKING/EVADE steering demo.') UPDATE_SPEED = 0.5 BORDER = 30 # Used to generate random positions for vehicles randpoint = lambda: Point2d(randint(BORDER, SCREEN_SIZE[0] - BORDER), randint(BORDER, SCREEN_SIZE[1] - BORDER)) # Vehicles (sheep/dog) and obstacle information NUMSHEEP = 29 SHEEP_RADIUS = 20 DOG_RADIUS = 20 WHISKER_FRONT = SHEEP_RADIUS * 1.25 WHISKER_SIDES = SHEEP_RADIUS * 1.1 WALL_WHISKERS = [ WHISKER_FRONT * Point2d(1, 0), WHISKER_SIDES * Point2d(1, 1).unit(), WHISKER_SIDES * Point2d(1, -1).unit() ] NUMVEHICLES = NUMSHEEP + 1 NUMOBSTACLES = 12 # Amount of time spent flocking
def test_vector_norms(self, x, y): if isnan(x * x + y * y) or isinf(x * x + y * y): self.assertRaises(OverflowError) else: vtest = Point2d(x, y) self.assertAlmostEqual(vtest.norm()**2, vtest.sqnorm())
def test_promote_integer_coords(self, x, y): vtest = Point2d(x, y) self.assertEqual(vtest.x, float(x)) self.assertEqual(vtest.y, float(y))
def test_zero_inplace(self, x, y): vtest = Point2d(x, y) vtest.zero() self.assertEqual(vtest, Point2d(0, 0))
from aiboids.point2d import Point2d from aiboids.vehicle2d import SimpleVehicle2d from aiboids.steering import WaypointPath from aiboids import pgrender if __name__ == "__main__": # Display set-up SCREEN_SIZE = (800, 640) screen, bgcolor = pgrender.setup(SCREEN_SIZE, 'Waypoints steering demo.') UPDATE_SPEED = 1.0 BORDER = 30 # Used to generate random positions and velocities for vehicles randpoint = lambda: Point2d(randint(BORDER, SCREEN_SIZE[0] - BORDER), randint(BORDER, SCREEN_SIZE[1] - BORDER)) # Number of vehicles and obstacles numveh = 2 numobs = 8 total = numveh + numobs # Waypoint/path information pathlen = 6 min_dist_sq = 80**2 # Load images images = dict() images['green'] = pgrender.boid_chevron(20, (0, 222, 0), (0, 0, 0)) images['yellow'] = pgrender.boid_chevron(20, (222, 222, 0), (0, 0, 0))
def test_rotations_are_orthogonal_in_degrees(self, x, y): vtest = Point2d(x, y) vperp = vtest.rotated_by(90, True) vperp2 = vtest.rotated_by(-90, True) self.assertAlmostEqual(vtest * vperp, 0.0) self.assertAlmostEqual(vtest * vperp2, 0.0)
def test_orthogonal_dot_product(self, x, y): if isnan(x * y) or isinf(x * y): self.assertRaises(OverflowError) else: self.assertEqual(Point2d(x, y) * Point2d(y, -x), 0.0) self.assertEqual(Point2d(x, y) * Point2d(-y, x), 0.0)
def negative_rotation_is_inverse(self, x, y, theta): vtest = Point2d(x, y) vrot = vtest.rotated_by(theta) self.assertEqual(vtest, vrot.rotated_by(-theta))
def test_doubling_vs_addition(self, x, y): vtest = Point2d(x, y) self.assertEqual(2 * vtest, vtest + vtest)
def test_tuple_vs_coords(self, x, y): vtest = Point2d(x, y) self.assertEqual(vtest.ntuple, (vtest.x, vtest.y))
def test_for_zero_vector(self): from aiboids.point2d import ZERO_VECTOR self.assertEqual(ZERO_VECTOR, Point2d(0, 0)) self.assertIsNot(ZERO_VECTOR, Point2d(0, 0))
sys.path.append(mypar) from aiboids.point2d import Point2d from aiboids.vehicle2d import SimpleVehicle2d from aiboids import pgrender if __name__ == "__main__": # Display set-up SCREEN_SIZE = (1024, 768) screen, bgcolor = pgrender.setup(SCREEN_SIZE, 'TAKECOVER/STALKING steering demo.') BORDER = 30 UPDATE_SPEED = 0.5 # Used to generate random positions and velocities for vehicles randpoint = lambda: Point2d(randint(BORDER, SCREEN_SIZE[0] - BORDER), randint(BORDER, SCREEN_SIZE[1] - BORDER)) # Number of vehicles and obstacles numveh = 3 numobs = 20 total = numveh + numobs VEH_RADIUS = 20 WHISKER_FRONT = VEH_RADIUS * 1.25 WHISKER_SIDES = WHISKER_FRONT * 1.1 WALL_WHISKERS = [ WHISKER_FRONT * Point2d(1, 0), WHISKER_SIDES * Point2d(1, 1).unit(), WHISKER_SIDES * Point2d(1, -1).unit() ] OBS_RADIUS = 12 # Load images
mypar = os.path.abspath(os.path.join(mydir, os.pardir)) sys.path.append(mypar) from aiboids.point2d import Point2d, ZERO_VECTOR from aiboids.vehicle2d import BasePointMass2d, SimpleVehicle2d from aiboids import pgrender if __name__ == "__main__": # Display set-up SCREEN_SIZE = (800, 640) screen, bgcolor = pgrender.setup(SCREEN_SIZE, 'SEEK/ARRIVE steering demo.') UPDATE_SPEED = 1.0 BORDER = 30 # Used to generate random positions and velocities for vehicles randpoint = lambda: Point2d(randint(BORDER, SCREEN_SIZE[0] - BORDER), randint(BORDER, SCREEN_SIZE[1] - BORDER)) # Number of vehicles and obstacles numveh = 3 numtargs = numveh total = numveh + numtargs # Images for pygame sprites images = dict() images['green'] = pgrender.boid_chevron(20, (0, 222, 0)) images['yellow'] = pgrender.boid_chevron(20, (222, 222, 0)) images['red'] = pgrender.boid_chevron(25, (222, 0, 0)) # Steering behaviour target images (generated here) img_targ = pygame.Surface((5, 5)) img_targ.fill((0, 0, 0))