Example #1
0
    def test_find_obstruction_when_transforming_line(self):
        """ test find_obstruction_when_transforming_line """
        raw_map = raw_map.RawMap(10, 10, 100 * [0])
        area_map = area_map.AreaMap(raw_map, lambda _: True)

        base = vector.PointF(2, 2)
        start = vector.PointF(8, 2)
        end = vector.PointF(2, 8)

        # print(area_map.find_tiles_in_triangle(base, start, end))

        self.assertEqual(
            area_map.find_obstruction_when_transforming_line(base, start, end),
            (1.0, None))

        area_map[vector.GridTile(4, 4)] = 0  # 4,4 is now unpassable
        self.assertEqual(
            area_map.find_obstruction_when_transforming_line(base, start, end),
            (0.4, vector.GridPoint(5, 4)))

        area_map[vector.GridTile(6, 3)] = 0  # 6,3 is now unpassable
        self.assertEqual(
            area_map.find_obstruction_when_transforming_line(base, start, end),
            (1. / 6., vector.GridTile(7, 3)))

        area_map[vector.GridTile(4, 2)] = 0  # 4,2 is now unpassable
        self.assertEqual(
            area_map.find_obstruction_when_transforming_line(base, start, end),
            (0., vector.GridPoint(5, 2)))

        area_map[vector.GridTile(2, 2)] = 0  # 2,2 is now unpassable
        self.assertEqual(
            area_map.find_obstruction_when_transforming_line(base, start, end),
            (0., vector.GridPoint(5, 2)))

        # test angle > 90
        base = vector.PointF(6, 4)
        start = vector.PointF(9, 5)
        end = vector.PointF(3, 5)
        self.assertEqual(
            area_map.find_obstruction_when_transforming_line(base, start, end),
            (2. / 3., vector.GridPoint(5, 5)))

        base = vector.PointF(3, 3)
        start = vector.PointF(0, 5)
        end = vector.PointF(9, 7)
        self.assertEqual(
            area_map.find_obstruction_when_transforming_line(base, start, end),
            (0.5, vector.GridPoint(4, 5)))

        area_map[vector.GridTile(6, 5)] = 0  # 6,5 is now unpassable
        self.assertEqual(
            area_map.find_obstruction_when_transforming_line(base, start, end),
            (0.5, vector.GridPoint(4, 5)))

        area_map[vector.GridTile(4, 4)] = -1  # 4,4 is now passable again
        self.assertEqual(
            area_map.find_obstruction_when_transforming_line(base, start, end),
            (5. / 7., vector.GridPoint(6, 6)))
Example #2
0
    def _create_area_map(self):
        """ create it """
        # create shortcuts
        width, height = self.raw_map.width, self.raw_map.height

        # initialise id first free id
        area_id = self.no_area_id + 1

        #
        # special case the map boundary since we have to understand the boundary edges
        # in particular the boundary will have area_id 'self.no_area_id + 1'
        #
        for x in range(width):
            # y = 0 line
            t = vector.GridTile(x, 0)
            self.__process_tile(area_id, t)

            if self.pass_test(self.raw_map[t]):
                p = vector.GridPoint(x, 0)
                p_xpp = vector.GridPoint(x + 1, 0)
                # inner side is to the right
                self.edges[area_id].append(vector.GridEdge(p, p_xpp))

            # y = max line
            t = vector.GridTile(x, height - 1)
            self.__process_tile(area_id, t)

            if self.pass_test(self.raw_map[t]):
                p = vector.GridPoint(x, height)
                p_xpp = vector.GridPoint(x + 1, height)
                # inner side is to the right
                self.edges[area_id].append(vector.GridEdge(p_xpp, p))

        for y in range(height):
            # x = 0 line
            t = vector.GridTile(0, y)
            self.__process_tile(area_id, t)

            if self.pass_test(self.raw_map[t]):
                p = vector.GridPoint(0, y, )
                p_ypp = vector.GridPoint(0, y + 1)
                # inner side is to the left
                self.edges[area_id].append(vector.GridEdge(p_ypp, p))

            # x = max line
            t = vector.GridTile(width - 1, y)
            self.__process_tile(area_id, t)

            if self.pass_test(self.raw_map[t]):
                p = vector.GridPoint(width, y, )
                p_ypp = vector.GridPoint(width, y + 1)
                # inner side is to the left
                self.edges[area_id].append(vector.GridEdge(p, p_ypp))

        # increment area_id
        area_id += 1

        # iterate over all data
        for t in self.raw_map.tiles_iterator():
            if self.__process_tile(area_id, t):
                # if we found something, increment area_id
                area_id += 1
Example #3
0
    def find_gridpoints_in_triangle_iterator(a, b, c):
        """ find all grid points in the triangle defined by the three points """

        assert (isinstance(a, vector.PointF))
        assert (isinstance(b, vector.PointF))
        assert (isinstance(c, vector.PointF))

        # tolerance for the conversion to integer values
        eps = 1e-6
        eps_floor = lambda x: math.floor(x + eps)
        eps_ceil = lambda x: math.ceil(x - eps)

        # sort the points by ascending y-coordinate
        p_y_min, p_mid, p_y_max = sorted([a, b, c], key=lambda p: p.y)

        # special case: empty triangle
        if p_y_min.y == p_y_max.y:
            return []

        # iterate over all y between
        #   (smallest integer >= p_y_min.y)
        # to
        #   (largest integer <= p_y_max.y)

        # (we use eps here to counter rounding artifacts)
        i_y_min = eps_ceil(p_y_min.y)
        i_y_max = eps_floor(p_y_max.y)

        # initialise y
        y = i_y_min

        while y <= p_mid.y:
            # compute the x value of the intersection const y
            # and the line p_y_min->p_y_max
            # i.e. solve the equation
            #  t (p_y_max - p_y_min) + p_y_min = s e_x + (0,y)
            # multiply the equation with e_y, then
            #  t (<p_y_max,e_y> - <p_y_min,e_y>) + <p_y_min,e_y> = y
            t = (y - p_y_min.y) / (p_y_max.y - p_y_min.y)
            # then we can calculate x
            x1 = t * (p_y_max.x - p_y_min.x) + p_y_min.x

            # compute the other x, it is the intersection with p_y_min->p_mid
            if p_mid.y - p_y_min.y != 0.:
                # careful: the denominator here can be zero
                t = (y - p_y_min.y) / (p_mid.y - p_y_min.y)
                x2 = t * (p_mid.x - p_y_min.x) + p_y_min.x
            else:
                # otherwise, p_y_min->p_mid is parallel to the x-axis
                x2 = p_mid.x

            # rearrange if needed
            if x2 < x1:
                x1, x2 = x2, x1

            # now find the largest integer interval in [x1,x2]
            # (we use eps here to counter rounding artifacts)
            i1 = eps_ceil(x1)
            i2 = eps_floor(x2)

            # communicate the vector
            for i in range(i1, i2 + 1):
                yield vector.GridPoint(i, y)

            # increase y
            y += 1

        while y <= i_y_max:
            # compute the x value of the intersection const y
            # and the line p_y_min->p_y_max
            t = (y - p_y_min.y) / (p_y_max.y - p_y_min.y)
            x1 = t * (p_y_max.x - p_y_min.x) + p_y_min.x

            # compute the other x, it is now the intersection with p_mid->p_y_max
            if p_y_max.y - p_mid.y != 0.:
                # careful: the denominator here can be zero
                t = (y - p_mid.y) / (p_y_max.y - p_mid.y)
                x2 = t * (p_y_max.x - p_mid.x) + p_mid.x
            else:
                # otherwise, p_y_min->p_mid is parallel to the x-axis
                x2 = p_mid.x

            # rearrange if needed
            if x2 < x1:
                x1, x2 = x2, x1

            # now find the largest integer interval in [x1,x2]
            # (we use eps here to counter rounding artifacts)
            i1 = eps_ceil(x1)
            i2 = eps_floor(x2)

            # communicate the vector
            for i in range(i1, i2 + 1):
                yield vector.GridPoint(i, y)

            # increase y
            y += 1
Example #4
0
    def find_tiles_in_triangle_iterator_slow(self, base, start, end):
        """ find all tiles in the triangle defined by the three points """

        assert (isinstance(base, vector.PointF))
        assert (isinstance(start, vector.PointF))
        assert (isinstance(end, vector.PointF))

        #
        # find orientation
        #
        # compute vectors originating from base
        v_start = start - base
        v_end = end - base
        v_far_edge = end - start

        # compute vector originating from base which goes to the left of v_start
        v_start_left = v_start.left()
        v_end_left = v_end.left()
        v_far_edge_left = v_far_edge.left()

        # find inner direction
        leftness = v_start_left * v_end

        if leftness > 0:
            # inner direction is to the left of v_start, i.e. to the right of v_end
            v_start_normal = v_start_left
            v_end_normal = -v_end_left
            v_far_edge_normal = v_far_edge_left
        elif leftness < 0:
            # inner direction is to the right of v_start
            v_start_normal = -v_start_left
            v_end_normal = v_end_left
            v_far_edge_normal = -v_far_edge_left
        else:  # leftness == 0
            # they are parallel: everything is fine because we assume that
            # the original lines are fine
            return []

        # the triangle is the intersection of the three half planes
        triangle = [
            halfplane.HalfPlane(base, v_start_normal),
            halfplane.HalfPlane(base, v_end_normal),
            halfplane.HalfPlane(start, v_far_edge_normal),
        ]

        def tile_intersects_triangle(tile):
            # tile polygon
            polygon = tile.polygon()
            # intersections
            for halfplane in triangle:
                polygon = halfplane.intersection(polygon)
                # we want a proper intersection, not just a line
                if polygon.node_count() <= 2:
                    return False
            # if end up here, then the polygon has a non-trivial
            # intersection with the triangle
            return True

        # find all tiles intersecting the triangle
        open_tiles = []
        all_tiles = []

        # find first tile which intersects the interior of the area
        p = vector.GridPoint(int(base.x), int(base.y))

        for tile in p.adjacent_tiles():
            if tile_intersects_triangle(tile):
                # we found our first tile
                open_tiles.append(tile)
                all_tiles.append(tile)
                break
        else:
            raise ValueError()

        # find all tiles in the triangle
        while open_tiles:
            # propagate open tiles
            tile = open_tiles.pop(0)
            # propagate to all nearby points (including the diagonal)
            for next_tile in tile.adjacent_tiles():
                # ignore points outside of the box
                if not self.contains(next_tile):
                    continue

                # if it was already treated, skip it
                if next_tile in all_tiles:
                    continue

                # does it intersect the triangle?
                if tile_intersects_triangle(next_tile):
                    # if yes, add it to open list
                    open_tiles.append(next_tile)
                    all_tiles.append(next_tile)

        # return all found tiles
        return all_tiles
Example #5
0
    def __expand_loop(self, area_id, loop):
        """
			expand the given loop and mark all new areas with
			the given area_id
		"""
        # compute an pseudo in-place update array
        update_loop = PseudoInPlaceUpdateArray(loop)

        # keep track if the loop was expanded
        loop_expanded = False

        # go through edges of loop and see if we can expand it
        # (use index based iterator as the list is going to change)
        while not update_loop.is_finished():
            # get current edge and compute outer tile, i.e. the tile to the left
            edge = update_loop.get(0)
            tile_outer = edge.left_tile()

            # keep the original if the tile does not define a valid tile,
            # i.e. the tile does not belong to the grid
            if not self.contains(tile_outer):
                update_loop.confirm()
                continue

            # keep the original if the outer tile was already assigned to an area
            if self[tile_outer] != self.area_map.no_area_id:
                update_loop.confirm()
                continue

            # set expanded to true as we are going to expand the current edge
            loop_expanded = True

            # mark the new area
            self[tile_outer] = area_id

            # get left facing vector
            v_left = edge.direction().left()

            # create new points by translating the old ones to the left
            new_a, new_b = edge.a + v_left, edge.b + v_left
            new_a = vector.GridPoint(new_a.x, new_a.y)
            new_b = vector.GridPoint(new_b.x, new_b.y)

            # the configuration looks like that:
            # new_a----new_b
            #     |    |
            #     |    |
            # ----a----b-----
            # i-1   i    i+1

            #
            # compute the plan
            #

            # if the (i-1)-th edge is the inverse of a->new_a,
            # then the (i-1)-th gets annihilated
            edge_imm = update_loop.get(-1)
            # it always holds that edge_imm.b == edge.a
            assert (edge_imm.b == edge.a)
            imm_annihilated = (edge_imm.a == new_a)

            # if the (i-1)-th edge get annihilated and
            # (i-2)-th edge is the inverse of the edge new_a->new_b,
            # then the (i-2)-th gets annihilated
            edge_immmm = update_loop.get(-2)
            if imm_annihilated:
                assert (edge_immmm.b == new_a)
            immmm_annihilated = imm_annihilated and (edge_immmm.a == new_b)

            # if the (i+1)-th edge is the inverse of new_b->b,
            # then the (i+1)-th gets annihilated
            edge_ipp = update_loop.get(+1)
            # it always holds that edge.b == edge[ipp].a
            assert (edge.b == edge_ipp.a)
            ipp_annihilated = (new_b == edge_ipp.b)

            # if the (i+1)-th edge get annihilated and
            # (i+2)-th edge is the inverse of the edge new_a->new_b,
            # then the (i+2)-th gets annihilated
            edge_ipppp = update_loop.get(+2)
            if ipp_annihilated:
                assert (edge_ipppp.a == new_b)
            ipppp_annihilated = ipp_annihilated and (edge_ipppp.b == new_a)

            #
            # add and remove edges accordingly
            #
            if imm_annihilated:
                # delete the edge
                update_loop.delete(-1)
                # if we also have to delete the other edge
                if immmm_annihilated:
                    update_loop.delete(-1)
            else:
                # we have to add the edge a->new_a
                new_edge = vector.GridEdge(edge.a, new_a)
                update_loop.insert(new_edge)

            # delete the current edge
            update_loop.delete(0)

            if immmm_annihilated or ipppp_annihilated:
                # we do not have to add the edge new_a->new_b
                pass
            else:
                # we have to add it
                new_edge = vector.GridEdge(new_a, new_b)
                update_loop.insert(new_edge)

            if ipp_annihilated:
                # delete the edge (-1 as we just deleted the current edge)
                update_loop.delete(+1 - 1)
                # if we also have to delete the other edge
                if ipppp_annihilated and not immmm_annihilated:
                    update_loop.delete(+1 - 1)
            else:
                # we have to add the new_b->edge b
                new_edge = vector.GridEdge(new_b, edge.b)
                update_loop.insert(new_edge)

        new_loop = update_loop.get_array()
        # if the loop is empty, add the smallest possible loop
        # (this is only a hack, still the situation probably only arises
        #  under synthetic conditions)
        if not new_loop:
            # create the smallest loop a->b and b->a
            edge = loop[0]
            inverse_edge = vector.GridEdge(edge.b, edge.a)
            new_loop = [edge, inverse_edge]
            return False, new_loop

        return loop_expanded, new_loop