Completed
Push — oop-api ( b83666...7acb6b )
by Kyle
01:04
created

Image.blit_rect()   A

Complexity

Conditions 1

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
c 1
b 0
f 0
dl 0
loc 13
rs 9.4285
1
"""This module focuses on improvements to the Python libtcod API.
2
"""
3
from __future__ import absolute_import as _
4
5
import os as _os
6
import sys as _sys
7
8
import platform as _platform
9
import weakref as _weakref
10
import functools as _functools
11
12
from tcod.libtcod import lib, ffi, BKGND_DEFAULT, BKGND_SET
13
14
def _unpack_char_p(char_p):
15
    if char_p == ffi.NULL:
16
        return ''
17
    return ffi.string(char_p).decode()
18
19
def _int(int_or_str):
20
    'return an integer where a single character string may be expected'
21
    if isinstance(int_or_str, str):
22
        return ord(int_or_str)
23
    if isinstance(int_or_str, bytes):
24
        return int_or_str[0]
25
    return int(int_or_str) # check for __count__
26
27
def _cdata(cdata):
28
    """covert value into a cffi.CData instance"""
29
    try: # first check for _CDataWrapper
30
        cdata = cdata.cdata
31
    except AttributeError: # assume cdata is valid
32
        pass
33
    except KeyError:
34
        pass
35
    if cdata is None or cdata == 0: # convert None to NULL
36
        cdata = ffi.NULL
37
    return cdata
38
39
if _sys.version_info[0] == 2: # Python 2
40
    def _bytes(string):
41
        if isinstance(string, unicode):
42
            return string.encode()
43
        return string
44
45
    def _unicode(string):
46
        if not isinstance(string, unicode):
47
            return string.decode()
48
        return string
49
50
else: # Python 3
51
    def _bytes(string):
52
        if isinstance(string, str):
53
            return string.encode()
54
        return string
55
56
    def _unicode(string):
57
        if isinstance(string, bytes):
58
            return string.decode()
59
        return string
60
61
def _fmt_bytes(string):
62
    return _bytes(string).replace(b'%', b'%%')
63
64
def _fmt_unicode(string):
65
    return _unicode(string).replace(u'%', u'%%')
66
67
class _PropagateException():
68
    """ context manager designed to propagate exceptions outside of a cffi
69
    callback context.  normally cffi suppresses the exception
70
71
    when propagate is called this class will hold onto the error until the
72
    control flow leaves the context, then the error will be raised
73
74
    with _PropagateException as propagate:
75
    # give propagate as onerror parameter for ffi.def_extern
76
    """
77
78
    def __init__(self):
79
        self.exc_info = None # (exception, exc_value, traceback)
80
81
    def propagate(self, *exc_info):
82
        """ set an exception to be raised once this context exits
83
84
        if multiple errors are caught, only keep the first exception raised
85
        """
86
        if not self.exc_info:
87
            self.exc_info = exc_info
88
89
    def __enter__(self):
90
        """ once in context, only the propagate call is needed to use this
91
        class effectively
92
        """
93
        return self.propagate
94
95
    def __exit__(self, type, value, traceback):
96
        """ if we're holding on to an exception, raise it now
97
98
        prefers our held exception over any current raising error
99
100
        self.exc_info is reset now in case of nested manager shenanigans
101
        """
102
        if self.exc_info:
103
            type, value, traceback = self.exc_info
104
            self.exc_info = None
105
        if type:
106
            # Python 2/3 compatible throw
107
            exception = type(value)
108
            exception.__traceback__ = traceback
109
            raise exception
110
111
class _CDataWrapper(object):
112
113
    def __init__(self, *args, **kargs):
114
        self.cdata = self._get_cdata_from_args(*args, **kargs)
115
        if self.cdata == None:
116
            self.cdata = ffi.NULL
117
        super(_CDataWrapper, self).__init__()
118
119
    def _get_cdata_from_args(self, *args, **kargs):
120
        if len(args) == 1 and isinstance(args[0], ffi.CData) and not kargs:
121
            return args[0]
122
        else:
123
            return None
124
125
126
    def __hash__(self):
127
        return hash(self.cdata)
128
129
    def __eq__(self, other):
130
        try:
131
            return self.cdata == other.cdata
132
        except AttributeError:
133
            return NotImplemented
134
135
    def __getattr__(self, attr):
136
        if 'cdata' in self.__dict__:
137
            return getattr(self.__dict__['cdata'], attr)
138
        raise AttributeError(attr)
139
140
    def __setattr__(self, attr, value):
141
        if hasattr(self, 'cdata') and hasattr(self.cdata, attr):
142
            setattr(self.cdata, attr, value)
143
        else:
144
            super(_CDataWrapper, self).__setattr__(attr, value)
145
146
def _assert_cdata_is_not_null(func):
147
    """Any BSP methods which use a cdata object in a TCOD call need to have
148
    a sanity check, otherwise it may end up passing a NULL pointer"""
149
    if __debug__:
150
        @_functools.wraps(func)
151
        def check_sanity(*args, **kargs):
152
            assert self.cdata != ffi.NULL and self.cdata is not None, \
153
                   'cannot use function, cdata is %r' % self.cdata
154
            return func(*args, **kargs)
155
    return func
156
157
class BSP(_CDataWrapper):
158
    """
159
160
161
    Attributes:
162
        x (int): Rectangle left coordinate.
163
        y (int): Rectangle top coordinate.
164
        w (int): Rectangle width.
165
        h (int): Rectangle height.
166
167
    Args:
168
        x (int): Rectangle left coordinate.
169
        y (int): Rectangle top coordinate.
170
        w (int): Rectangle width.
171
        h (int): Rectangle height.
172
173
    .. versionchanged:: 2.0
174
       You can create BSP's with this class contructor instead of using
175
       :any:`bsp_new_with_size`.
176
    """
177
178
    def __init__(self, *args, **kargs):
179
        self._reference = None # to prevent garbage collection
180
        self._children = _weakref.WeakSet() # used by _invalidate_children
181
        super(BSP, self).__init__(*args, **kargs)
182
        if self._get_cdata_from_args(*args, **kargs) is None:
183
            self._init(*args, **kargs)
184
185
    def _init(self, x, y, w, h):
186
        self.cdata = ffi.gc(lib.TCOD_bsp_new_with_size(x, y, w, h),
187
                             lib.TCOD_bsp_delete)
188
189
    def _pass_reference(self, reference):
190
        self._reference = reference
191
        self._reference._children.add(self)
192
        return self
193
194
    def _invalidate_children(self):
195
        """Invalidates BSP instances known to be based off of this one."""
196
        for child in self._children:
197
            child._reference = None
198
            child._invalidate_children()
199
            child.cdata = ffi.NULL
200
        self._children.clear()
201
202
    def __str__(self):
203
        """Provide a useful readout when printed."""
204
        if not self.cdata:
205
            return '<%s NULL!>' % self.__class__.__name__
206
207
        status = 'leaf'
208
        if not self.is_leaf():
209
            status = ('split at dicision=%i,orientation=%r' %
210
                      (self.get_division(), self.get_orientation()))
211
212
        return ('<%s(x=%i,y=%i,w=%i,h=%i)depth=%i,%s>' %
213
                (self.__class__.__name__,
214
                 self.x, self.y, self.w, self.h, self.get_depth(), status))
215
216
    def get_depth(self):
217
        """Return the depth of this node.
218
219
        Returns:
220
            int: This nodes depth.
221
222
        .. versionadded:: 2.0
223
        """
224
        return self.cdata.level
225
226
    def get_division(self):
227
        """Return the point where this node was divided into parts.
228
229
        Returns:
230
            Optional[int]: The integer of where the node was split or None.
231
232
        .. versionadded:: 2.0
233
        """
234
        if self.is_leaf():
235
            return None
236
        return self.cdata.position
237
238
    def get_orientation(self):
239
        """Return this nodes split orientation.
240
241
        Returns:
242
            Optional[Text]: 'horizontal', 'vertical', or None
243
244
        .. versionadded:: 2.0
245
        """
246
        if self.is_leaf():
247
            return None
248
        elif self.cdata.horizontal:
249
            return 'horizontal'
250
        else:
251
            return 'vertical'
252
253
    @_assert_cdata_is_not_null
254
    def split_once(self, orientation, position):
255
        """
256
257
        .. versionadded:: 2.0
258
        """
259
        # orientation = horz
260
        if orientation[:1].lower() == 'h':
261
            lib.TCOD_bsp_split_once(self.cdata, True, position)
262
        elif orientation[:1].lower() == 'v':
263
            lib.TCOD_bsp_split_once(self.cdata, False, position)
264
        else:
265
            raise ValueError("orientation must be 'horizontal' or 'vertical'"
266
                             "\nNot %r" % orientation)
267
268
    @_assert_cdata_is_not_null
269
    def split_recursive(self, depth, min_width, min_height,
270
                        max_horz_ratio, max_vert_raito, random=None):
271
        """
272
273
        .. versionadded:: 2.0
274
        """
275
        lib.TCOD_bsp_split_recursive(self.cdata, random or ffi.NULL,
276
                                      depth, min_width, min_height,
277
                                      max_horz_ratio, max_vert_raito)
278
279
    @_assert_cdata_is_not_null
280
    def resize(self, x, y, w, h):
281
        """Resize this BSP to the provided rectangle.
282
283
        Args:
284
            x (int): Rectangle left coordinate.
285
            y (int): Rectangle top coordinate.
286
            w (int): Rectangle width.
287
            h (int): Rectangle height.
288
289
        .. versionadded:: 2.0
290
        """
291
        lib.TCOD_bsp_resize(self.cdata, x, y, w, h)
292
293
    @_assert_cdata_is_not_null
294
    def get_left(self):
295
        """Return this BSP's 'left' child.
296
297
        Returns None if this BSP is a leaf node.
298
299
        Returns:
300
            Optional[BSP]: This nodes left/top child or None.
301
302
        .. versionadded:: 2.0
303
        """
304
        if self.is_leaf():
305
            return None
306
        return BSP(lib.TCOD_bsp_left(self.cdata))._pass_reference(self)
307
308
    @_assert_cdata_is_not_null
309
    def get_right(self):
310
        """Return this BSP's 'right' child.
311
312
        Returns None if this BSP is a leaf node.
313
314
        Returns:
315
            Optional[BSP]: This nodes right/bottom child or None.
316
317
        .. versionadded:: 2.0
318
        """
319
        if self.is_leaf():
320
            return None
321
        return BSP(lib.TCOD_bsp_right(self.cdata))._pass_reference(self)
322
323
    @_assert_cdata_is_not_null
324
    def get_parent(self):
325
        """Return this BSP's parent node.
326
327
        Returns:
328
            Optional[BSP]: Returns the parent node as a BSP instance.
329
                           Returns None if this BSP has no parent.
330
331
        .. versionadded:: 2.0
332
        """
333
        node = BSP(lib.TCOD_bsp_father(self.cdata))._pass_reference(self)
334
        if node.cdata == ffi.NULL:
335
            return None
336
        return node
337
338
    @_assert_cdata_is_not_null
339
    def get_children(self):
340
        """Return this instances immediate children, if any.
341
342
        Returns:
343
            Optional[Tuple[BSP, BSP]]:
344
                Returns a tuple of (left, right) BSP instances.
345
                Returns None if this BSP has no children.
346
347
        .. versionadded:: 2.0
348
        """
349
        if self.is_leaf():
350
            return None
351
        return (BSP(lib.TCOD_bsp_left(self.cdata))._pass_reference(self),
352
                BSP(lib.TCOD_bsp_right(self.cdata))._pass_reference(self))
353
354
    @_assert_cdata_is_not_null
355
    def walk(self):
356
        """Iterate over this BSP's hieracrhy.
357
358
        The iterator will include the instance which called it.
359
        It will traverse its own children and grandchildren, in no particular
360
        order.
361
362
        Returns:
363
            Iterator[BSP]: An iterator of BSP nodes.
364
365
        .. versionadded:: 2.0
366
        """
367
        children = self.get_children() or ()
368
        for child in children:
369
            for grandchild in child.walk():
370
                yield grandchild
371
        yield self
372
373
    @_assert_cdata_is_not_null
374
    def is_leaf(self):
375
        """Returns True if this node is a leaf.
376
377
        Returns:
378
            bool:
379
                True if this node is a leaf.
380
                False when this node has children.
381
382
        .. versionadded:: 2.0
383
        """
384
        return bool(lib.TCOD_bsp_is_leaf(self.cdata))
385
386
    @_assert_cdata_is_not_null
387
    def contains(self, x, y):
388
        """Returns True if this node contains these coordinates.
389
390
        Args:
391
            x (int): X position to check.
392
            y (int): Y position to check.
393
394
        Returns:
395
            bool: True if this node contains these coordinates.
396
                  Otherwise False.
397
398
        .. versionadded:: 2.0
399
        """
400
        return bool(lib.TCOD_bsp_contains(self.cdata, x, y))
401
402
    @_assert_cdata_is_not_null
403
    def find_node(self, x, y):
404
        """Return the deepest node which contains these coordinates.
405
406
        Returns:
407
            Optional[BSP]: BSP object or None.
408
409
        .. versionadded:: 2.0
410
        """
411
        node = BSP(lib.TCOD_bsp_find_node(self.cdata,
412
                                           x, y))._pass_reference(self)
413
        if node.cdata == ffi.NULL:
414
            node = None
415
        return node
416
417
class HeightMap(_CDataWrapper):
418
    """libtcod HeightMap instance
419
    """
420
421
    def __init__(self, *args, **kargs):
422
        super(HeightMap, self).__init__(*args, **kargs)
423
        if not self.cdata:
424
            self._init(*args, **kargs)
425
426
    def _init(self, width, height):
427
        self.cdata = ffi.gc(lib.TCOD_heightmap_new(width, height),
428
                            lib.TCOD_heightmap_delete)
429
430
431
class Key(_CDataWrapper):
432
    """Key Event instance
433
434
    Attributes:
435
        vk (int): TCOD_keycode_t key code
436
        c (int): character if vk == TCODK_CHAR else 0
437
        text (Text): text[TCOD_KEY_TEXT_SIZE]; text if vk == TCODK_TEXT else text[0] == '\0'
438
        pressed (bool): does this correspond to a key press or key release event ?
439
        lalt (bool): True when left alt is held.
440
        lctrl (bool): True when left control is held.
441
        lmeta (bool): True when left meta key is held.
442
        ralt (bool): True when right alt is held.
443
        rctrl (bool): True when right control is held.
444
        rmeta (bool): True when right meta key is held.
445
        shift (bool): True when any shift is held.
446
    """
447
448
    _BOOL_ATTRIBUTES = ('lalt', 'lctrl', 'lmeta',
449
                        'ralt', 'rctrl', 'rmeta', 'pressed', 'shift')
450
451
    def __init__(self, *args, **kargs):
452
        super(Key, self).__init__(*args, **kargs)
453
        if self.cdata == ffi.NULL:
454
            self.cdata = ffi.new('TCOD_key_t*')
455
456
    def __getattr__(self, attr):
457
        if attr in self._BOOL_ATTRIBUTES:
458
            return bool(getattr(self.cdata, attr))
459
        if attr == 'c':
460
            return ord(getattr(self.cdata, attr))
461
        if attr == 'text':
462
            return _unpack_char_p(getattr(self.cdata, attr))
463
        return super(Key, self).__getattr__(attr)
464
465
class Mouse(_CDataWrapper):
466
    """Mouse event instance
467
468
    Attributes:
469
        x (int): Absolute mouse position at pixel x.
470
        y (int):
471
        dx (int): Movement since last update in pixels.
472
        dy (int):
473
        cx (int): Cell coordinates in the root console.
474
        cy (int):
475
        dcx (int): Movement since last update in console cells.
476
        dcy (int):
477
        lbutton (bool): Left button status.
478
        rbutton (bool): Right button status.
479
        mbutton (bool): Middle button status.
480
        lbutton_pressed (bool): Left button pressed event.
481
        rbutton_pressed (bool): Right button pressed event.
482
        mbutton_pressed (bool): Middle button pressed event.
483
        wheel_up (bool): Wheel up event.
484
        wheel_down (bool): Wheel down event.
485
    """
486
487
    def __init__(self, *args, **kargs):
488
        super(Mouse, self).__init__(*args, **kargs)
489
        if self.cdata == ffi.NULL:
490
            self.cdata = ffi.new('TCOD_mouse_t*')
491
492
493
class Console(_CDataWrapper):
494
    """
495
    Args:
496
        width (int): Width of the new Console.
497
        height (int): Height of the new Console.
498
499
    .. versionadded:: 2.0
500
    """
501
502
    def __init__(self, *args, **kargs):
503
        self.cdata = self._get_cdata_from_args(*args, **kargs)
504
        if self.cdata is None:
505
            self._init(*args, **kargs)
506
507
    def _init(self, width, height):
508
        self.cdata = ffi.gc(lib.TCOD_console_new(width, height),
509
                            lib.TCOD_console_delete)
510
511
    def get_width(self):
512
        """Return the width of this console.
513
514
        Returns:
515
            int: The width of a Console.
516
        """
517
        return lib.TCOD_console_get_width(self.cdata)
518
519
    def get_height(self):
520
        """Return the height of this console.
521
522
        Returns:
523
            int: The height of a Console.
524
        """
525
        return lib.TCOD_console_get_height(self.cdata)
526
527
    def set_default_bg(self, color):
528
        """Change the default backround color for this console.
529
530
        Args:
531
            color (Union[Tuple[int, int, int], Sequence[int]]):
532
                An (r, g, b) sequence or Color instance.
533
        """
534
        lib.TCOD_console_set_default_background(self.cdata, color)
535
536
    def set_default_fg(self, color):
537
        """Change the default foreground color for this console.
538
539
        Args:
540
            color (Union[Tuple[int, int, int], Sequence[int]]):
541
                An (r, g, b) sequence or Color instance.
542
        """
543
        lib.TCOD_console_set_default_foreground(self.cdata, color)
544
545
    def clear(self):
546
        """Reset this console to its default colors and the space character.
547
        """
548
        lib.TCOD_console_clear(self.cdata)
549
550
    def put_char(self, x, y, ch, flag=BKGND_DEFAULT):
551
        """Draw the character c at x,y using the default colors and a blend mode.
552
553
        Args:
554
            x (int): Character x position from the left.
555
            y (int): Character y position from the top.
556
            c (Union[int, AnyStr]): Character to draw, can be an integer or string.
557
            flag (int): Blending mode to use, defaults to BKGND_DEFAULT.
558
        """
559
        lib.TCOD_console_put_char(self.cdata, x, y, _int(ch), flag)
560
561
    def put_char_ex(self, x, y, ch, fore, back):
562
        """Draw the character c at x,y using the colors fore and back.
563
564
        Args:
565
            x (int): Character x position from the left.
566
            y (int): Character y position from the top.
567
            c (Union[int, AnyStr]): Character to draw, can be an integer or string.
568
            fore (Union[Tuple[int, int, int], Sequence[int]]):
569
                An (r, g, b) sequence or Color instance.
570
            back (Union[Tuple[int, int, int], Sequence[int]]):
571
                An (r, g, b) sequence or Color instance.
572
        """
573
        lib.TCOD_console_put_char_ex(self.cdata, x, y,
574
                                 _int(ch), fore, back)
575
576
    def set_char_bg(self, x, y, col, flag=BKGND_SET):
577
        """Change the background color of x,y to col using a blend mode.
578
579
        Args:
580
            x (int): Character x position from the left.
581
            y (int): Character y position from the top.
582
            col (Union[Tuple[int, int, int], Sequence[int]]):
583
                An (r, g, b) sequence or Color instance.
584
            flag (int): Blending mode to use, defaults to BKGND_SET.
585
        """
586
        lib.TCOD_console_set_char_background(self.cdata, x, y, col, flag)
587
588
    def set_char_fg(self, x, y, color):
589
        """Change the foreground color of x,y to col.
590
591
        Args:
592
            x (int): Character x position from the left.
593
            y (int): Character y position from the top.
594
            color (Union[Tuple[int, int, int], Sequence[int]]):
595
                An (r, g, b) sequence or Color instance.
596
        """
597
        lib.TCOD_console_set_char_foreground(self.cdata, x, y, col)
598
599
    def set_char(self, x, y, ch):
600
        """Change the character at x,y to c, keeping the current colors.
601
602
        Args:
603
            x (int): Character x position from the left.
604
            y (int): Character y position from the top.
605
            c (Union[int, AnyStr]): Character to draw, can be an integer or string.
606
        """
607
        lib.TCOD_console_set_char(self.cdata, x, y, _int(ch))
608
609
    def set_default_bg_blend(self, flag):
610
        """Change the default blend mode for this console.
611
612
        Args:
613
            flag (int): Blend mode to use by default.
614
        """
615
        lib.TCOD_console_set_background_flag(self.cdata, flag)
616
617
    def get_default_bg_blend(self):
618
        """Return this consoles current blend mode.
619
        """
620
        return lib.TCOD_console_get_background_flag(self.cdata)
621
622
    def set_alignment(self, alignment):
623
        """Change this consoles current alignment mode.
624
625
        * tcod.LEFT
626
        * tcod.CENTER
627
        * tcod.RIGHT
628
629
        Args:
630
            alignment (int):
631
        """
632
        lib.TCOD_console_set_alignment(self.cdata, alignment)
633
634
    def get_alignment(self):
635
        """Return this consoles current alignment mode.
636
        """
637
        return lib.TCOD_console_get_alignment(self.cdata)
638
639
    def print_str(self, x, y, fmt):
640
        """Print a color formatted string on a console.
641
642
        Args:
643
            x (int): Character x position from the left.
644
            y (int): Character y position from the top.
645
            fmt (AnyStr): A unicode or bytes string optionaly using color codes.
646
        """
647
        lib.TCOD_console_print_utf(self.cdata, x, y, _fmt_unicode(fmt))
648
649
    def print_ex(self, x, y, flag, alignment, fmt):
650
        """Print a string on a console using a blend mode and alignment mode.
651
652
        Args:
653
            x (int): Character x position from the left.
654
            y (int): Character y position from the top.
655
        """
656
        lib.TCOD_console_print_ex_utf(self.cdata, x, y,
657
                                      flag, alignment, _fmt_unicode(fmt))
658
659
    def print_rect(self, x, y, w, h, fmt):
660
        """Print a string constrained to a rectangle.
661
662
        If h > 0 and the bottom of the rectangle is reached,
663
        the string is truncated. If h = 0,
664
        the string is only truncated if it reaches the bottom of the console.
665
666
667
668
        Returns:
669
            int: The number of lines of text once word-wrapped.
670
        """
671
        return lib.TCOD_console_print_rect_utf(
672
            self.cdata, x, y, width, height, _fmt_unicode(fmt))
673
674
    def print_rect_ex(self, x, y, w, h, flag, alignment, fmt):
675
        """Print a string constrained to a rectangle with blend and alignment.
676
677
        Returns:
678
            int: The number of lines of text once word-wrapped.
679
        """
680
        return lib.TCOD_console_print_rect_ex_utf(self.cdata,
681
            x, y, width, height, flag, alignment, _fmt_unicode(fmt))
682
683
    def get_height_rect(self, x, y, width, height, fmt):
684
        """Return the height of this text once word-wrapped into this rectangle.
685
686
        Returns:
687
            int: The number of lines of text once word-wrapped.
688
        """
689
        return lib.TCOD_console_get_height_rect_utf(
690
            self.cdata, x, y, width, height, _fmt_unicode(fmt))
691
692
    def rect(self, x, y, w, h, clr, flag=BKGND_DEFAULT):
693
        """Draw a the background color on a rect optionally clearing the text.
694
695
        If clr is True the affected tiles are changed to space character.
696
        """
697
        lib.TCOD_console_rect(self.cdata, x, y, w, h, clr, flag)
698
699
    def hline(self, x, y, width, flag=BKGND_DEFAULT):
700
        """Draw a horizontal line on the console.
701
702
        This always uses the character 196, the horizontal line character.
703
        """
704
        lib.TCOD_console_hline(self.cdata, x, y, width, flag)
705
706
    def vline(self, x, y, height, flag=BKGND_DEFAULT):
707
        """Draw a vertical line on the console.
708
709
        This always uses the character 179, the vertical line character.
710
        """
711
        lib.TCOD_console_vline(self.cdata, x, y, height, flag)
712
713
    def print_frame(self, x, y, w, h, clear=True, flag=BKGND_DEFAULT, fmt=b''):
714
        """Draw a framed rectangle with optinal text.
715
716
        This uses the default background color and blend mode to fill the
717
        rectangle and the default foreground to draw the outline.
718
719
        fmt will be printed on the inside of the rectangle, word-wrapped.
720
        """
721
        lib.TCOD_console_print_frame(self.cdata, x, y, w, h, clear, flag,
722
                                  _fmt_bytes(fmt))
723
724
    def get_default_bg(self):
725
        """Return this consoles default background color."""
726
        return Color._new_from_cdata(
727
            lib.TCOD_console_get_default_background(self.cdata))
728
729
    def get_default_fg(self):
730
        """Return this consoles default foreground color."""
731
        return Color._new_from_cdata(
732
            lib.TCOD_console_get_default_foreground(self.cdata))
733
734
    def get_char_bg(self, x, y):
735
        """Return the background color at the x,y of this console."""
736
        return Color._new_from_cdata(
737
            lib.TCOD_console_get_char_background(self.cdata, x, y))
738
739
    def get_char_fg(self, x, y):
740
        """Return the foreground color at the x,y of this console."""
741
        return Color._new_from_cdata(
742
            lib.TCOD_console_get_char_foreground(self.cdata, x, y))
743
744
    def get_char(self, x, y):
745
        """Return the character at the x,y of this console."""
746
        return lib.TCOD_console_get_char(self.cdata, x, y)
747
748
    def blit(self, x, y, w, h,
749
             dest, dest_x, dest_y, fg_alpha=1, bg_alpha=1):
750
        """Blit this console from x,y,w,h to the console dst at xdst,ydst."""
751
        lib.TCOD_console_blit(self.cdata, x, y, w, h,
752
                              _cdata(dst), dest_x, dest_y, fg_alpha, bg_alpha)
753
754
    def set_key_color(self, color):
755
        """Set a consoles blit transparent color."""
756
        lib.TCOD_console_set_key_color(self.cdata, color)
757
758
    def fill(self, ch=None, fg=None, bg=None):
759
        """Fill this console with the given numpy array values.
760
761
        Args:
762
            ch (Optional[:any:`numpy.ndarray`]):
763
                A numpy integer array with a shape of (width, height)
764
            fg (Optional[:any:`numpy.ndarray`]):
765
                A numpy integer array with a shape of (width, height, 3)
766
            bg (Optional[:any:`numpy.ndarray`]):
767
                A numpy integer array with a shape of (width, height, 3)
768
        """
769
        import numpy
770
        if ch:
771
            ch = numpy.ascontiguousarray(ch, dtype=numpy.intc)
772
            ch_array = ffi.cast('int *', ch.ctypes.data)
773
            lib.TCOD_console_fill_char(self.cdata, ch_array)
774 View Code Duplication
        if fg:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
775
            r = numpy.ascontiguousarray(fg[:,:,0], dtype=numpy.intc)
776
            g = numpy.ascontiguousarray(fg[:,:,1], dtype=numpy.intc)
777
            b = numpy.ascontiguousarray(fg[:,:,2], dtype=numpy.intc)
778
            cr = ffi.cast('int *', r.ctypes.data)
779
            cg = ffi.cast('int *', g.ctypes.data)
780
            cb = ffi.cast('int *', b.ctypes.data)
781
            lib.TCOD_console_fill_foreground(self.cdata, cr, cg, cb)
782 View Code Duplication
        if bg:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
783
            r = numpy.ascontiguousarray(bg[:,:,0], dtype=numpy.intc)
784
            g = numpy.ascontiguousarray(bg[:,:,1], dtype=numpy.intc)
785
            b = numpy.ascontiguousarray(bg[:,:,2], dtype=numpy.intc)
786
            cr = ffi.cast('int *', r.ctypes.data)
787
            cg = ffi.cast('int *', g.ctypes.data)
788
            cb = ffi.cast('int *', b.ctypes.data)
789
            lib.TCOD_console_fill_background(self.cdata, cr, cg, cb)
790
791
792
class Image(_CDataWrapper):
793
    """
794
    .. versionadded:: 2.0
795
796
    Args:
797
        width (int): Width of the new Image.
798
        height (int): Height of the new Image.
799
800
    Attributes:
801
        width (int): Read only width of this Image.
802
        height (int): Read only height of this Image.
803
    """
804
    def __init__(self, *args, **kargs):
805
        super(Image, self).__init__(*args, **kargs)
806
        if not self.cdata:
807
            self._init(*args, **kargs)
808
        self.width, self.height = self._get_size()
809
810
    def _init(self, width, height):
811
        self.cdata = ffi.gc(lib.TCOD_image_new(width, height),
812
                            lib.TCOD_image_delete)
813
814
    def clear(self, color):
815
        """Fill this entire Image with color.
816
817
        Args:
818
            color (Union[Tuple[int, int, int], Sequence[int]]):
819
                An (r, g, b) sequence or Color instance.
820
        """
821
        lib.TCOD_image_clear(self.cdata, color)
822
823
    def invert(self):
824
        """Invert all colors in this Image."""
825
        lib.TCOD_image_invert(self.cdata)
826
827
    def hflip(self):
828
        """Horizontally flip this Image."""
829
        lib.TCOD_image_hflip(self.cdata)
830
831
    def rotate90(self, rotations=1):
832
        """Rotate this Image clockwise in 90 degree steps.
833
834
        Args:
835
            rotations (int): Number of 90 degree clockwise rotations.
836
        """
837
        lib.TCOD_image_rotate90(self.cdata, rotations)
838
839
    def vflip(self):
840
        """Vertically flip this Image."""
841
        lib.TCOD_image_vflip(self.cdata)
842
843
    def scale(self, width, height):
844
        """Scale this Image to the new width and height.
845
846
        Args:
847
            width (int): The new width of the Image after scaling.
848
            height (int): The new height of the Image after scaling.
849
        """
850
        lib.TCOD_image_scale(self.cdata, width, height)
851
        self.width, self.height = width, height
852
853
    def set_key_color(self, color):
854
        """Set a color to be transparent during blitting functions.
855
856
        Args:
857
            color (Union[Tuple[int, int, int], Sequence[int]]):
858
                An (r, g, b) sequence or Color instance.
859
        """
860
        lib.TCOD_image_set_key_color(self.cdata, color)
861
862
    def get_alpha(self, x, y):
863
        """Get the Image alpha of the pixel at x, y.
864
865
        Args:
866
            x (int): X pixel of the image.  Starting from the left at 0.
867
            y (int): Y pixel of the image.  Starting from the top at 0.
868
869
        Returns:
870
            int: The alpha value of the pixel.
871
            With 0 being fully transparent and 255 being fully opaque.
872
        """
873
        return lib.TCOD_image_get_alpha(self.cdata, x, y)
874
875
    def refresh_console(self, console):
876
        """Update an Image created with :any:`tcod.image_from_console`.
877
878
        The console used with this function should have the same width and
879
        height as the Console given to :any:`tcod.image_from_console`.
880
        The font width and height must also be the same as when
881
        :any:`tcod.image_from_console` was called.
882
883
        Args:
884
            console (Console): A Console with a pixel width and height
885
                               matching this Image.
886
        """
887
        lib.TCOD_image_refresh_console(self.cdata, _cdata(console))
888
889
    def _get_size(self):
890
        """Return the (width, height) for this Image.
891
892
        Returns:
893
            Tuple[int, int]: The (width, height) of this Image
894
        """
895
        w = ffi.new('int *')
896
        h = ffi.new('int *')
897
        lib.TCOD_image_get_size(self.cdata, w, h)
898
        return w[0], h[0]
899
900
    def get_pixel(self, x, y):
901
        """Get the color of a pixel in this Image.
902
903
        Args:
904
            x (int): X pixel of the Image.  Starting from the left at 0.
905
            y (int): Y pixel of the Image.  Starting from the top at 0.
906
907
        Returns:
908
            Tuple[int, int, int]:
909
                An (r, g, b) tuple containing the pixels color value.
910
                Values are in a 0 to 255 range.
911
        """
912
        return lib.TCOD_image_get_pixel(self.cdata, x, y)
913
914
    def get_mipmap_pixel(self, left, top, right, bottom):
915
        """Get the average color of a rectangle in this Image.
916
917
        Parameters should stay within the following limits:
918
        * 0 <= left < right < Image.width
919
        * 0 <= top < bottom < Image.height
920
921
        Args:
922
            left (int): Left corner of the region.
923
            top (int): Top corner of the region.
924
            right (int): Right corner of the region.
925
            bottom (int): Bottom corner of the region.
926
927
        Returns:
928
            Tuple[int, int, int]:
929
                An (r, g, b) tuple containing the averaged color value.
930
                Values are in a 0 to 255 range.
931
        """
932
        color = lib.TCOD_image_get_mipmap_pixel(self.cdata,
933
                                                left, top, right, bottom)
934
        return (color.r, color.g, color.b)
935
936
    def put_pixel(self, x, y, color):
937
        """Change a pixel on this Image.
938
939
        Args:
940
            x (int): X pixel of the Image.  Starting from the left at 0.
941
            y (int): Y pixel of the Image.  Starting from the top at 0.
942
            color (Union[Tuple[int, int, int], Sequence[int]]):
943
                An (r, g, b) sequence or Color instance.
944
        """
945
        lib.TCOD_image_put_pixel(self.cdata, x, y, color)
946
947
    def blit(self, console, x, y, bg_blend, scale_x, scale_y, angle):
948
        """Blit onto a Console using scaling and rotation.
949
950
        Args:
951
            console (Console): Blit destination Console.
952
            x (int): Console X position for the center of the Image blit.
953
            y (int): Console Y position for the center of the Image blit.
954
                     The Image blit is centered on this position.
955
            bg_blend (int): Background blending mode to use.
956
            scale_x (float): Scaling along Image x axis.
957
                             Set to 1 for no scaling.  Must be over 0.
958
            scale_y (float): Scaling along Image y axis.
959
                             Set to 1 for no scaling.  Must be over 0.
960
            angle (float): Rotation angle in radians. (Clockwise?)
961
        """
962
        lib.TCOD_image_blit(self.cdata, _cdata(console), x, y, bg_blend,
963
                            scale_x, scale_y, angle)
964
965
    def blit_rect(self, console, x, y, width, height, bg_blend):
966
        """Blit onto a Console without scaling or rotation.
967
968
        Args:
969
            console (Console): Blit destination Console.
970
            x (int): Console tile X position starting from the left at 0.
971
            y (int): Console tile Y position starting from the top at 0.
972
            width (int): Use -1 for Image width.
973
            height (int): Use -1 for Image height.
974
            bg_blend (int): Background blending mode to use.
975
        """
976
        lib.TCOD_image_blit_rect(self.cdata, _cdata(console),
977
                                 x, y, width, height, bg_blend)
978
979
    def blit_2x(self, console, dest_x, dest_y,
980
                img_x=0, img_y=0, img_width=-1, img_height=-1):
981
        """Blit onto a Console with double resolution.
982
983
        Args:
984
            console (Console): Blit destination Console.
985
            dest_x (int): Console tile X position starting from the left at 0.
986
            dest_y (int): Console tile Y position starting from the top at 0.
987
            img_x (int): Left corner pixel of the Image to blit
988
            img_y (int): Top corner pixel of the Image to blit
989
            img_width (int): Width of the Image to blit.
990
                             Use -1 for the full Image width.
991
            img_height (int): Height of the Image to blit.
992
                              Use -1 for the full Image height.
993
        """
994
        lib.TCOD_image_blit_2x(self.cdata, _cdata(console), dest_x, dest_y,
995
                               img_x, img_y, img_width, img_height)
996
997
    def save_as(self, filename):
998
        """Save the Image to a 32-bit .bmp or .png file.
999
1000
        Args:
1001
            filename (AnyStr): File path to same this Image.
1002
        """
1003
        lib.TCOD_image_save(self.cdata, _bytes(filename))
1004
1005
class Random(_CDataWrapper):
1006
    """
1007
    .. versionadded:: 2.0
1008
1009
    If all you need is a random number generator then it's recommended
1010
    that you use the :any:`random` module from the Python standard library.
1011
1012
    Args:
1013
        seed (int): The RNG seed, should be a 32-bit integer.
1014
        algorithm (int): The algorithm to use.
1015
    """
1016
    def __init__(self, *args, **kargs):
1017
        super(Random, self).__init__(*args, **kargs)
1018
        if not self.cdata:
1019
            self._init(*args, **kargs)
1020
1021
    def _init(self, seed, algorithm):
1022
        self.cdata = ffi.gc(lib.TCOD_random_new_from_seed(algorithm, seed),
1023
                            lib.TCOD_random_delete)
1024
1025
1026
    def random_int(self, low, high, mean=None):
1027
        """Return a random integer from a linear or triangular range.
1028
1029
        Args:
1030
            low (int): The lower bound of the random range, inclusive.
1031
            high (int): The upper bound of the random range, inclusive.
1032
            mean (Optional[int]): The mean return value, or None.
1033
1034
        Returns:
1035
            int: A random number from the given range: low <= n <= high.
1036
        """
1037
        lib.TCOD_random_set_distribution(self.cdata,
1038
                                         lib.TCOD_DISTRIBUTION_LINEAR)
1039
        if mean is None:
1040
            return lib.TCOD_random_get_int(self.cdata, low, high)
1041
        return lib.TCOD_random_get_int_mean(self.cdata, low, high, mean)
1042
1043
1044
    def random_float(self, low, high, mean=None):
1045
        """Return a random float from a linear or triangular range.
1046
1047
        Args:
1048
            low (float): The lower bound of the random range.
1049
            high (float): The upper bound of the random range.
1050
            mean (Optional[float]): The mean return value, or None.
1051
1052
        Returns:
1053
            float: A random number from the given range: low <= n <= high.
1054
        """
1055
        lib.TCOD_random_set_distribution(self.cdata,
1056
                                         lib.TCOD_DISTRIBUTION_LINEAR)
1057
        if mean is None:
1058
            return lib.TCOD_random_get_double(self.cdata, low, high)
1059
        return lib.TCOD_random_get_double_mean(self.cdata, low, high, mean)
1060
1061
    def gaussian(self, mu, sigma):
1062
        """Return a number from a random gaussian distribution.
1063
1064
        Args:
1065
            mu (float): The mean returned value.
1066
            sigma (float): The standard deviation.
1067
1068
        Returns:
1069
            float: A random number derived from the given parameters.
1070
        """
1071
        lib.TCOD_random_set_distribution(self.cdata,
1072
                                         lib.TCOD_DISTRIBUTION_GAUSSIAN)
1073
        return lib.TCOD_random_get_double(self.cdata, mu, sigma)
1074
1075
    def inverse_gaussian(self, mu, sigma):
1076
        """Return a number from a random inverse gaussian distribution.
1077
1078
        Args:
1079
            mu (float): The mean returned value.
1080
            sigma (float): The standard deviation.
1081
1082
        Returns:
1083
            float: A random number derived from the given parameters.
1084
        """
1085
        lib.TCOD_random_set_distribution(self.cdata,
1086
            lib.TCOD_DISTRIBUTION_GAUSSIAN_INVERSE)
1087
        return lib.TCOD_random_get_double(self.cdata, mu, sigma)
1088
1089
    def gaussian_range(self, low, high, mean=None):
1090
        """Return a random gaussian number clamped to a range.
1091
1092
        When ``mean`` is None it will be automatically determined
1093
        from the ``low`` and ``high`` parameters.
1094
1095
        Args:
1096
            low (float): The lower bound of the random range.
1097
            high (float): The upper bound of the random range.
1098
            mean (Optional[float]): The mean return value, or None.
1099
1100
        Returns:
1101
            float: A clamped gaussian number.
1102
        """
1103
        lib.TCOD_random_set_distribution(self.cdata,
1104
            lib.TCOD_DISTRIBUTION_GAUSSIAN_RANGE)
1105
        if mean is None:
1106
            return lib.TCOD_random_get_double(self.cdata, low, high)
1107
        return lib.TCOD_random_get_double_mean(self.cdata, low, high, mean)
1108
1109
    def inverse_gaussian_range(self, low, high, mean=None):
1110
        """Return a random inverted gaussian number clamped to a range.
1111
1112
        When ``mean`` is None it will be automatically determined
1113
        from the ``low`` and ``high`` parameters.
1114
1115
        Args:
1116
            low (float): The lower bound of the random range.
1117
            high (float): The upper bound of the random range.
1118
            mean (Optional[float]): The mean return value, or None.
1119
1120
        Returns:
1121
            float: A clamped inverse gaussian number.
1122
        """
1123
        lib.TCOD_random_set_distribution(self.cdata,
1124
            lib.TCOD_DISTRIBUTION_GAUSSIAN_RANGE_INVERSE)
1125
        if mean is None:
1126
            return lib.TCOD_random_get_double(self.cdata, low, high)
1127
        return lib.TCOD_random_get_double_mean(self.cdata, low, high, mean)
1128
1129
    # TODO: Eventually add these functions:
1130
    #def save(self):
1131
    #    return ffi.gc(lib.TCOD_random_save(self.cdata),
1132
    #                  lib.TCOD_random_delete)
1133
    #def restore(self, backup):
1134
    #    lib.TCOD_random_restore(self.cdata, backup)
1135
1136
class Noise(_CDataWrapper)
1137
    """
1138
    .. versionadded:: 2.0
1139
1140
    Args:
1141
        dimentions (int): Must be from 1 to 4.
1142
        noise_type (int): Defaults to NOISE_SIMPLEX
1143
        hurst (float):
1144
        lacunarity (float):
1145
        rand (Optional[Random]):
1146
    """
1147
    def __init__(self, *args, **kargs):
1148
        self.octants = 4
1149
        self._index_ctype = 'float[4]'
1150
        self._cdata_random = None # keep alive the random cdata instance
1151
        self._noise_type = None
1152
        self._dimentions = None
1153
        self._hurst = None
1154
        self._lacunarity = None
1155
        super(Noise, self).__init__(*args, **kargs)
1156
        if not self.cdata:
1157
            self._init(*args, **kargs)
1158
1159
    def _init(self, dimentions, noise_type=NOISE_SIMPLEX,
1160
              hurst=NOISE_DEFAULT_HURST, lacunarity=NOISE_DEFAULT_LACUNARITY,
1161
              octants=4, rand=None):
1162
        self._cdata_random = _cdata(rand)
1163
        self._noise_type = NOISE_SIMPLEX
1164
        self._dimentions = dimentions
1165
        self._hurst = hurst
1166
        self._lacunarity = lacunarity
1167
        self.octants = octants
1168
        self._index_ctype = 'float[%i]' % dimentions
1169
        self._regenerate_noise()
1170
1171
    def _regenerate_noise(self):
1172
        self.cdata = ffi.gc(lib.TCOD_noise_new(self._dimentions, self._hurst,
1173
                                               self._lacunarity,
1174
                                               self._cdata_random),
1175
                            lib.TCOD_noise_delete)
1176
1177
    @property
1178
    def noise_type(self):
1179
        return self._noise_type
1180
    @noise_type.setter
1181
    def noise_type(self, value):
1182
        self._noise_type = value
1183
        lib.TCOD_noise_set_type(self.cdata, value)
1184
1185
    @property
1186
    def dimentions(self):
1187
        return self._dimentions
1188
    @dimentions.setter
1189
    def dimentions(self, value):
1190
        self._dimentions = value
1191
        self._index_ctype = 'float[%i]' % value
1192
        self._regenerate_noise()
1193
1194
    @property
1195
    def hurst(self):
1196
        return self._hurst
1197
    @hurst.setter
1198
    def hurst(self, value):
1199
        self._hurst = value
1200
        self._regenerate_noise()
1201
1202
    @property
1203
    def lacunarity(self):
1204
        return self._lacunarity
1205
    @hurst.setter
1206
    def lacunarity(self, value):
1207
        self._lacunarity = value
1208
        self._regenerate_noise()
1209
1210
    def get_noise(self, *xyzw):
1211
        """Return the noise value at the xyzw point.
1212
1213
        Args:
1214
            xyzw (float):
1215
        """
1216
        return lib.TCOD_noise_get(self.cdata, ffi.new(self._index_ctype, xyzw))
1217
1218
    def get_fbm(self, *xyzw):
1219
        """Returh the fractional Brownian motion at the xyzw point.
1220
        """
1221
        return lib.TCOD_noise_get_fbm(self.cdata,
1222
                                      ffi.new(self._index_ctype, xyzw),
1223
                                      self.octants)
1224
1225
    def get_turbulence(self, *xyzw):
1226
        """Return the turbulence value at the xyzw point.
1227
        """
1228
        return lib.TCOD_noise_get_turbulence(self.cdata,
1229
                                             ffi.new(self._index_ctype, xyzw),
1230
                                             self.octants)
1231
1232
def clipboard_set(string):
1233
    """Set the clipboard contents to string.
1234
1235
    Args:
1236
        string (AnyStr): The string to set the clipboard to.
1237
1238
    .. versionadded:: 2.0
1239
    """
1240
    lib.TCOD_sys_clipboard_set(_bytes(string))
1241
1242
def clipboard_get():
1243
    """Return the current contents of the clipboard.
1244
1245
    Returns:
1246
        Text: The clipboards current contents.
1247
1248
    .. versionadded:: 2.0
1249
    """
1250
    return _unpack_char_p(lib.TCOD_sys_clipboard_get())
1251
1252
@ffi.def_extern()
1253
def _pycall_bsp_callback(node, handle):
1254
    """static bool _pycall_bsp_callback(TCOD_bsp_t *node, void *userData);"""
1255
    func, userData, propagate = ffi.from_handle(handle)
1256
    try:
1257
        return func(BSP(node), userData)
1258
    except BaseException:
1259
        propagate(*_sys.exc_info())
1260
        return False
1261
1262
__all__ = [_name for _name in list(globals()) if _name[0] != '_']
1263