|
9 | 9 |
|
10 | 10 | from pygorithm.geometry import (vector2, polygon2, rect2)
|
11 | 11 |
|
| 12 | +class QuadTreeEntity(object): |
| 13 | + """ |
| 14 | + This is the minimum information required for an object to |
| 15 | + be usable in a quadtree as an entity. Entities are the |
| 16 | + things that you are trying to compare in a quadtree. |
| 17 | + |
| 18 | + :ivar aabb: the axis-aligned bounding box of this entity |
| 19 | + :type aabb: :class:`pygorithm.geometry.rect2.Rect2` |
| 20 | + """ |
| 21 | + def __init__(self, aabb): |
| 22 | + """ |
| 23 | + Create a new quad tree entity with the specified aabb |
| 24 | + |
| 25 | + :param aabb: axis-aligned bounding box |
| 26 | + :type aabb: :class:`pygorithm.geometry.rect2.Rect2` |
| 27 | + """ |
| 28 | + pass |
| 29 | + |
| 30 | + def __repr__(self): |
| 31 | + """ |
| 32 | + Create an unambiguous representation of this entity. |
| 33 | + |
| 34 | + Example: |
| 35 | + |
| 36 | + .. code-block:: python |
| 37 | + |
| 38 | + from pygorithm.geometry import (vector2, rect2) |
| 39 | + from pygorithm.data_structures import quadtree |
| 40 | + |
| 41 | + _ent = quadtree.QuadTreeEntity(rect2.Rect2(5, 5)) |
| 42 | + |
| 43 | + # prints quadtreeentity(aabb=rect2(width=5, height=5, mincorner=vector2(x=0, y=0))) |
| 44 | + print(repr(_ent)) |
| 45 | + |
| 46 | + :returns: unambiguous representation of this quad tree entity |
| 47 | + :rtype: string |
| 48 | + """ |
| 49 | + pass |
| 50 | + |
| 51 | + def __str__(self): |
| 52 | + """ |
| 53 | + Create a human readable representation of this entity |
| 54 | + |
| 55 | + Example: |
| 56 | + |
| 57 | + .. code-block:: python |
| 58 | + |
| 59 | + from pygorithm.geometry import (vector2, rect2) |
| 60 | + from pygorithm.data_structures import quadtree |
| 61 | + |
| 62 | + _ent = quadtree.QuadTreeEntity(rect2.Rect2(5, 5)) |
| 63 | + |
| 64 | + # prints entity(at rect(5x5 at <0, 0>)) |
| 65 | + print(str(_ent)) |
| 66 | + |
| 67 | + :returns: human readable representation of this entity |
| 68 | + :rtype: string |
| 69 | + """ |
| 70 | + pass |
| 71 | + |
12 | 72 | class QuadTree(object):
|
13 | 73 | """
|
14 | 74 | A quadtree is a sorting tool for two-dimensional space, most
|
15 | 75 | commonly used to reduce the number of required collision
|
16 | 76 | calculations in a two-dimensional scene. In this context,
|
17 | 77 | the scene is stepped without collision detection, then a
|
18 | 78 | quadtree is constructed from all of the boundaries
|
| 79 | + |
| 80 | + .. caution:: |
| 81 | + |
| 82 | + Just because a quad tree has split does not mean entities will be empty. Any |
| 83 | + entities which overlay any of the lines of the split will be included in the |
| 84 | + parent of the quadtree. |
| 85 | + |
| 86 | + .. tip:: |
| 87 | + |
| 88 | + It is important to tweak bucket size and depth to the problem, but a common error |
| 89 | + is too small a bucket size. It is typically not reasonable to have a bucket size |
| 90 | + smaller than 16; A good starting point is 64, then modify as appropriate. Larger |
| 91 | + buckets reduce the overhead of the quad tree which could easily exceed the improvement |
| 92 | + from reduced collision checks. The max depth is typically just a sanity check since |
| 93 | + depth greater than 4 or 5 would either indicate a badly performing quadtree (too |
| 94 | + dense objects, use an r-tree or kd-tree) or a very large world (where an iterative |
| 95 | + quadtree implementation would be appropriate). |
| 96 | + |
| 97 | + :ivar bucket_size: maximum number objects per bucket (before :py:attr:`.max_depth`) |
| 98 | + :type bucket_size: int |
| 99 | + :ivar max_depth: maximum depth of the quadtree |
| 100 | + :type max_depth: int |
| 101 | + :ivar depth: the depth of this node (0 being the topmost) |
| 102 | + :type depth: int |
| 103 | + :ivar location: where this quad tree node is situated |
| 104 | + :type location: :class:`pygorithm.geometry.rect2.Rect2` |
| 105 | + :ivar entities: the entities in this quad tree and in NO OTHER related quad tree |
| 106 | + :type entities: list of :class:`.QuadTreeEntity` |
| 107 | + :ivar children: either None or the 4 :class:`.QuadTree` children of this node |
| 108 | + :type children: None or list of :class:`.QuadTree` |
19 | 109 | """
|
| 110 | + |
| 111 | + def __init__(self, bucket_size, max_depth, location, depth = 0, entities = None): |
| 112 | + """ |
| 113 | + Initialize a new quad tree. |
| 114 | + |
| 115 | + .. warning:: |
| 116 | + |
| 117 | + Passing entities to this quadtree will NOT cause it to split automatically! |
| 118 | + You must call :py:meth:`.think` for that. This allows for more predictable |
| 119 | + performance per line. |
| 120 | + |
| 121 | + :param bucket_size: the number of entities in this quadtree |
| 122 | + :type bucket_size: int |
| 123 | + :param max_depth: the maximum depth for automatic splitting |
| 124 | + :type max_depth: int |
| 125 | + :param location: where this quadtree is located |
| 126 | + :type location: :class:`pygorithm.geometry.rect2.Rect2` |
| 127 | + :param depth: the depth of this node |
| 128 | + :type depth: int |
| 129 | + :param entities: the entities to initialize this quadtree with |
| 130 | + :type entities: list of :class:`.QuadTreeEntity` or None for empty list |
| 131 | + """ |
| 132 | + pass |
| 133 | + |
| 134 | + def think(self, recursive = False): |
| 135 | + """ |
| 136 | + Call :py:meth:`.split` if appropriate |
| 137 | + |
| 138 | + Split this quad tree if it has not split already and it has more |
| 139 | + entities than :py:attr:`.bucket_size` and :py:attr:`.depth` is |
| 140 | + less than :py:attr:`.max_depth`. |
| 141 | + |
| 142 | + If `recursive` is True, think is called on the :py:attr:`.children` with |
| 143 | + recursive set to True after splitting. |
| 144 | + |
| 145 | + :param recursive: if `think(True)` should be called on :py:attr:`.children` (if there are any) |
| 146 | + :type recursive: bool |
| 147 | + """ |
| 148 | + pass |
| 149 | + |
| 150 | + def split(self): |
| 151 | + """ |
| 152 | + Split this quadtree. |
| 153 | + |
| 154 | + .. caution:: |
| 155 | + |
| 156 | + A call to split will always split the tree or raise an error. Use |
| 157 | + :py:meth:`.think` if you want to ensure the quadtree is operating |
| 158 | + efficiently. |
| 159 | + |
| 160 | + .. caution:: |
| 161 | + |
| 162 | + This function will not respect :py:attr:`.bucket_size` or |
| 163 | + :py:attr:`.max_depth`. |
| 164 | + |
| 165 | + :raises ValueError: if :py:attr:`.children` is not empty |
| 166 | + """ |
| 167 | + pass |
| 168 | + |
| 169 | + def get_quadrant(self, entity): |
| 170 | + """ |
| 171 | + Calculate the quadrant that the specified entity belongs to. |
| 172 | + |
| 173 | + Quadrants are: |
| 174 | + |
| 175 | + - -1: None (it overlaps 2 or more quadrants) |
| 176 | + - 0: Top-left |
| 177 | + - 1: Top-right |
| 178 | + - 2: Bottom-right |
| 179 | + - 3: Bottom-left |
| 180 | + |
| 181 | + .. caution:: |
| 182 | + |
| 183 | + This function does not verify the entity is contained in this quadtree. |
| 184 | + |
| 185 | + This operation takes O(1) time. |
| 186 | + |
| 187 | + :param entity: the entity to place |
| 188 | + :type entity: :class:`.QuadTreeEntity` |
| 189 | + :returns: quadrant |
| 190 | + :rtype: int |
| 191 | + """ |
| 192 | + pass |
| 193 | + |
| 194 | + def insert_and_think(self, entity): |
| 195 | + """ |
| 196 | + Insert the entity into this or the appropriate child. |
| 197 | + |
| 198 | + This also acts as thinking (recursively). Using :py:meth:`.insert_and_think` |
| 199 | + iteratively is slightly less efficient but has more predictable performance |
| 200 | + than initializing with a large number of entities then thinking is slightly |
| 201 | + faster but may hang. Both may exceed recursion depth if :py:attr:`.max_depth` |
| 202 | + is too large. |
| 203 | + |
| 204 | + :param entity: the entity to insert |
| 205 | + :type entity: :class:`.QuadTreeEntity` |
| 206 | + """ |
| 207 | + pass |
| 208 | + |
| 209 | + def retrieve_collidables(self, entity, predicate = None): |
| 210 | + """ |
| 211 | + Find all entities that could collide with the specified entity. |
| 212 | + |
| 213 | + .. warning:: |
| 214 | + |
| 215 | + If entity is, itself, in the quadtree, it will be returned. The |
| 216 | + predicate may be used to prevent this using your preferred equality |
| 217 | + method. |
| 218 | + |
| 219 | + The predicate takes 1 positional argument (the entity being considered) |
| 220 | + and returns `False` if the entity should never be returned, even if it |
| 221 | + might collide with the entity. It should return `True` otherwise. |
| 222 | + |
| 223 | + :param entity: the entity to find collidables for |
| 224 | + :type entity: :class:`.QuadTreeEntity` |
| 225 | + :param predicate: the predicate |
| 226 | + :type predicate: :class:`types.FunctionType` or None |
| 227 | + :returns: potential collidables (never `None) |
| 228 | + :rtype: list of :class:`.QuadTreeEntity` |
| 229 | + """ |
| 230 | + pass |
| 231 | + |
| 232 | + def find_entities_per_depth(self): |
| 233 | + """ |
| 234 | + Calculate the number of nodes and entities at each depth level in this |
| 235 | + quad tree. Only returns for depth levels at or equal to this node. |
| 236 | + |
| 237 | + This is implemented iteratively. See :py:meth:`.__str__` for usage example. |
| 238 | + |
| 239 | + :returns: dict of depth level to (number of nodes, number of entities) |
| 240 | + :rtype: dict int: (int, int) |
| 241 | + """ |
| 242 | + pass |
| 243 | + |
| 244 | + def sum_entities(self, entities_per_depth=None): |
| 245 | + """ |
| 246 | + Sum the number of entities in this quad tree and all lower quad trees. |
| 247 | + |
| 248 | + If `entities_per_depth` is not None, that array is used to calculate the sum |
| 249 | + of entities rather than traversing the tree. Either way, this is implemented |
| 250 | + iteratively. See :py:meth:`.__str__` for usage example. |
| 251 | + |
| 252 | + :param entities_per_depth: the result of :py:meth:`.find_entities_per_depth` |
| 253 | + :type entities_per_depth: `dict int: (int, int)` or None |
| 254 | + :returns: number of entities in this and child nodes |
| 255 | + :rtype: int |
| 256 | + """ |
| 257 | + pass |
| 258 | + |
| 259 | + def calculate_avg_ents_per_leaf(self): |
| 260 | + """ |
| 261 | + Calculate the average number of entities per leaf node on this and child |
| 262 | + quad trees. |
| 263 | + |
| 264 | + In the ideal case, the average entities per leaf is equal to the bucket size, |
| 265 | + implying maximum efficiency. Note that, as always with averages, this might |
| 266 | + be misleading if this tree has reached its max depth. |
| 267 | + |
| 268 | + This is implemented iteratively. See :py:meth:`.__str__` for usage example. |
| 269 | + |
| 270 | + :returns: average number of entities at each leaf node |
| 271 | + :rtype: :class:`numbers.Number` |
| 272 | + """ |
| 273 | + pass |
| 274 | + |
| 275 | + def calculate_weight_misplaced_ents(self, sum_entities=None): |
| 276 | + """ |
| 277 | + Calculate a rating for misplaced entities. |
| 278 | + |
| 279 | + A misplaced entity is one that is not on a leaf node. That weight is multiplied |
| 280 | + by 4*remaining maximum depth of that node, to indicate approximately how |
| 281 | + many additional calculations are required. |
| 282 | + |
| 283 | + The result is then divided by the total number of entities on this node (either |
| 284 | + calculated using :py:meth:`.sum_entities` or provided) to get the approximate |
| 285 | + cost of the misplaced nodes in comparison with the placed nodes. A value greater |
| 286 | + than 1 implies a different tree type (such as r-tree or kd-tree) should probably be |
| 287 | + used. |
| 288 | + |
| 289 | + This is implemented iteratively. See :py:meth:`.__str__` for usage example. |
| 290 | + |
| 291 | + :param sum_entities: the number of entities on this node |
| 292 | + :type sum_entities: int or None |
| 293 | + :returns: weight of misplaced entities |
| 294 | + :rtype: :class:`numbers.Number` |
| 295 | + """ |
| 296 | + pass |
| 297 | + |
| 298 | + def __repr__(self): |
| 299 | + """ |
| 300 | + Create an unambiguous, recursive representation of this quad tree. |
| 301 | + |
| 302 | + Example: |
| 303 | + |
| 304 | + .. code-block:: python |
| 305 | + |
| 306 | + from pygorithm.geometry import (vector2, rect2) |
| 307 | + from pygorithm.data_structures import quadtree |
| 308 | + |
| 309 | + # create a tree with a up to 2 entities in a bucket that |
| 310 | + # can have a depth of up to 5. |
| 311 | + _tree = quadtree.QuadTree(2, 5, rect2.Rect2(100, 100)) |
| 312 | + |
| 313 | + # add a few entities to the tree |
| 314 | + _tree.insert_and_think(quadtree.QuadTreeEntity(rect2.Rect2(2, 2, vector2.Vector2(5, 5)))) |
| 315 | + _tree.insert_and_think(quadtree.QuadTreeEntity(rect2.Rect2(2, 2, vector2.Vector2(95, 5)))) |
| 316 | + |
| 317 | + # prints quadtree(bucket_size=2, max_depth=5, location=rect2(width=100, height=100, mincorner=vector2(x=0, y=0)), depth=0, entities=[], children=[ quadtree(bucket_size=2, max_depth=5, location=rect2(width=50, height=50, mincorner=vector2(x=0, y=0)), depth=1, entities=[ quadtreeentity(aabb=rect2(width=2, height=2, mincorner=vector2(x=5, y=5))) ], children=[]), quadtree(bucket_size=2, max_depth=5, location=rect2(width=50, height=50, mincorner=vector2(x=50, y=0)), depth=1, entities=[ quadtreeentity(aabb=rect2(width=2, height=2, mincorner=vector2(x=95, y=5))) ], children=[]), quadtree(bucket_size=2, max_depth=5, location=rect2(width=50, height=50, mincorner=vector2(x=50, y=50)), depth=1, entities=[], children=[]), quadtree(bucket_size=2, max_depth=5, location=rect2(width=50, height=50, mincorner=vector2(x=0, y=50)), depth=1, entities=[], children=[]) ]) |
| 318 | + print(repr(_tree)) |
| 319 | + |
| 320 | + :returns: unambiguous, recursive representation of this quad tree |
| 321 | + :rtype: string |
| 322 | + """ |
| 323 | + pass |
| 324 | + |
| 325 | + def __str__(self): |
| 326 | + """ |
| 327 | + Create a human-readable representation of this quad tree |
| 328 | + |
| 329 | + .. caution:: |
| 330 | + |
| 331 | + Because of the complexity of quadtrees it takes a fair amount of calculation to |
| 332 | + produce something somewhat legible. All returned statistics have paired functions. |
| 333 | + This uses only iterative algorithms to calculate statistics. |
| 334 | + |
| 335 | + Example: |
| 336 | + |
| 337 | + .. code-block:: python |
| 338 | + |
| 339 | + from pygorithm.geometry import (vector2, rect2) |
| 340 | + from pygorithm.data_structures import quadtree |
| 341 | + |
| 342 | + # create a tree with a up to 2 entities in a bucket that |
| 343 | + # can have a depth of up to 5. |
| 344 | + _tree = quadtree.QuadTree(2, 5, rect2.Rect2(100, 100)) |
| 345 | + |
| 346 | + # add a few entities to the tree |
| 347 | + _tree.insert_and_think(quadtree.QuadTreeEntity(rect2.Rect2(2, 2, vector2.Vector2(5, 5)))) |
| 348 | + _tree.insert_and_think(quadtree.QuadTreeEntity(rect2.Rect2(2, 2, vector2.Vector2(95, 5)))) |
| 349 | + |
| 350 | + # prints quadtree(at rect(100x100 at <0, 0>) with 0 entities here (2 in total); (nodes, entities) per depth: [ 0: (1, 0), 1: (4, 2) ] (max depth: 5), avg ent/leaf: 0.5 (target 2), misplaced weight = 0 (0 best, >1 bad)) |
| 351 | + |
| 352 | + :returns: human-readable representation of this quad tree |
| 353 | + :rtype: string |
| 354 | + """ |
| 355 | + pass |
| 356 | + |
20 | 357 | @staticmethod
|
21 | 358 | def get_code():
|
22 | 359 | """
|
|
0 commit comments