Grid.py 6.21 KB
Newer Older
Karol Actun's avatar
Karol Actun committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
from abc import ABC, abstractmethod


class Grid(ABC):

    @property
    @abstractmethod
    def size(self):
        pass

    @property
    @abstractmethod
    def directions(self):
        pass

    @abstractmethod
    def is_valid_location(self, location):
        """
        checks if given location is a valid location for matter in this grid
        :param location: (float, float, float)
        :return: true = location is valid, false = location invalid
        """
        pass

    @abstractmethod
    def get_nearest_location(self, coordinates):
        """
        calculates the nearest location to given coordinates
        :param coordinates: (float, float, float)
        :return: valid location
        """
        pass

    def get_directions_dictionary(self):
        """
        returns a dictionary of the directions, with direction names (string) as keys
        and the direction vectors (3d tuple) as values
        :return: dictionary with  - 'string: (float, float,float)'
        """
        return self.directions

    def get_directions_list(self):
        """
        returns a list of the direction vectors
        :return: list of 3d tuples - '(float, float, float)'
        """
        return list(self.directions.values())

    def get_directions_names(self):
        """
        returns a list of direction names
        :return: list of strings
        """
        return list(self.directions.keys())

    def get_lines(self):
        """
        FOR VISUALIZATION!
        calculates line data in this grids directions for the visualization.
        output is a list of start and end points. the start point is always the center of this grid and the end points
        are the directions but only half in length
        :return: list of vectors, [(sx,sy,sz), (ex,ey,ez), (sx,sy,sz), (ea,eb,ec), ...]
        """
        lines = []
        for d in self.get_directions_list():
            lines.append(self.get_center())
            hd = (d[0] * 0.5, d[1] * 0.5, d[2] * 0.5)
            lines.append(hd)
        return lines

    @abstractmethod
    def get_box(self, width):
        """
        calculates locations in a box
        :return: list of 3d coordinates: [(x_start_l0, y_start_l0), (x_end_l0, y_end_l0), (x_start_l1, y_start_l1), ...)
        """
        pass

    @abstractmethod
    def get_dimension_count(self):
        """
        returns the amount of dimensions
        :return: integer, amount of dimensions (3 or 2 presumably)
        """
        pass

    @abstractmethod
    def get_distance(self, start, end):
        """
        the metric or distance function for this grid
        :param start: location, (float, float, float) tuple, start of path
        :param end: location, (float, float, float) tuple, end of path
        :return: integer, minimal amount of steps between start and end
        """
        pass

    @staticmethod
    def get_location_in_direction(position, direction):
        """
        calculates a new position from current position and direction
        :param position: location, (float, float, float) tuple, current position
        :param direction: location, (float, float, float) tuple, direction
        :return: location, (float, float, float) tuple, new position
        """
        new_pos = []
        for i in range(len(position)):
            new_pos.append(position[i]+direction[i])
        return tuple(new_pos)

    def get_center(self):
        """
        returns the center of the grid. usually (0,0,0)
        :return: location, (float, float, float) tuple
        """
        return 0.0, 0.0, 0.0

    def get_scaling(self):
        """
        returns the x,y,z scaling for the visualization. usually (1,1,1) = no scaling
        :return: x,y,z scaling values: float, float, float
        """
        return 1.0, 1.0, 1.0

    def get_adjacent_locations(self, location):
        """
        calculates a set of adjacent locations of the given location
        :param location: the location of which the neighboring locations should be calculated
        :return: a set of locations
        """
        n = set()
        for d in self.get_directions_list():
            n.add(self.get_location_in_direction(location, d))
        return n

    def _get_adjacent_locations_not_in_set(self, location, not_in_set):
        """
        the same as 'get_neighboring_locations', but doesn't return locations which are in the given 'not_in_set'.
        :param location: the location of which the neighboring locations should be calculated
        :param not_in_set: set of locations, which should not be included in the result
        :return: a set of locations
        """
        result = set()
        for d in self.get_directions_list():
            n = self.get_location_in_direction(location, d)
            if n not in not_in_set:
                result.add(n)
        return result

    def get_n_sphere(self, location, radius):
        """
        calculates the n-sphere of this grid
        :param location: center of the circle/sphere
        :param radius: radius of the circle/sphere
        :return: set of locations
        """
        result = set()
        ns = self.get_adjacent_locations(location)
        current_ns = ns
        result.update(ns)

        for i in range(radius):
            tmp = set()
            for n in current_ns:
                ns = self._get_adjacent_locations_not_in_set(n, result)
                tmp.update(ns)
                result.update(ns)
            current_ns = tmp

        return result

    def get_n_sphere_border(self, location, radius):
        """
        calculates the border of an n-sphere around the center with the given radius
        :param location: center of the ring
        :param radius: radius of the ring
        :return: set of locations
        """
        if radius == 0:
            r = set()
            r.add(location)
            return r

        seen = set()
        ns = self.get_adjacent_locations(location)
        current_ns = ns
        seen.update(ns)
        seen.add(location)

        for i in range(radius-1):
            tmp = set()
            for n in current_ns:
                ns = self._get_adjacent_locations_not_in_set(n, seen)
                seen.update(ns)
                tmp.update(ns)
            current_ns = tmp

        return current_ns