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

        p = vector.PointF(3, 4)
        a = vector.PointF(2, 0)
        b = vector.PointF(4, 0)
        p0 = a

        # direct path p->(3,0) is valid and optimal
        self.assertEqual(area_map.optimise_point_to_line(p, p0, a, b),
                         vector.PathF([p, vector.PointF(3, 0)]))

        # direct path p->a is valid and optimal
        p = vector.PointF(1, 4)
        self.assertEqual(area_map.optimise_point_to_line(p, p0, a, b),
                         vector.PathF([p, a]))

        # direct path p->b is valid and optimal
        p = vector.PointF(5, 4)
        self.assertEqual(area_map.optimise_point_to_line(p, p0, a, b),
                         vector.PathF([p, b]))

        print("h")
        # direct path p->b is not valid anymore, but
        # p->(3,2)->(3,0) is valid and optimal.
        # also note that p->a is valid.
        area_map[vector.GridTile(3, 1)] = 0  # 3,1 is now unpassable
        p = vector.PointF(4, 4)
        self.assertEqual(
            area_map.optimise_point_to_line(p, p0, a, b),
            vector.PathF([p, vector.PointF(3, 2),
                          vector.PointF(3, 0)]))
Example #2
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 #3
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 #4
0
 def tiles_iterator(self):
     """ returns an iterator over all tiles """
     for x in range(self.width):
         for y in range(self.height):
             yield vector.GridTile(x, y)
Example #5
0
    def find_tiles_in_triangle_iterator(self, a, b, c):
        """ find all tiles 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 []

        # helper
        def intersection_with_line(y, a, b):
            """
                intersection of y const with the line a->b
                if a->b is parallel to the x-axis, the point b is used
            """
            if b.y - a.y != 0.:
                # careful: the denominator can be zero
                t = (y - a.y) / (b.y - a.y)
                return t * (b.x - a.x) + a.x
            else:
                # otherwise, a->b is parallel to the x-axis
                return b.x

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

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

        # special cases:
        # 1. i_y_min == i_y_mid: handled below
        # 2. i_y_max == i_y_mid: handled implicitly
        # 3. i_y_min == i_y_max: cannot happen, excluded above

        # initialise lower_x_min/max
        lower_x_min = eps_floor(p_y_min.x)
        lower_x_max = eps_ceil(p_y_min.x)

        # special case 1.: p_y_min.y == p_mid.y and both are integers
        # then we have to add p_mid.x to the range
        if i_y_min == i_y_mid:
            lower_x_min = min(lower_x_min, eps_floor(p_mid.x))
            lower_x_max = max(lower_x_max, eps_ceil(p_mid.x))

        for y in range(i_y_min, i_y_max):
            # intersection of y+1 const with p_y_min->p_y_max
            if y + 1 <= p_y_max.y:
                x1 = intersection_with_line(y + 1, p_y_min, p_y_max)
            else:
                # but only if it makes sense
                x1 = p_y_max.x

            # find x2
            if y + 1 == i_y_mid:
                # this is the change from p_y_min->p_mid to p_mid->p_y_max
                x2 = p_mid.x
            elif y + 1 <= p_mid.y:
                # we are looking at p_y_min->p_mid
                x2 = intersection_with_line(y + 1, p_y_min, p_mid)
            elif y + 1 <= p_y_max.y:
                # we are looking at p_mid->p_y_max
                # careful: if the line is parallel to the x-axis,
                #          we want p_mid, hence it comes last
                x2 = intersection_with_line(y + 1, p_y_max, p_mid)
            else:
                x2 = p_y_max.x

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

            # now find the smallest integer interval containing [x1,x2]
            upper_x_min, upper_x_max = eps_floor(x1), eps_ceil(x2)

            # communicate all the tiles between
            #  min(lower_x_min,upper_x_min)
            # and
            #   max(lower_x_max,upper_x_max)
            i_min = min(lower_x_min, upper_x_min)
            i_max = max(lower_x_max, upper_x_max)

            # communicate the tile
            for i in range(i_min, i_max):
                tile = vector.GridTile(i, y)
                if self.contains(tile):
                    yield tile

            # swap upper to lower
            lower_x_min, lower_x_max = upper_x_min, upper_x_max
Example #6
0
    def optimise_path_loose_ends(self):
        """ test optimise_path_loose_ends """
        raw_map = raw_map.RawMap(10, 10, 100 * [0])
        area_map = area_map.AreaMap(raw_map, lambda _: True)

        start_a = vector.PointF(2, 0)
        start_b = vector.PointF(4, 0)
        end_a = vector.PointF(4, 4)
        end_b = vector.PointF(6, 4)

        # direct start_b->end_a is valid and optimal
        path = vector.PathF([start_a, end_b])
        self.assertEqual(
            area_map.optimise_path_loose_ends(path, start_a, start_b, end_a,
                                              end_b),
            vector.PathF([start_b, end_a]))

        area_map[vector.GridTile(3, 1)] = 0  # 3,1 is now unpassable
        # direct start_b->end_a is not valid anymore. however
        # (3,0)->(3,2)->end_a is. note that start_a->end_a is valid
        path = vector.PathF([start_a, end_a])
        self.assertEqual(
            area_map.optimise_path_loose_ends(path, start_a, start_b, end_a,
                                              end_b),
            vector.PathF([vector.PointF(3, 0),
                          vector.PointF(3, 2), end_a]))

        area_map[vector.GridTile(3, 1)] = -1  # 3,1 is now passable again

        # small number
        eps = 0.001

        # slow convergence (fixed by preprocess step)
        start_a = vector.PointF(2, 0)
        start_b = vector.PointF(4, eps)
        end_a = vector.PointF(2, 4)
        end_b = vector.PointF(4, 4 - eps)

        # direct start_b->end_b is valid and optimal
        path = vector.PathF([start_a, end_a])
        self.assertEqual(
            area_map.optimise_path_loose_ends(path, start_a, start_b, end_a,
                                              end_b),
            vector.PathF([start_b, end_b]))

        area_map[vector.GridTile(2, 1)] = 0  # 2,1 is now unpassable
        # prevented convergence catastrophe (fixed by preprocess step)
        start_a = vector.PointF(0, 0)
        start_b = vector.PointF(10, 1.5)
        end_a = vector.PointF(0, 3)
        end_b = vector.PointF(10, 1.5)

        # optimal is ???->(3,1)->(3,2)->???
        path = vector.PathF([start_a, end_a])
        self.assertEqual(
            area_map.optimise_path_loose_ends(path, start_a, start_b, end_a,
                                              end_b),
            vector.PathF([
                vector.PointF(860 / 409., 129 / 409.),
                vector.PointF(2, 1),
                vector.PointF(2, 2),
                vector.PointF(860 / 409., 1098 / 409.)
            ]))

        # convergence catastrophe (fixed by preprocess step)
        start_a = vector.PointF(2, 0)
        start_b = vector.PointF(4, eps)
        end_a = vector.PointF(2, 2 * eps)
        end_b = vector.PointF(4, eps)

        # degerenates to start_b=end_b
        path = vector.PathF([start_a, end_a])
        self.assertEqual(
            area_map.optimise_path_loose_ends(path, start_a, start_b, end_a,
                                              end_b),
            vector.PathF([start_b, end_b]))