Completed
Push — master ( c91eaa...c71608 )
by Kyle
01:17
created

BSP.__getattr__()   A

Complexity

Conditions 1

Size

Total Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
dl 0
loc 2
rs 10
c 0
b 0
f 0
1
0 ignored issues
show
Coding Style introduced by
This module should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
2
import sys as _sys
3
4
import weakref as _weakref
5
import functools as _functools
6
7
from .libtcod import _lib, _ffi, _PropagateException
8
9
@_ffi.def_extern()
10
def _pycall_bsp_callback(node, handle):
11
    """static bool _pycall_bsp_callback(TCOD_bsp_t *node, void *userData);"""
12
    func, userData, propagate = _ffi.from_handle(handle)
13
    try:
14
        return func(BSP.from_cdata(node), userData)
15
    except BaseException:
16
        propagate(*_sys.exc_info())
17
        return False
18
19
def _ensure_sanity(func):
20
    """Any BSP methods which use a cdata object in a TCOD call need to have
21
    a sanity check, otherwise it may end up passing a NULL pointer"""
22
    if __debug__:
23
        @_functools.wraps(func)
24
        def check_sanity(*args, **kargs):
0 ignored issues
show
Coding Style introduced by
This function should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
Unused Code introduced by
The variable check_sanity seems to be unused.
Loading history...
25
            assert self.cdata != _ffi.NULL, 'This BSP instance was deleted!'
0 ignored issues
show
Comprehensibility Best Practice introduced by
Undefined variable 'self'
Loading history...
26
            return func(*args, **kargs)
27
    return func
28
29
class BSP(object):
30
    """
31
32
    .. attribute:: x
33
    .. attribute:: y
34
    .. attribute:: w
35
    .. attribute:: h
36
37
    :param int x: rectangle left coordinate
38
    :param int y: rectangle top coordinate
39
    :param int w: rectangle width
40
    :param int h: rectangle height
41
42
    .. versionchanged:: 2.0
43
       You can create BSP's with this class contructor instead of using
44
       :any:`bsp_new_with_size`.
45
46
    """
47
48
    def __new__(cls, x, y, w, h):
49
        """
50
51
        .. versionchanged:: 2.0
52
        """
53
        self = object.__new__(cls)
54
        self.cdata = _ffi.gc(_lib.TCOD_bsp_new_with_size(x, y, w, h),
55
                             _lib.TCOD_bsp_delete)
56
        self._reference = None # to prevent garbage collection
57
        self._children = _weakref.WeakSet() # used by _invalidate_children
58
        return self
59
60
    @classmethod
61
    def from_cdata(cls, cdata, reference=None):
62
        """Create a BSP instance from a "TCOD_bsp_t*" pointer.
63
64
        This is an alternative constructor, normally for internal use.
65
66
        :param TCOD_bsp_t* cdata: Must be a TCOD_bsp_t*
67
                                 :any:`CData <ffi-cdata>` instance.
68
        :param BSP reference: Used internally to prevent the root BSP
69
                              from becoming garbage collected.
70
71
        .. versionadded:: 2.0
72
        """
73
        self = object.__new__(cls)
74
        self.cdata = cdata
75
        self._reference = reference
76
        self._children = _weakref.WeakSet()
77
        if reference:
78
            reference._children.add(self)
79
        return self
80
81
    def _invalidate_children(self):
82
        """Invalidates BSP instances known to be based off of this one."""
83
        for child in self._children:
84
            child._reference = None
85
            child._invalidate_children()
86
            child.cdata = _ffi.NULL
87
        self._children.clear()
88
89
    def __repr__(self):
90
        """Provide a useful readout when printed."""
91
        if not self.cdata:
92
            return '<%s NULL!>' % self.__class__.__name__
93
94
        status = 'leaf'
95
        if not self.is_leaf():
96
            status = ('split at dicision=%i,orientation=%r' %
97
                      (self.get_division(), self.get_orientation()))
98
99
        return ('<%s(x=%i,y=%i,w=%i,h=%i)depth=%i,%s>' %
100
                (self.__class__.__name__,
101
                 self.x, self.y, self.w, self.h, self.get_depth(), status))
102
103
    def __hash__(self):
104
        return hash(self.cdata)
105
106
    def __eq__(self, other):
107
        try:
108
            return self.cdata == other.cdata
109
        except AttributeError:
110
            return NotImplemented
111
112
    def __getattr__(self, attr):
113
        return getattr(self.__dict__['cdata'], attr)
114
115
    def __setattr__(self, attr, value):
116
        if attr != 'cdata' and hasattr(self.cdata, attr):
117
            setattr(self.cdata, attr, value)
118
            return
119
        object.__setattr__(self, attr, value)
120
121
    def get_depth(self):
122
        """Return the depth of this node.
123
124
        :rtype: int
125
126
        .. versionadded:: 2.0
127
        """
128
        return self.cdata.level
129
130
    def get_division(self):
131
        """Return the point where this node was divided into parts.
132
133
        :rtype: :any:`int` or :any:`None`
134
135
        .. versionadded:: 2.0
136
        """
137
        if self.is_leaf():
138
            return None
139
        return self.cdata.position
140
141
    def get_orientation(self):
142
        """
143
144
        :rtype: str
145
146
        .. versionadded:: 2.0
147
        """
148
        if self.is_leaf():
149
            return ''
150
        elif self.cdata.horizontal:
151
            return 'horizontal'
152
        else:
153
            return 'vertical'
154
155
    @_ensure_sanity
156
    def split_once(self, orientation, position):
157
        """
158
159
        :rtype: tuple
160
161
        .. versionadded:: 2.0
162
        """
163
        # orientation = horz
164
        if orientation[:1].lower() == 'h':
165
            _lib.TCOD_bsp_split_once(self.cdata, True, position)
166
        elif orientation[:1].lower() == 'v':
167
            _lib.TCOD_bsp_split_once(self.cdata, False, position)
168
        else:
169
            raise ValueError("orientation must be 'horizontal' or 'vertical'"
170
                             "\nNot %r" % orientation)
171
        return self.get_children()
172
173
    @_ensure_sanity
174
    def split_recursive(self, depth, min_width, min_height,
175
                        max_horz_ratio, max_vert_raito, random=None):
176
        """
177
178
        :rtype: iter
179
180
        .. versionadded:: 2.0
181
        """
182
        _lib.TCOD_bsp_split_recursive(self.cdata, random or _ffi.NULL,
183
                                      depth, min_width, min_height,
184
                                      max_horz_ratio, max_vert_raito)
185
        return self.walk()
186
187
    @_ensure_sanity
188
    def resize(self, x, y, w, h):
189
        """Resize this BSP to the provided rectangle.
190
191
        :param int x: rectangle left coordinate
192
        :param int y: rectangle top coordinate
193
        :param int w: rectangle width
194
        :param int h: rectangle height
195
196
        .. versionadded:: 2.0
197
        """
198
        _lib.TCOD_bsp_resize(self.cdata, x, y, w, h)
199
200
    @_ensure_sanity
201
    def get_left(self):
202
        """Return this BSP's 'left' child.
203
204
        Returns None if this BSP is a leaf node.
205
206
        :return: BSP's left/top child or None.
207
        :rtype: :any:`BSP` or :any:`None`
208
209
        .. versionadded:: 2.0
210
        """
211
        if self.is_leaf():
212
            return None
213
        return BSP.from_cdata(_lib.TCOD_bsp_left(self.cdata), self)
214
215
    @_ensure_sanity
216
    def get_right(self):
217
        """Return this BSP's 'right' child.
218
219
        Returns None if this BSP is a leaf node.
220
221
        :return: BSP's right/bottom child or None.
222
        :rtype: :any:`BSP` or :any:`None`
223
224
        .. versionadded:: 2.0
225
        """
226
        if self.is_leaf():
227
            return None
228
        return BSP.from_cdata(_lib.TCOD_bsp_right(self.cdata), self)
229
230
    @_ensure_sanity
231
    def get_parent(self):
232
        """Return this BSP's parent node.
233
234
        :return: Returns the parent node as a BSP instance.
235
                 Returns None if this BSP has no parent.
236
        :rtype: :any:`BSP` or :any:`None`
237
238
        .. versionadded:: 2.0
239
        """
240
        node = BSP.from_cdata(_lib.TCOD_bsp_father(self.cdata), self)
241
        if node.cdata == _ffi.NULL:
242
            return None
243
        return node
244
245
    @_ensure_sanity
246
    def get_children(self):
247
        """Return as a tuple, this instances immediate children, if any.
248
249
        :return: Returns a tuple of (left, right) BSP instances.
250
                 The returned tuple is empty if this BSP has no children.
251
        :rtype: tuple
252
253
        .. versionadded:: 2.0
254
        """
255
        if self.is_leaf():
256
            return ()
257
        return (BSP.from_cdata(_lib.TCOD_bsp_left(self.cdata), self),
258
                BSP.from_cdata(_lib.TCOD_bsp_right(self.cdata), self))
259
260
    @_ensure_sanity
261
    def walk(self):
262
        """Iterate over this BSP's hieracrhy.
263
264
        The iterator will include the instance which called it.
265
        It will traverse its own children and grandchildren, in no particular
266
        order.
267
268
        :return: Returns an iterator of BSP instances.
269
        :rtype: iter
270
271
        .. versionadded:: 2.0
272
        """
273
        for child in self.get_children():
274
            for grandchild in child.walk():
275
                yield grandchild
276
        yield self
277
278
    @_ensure_sanity
279
    def is_leaf(self):
280
        """Returns True if this node is a leaf.  False when this node has children.
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (83/79).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
281
282
        :rtype: bool
283
284
        .. versionadded:: 2.0
285
        """
286
        return bool(_lib.TCOD_bsp_is_leaf(self.cdata))
287
288
    @_ensure_sanity
289
    def contains(self, x, y):
290
        """Returns True if this node contains these coordinates.
291
292
        :rtype: bool
293
294
        .. versionadded:: 2.0
295
        """
296
        return bool(_lib.TCOD_bsp_contains(self.cdata, x, y))
297
298
    @_ensure_sanity
299
    def find_node(self, x, y):
300
        """Return the deepest node which contains these coordinates.
301
302
        :rtype: :any:`BSP` or :any:`None`
303
304
        .. versionadded:: 2.0
305
        """
306
        node = BSP.from_cdata(_lib.TCOD_bsp_find_node(self.cdata, x, y), self)
307
        if node.cdata == _ffi.NULL:
308
            node = None
309
        return node
310
311
312
def bsp_new_with_size(x, y, w, h):
313
    """Create a new :any:`BSP` instance with the given rectangle.
314
315
    :param int x: rectangle left coordinate
316
    :param int y: rectangle top coordinate
317
    :param int w: rectangle width
318
    :param int h: rectangle height
319
    :rtype: BSP
320
321
    .. deprecated:: 2.0
322
       Calling the :any:`BSP` class instead.
323
    """
324
    return BSP(x, y, w, h)
325
326
def bsp_split_once(node, horizontal, position):
327
    """
328
    .. deprecated:: 2.0
329
       Use :any:`BSP.split_once` instead.
330
    """
331
    node.split_once('h' if horizontal else 'v', position)
332
333
def bsp_split_recursive(node, randomizer, nb, minHSize, minVSize, maxHRatio,
0 ignored issues
show
Coding Style introduced by
This function should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
334
                        maxVRatio):
335
    node.split_recursive(nb, minHSize, minVSize,
336
                         maxHRatio, maxVRatio, randomizer)
337
338
def bsp_resize(node, x, y, w, h):
339
    """
340
    .. deprecated:: 2.0
341
       Use :any:`BSP.resize` instead.
342
    """
343
    node.resize(x, y, w, h)
344
345
def bsp_left(node):
346
    """
347
    .. deprecated:: 2.0
348
       Use :any:`BSP.get_left` instead.
349
    """
350
    return node.get_left()
351
352
def bsp_right(node):
353
    """
354
    .. deprecated:: 2.0
355
       Use :any:`BSP.get_right` instead.
356
    """
357
    return node.get_right()
358
359
def bsp_father(node):
360
    """
361
    .. deprecated:: 2.0
362
       Use :any:`BSP.get_parent` instead.
363
    """
364
    return node.get_parent()
365
366
def bsp_is_leaf(node):
367
    """
368
    .. deprecated:: 2.0
369
       Use :any:`BSP.is_leaf` instead.
370
    """
371
    return node.is_leaf()
372
373
def bsp_contains(node, cx, cy):
374
    """
375
    .. deprecated:: 2.0
376
       Use :any:`BSP.contains` instead.
377
    """
378
    return node.contains(cx, cy)
379
380
def bsp_find_node(node, cx, cy):
381
    """
382
    .. deprecated:: 2.0
383
       Use :any:`BSP.find_node` instead.
384
    """
385
    return node.find_node(cx, cy)
386
387
def _bsp_traverse(node, func, callback, userData):
388
    """pack callback into a handle for use with the callback
389
    _pycall_bsp_callback
390
    """
391
    with _PropagateException() as propagate:
392
        handle = _ffi.new_handle((callback, userData, propagate))
393
        func(node.cdata, _lib._pycall_bsp_callback, handle)
394
395
def bsp_traverse_pre_order(node, callback, userData=0):
396
    """Traverse this nodes hierarchy with a callback.
397
398
    .. deprecated:: 2.0
399
       Use :any:`BSP.walk` instead.
400
    """
401
    _bsp_traverse(node, _lib.TCOD_bsp_traverse_pre_order, callback, userData)
402
403
def bsp_traverse_in_order(node, callback, userData=0):
404
    """Traverse this nodes hierarchy with a callback.
405
406
    .. deprecated:: 2.0
407
       Use :any:`BSP.walk` instead.
408
    """
409
    _bsp_traverse(node, _lib.TCOD_bsp_traverse_in_order, callback, userData)
410
411
def bsp_traverse_post_order(node, callback, userData=0):
412
    """Traverse this nodes hierarchy with a callback.
413
414
    .. deprecated:: 2.0
415
       Use :any:`BSP.walk` instead.
416
    """
417
    _bsp_traverse(node, _lib.TCOD_bsp_traverse_post_order, callback, userData)
418
419
def bsp_traverse_level_order(node, callback, userData=0):
420
    """Traverse this nodes hierarchy with a callback.
421
422
    .. deprecated:: 2.0
423
       Use :any:`BSP.walk` instead.
424
    """
425
    _bsp_traverse(node, _lib.TCOD_bsp_traverse_level_order, callback, userData)
426
427
def bsp_traverse_inverted_level_order(node, callback, userData=0):
428
    """Traverse this nodes hierarchy with a callback.
429
430
    .. deprecated:: 2.0
431
       Use :any:`BSP.walk` instead.
432
    """
433
    _bsp_traverse(node, _lib.TCOD_bsp_traverse_inverted_level_order,
434
                  callback, userData)
435
436
def bsp_remove_sons(node):
437
    """Delete all children of a given node.  Not recommended.
438
439
    .. note::
440
       This function will add unnecessary complexity to your code.
441
       Don't use it.
442
443
    .. deprecated:: 2.0
444
       BSP deletion is automatic.
445
    """
446
    node._invalidate_children()
447
    _lib.TCOD_bsp_remove_sons(node.cdata)
448
449
def bsp_delete(node):
0 ignored issues
show
Unused Code introduced by
The argument node seems to be unused.
Loading history...
450
    """Exists for backward compatibility.  Does nothing.
451
452
    BSP's created by this library are automatically garbage collected once
453
    there are no references to the tree.
454
    This function exists for backwards compatibility.
455
456
    .. deprecated:: 2.0
457
       BSP deletion is automatic.
458
    """
459
    pass
460
461
__all__ = [_name for _name in list(globals()) if _name[0] != '_']
462