Example #1
0
def test_convex_hull(stratery='graham-scan', points=10000):
    g = Generator()
    a = g.next_batch(points)
    c = ConvexHull(a, stratery)
    time = c.start()
    print('Escape time ' + str(time))
    c.show()
    def initializeB2S2Algo(self, m_value=4):
        '''
			Initialize algo elements
			1. Convex Hull.
			2. Heap.
			3. writing available stats of algo to output file.
		'''
        self.dominance_check = 0
        #### Conveh Hull calculation and points retrival
        convex_hull_start_time = time.time()
        self.ch = ConvexHull(self.query_points)
        self.ch.findConvexHull()
        convex_hull_end_time = time.time()
        self.ch.convertPointsToIndividualCoordinates()
        self.convexHullPoints = self.ch.returnConvexHullPoints()

        #### RTree Initialization and heap initialization
        self.RTree = RTreeInstance(points=self.data_points,
                                   max_entries=m_value)
        self.RTree.insertDataIntoRTree()
        rtree_root = self.RTree.root
        dp_root_bb = self.RTree.root.get_bounding_rect()
        self.insertIntoHeap(dp_root_bb, rtree_root)
        self.box = dp_root_bb
        self.RTree.traverse(self.RTree.countNodes)
        # file = open('out.txt', 'w')
        # file.write(str(self.RTree.returnNodesCount()))
        # file.close()

        #### Writing pre-stats to supplied output file
        dp_l1 = abs(dp_root_bb.min_x - dp_root_bb.max_x)
        dp_l2 = abs(dp_root_bb.min_y - dp_root_bb.max_y)

        qp_area, qp_rect = self.returnQueryPointsArea()

        data = {}
        data['No. Of Data Points   '] = len(self.returnDataPoints())
        data['No. Of Query Points  '] = len(self.returnQueryPoints())
        data['Value of M in R-tree '] = m_value
        data['Total Convex Hull pts'] = len(self.convexHullPoints)
        data['Convex Hull exec time'] = str(
            abs(convex_hull_end_time - convex_hull_start_time)) + 's'
        data['Total Nodes in RTree '] = self.RTree.returnNodesCount()
        data['Data points MBR area '] = str(dp_l1 * dp_l2) + "units"
        data['Query points MBR area'] = str(qp_area) + "units"
        data['Data Points Rect     '] = str([
            (dp_root_bb.min_x, dp_root_bb.min_y),
            (dp_root_bb.max_x, dp_root_bb.max_y)
        ])
        data['Query Points Rect    '] = str(qp_rect)
        data['Query Points MBR %age'] = str(qp_area / (dp_l1 * dp_l2))

        self.writeToOutputFile(data)
def find_path(polygons, waypoints):
    path = []
    temp_waypoints = waypoints.copy()

    for i in range(0, len(temp_waypoints)-1):
        temp_polygons = polygons.copy()
        
        p1 = temp_waypoints[i]
        p2 = temp_waypoints[i+1]
        
        intersecting = get_polygons_intersecting_line(temp_polygons, p1, p2) # Find any polygons between the waypoints

        if (len(intersecting)>0): # If there are intersecting polygons...
            all_points = []
            all_points.append(p1)
            all_points.append(p2)

            for polygon in intersecting:
                for point in polygon:
                    all_points.append(point)

            ch = ConvexHull()
            ch.set_points(all_points)
            ch.compute()
            cp = ch.get_computed_points()
            a = cp.index(p1)
            b = cp.index(p2)

            points = None
            if (a<b):
                points = cp[a:b+1]
            else:
                a, b = b, a
                points = cp[a:b+1]
                points.reverse()        

            path.extend(points)
        else: # If there are no intersecting polygons...
            path.append(p1)
            path.append(p2)

    return path


        
Example #4
0
class ClusterOPT:
    """
    implementation of cluster opt for cvrp problem,
    it's compare each convex hull point of each route and move it if it make better route
    """
    solution = None

    route_convexes = None
    ch = None

    def __init__(self, solution):
        self.solution = solution
        self.ch = ConvexHull(self.solution.instance)
        self.route_convexes = [self.ch.convex_hull(route[:-1])
                               for route in self.solution.routes]

    def distance(self, i, j):
        return self.solution.instance.pre_distance(i, j)

    def route_length(self, route):
        return self.solution.instance.route_length(route)

    @staticmethod
    def mid_point(points):
        return Point(sum(points) / len(points))

    @staticmethod
    def cluster_permutation(N):
        "Generate all segments combinations"
        return ((i, j)
                for i in range(N)
                for j in range(i+1, N))

    def closest_customer_point(self, target_point, points):
        return self.solution.instance.closest_customer_point(
            target_point, excluded=[0], included=points)

    def convex_opt_swap_if_improvement(self, tour_i, tour_j, closest_from_j_i, closest_from_i_j):
        A, B, C = tour_i[closest_from_j_i -
                         1], tour_i[closest_from_j_i], tour_i[(closest_from_j_i + 1)]
        D, E, F = tour_j[closest_from_i_j -
                         1], tour_j[closest_from_i_j], tour_j[(closest_from_i_j + 1)]
        # print(A, B, C)
        # print(D, E, F)
        old_distance_i = self.distance(A, B) + self.distance(B, C)
        old_distance_j = self.distance(D, E) + self.distance(E, F)

        new_distance_i = self.distance(A, E) + self.distance(E, C)
        new_distance_j = self.distance(D, B) + self.distance(B, F)
        # true means new distance makes better tour
        # print("i:", old_distance_i > new_distance_i)
        # true means new distance makes better tour
        # print("j:", old_distance_j > new_distance_j)

        # true means new distance makes better tour
        # print("all:", (old_distance_i + old_distance_j)
        #       > new_distance_i + new_distance_j)
        if (old_distance_i + old_distance_j) > (new_distance_i + new_distance_j):
            tour_i[closest_from_j_i], tour_j[closest_from_i_j] = tour_j[closest_from_i_j], tour_i[closest_from_j_i]
            return True

    def convex_opt_move_if_improvement(self, tour_i, tour_j, closest_from_i_j, closest_from_j_i):
        # print(A, B, C)
        # print(tour_i, tour_j, closest_from_i_j,
        #       closest_from_j_i, (closest_from_i_j + 1))
        A, B, C = tour_i[closest_from_j_i -
                         1], tour_i[closest_from_j_i], tour_i[(closest_from_j_i + 1)]

        D, E, F = tour_j[closest_from_i_j -
                         1], tour_j[closest_from_i_j], tour_j[(closest_from_i_j + 1)]

        old_distance_i = self.distance(A, B) + self.distance(B, C)
        old_distance_j = self.distance(D, E) + self.distance(E, F)

        new_distance_before_i = self.distance(
            A, E) + self.distance(E, B) + self.distance(B, C)
        new_distance_after_i = self.distance(
            A, B) + self.distance(B, E) + self.distance(E, C)
        new_distance_j = self.distance(D, F)

        if new_distance_before_i < new_distance_after_i:  # better visit E before B
            if (old_distance_i + old_distance_j) > (new_distance_before_i + new_distance_j):
                # it makes overall tour better
                moved_point_j = tour_j.pop(closest_from_i_j)
                # print("move", moved_point_j, "to",
                #       tour_i, "before", tour_i[closest_from_j_i])
                tour_i.insert(closest_from_j_i, moved_point_j)
                return True
        else:  # better visit B then E
            if (old_distance_i + old_distance_j) > (new_distance_after_i + new_distance_j):
                # it makes overall tour better
                moved_point_j = tour_j.pop(closest_from_i_j)
                # print("move", moved_point_j, "to",
                #       tour_i, "after", tour_i[closest_from_j_i])
                tour_i.insert(closest_from_j_i+1, moved_point_j)
                return True

    def is_capacity_reached_when_swap(self, route_i, route_j, customer_i, customer_j):
        max_capacity = self.solution.instance.capacity
        # print("is_capacity_reach_when_swap", route_i,
        #       route_j, customer_i, customer_j)
        new_capacity_i = self.solution.route_index_capacity(
            route_i) - self.solution.instance.node_capacity(customer_i) + self.solution.instance.node_capacity(customer_j)
        new_capacity_j = self.solution.route_index_capacity(
            route_j) - self.solution.instance.node_capacity(customer_j) + self.solution.instance.node_capacity(customer_i)
        return new_capacity_i >= max_capacity or new_capacity_j >= max_capacity

    def is_capacity_reached_when_move(self, route_i, customer):
        max_capacity = self.solution.instance.capacity
        # print("curr capacity", self.solution.route_index_capacity(
        # route_i))
        new_capacity_i = self.solution.route_index_capacity(
            route_i) + self.solution.instance.node_capacity(customer)

        # print("new capacity", new_capacity_i)
        return new_capacity_i >= max_capacity

    def convex_opt_closest_gain(self, routes):
        improvements = True
        while improvements:
            for (i, j) in self.cluster_permutation(len(routes)):
                # print("before {} and {}".format(i, j), routes[i], routes[j])
                mid_point_i = self.mid_point(
                    [point[1] for point in self.route_convexes[i]])
                mid_point_j = self.mid_point(
                    [point[1] for point in self.route_convexes[j]])

                convex_indexed_i = [point[0]
                                    for point in self.route_convexes[i]]
                convex_indexed_j = [point[0]
                                    for point in self.route_convexes[j]]
                i_j_index = self.closest_customer_point(
                    mid_point_i, convex_indexed_j)[0]
                j_i_index = self.closest_customer_point(
                    mid_point_j, convex_indexed_i)[0]
                    
                if i_j_index == -1 or j_i_index == -1:
                    continue

                closest_from_i_j = routes[j].index(i_j_index)
                closest_from_j_i = routes[i].index(j_i_index)

                # print(self.is_capacity_reach_when_swap(
                #     i, j, routes[i][closest_from_j_i], routes[j][closest_from_i_j]))

                # print("current capacity_{}".format(i), self.solution.route_index_capacity(
                #     i), "current capacity_{}".format(j), self.solution.route_index_capacity(j))

                if not self.is_capacity_reached_when_move(i, routes[j][closest_from_i_j]):
                    improvements = self.convex_opt_move_if_improvement(
                        routes[i], routes[j], closest_from_i_j, closest_from_j_i)
                elif not self.is_capacity_reached_when_move(j, routes[i][closest_from_j_i]):
                    improvements = self.convex_opt_move_if_improvement(
                        routes[j], routes[i], closest_from_j_i, closest_from_i_j)
                elif not self.is_capacity_reached_when_swap(
                        i, j, routes[i][closest_from_j_i], routes[j][closest_from_i_j]):

                    # print("closest_from_{}_{}".format(i, j), closest_from_i_j,
                    #       "closest_from_{}_{}".format(j, i), closest_from_j_i)
                    # print("mid_point_{}".format(i), mid_point_i,
                    #       "mid_point_{}".format(j), mid_point_j)

                    improvements = self.convex_opt_swap_if_improvement(
                        routes[i], routes[j], closest_from_j_i, closest_from_i_j)

                # print("after capacity_{}".format(i), self.solution.route_index_capacity(
                #     i), "after capacity_{}".format(j), self.solution.route_index_capacity(j))
                if improvements:
                    # print("after {} and {}".format(i, j), routes[i], routes[j])
                    self.route_convexes = [self.ch.convex_hull(route[:-1])
                                           for route in self.solution.routes]

            if bool(improvements) or improvements is None:
                # check if route is removed from solution
                routes = [route for route in routes if len(route) > 2]
                return routes
            # debugging plot
            # style = 'bo-'
            # plt.plot()
            # plt.text(mid_point.x, mid_point.y, '  ' + "mid")
            # plt.plot(mid_point.x, mid_point.y, style)
            # for (label, p) in route_convexes[route_index]:
            #     plt.text(p.x, p.y, '  ' + str(label))

            # plt.plot([point[1].x for point in route_convexes[route_index]] + [route_convexes[route_index][0][1].x], [
            #         point[1].y for point in route_convexes[route_index]] + [route_convexes[route_index][0][1].y], style)
            # plt.show()

    def run(self):
        self.solution.routes = self.convex_opt_closest_gain(
            self.solution.routes)
        return self.solution
Example #5
0
 def __init__(self, solution):
     self.solution = solution
     self.ch = ConvexHull(self.solution.instance)
     self.route_convexes = [self.ch.convex_hull(route[:-1])
                            for route in self.solution.routes]
Example #6
0
        edge_weighted=True,
        node_weighted=True,
        node_weight_generator=generator,
        # node_weights=node_weights,
        seed=seed)

    terminals = [
        1265, 1134, 1482, 1721, 677, 1567, 814, 1879, 635, 838, 2077, 2227, 911
    ]
    poi = 1226
    # terminals = [25, 85, 33, 67]
    # poi = 55

    suitability_graph = SuitabilityGraph()
    suitability_graph.append_graph(graph)

    # suitability_graph.contract_suitable_regions(generator)
    suitable_nodes = suitability_graph.get_suitable_nodes(generator)
    dist_paths = {n: dijkstra(suitability_graph, n) for n in suitable_nodes}

    start_time = time.clock()
    ch = ConvexHull(suitability_graph, terminals, poi, dist_paths)
    convex_hull = ch.compute(generator)
    print("time elapsed:", time.clock() - start_time)

    ngh = NetworkXGraphHelper(suitability_graph)
    ngh.draw_graph(nodes_2=terminals,
                   nodes_1=convex_hull,
                   node_weight_generator=generator,
                   print_edge_labels=False,
                   print_node_labels=False)
class B2S2Algo(object):
    """docstring for B2S2Algo"""
    def __init__(self,
                 data_points=None,
                 query_points=None,
                 data_points_file=None,
                 query_points_file=None,
                 output_file=None):
        '''
			Constructor:
			Input: data_points/data_points_file
				   query_points/query_points_file
				   output_file
		'''
        super(B2S2Algo, self).__init__()
        if data_points:
            self.data_points = data_points
        else:
            self.data_points = self.readDataFromFile(data_points_file)

        if query_points:
            self.data_points = data_points
        else:
            self.query_points = self.readDataFromFile(query_points_file)

        if output_file:
            self.output_file = output_file
            open(self.output_file, 'w').close()
        else:
            self.output_file = 'output.log'
            open(self.output_file, 'w').close()
        self.box = None
        self.skyline_points = set()
        self.minheap = []
        self.min_heap_ele_count = 0
        self.count_rtree_nodes_accessed = 0
        self.dominance_check = 0

    def writeToOutputFile(self, data_dict):
        '''
			function to write stat file of the algo results
			input: data_dict, eg.: {'query size' : 2, 'algo time': '100s'}
		'''
        with open(self.output_file, 'a') as file:
            for key, value in data_dict.items():
                file.write(str(key) + "\t\t\t= " + str(value) + "\n")

    def writingRemainingStatsofAlgo(self, algo_time):
        '''
			Writing final remaining stats of algo 
			full algo time.
			dominance check.
			no. of rtree nodes accessed.
		'''
        data = {}
        data['Algorithm run time    '] = str(algo_time) + "s"
        data['No, of Dominance Check'] = str(self.dominance_check)
        data['R-tree Nodes accessed '] = str(
            self.returnRtreeNodesAccessCount())
        self.writeToOutputFile(data)

    def returnQueryPointsArea(self):
        '''
			Function to return Q MBR
			i.e. total area covered by query points.
		'''
        qp = np.array(self.query_points)
        min_x = min(qp[:, 0])
        max_x = max(qp[:, 0])
        min_y = min(qp[:, 1])
        max_y = max(qp[:, 1])
        qp_rect = [(min_x, min_y), (max_x, max_y)]

        return abs(max_x - min_x) * abs(max_y - min_y), qp_rect

    def returnDataPoints(self):
        return self.data_points

    def returnQueryPoints(self):
        return self.query_points

    def returnRtreeNodesAccessCount(self):
        return self.count_rtree_nodes_accessed

    def returnTotalNodesInRTree(self):
        return self.RTree.returnNodesCount()

    def readDataFromFile(self, filename):
        '''
			input:-
			filename: name of the file containing row wise space separated points
			output:-
			set_of_points = [(x,y),....]
		'''
        set_of_points = []
        try:
            file = open(filename, 'r')
            for line in file:
                tokens = line.strip().split()
                set_of_points.append((float(tokens[0]), float(tokens[1])))
        except Exception as e:
            print(e)
            exit()

        return set_of_points

    def plotPointsOnGraph(self):
        '''
			For visualizing the algo resutls
		'''
        if self.data_points and self.query_points and self.skyline_points:
            red_patch = mpatches.Patch(color='red', label='data points')
            green_patch = mpatches.Patch(color='green', label='query points')
            blue_patch = mpatches.Patch(color='blue', label='skyline points')
            plt.legend(handles=[red_patch])
            for pt in self.data_points:
                plt.plot(pt[0], pt[1], 'ro')
            for pt in self.query_points:
                plt.plot(pt[0], pt[1], 'go')
            for pt in self.skyline_points:
                plt.plot(pt.point[0], pt.point[1], 'bo')
            plt.legend(handles=[red_patch, green_patch, blue_patch])
            plt.show()
        else:
            print('data not available')

    def findDistanceBetweenPoints(self, point1, point2):
        if point1 is not None and point2 is not None:
            return np.sqrt(
                np.square(point1[0] - point2[0]) +
                np.square(point1[1] - point2[1]))
        else:
            return None

    def findDistanceBetweenPointAndRect(self, point, rect):
        '''
			input:-
			point - (x,y)
			rect - Rect instance
		'''
        if (point[0] >= rect.min_x) and (point[0] <= rect.max_x) and (
                point[1] >= rect.min_y) and (point[1] <= rect.max_y):
            return 0
        elif (point[0] >= rect.min_x) and (point[0] <= rect.max_x):
            return min(abs(point[1] - rect.min_y), abs(point[1] - rect.max_y))
        elif (point[1] >= rect.min_y) and (point[1] <= rect.max_y):
            return min(abs(point[0] - rect.min_x), abs(point[0] - rect.max_x))
        elif (point[0] < rect.min_x) and (point[1] < rect.min_y):
            return self.findDistanceBetweenPoints(point,
                                                  (rect.min_x, rect.min_y))
        elif (point[0] > rect.max_x) and (point[1] > rect.max_y):
            return self.findDistanceBetweenPoints(point,
                                                  (rect.max_x, rect.max_y))
        elif (point[0] < rect.min_x) and (point[1] > rect.max_y):
            return self.findDistanceBetweenPoints(point,
                                                  (rect.min_x, rect.max_y))
        elif (point[0] > rect.max_x) and (point[1] < rect.min_y):
            return self.findDistanceBetweenPoints(point,
                                                  (rect.max_x, rect.min_y))
        else:
            return None

    def mindistPointandSet(self, point, set_of_points):
        '''
			input:-
			point - (x,y)
			set_of_points - set of (x,y)'s
		'''
        total_dist = 0
        for pt in set_of_points:
            dist = self.findDistanceBetweenPoints(pt, point)
            if dist:
                total_dist += dist

        return total_dist

    def mindistRectAndSet(self, rect, set_of_points):
        '''
			input:-
			rect - Rect instance
			set_of_points - set of (x,y)'s
		'''
        total_dist = 0
        # print(set_of_points)
        for pt in set_of_points:
            # print(pt)
            dist = self.findDistanceBetweenPointAndRect(pt, rect)
            if dist:
                total_dist += dist

        return total_dist

    def isRectIntersectsBoxB(self, rect):
        '''
			Assumes that your self.box is not None
		'''
        if (self.box.min_x > rect.max_x) or (rect.min_x > self.box.max_x):
            return False
        if (self.box.min_y > rect.max_y) or (rect.min_y > self.box.max_y):
            return False

        return True

    def newBoxB(self, rect):
        '''
			For finding new box B of mentioned in algo line no. 12.
		'''
        if self.box:
            min_x = max(self.box.min_x, rect.min_x)
            min_y = max(self.box.min_y, rect.min_y)
            max_x = min(self.box.max_x, rect.max_x)
            max_y = min(self.box.max_y, rect.max_y)
            return Rect(min_x, min_y, max_x, max_y)

        else:
            return rect

    def isEntryDominated(self, entry_rect):
        self.dominance_check += 1
        for sp in self.skyline_points:
            if entry_rect.min_x == entry_rect.max_x and entry_rect.min_y == entry_rect.max_y:
                if sp.isPointDominated((entry_rect.min_x, entry_rect.min_y)):
                    return True
            else:
                if sp.isRectangleDominated(entry_rect):
                    return True
        return False

    def addToSkyline(self, entry_rect):
        x = entry_rect.min_x
        y = entry_rect.min_y
        sp = SkylinePoint((x, y))
        sp.calculateMBR(self.convexHullPoints)
        self.skyline_points.add(sp)
        return sp.returnMBR()

    def insertIntoHeap(self, priority, entry):
        key = self.mindistRectAndSet(priority, self.convexHullPoints)
        heapq.heappush(self.minheap, (key, self.min_heap_ele_count, entry))
        self.min_heap_ele_count += 1

    def initializeB2S2Algo(self, m_value=4):
        '''
			Initialize algo elements
			1. Convex Hull.
			2. Heap.
			3. writing available stats of algo to output file.
		'''
        self.dominance_check = 0
        #### Conveh Hull calculation and points retrival
        convex_hull_start_time = time.time()
        self.ch = ConvexHull(self.query_points)
        self.ch.findConvexHull()
        convex_hull_end_time = time.time()
        self.ch.convertPointsToIndividualCoordinates()
        self.convexHullPoints = self.ch.returnConvexHullPoints()

        #### RTree Initialization and heap initialization
        self.RTree = RTreeInstance(points=self.data_points,
                                   max_entries=m_value)
        self.RTree.insertDataIntoRTree()
        rtree_root = self.RTree.root
        dp_root_bb = self.RTree.root.get_bounding_rect()
        self.insertIntoHeap(dp_root_bb, rtree_root)
        self.box = dp_root_bb
        self.RTree.traverse(self.RTree.countNodes)
        # file = open('out.txt', 'w')
        # file.write(str(self.RTree.returnNodesCount()))
        # file.close()

        #### Writing pre-stats to supplied output file
        dp_l1 = abs(dp_root_bb.min_x - dp_root_bb.max_x)
        dp_l2 = abs(dp_root_bb.min_y - dp_root_bb.max_y)

        qp_area, qp_rect = self.returnQueryPointsArea()

        data = {}
        data['No. Of Data Points   '] = len(self.returnDataPoints())
        data['No. Of Query Points  '] = len(self.returnQueryPoints())
        data['Value of M in R-tree '] = m_value
        data['Total Convex Hull pts'] = len(self.convexHullPoints)
        data['Convex Hull exec time'] = str(
            abs(convex_hull_end_time - convex_hull_start_time)) + 's'
        data['Total Nodes in RTree '] = self.RTree.returnNodesCount()
        data['Data points MBR area '] = str(dp_l1 * dp_l2) + "units"
        data['Query points MBR area'] = str(qp_area) + "units"
        data['Data Points Rect     '] = str([
            (dp_root_bb.min_x, dp_root_bb.min_y),
            (dp_root_bb.max_x, dp_root_bb.max_y)
        ])
        data['Query Points Rect    '] = str(qp_rect)
        data['Query Points MBR %age'] = str(qp_area / (dp_l1 * dp_l2))

        self.writeToOutputFile(data)

    def runB2S2Algo(self):
        '''
			**IMPORATANT : call this method after you have called object.initializeB2S2Algo()
			i.e. Algorithm other parts are initialized
			B2S2 Full algo steps.
		'''
        self.count_rtree_nodes_accessed = 0
        while self.minheap:
            entry = heapq.heappop(self.minheap)[2]
            bounding_box = None
            if type(entry) == Rect:
                bounding_box = entry
            else:
                bounding_box = entry.get_bounding_rect()
            if not self.isRectIntersectsBoxB(bounding_box):
                continue
            if self.ch.isRectangleInsideConvexHull(bounding_box) or \
             not self.isEntryDominated(bounding_box):
                self.count_rtree_nodes_accessed += 1
                if type(entry) == Rect:
                    mbr_of_entry = self.addToSkyline(entry)
                    self.box = self.newBoxB(mbr_of_entry)
                else:
                    for entry_dash in entry.entries:
                        if not self.isRectIntersectsBoxB(entry_dash.rect):
                            continue
                        if self.ch.isRectangleInsideConvexHull(entry_dash.rect) or \
                         not self.isEntryDominated(entry_dash.rect):
                            # print(self.mindistRectAndSet(entry_dash.rect, self.convexHullPoints), )
                            key = self.mindistRectAndSet(
                                entry_dash.rect, self.convexHullPoints)
                            if entry_dash.child:
                                # heapq.heappush(self.minheap, (key, entry_dash.child))
                                self.insertIntoHeap(entry_dash.rect,
                                                    entry_dash.child)
                            else:
                                # heapq.heappush(self.minheap, (key, entry_dash.rect))
                                self.insertIntoHeap(entry_dash.rect,
                                                    entry_dash.rect)

        return self.skyline_points