1# Copyright 2004-2021 Tom Rothamel <pytom@bishoujo.us> 2# 3# Permission is hereby granted, free of charge, to any person 4# obtaining a copy of this software and associated documentation files 5# (the "Software"), to deal in the Software without restriction, 6# including without limitation the rights to use, copy, modify, merge, 7# publish, distribute, sublicense, and/or sell copies of the Software, 8# and to permit persons to whom the Software is furnished to do so, 9# subject to the following conditions: 10# 11# The above copyright notice and this permission notice shall be 12# included in all copies or substantial portions of the Software. 13# 14# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 22from __future__ import division, absolute_import, with_statement, print_function, unicode_literals 23from renpy.compat import * 24 25# This file contains displayables that move, zoom, rotate, or otherwise 26# transform displayables. (As well as displayables that support them.) 27import math 28import types # @UnresolvedImport 29 30import renpy.display # @UnusedImport 31from renpy.display.layout import Container 32 33from renpy.display.accelerator import transform_render 34from renpy.atl import position, any_object, bool_or_none, float_or_none, matrix, mesh 35 36# The null object that's used if we don't have a defined child. 37null = None 38 39 40def get_null(): 41 global null 42 43 if null is None: 44 null = renpy.display.layout.Null() 45 renpy.display.motion.null = null 46 47 return null 48 49# Convert a position from cartesian to polar coordinates. 50 51 52def cartesian_to_polar(x, y, xaround, yaround): 53 """ 54 Converts cartesian coordinates to polar coordinates. 55 """ 56 57 dx = x - xaround 58 dy = y - yaround 59 60 radius = math.hypot(dx, dy) 61 angle = math.atan2(dx, -dy) / math.pi * 180 62 63 if angle < 0: 64 angle += 360 65 66 return angle, radius 67 68 69def polar_to_cartesian(angle, radius, xaround, yaround): 70 """ 71 Converts polart coordinates to cartesian coordinates. 72 """ 73 74 angle = angle * math.pi / 180 75 76 dx = radius * math.sin(angle) 77 dy = -radius * math.cos(angle) 78 79 x = type(xaround)(xaround + dx) 80 y = type(yaround)(yaround + dy) 81 82 return x, y 83 84 85def first_not_none(*args): 86 """ 87 Returns the first argument that is not None. 88 """ 89 90 for i in args: 91 if i is not None: 92 return i 93 return i 94 95 96class TransformState(renpy.object.Object): 97 98 last_angle = None 99 100 def __init__(self): 101 102 # Most fields on this object are set by add_property, at the bottom 103 # of this file. 104 105 # An xpos (etc) inherited from our child overrides an xpos inherited 106 # from an old transform, but not an xpos set in the current transform. 107 # 108 # inherited_xpos stores the inherited_xpos, which is overridden by the 109 # xpos, if not None. 110 self.inherited_xpos = None 111 self.inherited_ypos = None 112 self.inherited_xanchor = None 113 self.inherited_yanchor = None 114 115 # The last angle that was rotated to. 116 self.last_angle = None 117 118 def take_state(self, ts): 119 120 d = self.__dict__ 121 122 for k in all_properties: 123 d[k] = getattr(ts, k) 124 125 self.last_angle = ts.last_angle 126 127 # Set the position and anchor to None, so inheritance works. 128 if self.perspective is None: 129 self.xpos = None 130 self.ypos = None 131 self.xanchor = None 132 self.yanchor = None 133 134 # Take the computed position properties, not the 135 # raw ones. 136 (self.inherited_xpos, 137 self.inherited_ypos, 138 self.inherited_xanchor, 139 self.inherited_yanchor, 140 _, 141 _, 142 _) = ts.get_placement() 143 144 self.xoffset = ts.xoffset 145 self.yoffset = ts.yoffset 146 self.subpixel = ts.subpixel 147 148 # Returns a dict, with p -> (old, new) where p is a property that 149 # has changed between this object and the new object. 150 def diff(self, newts): 151 152 rv = { } 153 154 for prop in diff2_properties: 155 new = getattr(newts, prop) 156 old = getattr(self, prop) 157 158 if new != old: 159 rv[prop] = (old, new) 160 161 for prop in diff4_properties: 162 163 new = getattr(newts, prop) 164 old = getattr(self, prop) 165 166 if new is None: 167 new = getattr(newts, "inherited_" + prop) 168 if old is None: 169 old = getattr(self, "inherited_" + prop) 170 171 if new != old: 172 rv[prop] = (old, new) 173 174 return rv 175 176 def get_placement(self, cxoffset=0, cyoffset=0): 177 178 if self.perspective is not None: 179 return ( 180 0, 181 0, 182 0, 183 0, 184 cxoffset, 185 cyoffset, 186 False, 187 ) 188 189 return ( 190 first_not_none(self.xpos, self.inherited_xpos), 191 first_not_none(self.ypos, self.inherited_ypos), 192 first_not_none(self.xanchor, self.inherited_xanchor), 193 first_not_none(self.yanchor, self.inherited_yanchor), 194 self.xoffset + cxoffset, 195 self.yoffset + cyoffset, 196 self.subpixel, 197 ) 198 199 # These update various properties. 200 def get_xalign(self): 201 return self.xpos 202 203 def set_xalign(self, v): 204 self.xpos = v 205 self.xanchor = v 206 207 xalign = property(get_xalign, set_xalign) 208 209 def get_yalign(self): 210 return self.ypos 211 212 def set_yalign(self, v): 213 self.ypos = v 214 self.yanchor = v 215 216 yalign = property(get_yalign, set_yalign) 217 218 def get_around(self): 219 return (self.xaround, self.yaround) 220 221 def set_around(self, value): 222 self.xaround, self.yaround = value 223 self.xanchoraround, self.yanchoraround = None, None 224 225 def set_alignaround(self, value): 226 self.xaround, self.yaround = value 227 self.xanchoraround, self.yanchoraround = value 228 229 around = property(get_around, set_around) 230 alignaround = property(get_around, set_alignaround) 231 232 def get_angle(self): 233 xpos = first_not_none(self.xpos, self.inherited_xpos, 0) 234 ypos = first_not_none(self.ypos, self.inherited_ypos, 0) 235 angle, _radius = cartesian_to_polar(xpos, ypos, self.xaround, self.yaround) 236 return angle 237 238 def get_radius(self): 239 xpos = first_not_none(self.xpos, self.inherited_xpos, 0) 240 ypos = first_not_none(self.ypos, self.inherited_ypos, 0) 241 _angle, radius = cartesian_to_polar(xpos, ypos, self.xaround, self.yaround) 242 return radius 243 244 def set_angle(self, value): 245 xpos = first_not_none(self.xpos, self.inherited_xpos, 0) 246 ypos = first_not_none(self.ypos, self.inherited_ypos, 0) 247 _angle, radius = cartesian_to_polar(xpos, ypos, self.xaround, self.yaround) 248 angle = value 249 self.xpos, self.ypos = polar_to_cartesian(angle, radius, self.xaround, self.yaround) 250 251 if self.xanchoraround: 252 self.xanchor, self.yanchor = polar_to_cartesian(angle, radius, self.xaround, self.yaround) 253 254 def set_radius(self, value): 255 xpos = first_not_none(self.xpos, self.inherited_xpos, 0) 256 ypos = first_not_none(self.ypos, self.inherited_ypos, 0) 257 angle, _radius = cartesian_to_polar(xpos, ypos, self.xaround, self.yaround) 258 radius = value 259 self.xpos, self.ypos = polar_to_cartesian(angle, radius, self.xaround, self.yaround) 260 261 if self.xanchoraround: 262 self.xanchor, self.yanchor = polar_to_cartesian(angle, radius, self.xaround, self.yaround) 263 264 angle = property(get_angle, set_angle) 265 radius = property(get_radius, set_radius) 266 267 def get_pos(self): 268 return self.xpos, self.ypos 269 270 def set_pos(self, value): 271 self.xpos, self.ypos = value 272 273 pos = property(get_pos, set_pos) 274 275 def get_anchor(self): 276 return self.xanchor, self.yanchor 277 278 def set_anchor(self, value): 279 self.xanchor, self.yanchor = value 280 281 anchor = property(get_anchor, set_anchor) 282 283 def get_align(self): 284 return self.xpos, self.ypos 285 286 def set_align(self, value): 287 self.xanchor, self.yanchor = value 288 self.xpos, self.ypos = value 289 290 align = property(get_align, set_align) 291 292 def get_offset(self): 293 return self.xoffset, self.yoffset 294 295 def set_offset(self, value): 296 self.xoffset, self.yoffset = value 297 298 offset = property(get_offset, set_offset) 299 300 def get_xysize(self): 301 return self.xsize, self.ysize 302 303 def set_xysize(self, value): 304 if value is None: 305 value = (None, None) 306 self.xsize, self.ysize = value 307 308 xysize = property(get_xysize, set_xysize) 309 310 def set_size(self, value): 311 if value is None: 312 self.xysize = None 313 else: 314 self.xysize = tuple(int(x) if isinstance(x, float) else x for x in value) 315 316 size = property(get_xysize, set_size) 317 318 def set_xcenter(self, value): 319 self.xpos = value 320 self.xanchor = 0.5 321 322 def get_xcenter(self): 323 return self.xpos 324 325 def set_ycenter(self, value): 326 self.ypos = value 327 self.yanchor = 0.5 328 329 def get_ycenter(self): 330 return self.ypos 331 332 xcenter = property(get_xcenter, set_xcenter) 333 ycenter = property(get_ycenter, set_ycenter) 334 335 def get_xycenter(self): 336 return self.xcenter, self.ycenter 337 338 def set_xycenter(self, value): 339 if value is None: 340 value = (None, None) 341 self.xcenter, self.ycenter = value 342 343 xycenter = property(get_xycenter, set_xycenter) 344 345 346class Proxy(object): 347 """ 348 This class proxies a field from the transform to its state. 349 """ 350 351 def __init__(self, name): 352 self.name = name 353 354 def __get__(self, instance, owner): 355 return getattr(instance.state, self.name) 356 357 def __set__(self, instance, value): 358 return setattr(instance.state, self.name, value) 359 360 361class Transform(Container): 362 """ 363 Documented in sphinx, because we can't scan this object. 364 """ 365 366 __version__ = 5 367 transform_event_responder = True 368 369 def after_upgrade(self, version): 370 371 if version < 1: 372 self.active = False 373 self.state = TransformState() 374 375 self.state.xpos = self.xpos or 0 376 self.state.ypos = self.ypos or 0 377 self.state.xanchor = self.xanchor or 0 378 self.state.yanchor = self.yanchor or 0 379 self.state.alpha = self.alpha 380 self.state.rotate = self.rotate 381 self.state.zoom = self.zoom 382 self.state.xzoom = self.xzoom 383 self.state.yzoom = self.yzoom 384 385 self.hide_request = False 386 self.hide_response = True 387 388 if version < 2: 389 self.st = 0 390 self.at = 0 391 392 if version < 3: 393 self.st_offset = 0 394 self.at_offset = 0 395 self.child_st_base = 0 396 397 if version < 4: 398 self.style_arg = 'transform' 399 400 if version < 5: 401 self.replaced_request = False 402 self.replaced_response = True 403 404 DEFAULT_ARGUMENTS = { 405 "selected_activate" : { }, 406 "selected_hover" : { }, 407 "selected_idle" : { }, 408 "selected_insensitive" : { }, 409 "activate" : { }, 410 "hover" : { }, 411 "idle" : { }, 412 "insensitive" : { }, 413 "" : { }, 414 } 415 416 # Compatibility with old versions of the class. 417 active = False 418 children = False 419 arguments = DEFAULT_ARGUMENTS 420 421 # Default before we set this. 422 child_size = (0, 0) 423 424 def __init__(self, 425 child=None, 426 function=None, 427 style="default", 428 focus=None, 429 default=False, 430 _args=None, 431 432 **kwargs): 433 434 self.kwargs = kwargs 435 self.style_arg = style 436 437 super(Transform, self).__init__(style=style, focus=focus, default=default, _args=_args) 438 439 self.function = function 440 441 child = renpy.easy.displayable_or_none(child) 442 if child is not None: 443 self.add(child) 444 445 self.state = TransformState() 446 447 if kwargs: 448 449 # A map from prefix -> (prop -> value) 450 self.arguments = { } 451 452 # Fill self.arguments with a 453 for k, v in kwargs.items(): 454 455 prefix = "" 456 prop = k 457 458 while True: 459 460 if prop in renpy.atl.PROPERTIES and (not prefix or prefix in Transform.DEFAULT_ARGUMENTS): 461 462 if prefix not in self.arguments: 463 self.arguments[prefix] = { } 464 465 self.arguments[prefix][prop] = v 466 break 467 468 new_prefix, _, prop = prop.partition("_") 469 470 if not prop: 471 raise Exception("Unknown transform property: %r" % k) 472 473 if prefix: 474 prefix = prefix + "_" + new_prefix 475 else: 476 prefix = new_prefix 477 478 if "" in self.arguments: 479 for k, v in self.arguments[""].items(): 480 setattr(self.state, k, v) 481 482 else: 483 self.arguments = None 484 485 # This is the matrix transforming our coordinates into child coordinates. 486 self.forward = None 487 488 # Have we called the function at least once? 489 self.active = False 490 491 # Have we been requested to hide? 492 self.hide_request = False 493 494 # True if it's okay for us to hide. 495 self.hide_response = True 496 497 # Have we been requested to replaced? 498 self.replaced_request = False 499 500 # True if it's okay for us to replaced. 501 self.replaced_response = True 502 503 self.st = 0 504 self.at = 0 505 self.st_offset = 0 506 self.at_offset = 0 507 508 self.child_st_base = 0 509 510 def visit(self): 511 if self.child is None: 512 return [ ] 513 else: 514 return [ self.child ] 515 516 # The default function chooses entries from self.arguments that match 517 # the style prefix, and applies them to the state. 518 def default_function(self, state, st, at): 519 520 if self.arguments is None: 521 return None 522 523 prefix = self.style.prefix.strip("_") 524 prefixes = [ ] 525 526 while prefix: 527 prefixes.insert(0, prefix) 528 _, _, prefix = prefix.partition("_") 529 530 prefixes.insert(0, "") 531 532 for i in prefixes: 533 d = self.arguments.get(i, None) 534 535 if d is None: 536 continue 537 538 for k, v in d.items(): 539 setattr(state, k, v) 540 541 return None 542 543 def set_transform_event(self, event): 544 if self.child is not None: 545 self.child.set_transform_event(event) 546 self.last_child_transform_event = event 547 548 super(Transform, self).set_transform_event(event) 549 550 def take_state(self, t): 551 """ 552 Takes the transformation state from object t into this object. 553 """ 554 555 if self is t: 556 return 557 558 if not isinstance(t, Transform): 559 return 560 561 self.state.take_state(t.state) 562 563 if isinstance(self.child, Transform) and isinstance(t.child, Transform): 564 self.child.take_state(t.child) 565 566 if (self.child is None) and (t.child is not None): 567 self.add(t.child) 568 self.child_st_base = t.child_st_base 569 570 # The arguments will be applied when the default function is 571 # called. 572 573 def take_execution_state(self, t): 574 """ 575 Takes the execution state from object t into this object. This is 576 overridden by renpy.atl.TransformBase. 577 """ 578 579 if self is t: 580 return 581 582 if not isinstance(t, Transform): 583 return 584 585 self.hide_request = t.hide_request 586 self.replaced_request = t.replaced_request 587 588 self.state.xpos = t.state.xpos 589 self.state.ypos = t.state.ypos 590 self.state.xanchor = t.state.xanchor 591 self.state.yanchor = t.state.yanchor 592 593 self.child_st_base = t.child_st_base 594 595 if isinstance(self.child, Transform) and isinstance(t.child, Transform): 596 self.child.take_execution_state(t.child) 597 598 def copy(self): 599 """ 600 Makes a copy of this transform. 601 """ 602 603 d = self() 604 d.kwargs = { } 605 d.take_state(self) 606 d.take_execution_state(self) 607 d.st = self.st 608 d.at = self.at 609 610 return d 611 612 def _change_transform_child(self, child): 613 rv = self.copy() 614 615 if self.child is not None: 616 rv.set_child(self.child._change_transform_child(child)) 617 618 return rv 619 620 def _handles_event(self, event): 621 622 if (event == "replaced") and (not self.active): 623 return True 624 625 if self.function is not None: 626 return True 627 628 if self.child and self.child._handles_event(event): 629 return True 630 631 return False 632 633 def _hide(self, st, at, kind): 634 635 # Prevent time from ticking backwards, as can happen if we replace a 636 # transform but keep its state. 637 if st + self.st_offset <= self.st: 638 self.st_offset = self.st - st 639 if at + self.at_offset <= self.at: 640 self.at_offset = self.at - at 641 642 self.st = st = st + self.st_offset 643 self.at = at = at + self.at_offset 644 645 if not self.active: 646 self.update_state() 647 648 if not self.child: 649 return None 650 651 if not (self.hide_request or self.replaced_request): 652 d = self.copy() 653 else: 654 d = self 655 656 d.st_offset = self.st_offset 657 d.at_offset = self.at_offset 658 659 if isinstance(self, ATLTransform): 660 d.atl_st_offset = self.atl_st_offset if (self.atl_st_offset is not None) else self.st_offset 661 662 if kind == "hide": 663 d.hide_request = True 664 else: 665 d.replaced_request = True 666 667 d.hide_response = True 668 d.replaced_response = True 669 670 if d.function is not None: 671 d.function(d, st, at) 672 elif isinstance(d, ATLTransform): 673 d.execute(d, st, at) 674 675 new_child = d.child._hide(st - self.st_offset, at - self.st_offset, kind) 676 677 if new_child is not None: 678 d.child = new_child 679 d.hide_response = False 680 d.replaced_response = False 681 682 if (not d.hide_response) or (not d.replaced_response): 683 renpy.display.render.redraw(d, 0) 684 return d 685 686 return None 687 688 def set_child(self, child, duplicate=True): 689 690 child = renpy.easy.displayable(child) 691 692 if duplicate and child._duplicatable: 693 child = child._duplicate(self._args) 694 child._unique() 695 696 if child._duplicatable: 697 self._duplicatable = True 698 699 self.child = child 700 self.children = [ child ] 701 702 self.child_st_base = self.st 703 704 child.per_interact() 705 706 renpy.display.render.invalidate(self) 707 708 def update_state(self): 709 """ 710 This updates the state to that at self.st, self.at. 711 """ 712 713 # NOTE: This function is duplicated (more or less) in ATLTransform. 714 715 self.hide_response = True 716 self.replaced_response = True 717 718 # If we have to, call the function that updates this transform. 719 if self.arguments is not None: 720 self.default_function(self, self.st, self.at) 721 722 if self.function is not None: 723 fr = self.function(self, self.st, self.at) 724 725 # Order a redraw, if necessary. 726 if fr is not None: 727 renpy.display.render.redraw(self, fr) 728 729 self.active = True 730 731 # The render method is now defined in accelerator.pyx. 732 def render(self, width, height, st, at): 733 return transform_render(self, width, height, st, at) 734 735 def event(self, ev, x, y, st): 736 737 if self.hide_request: 738 return None 739 740 if not self.state.events: 741 return 742 743 children = self.children 744 offsets = self.offsets 745 746 if not offsets: 747 return None 748 749 for i in range(len(self.children) - 1, -1, -1): 750 751 d = children[i] 752 xo, yo = offsets[i] 753 754 cx = x - xo 755 cy = y - yo 756 757 # Transform screen coordinates to child coordinates. 758 cx, cy = self.forward.transform(cx, cy) 759 760 rv = d.event(ev, cx, cy, st) 761 if rv is not None: 762 return rv 763 764 return None 765 766 def __call__(self, child=None, take_state=True, _args=None): 767 768 if child is None: 769 child = self.child 770 771 if (child is not None) and (child._duplicatable): 772 child = child._duplicate(_args) 773 774 rv = Transform( 775 child=child, 776 function=self.function, 777 style=self.style_arg, 778 _args=_args, 779 **self.kwargs) 780 781 rv.take_state(self) 782 783 return rv 784 785 def _unique(self): 786 if self.child and self.child._duplicatable: 787 self._duplicatable = True 788 else: 789 self._duplicatable = False 790 791 def get_placement(self): 792 793 if not self.active: 794 self.update_state() 795 796 if self.child is not None: 797 cxpos, cypos, cxanchor, cyanchor, cxoffset, cyoffset, csubpixel = self.child.get_placement() 798 799 # Use non-None elements of the child placement as defaults. 800 state = self.state 801 802 if renpy.config.transform_uses_child_position: 803 804 if cxpos is not None: 805 state.inherited_xpos = cxpos 806 if cxanchor is not None: 807 state.inherited_xanchor = cxanchor 808 if cypos is not None: 809 state.inherited_ypos = cypos 810 if cyanchor is not None: 811 state.inherited_yanchor = cyanchor 812 813 state.subpixel |= csubpixel 814 815 else: 816 cxoffset = 0 817 cyoffset = 0 818 819 cxoffset = cxoffset or 0 820 cyoffset = cyoffset or 0 821 822 rv = self.state.get_placement(cxoffset, cyoffset) 823 824 if self.state.transform_anchor: 825 826 xpos, ypos, xanchor, yanchor, xoffset, yoffset, subpixel = rv 827 if (xanchor is not None) and (yanchor is not None): 828 829 cw, ch = self.child_size 830 rw, rh = self.render_size 831 832 if xanchor.__class__ is float: 833 xanchor *= cw 834 if yanchor.__class__ is float: 835 yanchor *= ch 836 837 xanchor -= cw / 2.0 838 yanchor -= ch / 2.0 839 840 xanchor, yanchor = self.reverse.transform(xanchor, yanchor) 841 842 xanchor += rw / 2.0 843 yanchor += rh / 2.0 844 845 xanchor = renpy.display.core.absolute(xanchor) 846 yanchor = renpy.display.core.absolute(yanchor) 847 848 rv = (xpos, ypos, xanchor, yanchor, xoffset, yoffset, subpixel) 849 850 return rv 851 852 def update(self): 853 """ 854 This should be called when a transform property field is updated outside 855 of the callback method, to ensure that the change takes effect. 856 """ 857 858 renpy.display.render.invalidate(self) 859 860 _duplicatable = True 861 862 def _duplicate(self, args): 863 864 if args and args.args: 865 args.extraneous() 866 867 if not self._duplicatable: 868 return self 869 870 rv = self(_args=args) 871 rv.take_execution_state(self) 872 rv._unique() 873 874 return rv 875 876 def _in_current_store(self): 877 878 if self.child is None: 879 return self 880 881 child = self.child._in_current_store() 882 if child is self.child: 883 return self 884 rv = self() 885 rv.take_execution_state(self) 886 rv.child = child 887 rv._unique() 888 889 return rv 890 891 892class ATLTransform(renpy.atl.ATLTransformBase, Transform): 893 894 def __init__(self, atl, child=None, context={}, parameters=None, **properties): 895 renpy.atl.ATLTransformBase.__init__(self, atl, context, parameters) 896 Transform.__init__(self, child=child, **properties) 897 898 self.raw_child = self.child 899 900 def update_state(self): 901 """ 902 This updates the state to that at self.st, self.at. 903 """ 904 905 self.hide_response = True 906 self.replaced_response = True 907 908 fr = self.execute(self, self.st, self.at) 909 910 # Order a redraw, if necessary. 911 if fr is not None: 912 renpy.display.render.redraw(self, fr) 913 914 self.active = True 915 916 def __repr__(self): 917 return "<ATL Transform {:x} {!r}>".format(id(self), self.atl.loc) 918 919 920# Names of transform properties, and if the property should be handles with 921# diff2 or diff2. 922all_properties = set() 923diff2_properties = set() 924diff4_properties = set() 925 926# Uniforms and GL properties. 927uniforms = set() 928gl_properties = set() 929 930 931def add_property(name, atl=any_object, default=None, diff=2): 932 """ 933 Adds an ATL property. 934 """ 935 936 if name in all_properties: 937 return 938 939 all_properties.add(name) 940 setattr(TransformState, name, default) 941 setattr(Transform, name, Proxy(name)) 942 renpy.atl.PROPERTIES[name] = atl 943 944 if diff == 2: 945 diff2_properties.add(name) 946 elif diff == 4: 947 diff4_properties.add(name) 948 949 950def add_uniform(name): 951 """ 952 Adds a uniform with `name` to Transform and ATL. 953 """ 954 955 if not name.startswith("u_"): 956 return 957 958 if name in renpy.gl2.gl2draw.standard_uniforms: 959 return 960 961 add_property(name, diff=2) 962 963 uniforms.add(name) 964 965 966def add_gl_property(name): 967 """ 968 Adds a GL property with `name` to Transform and ATL. 969 """ 970 971 add_property(name, diff=None) 972 973 gl_properties.add(name) 974 975 976add_property("additive", float, 0.0) 977add_property("alpha", float, 1.0) 978add_property("blend", any_object, None) 979add_property("blur", float_or_none, None) 980add_property("corner1", (float, float), None) 981add_property("corner2", (float, float), None) 982add_property("crop", (float, float, float, float), None) 983add_property("crop_relative", bool, False) 984add_property("debug", any_object, None) 985add_property("delay", float, 0) 986add_property("events", bool, True) 987add_property("fit", str, None) 988add_property("matrixanchor", (position, position), None) 989add_property("matrixcolor", matrix, None) 990add_property("matrixtransform", matrix, None) 991add_property("maxsize", (int, int), None) 992add_property("mesh", mesh, False, diff=None) 993add_property("mesh_pad", any_object, None) 994add_property("nearest", bool_or_none, None) 995add_property("perspective", any_object, None) 996add_property("rotate", float, None) 997add_property("rotate_pad", bool, True) 998add_property("shader", any_object, None, diff=None) 999add_property("subpixel", bool, False) 1000add_property("transform_anchor", bool, False) 1001add_property("zoom", float, 1.0) 1002 1003add_property("xanchoraround", float, 0.0) 1004add_property("xanchor", position, None, diff=4) 1005add_property("xaround", position, 0.0) 1006add_property("xoffset", float, 0.0) 1007add_property("xpan", float_or_none, None) 1008add_property("xpos", position, None, diff=4) 1009add_property("xsize", position, None) 1010add_property("xtile", int, 1) 1011add_property("xzoom", float, 1.0) 1012 1013add_property("yanchoraround", float, 0.0) 1014add_property("yanchor", position, None, diff=4) 1015add_property("yaround", position, 0.0) 1016add_property("yoffset", float, 0.0) 1017add_property("ypan", float_or_none, None) 1018add_property("ypos", position, None, diff=4) 1019add_property("ysize", position, None) 1020add_property("ytile", int, 1) 1021add_property("yzoom", float, 1.0) 1022 1023add_property("zpos", float, 0.0) 1024add_property("zzoom", bool, False) 1025 1026add_gl_property("gl_anisotropic") 1027add_gl_property("gl_blend_func") 1028add_gl_property("gl_color_mask") 1029add_gl_property("gl_depth") 1030add_gl_property("gl_mipmap") 1031add_gl_property("gl_pixel_perfect") 1032add_gl_property("gl_texture_scaling") 1033add_gl_property("gl_texture_wrap") 1034 1035ALIASES = { 1036 "alignaround" : (float, float), 1037 "align" : (float, float), 1038 "anchor" : (position, position), 1039 "angle" : float, 1040 "around" : (position, position), 1041 "offset" : (int, int), 1042 "pos" : (position, position), 1043 "radius" : float, 1044 "size" : (int, int), 1045 "xalign" : float, 1046 "xcenter" : position, 1047 "xycenter" : (position, position), 1048 "xysize" : (position, position), 1049 "yalign" : float, 1050 "ycenter" : position, 1051 } 1052 1053renpy.atl.PROPERTIES.update(ALIASES) 1054 1055for name in ALIASES: 1056 setattr(Transform, name, Proxy(name)) 1057