def test_update(self): obj = object() # In [(-10, -10), (0, 0)] quadrant bbox = BoundingBox([Vec2(-3, -3), Vec2(-1, -1)]) self.tree.insert(bbox, obj) # In [(0, 0), (10, 10)] quadrant new_bbox = BoundingBox([Vec2(1, 1), Vec2(4, 4)]) self.tree.update(new_bbox, obj) self.assertEqual(self._get_nodes_data(self.tree), { "level": 0, "nodes": [ {"level": 1}, {"level": 1, "nodes": [ {"level": 2}, {"level": 2}, {"level": 2}, {"level": 2, "nodes": [ {"level": 3}, {"level": 3}, {"level": 3}, {"level": 3, "objects": [obj]} ]}, ]}, {"level": 1}, {"level": 1}, ] })
def test_insert_two_quadrants(self): bbox = BoundingBox([Vec2(1, -1), Vec2(4, 4)]) self.tree.insert(bbox, bbox) self.assertEqual(self._get_nodes_data(self.tree), { "level": 0, "nodes": [ {"level": 1}, {"level": 1, "nodes": [ {"level": 2}, {"level": 2}, {"level": 2}, {"level": 2, "nodes": [ {"level": 3}, {"level": 3}, {"level": 3}, {"level": 3, "objects": [bbox]} ]}, ]}, {"level": 1, "nodes": [ {"level": 2, "nodes": [ {"level": 3, "objects": [bbox]}, {"level": 3}, {"level": 3}, {"level": 3}, ]}, {"level": 2}, {"level": 2}, {"level": 2}, ]}, {"level": 1}, ] })
def split(self): level = self._level if self._nw is not None: raise RuntimeError("Already splitted") if level == self._max_level: raise RuntimeError("Already splitted to max level") min_point = self._bbox.min_point max_point = self._bbox.max_point center_point = self._bbox.center min_x, min_y = min_point max_x, max_y = max_point c_x, c_y = center_point new_level = level + 1 self._nw = self.__class__(parent=self, bbox=BoundingBox( [Vec2(min_x, c_y), Vec2(c_x, max_y)]), level=new_level) self._sw = self.__class__(parent=self, bbox=BoundingBox([min_point, center_point]), level=new_level) self._ne = self.__class__(parent=self, bbox=BoundingBox([center_point, max_point]), level=new_level) self._se = self.__class__(parent=self, bbox=BoundingBox( [Vec2(c_x, min_y), Vec2(max_x, c_y)]), level=new_level)
def convert_to_bboxes(data): BoundingBoxes = [] for i in data: minvec = Vec2(i.bbmin[0], i.bbmin[1]) maxvec = Vec2(i.bbmax[0], i.bbmax[1]) BoundingBoxes.append(BoundingBox((minvec, maxvec))) return BoundingBoxes
def test_border_intersection(self): c1 = Circle(Vec2(0, 0), 2) c2 = Circle(Vec2(3, 4), 3) self.assertFalse(intersects(c1, c2)) self.assertTrue(intersects(c1, c2, border=True)) self.assertFalse(contains(c1, c2)) self.assertFalse(contains(c2, c1))
def test_character_load(self, patched): character = Character(character_id=1, initial_position=Vec2(0, 0), initial_viewport=Vec2(0, 1)) # We have a static character in position (0, 0) load_time = 10 self.world.load_character(character, timestamp=10) self.assertEqual(patched.call_count, 1, "`World.notify` was not called on character load") self.assertEqual(tuple(patched.call_args), ((), dict(affects=[character], event="character_load", character=character, timestamp=load_time))) # try to load another char and see, that both were notified about the # event another_character = Character(character_id=2, initial_position=Vec2(0, 10), initial_viewport=Vec2(0, 1)) self.world.load_character(another_character, timestamp=10) self.assertEqual( patched.call_count, 2, "`World.notify` was not called on 2nd character load") self.assertEqual(tuple(patched.call_args), ((), dict(affects=[another_character, character], event="character_load", character=another_character, timestamp=load_time)))
def test_bbox_in_circle_border(self): # Egyptian triangle (3, 4, 5) c = Circle(Vec2(0, 0), 5) r = BoundingBox([Vec2(-3, -4), Vec2(3, 4)]) self.assertTrue(intersects(c, r)) self.assertTrue(contains(c, r)) self.assertFalse(contains(r, c))
def test_border_intersection(self): bbox = BoundingBox([Vec2(0, 0), Vec2(2, 2)]) pol = Polygon([Vec2(0, 0), Vec2(1, 0), Vec2(1, -2)]) self.assertFalse(intersects(bbox, pol)) self.assertTrue(intersects(bbox, pol, border=True)) self.assertFalse(contains(pol, bbox)) self.assertFalse(contains(bbox, pol))
def test_corner_intersection(self): b1 = BoundingBox([Vec2(0, 0), Vec2(2, 2)]) b2 = BoundingBox([Vec2(2, 2), Vec2(5, 7)]) self.assertFalse(intersects(b1, b2)) self.assertTrue(intersects(b1, b2, border=True)) self.assertFalse(contains(b1, b2)) self.assertFalse(contains(b2, b1))
def __init__(self, orientation='height', line=LineSegment.from_points([Vec2(0, 0), Vec2(1, 0)]), alt_of=None): super(LineRepresentation, self).__init__(alt_of) self.line = line # extend the LineSegment to include a bounding_box field, planar doesn't have that originally self.line.bounding_box = BoundingBox.from_points(self.line.points) self.num_dim = 1 self.middle = line.mid self.alt_representations = [PointRepresentation(self.line.mid, self)] classes = [Landmark.END, Landmark.MIDDLE, Landmark.END] if orientation == 'height' \ else [Landmark.SIDE, Landmark.MIDDLE, Landmark.SIDE] self.landmarks = { 'start': Landmark('start', PointRepresentation(self.line.start), self, classes[0]), 'end': Landmark('end', PointRepresentation(self.line.end), self, classes[2]), 'middle': Landmark('middle', PointRepresentation(self.line.mid), self, classes[1]), }
def mirror_point(point_a, point_b, point_to_mirror): vec_a = Vec2(point_a[0], point_a[1]) vec_b = Vec2(point_b[0] - point_a[0], point_b[1] - point_a[1]) line = Line(vec_a, vec_b) org_vec = Vec2(point_to_mirror[0], point_to_mirror[1]) reflect_vec = line.reflect(org_vec) return reflect_vec.x, reflect_vec.y
def test_parse(self): obj = { "regular": [{ "x": 1, "y": 2 }], "silver": [{ "x": 3, "y": 4 }], "gold": [{ "x": 5, "y": 6 }] } resources = Resources.parse(obj) self.assertEqual(1, len(resources.regular)) self.assertTrue(resources.regular[0].almost_equals(Vec2(1, 2))) self.assertEqual(1, len(resources.silver)) self.assertTrue(resources.silver[0].almost_equals(Vec2(3, 4))) self.assertEqual(1, len(resources.gold)) self.assertTrue(resources.gold[0].almost_equals(Vec2(5, 6)))
def get_line_features(lmk): ps = [Vec2(0, 0), Vec2(1, 0)] ls = LineSegment.from_points(ps) dist_start = lmk.distance_to(ls.start) dist_end = lmk.distance_to(ls.end) dist_mid = lmk.distance_to(ls.mid) tl = Line.from_normal(ls.direction, ls.start.x) dir_start = -1 if tl.point_left(lmk) else 1 tl = Line.from_normal(ls.direction, ls.end.x) dir_end = -1 if tl.point_left(lmk) else 1 tl = Line.from_normal(ls.direction, ls.mid.x) dir_mid = -1 if tl.point_left(lmk) else 1 return { 'dist_start': dist_start, 'dist_mid': dist_mid, 'dist_end': dist_end, 'dir_start': dir_start, 'dir_mid': dir_mid, 'dir_end': dir_end, }
def test_trade_sets_actions_trade(self): cell = Cell(1, 2, 3, Vec2(4, 5), Vec2(6, 7)) cell.trade(42) actions = cell.actions() self.assertEqual(42, actions.trade) self.assertIsNotNone(actions.target)
def test_remove(self): # In [(0, 0), (10, 10)] quadrant bbox = BoundingBox([Vec2(1, 1), Vec2(4, 4)]) self.tree.insert(bbox, bbox) self.tree.remove(bbox) self.assertEqual(self._get_nodes_data(self.tree), { "level": 0})
def test_actions_sets_actions_target(self): cell = Cell(1, 2, 3, Vec2(4, 5), Vec2(6, 7)) cell.move(Vec2(6, 7)) actions = cell.actions() self.assertEqual(1, actions.cell_id) self.assertTrue(Vec2(6, 7).almost_equals(actions.target))
def test_burst_sets_actions_burst(self): cell = Cell(1, 2, 3, Vec2(4, 5), Vec2(6, 7)) cell.burst() actions = cell.actions() self.assertTrue(actions.burst) self.assertIsNotNone(actions.target)
def test_circle_in_bbox_border(self): # contains have no need for `border` attributes. It always includes # border points. c = Circle(Vec2(0, 0), 4) r = BoundingBox.from_center(Vec2(0, 0), 8, 8) self.assertTrue(intersects(c, r)) self.assertTrue(contains(r, c)) self.assertFalse(contains(c, r))
def test_get_average_vector(self): lines = [self.create_simple_line_seg((0, 0), (1, 1)), \ self.create_simple_line_seg((1, 1), (3, -7)), \ self.create_simple_line_seg((1, 0), (-4, 4)), \ self.create_simple_line_seg((4, 2), (1, -3))] self.assertEquals(get_average_vector(lines), Vec2(11, -8)) self.assertEquals( get_average_vector([self.create_simple_line_seg((0, 1), (2, 9))]), Vec2(2, 8))
def _spawn_asteroids(self, timestamp, asteroid_count): for i in range(asteroid_count): position = Vec2(random.randint(-self.width/2, self.width/2), random.randint(-self.height/2, self.height/2)) position = position * 0.9 angle = random.randint(0, 360) speed = random.randint( self.ASTEROID_SPEED_MIN, self.ASTEROID_SPEED_MAX) velocity = Vec2.polar(angle, speed) ast = Asteroid(position, velocity, timestamp) self._add_asteroid(timestamp, ast)
def test_character_collision_2_chars(self, patched): character = Character( character_id=1, initial_position=Vec2(0, 0), initial_viewport=Vec2(0, 1)) self.world.load_character(character, timestamp=0) character2 = Character( character_id=2, initial_position=Vec2(30, 30), initial_viewport=Vec2(0, 1)) self.world.load_character(character2, timestamp=0) self.world.update_character( character, velocity=Vec2(1, 1), viewport=Vec2.polar(angle=45, length=1), timestamp=0) self.world.update_character( character2, velocity=Vec2(-1, -1), viewport=Vec2.polar(angle=45, length=1), timestamp=0) self.loop.run_until_complete(asyncio.sleep(0.1, loop=self.loop)) self.assertEqual( patched.call_count, 2, "`World.notify` was not called on character passive move") self.assertEqual( tuple(patched.call_args), ((), dict( affects=[character], event="character_move", character=character, timestamp=0 ))) self.loop.run_until_complete(asyncio.sleep(1, loop=self.loop)) self.assertEqual( patched.call_count, 3, "`World.notify` was not called on character passive move") self.assertEqual( tuple(patched.call_args), ((), dict( affects=[character], event="character_move", character=character, timestamp=1 ))) self.assertEqual( character.position, Vec2(1, 1))
def get_primary_axes(self): return [ Line.from_points([ Vec2(self.rect.min_point.x, self.rect.center.y), Vec2(self.rect.max_point.x, self.rect.center.y) ]), Line.from_points([ Vec2(self.rect.center.x, self.rect.min_point.y), Vec2(self.rect.center.x, self.rect.max_point.y) ]) ]
def __init__(self, position: Vec2, direction: Vec2, timestamp: int): r = self.radius = 0.15 self.ttl = self.BULLET_TTL shape = Polygon([ Vec2(r, r), Vec2(-r, r), Vec2(-r, -r), Vec2(r, -r), ]) super().__init__(position, shape, timestamp) self._velocity = direction * self.BULLET_SPEED
def handle_character_movement(self, packet): character = self.world.get_character(packet.character_id) velocity = Vec2(packet.velocity_x, packet.velocity_y) viewport = Vec2(packet.velocity_x, packet.velocity_y) if packet.rotation not in [-1, 0, 1]: raise ValidationError( fields={ 'rotation': "Not in [-1, 0, 1]" }) character.movement_update( velocity=velocity, viewport=viewport, rotation=packet.rotation, jump=packet.jump)
def new_origin_x(cls, width: float, length: float) -> "DirectedRectangle": """Creates a new rect, centered at origin, headed in direction of the X axis.""" center = Point(0, 0) top_right = center + Vec2(length / 2, width / 2) shape = Polygon.from_points([ top_right, top_right - Vec2(0, width), top_right - Vec2(length, width), top_right - Vec2(length, 0), ]) return cls(Ray(center, Vec2(1, 0)), shape)
def circle_to_bbox(circle, bbox, border=False): c_center = circle.center c_radius = circle.radius c_x, c_y = c_center # Fast check for optimistic cases if bbox.contains_point(c_center): return True # Little simpler to checking bboxes inflated = bbox.inflate(c_radius * 2) if not inflated.contains_point(c_center): # Lower and left bounds are not border inclusive. Should respect # `border` option inf_min_x, inf_min_y = inflated.min_point inf_max_x, inf_max_y = inflated.max_point if not (border and ( (c_x == inf_min_x and inf_min_y < c_y < inf_max_y) or (c_y == inf_min_y and inf_min_x < c_x < inf_max_x))): return False min_x, min_y = bbox.min_point max_x, max_y = bbox.max_point # Any chance we don't have collision is when circle is near corners # Below if c_y < min_y: # Below Right if c_x > max_x: d = Vec2(max_x, min_y).distance_to(c_center) # Below Left elif c_x < min_x: d = Vec2(min_x, min_y).distance_to(c_center) else: return True # Above elif c_y > max_y: # Above Right if c_x > max_x: d = Vec2(max_x, max_y).distance_to(c_center) # Above Left elif c_x < min_x: d = Vec2(min_x, max_y).distance_to(c_center) else: return True else: return True # Check if center is far enough from corner if border and d > c_radius: return False if not border and d >= c_radius: return False return True
def circle_contains_bbox(circle, bbox): c_center = circle.center c_radius = circle.radius min_x, min_y = bbox.min_point max_x, max_y = bbox.max_point return ( # Bottom Right Vec2(max_x, min_y).distance_to(c_center) <= c_radius and # Bottom Left Vec2(min_x, min_y).distance_to(c_center) <= c_radius and # Upper Right Vec2(max_x, max_y).distance_to(c_center) <= c_radius and # Upper Left Vec2(min_x, max_y).distance_to(c_center) <= c_radius)
def test_update_game_calls_send_actions(self): cells = [ Cell(0, 5, 10, Vec2(0, 0), Vec2(1, 1)), Cell(1, 5, 10, Vec2(0, 0), Vec2(1, 1)), Cell(2, 5, 10, Vec2(0, 0), Vec2(1, 1)) ] player = Player(0, "", 10, True, cells) game = Game(0, 0, 0, [player], Resources([], [], []), Map(0, 0), []) cells[0].target = Vec2(2, 2) cells[0].burst() cells[1].split() cells[1].trade(3) class MockApi: def send_actions(self, game_id, actions): self.actions = actions api = MockApi() update_game(api, game, lambda x: None) self.assertEqual(len(api.actions), 2) self.assertTrue(api.actions[0].target.almost_equals(Vec2(2, 2))) self.assertTrue(api.actions[0].burst) self.assertTrue(api.actions[1].split) self.assertEqual(3, api.actions[1].trade)
def __init__( self, position: Vec2, velocity: Vec2, angle: int, timestamp: int, ): shape = Polygon([ Vec2(4, 0), Vec2(-2, -2), Vec2(-1, 0), Vec2(-2, 2), ]) super().__init__(position, shape, timestamp) self._velocity = velocity self._angle = angle
def follow_wall(self, x, y, theta): """ This function is called when the state machine enters the wallfollower state. """ self.wp_start_wall_point = Vec2(x, y) flag = True """ Check if current wall following start point is already inserted into list "self.begin_point_list". If not, append to list """ for x in range(len(self.begin_point_list)): wp_point = self.begin_point_list[x][0] if abs(wp_point.distance_to(self.wp_start_wall_point) <= 1): flag = False if (flag == True): rospy.loginfo("Inserting Begining point to list") self.begin_point_list.append((self.wp_start_wall_point, 0)) rospy.loginfo("Start Wall Follow.") self.now = rospy.get_rostime() """ If this is first hit on any wall, estimate straight line to goal, use this straight line for checking position later """ if len(self.leave_point_list) < 1: rospy.loginfo(" Plot Line...........") self.ln_goal_line = Line.from_points( [self.wp_start_wall_point, self.wp_goal_point]) pass
def __init__(self, position: Vec2, velocity: Vec2, timestamp: int): shape = Polygon([ Vec2(2, 4), Vec2(4, 2), Vec2(2, 1), Vec2(5, -1), Vec2(2, -4), Vec2(-2, -4), Vec2(-4, -2), Vec2(-4, 2), Vec2(-2, 4), Vec2(0, 3), ]) self.radius = max((ob.length for ob in shape)) super().__init__(position, shape, timestamp) self._velocity = velocity
def fire_bullet(self, timestamp): dt = timestamp - self._timestamp position = self._position_in(dt) # Bullet is created at pin of the ship. Which is 4 units further than # center direction = Vec2.polar(self._angle) position += direction * 4 bullet = Bullet( position, direction, timestamp) self._world._add_bullet(timestamp, bullet)
def is_time_to_leave_wall(self, x, y, theta): """ This function is regularly called from the wallfollower state to check the brain's belief about whether it is the right time (or place) to leave the wall and move straight to the goal. """ theta = degrees(theta); self.current_theta =theta; self.wp_current_position = Point(x,y); self.current_direction = Vec2.polar(angle = theta,length = 1); #Robot Orientation Line. self.ln_current_orentation = Line(Vec2(x,y),self.current_direction); # the prependicular line to the path self.ln_distance = self.ln_path.perpendicular(self.wp_current_position); distance_to_path= self.ln_path.distance_to(Point(x,y)); self.distance_to_path = distance_to_path; distance_to_destination = self.ln_current_orentation.distance_to(self.wp_destination); if(abs(distance_to_path) > 1): self.path_started =True; self.distance_to_goal = self.wp_destination.distance_to(Point(x,y)); """ checking if distance to the straight path is approx. 0 and if destenation on the opposit side of wall then leave the path NOTE and TODO: works only for the circles not for complex path. """ if(abs(distance_to_path) < self.TOLERANCE() and self.distance_to_goal < self.distance_when_left and self.is_destination_opposite_to_wall(distance_to_destination) and self.path_started): # is robot started following wall! self.wp_wf_stop = Point(x,y); return True; return False
from timeit import timeit from planar import Vec2 from planar.polygon import Polygon times = 500 def rand_pt(span=10): return Vec2(random() * span - 0, random() * span - 0.5) pts = [Vec2(i, random() * 10.0 + 5.001) for i in range(359)] for sides in [4, 5, 6, 7, 8, 9, 10, 20, 40, 80, 160, 320, 640]: angles = sorted(set(random() * 360.0 for i in range(sides))) if random() > 0.5: angles.reverse() poly = Polygon((Vec2.polar(a, 5) for a in angles)) assert poly.is_convex tangents = poly._pt_tangents cvx_tangents = poly.tangents_to_point for pt in pts: assert not poly.contains_point(pt) tans = tangents(pt) cvx_tans = cvx_tangents(pt) assert tans == cvx_tans, (tans, cvx_tans, sides, pt, list(poly)) def null(): pt_tangents = poly._pt_tangents for pt in pts: pass
def bump(self, timestamp): self._update_time(timestamp) direction = Vec2.polar(self._angle) self._velocity += direction * self.BUMP_AMPLITUDE