summaryrefslogtreecommitdiff
path: root/unrpyc/renpy/display/movetransition.py
diff options
context:
space:
mode:
authorAlex Xu <alex_y_xu@yahoo.ca>2013-08-22 22:45:26 -0400
committerAlex Xu <alex_y_xu@yahoo.ca>2013-08-22 22:45:26 -0400
commit718936110b9511631fa1f4396be992752bf8b719 (patch)
treea871768c06adc2959f8f0d69869532d36a95ffab /unrpyc/renpy/display/movetransition.py
parentece6cf9fbfdba9dac8d7bf98516a840c955a4853 (diff)
downloadhtml5ks-718936110b9511631fa1f4396be992752bf8b719.tar.xz
html5ks-718936110b9511631fa1f4396be992752bf8b719.zip
include renpy
Diffstat (limited to 'unrpyc/renpy/display/movetransition.py')
-rw-r--r--unrpyc/renpy/display/movetransition.py640
1 files changed, 640 insertions, 0 deletions
diff --git a/unrpyc/renpy/display/movetransition.py b/unrpyc/renpy/display/movetransition.py
new file mode 100644
index 0000000..1b3bdd7
--- /dev/null
+++ b/unrpyc/renpy/display/movetransition.py
@@ -0,0 +1,640 @@
+# Copyright 2004-2013 Tom Rothamel <pytom@bishoujo.us>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation files
+# (the "Software"), to deal in the Software without restriction,
+# including without limitation the rights to use, copy, modify, merge,
+# publish, distribute, sublicense, and/or sell copies of the Software,
+# and to permit persons to whom the Software is furnished to do so,
+# subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+# NOTE:
+# Transitions need to be able to work even when old_widget and new_widget
+# are None, at least to the point of making it through __init__. This is
+# so that prediction of images works.
+
+import renpy.display
+
+# Utility function used by MoveTransition et al.
+def position(d):
+
+ xpos, ypos, xanchor, yanchor, _xoffset, _yoffset, _subpixel = d.get_placement()
+
+ if xpos is None:
+ xpos = 0
+ if ypos is None:
+ ypos = 0
+ if xanchor is None:
+ xanchor = 0
+ if yanchor is None:
+ yanchor = 0
+
+ return xpos, ypos, xanchor, yanchor
+
+def offsets(d):
+
+ _xpos, _ypos, _xanchor, _yanchor, xoffset, yoffset, _subpixel = d.get_placement()
+
+ if renpy.config.movetransition_respects_offsets:
+ return { 'xoffset' : xoffset, 'yoffset' : yoffset }
+ else:
+ return { }
+
+
+# These are used by MoveTransition.
+def MoveFactory(pos1, pos2, delay, d, **kwargs):
+ if pos1 == pos2:
+ return d
+
+ return renpy.display.motion.Move(pos1, pos2, delay, d, **kwargs)
+
+def default_enter_factory(pos, delay, d, **kwargs):
+ return d
+
+def default_leave_factory(pos, delay, d, **kwargs):
+ return None
+
+# These can be used to move things in and out of the screen.
+def MoveIn(pos, pos1, delay, d, **kwargs):
+
+ def aorb(a, b):
+ if a is None:
+ return b
+ return a
+
+ pos = tuple([aorb(a, b) for a, b in zip(pos, pos1)])
+ return renpy.display.motion.Move(pos, pos1, delay, d, **kwargs)
+
+def MoveOut(pos, pos1, delay, d, **kwargs):
+
+ def aorb(a, b):
+ if a is None:
+ return b
+ return a
+
+ pos = tuple([aorb(a, b) for a, b in zip(pos, pos1)])
+ return renpy.display.motion.Move(pos1, pos, delay, d, **kwargs)
+
+def ZoomInOut(start, end, pos, delay, d, **kwargs):
+
+ xpos, ypos, xanchor, yanchor = pos
+
+ FactorZoom = renpy.display.motion.FactorZoom
+
+ if end == 1.0:
+ return FactorZoom(start, end, delay, d, after_child=d, opaque=False,
+ xpos=xpos, ypos=ypos, xanchor=xanchor, yanchor=yanchor, **kwargs)
+ else:
+ return FactorZoom(start, end, delay, d, opaque=False,
+ xpos=xpos, ypos=ypos, xanchor=xanchor, yanchor=yanchor, **kwargs)
+
+def RevolveInOut(start, end, pos, delay, d, **kwargs):
+ return renpy.display.motion.Revolve(start, end, delay, d, pos=pos, **kwargs)
+
+
+def OldMoveTransition(delay, old_widget=None, new_widget=None, factory=None, enter_factory=None, leave_factory=None, old=False, layers=[ 'master' ]):
+ """
+ Returns a transition that attempts to find images that have changed
+ position, and moves them from the old position to the new transition, taking
+ delay seconds to complete the move.
+
+ If `factory` is given, it is expected to be a function that takes as
+ arguments: an old position, a new position, the delay, and a
+ displayable, and to return a displayable as an argument. If not
+ given, the default behavior is to move the displayable from the
+ starting to the ending positions. Positions are always given as
+ (xpos, ypos, xanchor, yanchor) tuples.
+
+ If `enter_factory` or `leave_factory` are given, they are expected
+ to be functions that take as arguments a position, a delay, and a
+ displayable, and return a displayable. They are applied to
+ displayables that are entering or leaving the scene,
+ respectively. The default is to show in place displayables that
+ are entering, and not to show those that are leaving.
+
+ If `old` is True, then factory moves the old displayable with the
+ given tag. Otherwise, it moves the new displayable with that
+ tag.
+
+ `layers` is a list of layers that the transition will be applied
+ to.
+
+ Images are considered to be the same if they have the same tag, in
+ the same way that the tag is used to determine which image to
+ replace or to hide. They are also considered to be the same if
+ they have no tag, but use the same displayable.
+
+ Computing the order in which images are displayed is a three-step
+ process. The first step is to create a list of images that
+ preserves the relative ordering of entering and moving images. The
+ second step is to insert the leaving images such that each leaving
+ image is at the lowest position that is still above all images
+ that were below it in the original scene. Finally, the list
+ is sorted by zorder, to ensure no zorder violations occur.
+
+ If you use this transition to slide an image off the side of the
+ screen, remember to hide it when you are done. (Or just use
+ a leave_factory.)
+ """
+
+ if factory is None:
+ factory = MoveFactory
+
+ if enter_factory is None:
+ enter_factory = default_enter_factory
+
+ if leave_factory is None:
+ leave_factory = default_leave_factory
+
+ use_old = old
+
+ def merge_slide(old, new):
+
+ # If new does not have .layers or .scene_list, then we simply
+ # insert a move from the old position to the new position, if
+ # a move occured.
+
+ if (not isinstance(new, renpy.display.layout.MultiBox)
+ or (new.layers is None and new.layer_name is None)):
+
+ if use_old:
+ child = old
+ else:
+ child = new
+
+ old_pos = position(old)
+ new_pos = position(new)
+
+ if old_pos != new_pos:
+ return factory(old_pos,
+ new_pos,
+ delay,
+ child,
+ **offsets(child)
+ )
+
+ else:
+ return child
+
+ # If we're in the layers_root widget, merge the child widgets
+ # for each layer.
+ if new.layers:
+
+ assert old.layers
+
+ rv = renpy.display.layout.MultiBox(layout='fixed')
+ rv.layers = { }
+
+ for layer in renpy.config.layers:
+
+ f = new.layers[layer]
+
+ if (isinstance(f, renpy.display.layout.MultiBox)
+ and layer in layers
+ and f.scene_list is not None):
+
+ f = merge_slide(old.layers[layer], new.layers[layer])
+
+ rv.layers[layer] = f
+ rv.add(f)
+
+ return rv
+
+ # Otherwise, we recompute the scene list for the two widgets, merging
+ # as appropriate.
+
+ # Wraps the displayable found in SLE so that the various timebases
+ # are maintained.
+ def wrap(sle):
+ return renpy.display.layout.AdjustTimes(sle.displayable, sle.show_time, sle.animation_time)
+
+ def tag(sle):
+ return sle.tag or sle.displayable
+
+ def merge(sle, d):
+ rv = sle.copy()
+ rv.show_time = 0
+ rv.displayable = d
+ return rv
+
+ def entering(sle):
+ new_d = wrap(new_sle)
+ move = enter_factory(position(new_d), delay, new_d, **offsets(new_d))
+
+ if move is None:
+ return
+
+ rv_sl.append(merge(new_sle, move))
+
+ def leaving(sle):
+ old_d = wrap(sle)
+ move = leave_factory(position(old_d), delay, old_d, **offsets(old_d))
+
+ if move is None:
+ return
+
+ move = renpy.display.layout.IgnoresEvents(move)
+ rv_sl.append(merge(old_sle, move))
+
+
+ def moving(old_sle, new_sle):
+ old_d = wrap(old_sle)
+ new_d = wrap(new_sle)
+
+ if use_old:
+ child = old_d
+ else:
+ child = new_d
+
+ move = factory(position(old_d), position(new_d), delay, child, **offsets(child))
+ if move is None:
+ return
+
+ rv_sl.append(merge(new_sle, move))
+
+
+ # The old, new, and merged scene_lists.
+ old_sl = old.scene_list[:]
+ new_sl = new.scene_list[:]
+ rv_sl = [ ]
+
+
+ # A list of tags in old_sl, new_sl, and rv_sl.
+ old_map = dict((tag(i), i) for i in old_sl if i is not None)
+ new_tags = set(tag(i) for i in new_sl if i is not None)
+ rv_tags = set()
+
+ while old_sl or new_sl:
+
+ # If we have something in old_sl, then
+ if old_sl:
+
+ old_sle = old_sl[0]
+ old_tag = tag(old_sle)
+
+ # If the old thing has already moved, then remove it.
+ if old_tag in rv_tags:
+ old_sl.pop(0)
+ continue
+
+ # If the old thing does not match anything in new_tags,
+ # have it enter.
+ if old_tag not in new_tags:
+ leaving(old_sle)
+ rv_tags.add(old_tag)
+ old_sl.pop(0)
+ continue
+
+
+ # Otherwise, we must have something in new_sl. We want to
+ # either move it or have it enter.
+
+ new_sle = new_sl.pop(0)
+ new_tag = tag(new_sle)
+
+ # If it exists in both, move.
+ if new_tag in old_map:
+ old_sle = old_map[new_tag]
+
+ moving(old_sle, new_sle)
+ rv_tags.add(new_tag)
+ continue
+
+ else:
+ entering(new_sle)
+ rv_tags.add(new_tag)
+ continue
+
+ # Sort everything by zorder, to ensure that there are no zorder
+ # violations in the result.
+ rv_sl.sort(key=lambda a : a.zorder)
+
+ layer = new.layer_name
+ rv = renpy.display.layout.MultiBox(layout='fixed', focus=layer, **renpy.game.interface.layer_properties[layer])
+ rv.append_scene_list(rv_sl)
+ rv.layer_name = layer
+
+ return rv
+
+
+ # This calls merge_slide to actually do the merging.
+
+ rv = merge_slide(old_widget, new_widget)
+ rv.delay = delay # W0201
+
+ return rv
+
+##############################################################################
+# New Move Transition (since 6.14)
+
+
+class MoveInterpolate(renpy.display.core.Displayable):
+ """
+ This displayable has two children. It interpolates between the positions
+ of its two children to place them on the screen.
+ """
+
+ def __init__(self, delay, old, new, use_old, time_warp):
+ super(MoveInterpolate, self).__init__()
+
+ # The old and new displayables.
+ self.old = old
+ self.new = new
+
+ # Should we display the old displayable?
+ self.use_old = False
+
+ # Time warp function or None.
+ self.time_warp = time_warp
+
+ # The width of the screen.
+ self.screen_width = 0
+ self.screen_height = 0
+
+ # The width of the selected child.
+ self.child_width = 0
+ self.child_height = 0
+
+ # The delay and st.
+ self.delay = delay
+ self.st = 0
+
+ def render(self, width, height, st, at):
+ self.screen_width = width
+ self.screen_height = height
+
+ old_r = renpy.display.render.render(self.old, width, height, st, at)
+ new_r = renpy.display.render.render(self.new, width, height, st, at)
+
+ if self.use_old:
+ cr = old_r
+ else:
+ cr = new_r
+
+ self.child_width, self.child_height = cr.get_size()
+ self.st = st
+
+ if self.st < self.delay:
+ renpy.display.render.redraw(self, 0)
+
+ return cr
+
+ def child_placement(self, child):
+
+ def based(v, base):
+ if v is None:
+ return 0
+ elif isinstance(v, int):
+ return v
+ elif isinstance(v, renpy.display.core.absolute):
+ return v
+ else:
+ return v * base
+
+ xpos, ypos, xanchor, yanchor, xoffset, yoffset, subpixel = child.get_placement()
+
+ xpos = based(xpos, self.screen_width)
+ ypos = based(ypos, self.screen_height)
+ xanchor = based(xanchor, self.child_width)
+ yanchor = based(yanchor, self.child_height)
+
+ return xpos, ypos, xanchor, yanchor, xoffset, yoffset, subpixel
+
+ def get_placement(self):
+
+ if self.st > self.delay:
+ done = 1.0
+ else:
+ done = self.st / self.delay
+
+ if self.time_warp is not None:
+ done = self.time_warp(done)
+
+ absolute = renpy.display.core.absolute
+
+ def I(a, b):
+ return absolute(a + done * (b - a))
+
+ old_xpos, old_ypos, old_xanchor, old_yanchor, old_xoffset, old_yoffset, old_subpixel = self.child_placement(self.old)
+ new_xpos, new_ypos, new_xanchor, new_yanchor, new_xoffset, new_yoffset, new_subpixel = self.child_placement(self.new)
+
+ xpos = I(old_xpos, new_xpos)
+ ypos = I(old_ypos, new_ypos)
+ xanchor = I(old_xanchor, new_xanchor)
+ yanchor = I(old_yanchor, new_yanchor)
+ xoffset = I(old_xoffset, new_xoffset)
+ yoffset = I(old_yoffset, new_yoffset)
+ subpixel = old_subpixel or new_subpixel
+
+ return xpos, ypos, xanchor, yanchor, xoffset, yoffset, subpixel
+
+
+def MoveTransition(delay, old_widget=None, new_widget=None, enter=None, leave=None, old=False, layers=[ 'master' ], time_warp=None, enter_time_warp=None, leave_time_warp=None):
+ """
+ :doc: transition function
+ :args: (delay, enter=None, leave=None, old=False, layers=['master'], time_warp=None, enter_time_warp=None, leave_time_warp=None)
+ :name: MoveTransition
+
+ Returns a transition that interpolates the position of images (with the
+ same tag) in the old and new scenes.
+
+ `delay`
+ The time it takes for the interpolation to finish.
+
+ `enter`
+ If not None, images entering the scene will also be moved. The value
+ of `enter` should be a transform that is applied to the image to
+ get its starting position.
+
+ `leave`
+ If not None, images leaving the scene will also be move. The value
+ of `leave` should be a transform that is applied to the image to
+ get its ending position.
+
+ `old`
+ If true, the old image will be used in preference to the new one.
+
+ `layers`
+ A list of layers that moves are applied to.
+
+ `time_warp`
+ A time warp function that's applied to the interpolation. This
+ takes a number between 0.0 and 1.0, and should return a number in
+ the same range.
+
+ `enter_time_warp`
+ A time warp function that's applied to images entering the scene.
+
+ `enter_time_warp`
+ A time warp function that's applied to images leaving the scene.
+
+ """
+
+ use_old = old
+
+ def merge_slide(old, new):
+
+ # If new does not have .layers or .scene_list, then we simply
+ # insert a move from the old position to the new position, if
+ # a move occured.
+
+ if (not isinstance(new, renpy.display.layout.MultiBox)
+ or (new.layers is None and new.layer_name is None)):
+
+ if old is new:
+ return new
+ else:
+ return MoveInterpolate(delay, old, new, use_old, time_warp)
+
+
+ # If we're in the layers_root widget, merge the child widgets
+ # for each layer.
+ if new.layers:
+
+ assert old.layers
+
+ rv = renpy.display.layout.MultiBox(layout='fixed')
+
+ for layer in renpy.config.layers:
+
+ f = new.layers[layer]
+
+ if (isinstance(f, renpy.display.layout.MultiBox)
+ and layer in layers
+ and f.scene_list is not None):
+
+ f = merge_slide(old.layers[layer], new.layers[layer])
+
+ rv.add(f)
+
+ return rv
+
+ # Otherwise, we recompute the scene list for the two widgets, merging
+ # as appropriate.
+
+ # Wraps the displayable found in SLE so that the various timebases
+ # are maintained.
+ def wrap(sle):
+ return renpy.display.layout.AdjustTimes(sle.displayable, sle.show_time, sle.animation_time)
+
+ def tag(sle):
+ return sle.tag or sle.displayable
+
+ def merge(sle, d):
+ rv = sle.copy()
+ rv.show_time = 0
+ rv.displayable = d
+ return rv
+
+ def entering(sle):
+
+ if not enter:
+ return
+
+ new_d = wrap(new_sle)
+ move = MoveInterpolate(delay, enter(new_d), new_d, False, enter_time_warp)
+ rv_sl.append(merge(new_sle, move))
+
+ def leaving(sle):
+
+ if not leave:
+ return
+
+ old_d = wrap(sle)
+ move = MoveInterpolate(delay, old_d, leave(old_d), True, leave_time_warp)
+ move = renpy.display.layout.IgnoresEvents(move)
+ rv_sl.append(merge(old_sle, move))
+
+
+ def moving(old_sle, new_sle):
+
+ if old_sle.displayable is new_sle.displayable:
+ rv_sl.append(new_sle)
+ return
+
+ old_d = wrap(old_sle)
+ new_d = wrap(new_sle)
+
+ move = MoveInterpolate(delay, old_d, new_d, use_old, time_warp)
+
+ rv_sl.append(merge(new_sle, move))
+
+
+ # The old, new, and merged scene_lists.
+ old_sl = old.scene_list[:]
+ new_sl = new.scene_list[:]
+ rv_sl = [ ]
+
+ # A list of tags in old_sl, new_sl, and rv_sl.
+ old_map = dict((tag(i), i) for i in old_sl if i is not None)
+ new_tags = set(tag(i) for i in new_sl if i is not None)
+ rv_tags = set()
+
+ while old_sl or new_sl:
+
+ # If we have something in old_sl, then
+ if old_sl:
+
+ old_sle = old_sl[0]
+ old_tag = tag(old_sle)
+
+ # If the old thing has already moved, then remove it.
+ if old_tag in rv_tags:
+ old_sl.pop(0)
+ continue
+
+ # If the old thing does not match anything in new_tags,
+ # have it enter.
+ if old_tag not in new_tags:
+ leaving(old_sle)
+ rv_tags.add(old_tag)
+ old_sl.pop(0)
+ continue
+
+
+ # Otherwise, we must have something in new_sl. We want to
+ # either move it or have it enter.
+
+ new_sle = new_sl.pop(0)
+ new_tag = tag(new_sle)
+
+ # If it exists in both, move.
+ if new_tag in old_map:
+ old_sle = old_map[new_tag]
+
+ moving(old_sle, new_sle)
+ rv_tags.add(new_tag)
+ continue
+
+ else:
+ entering(new_sle)
+ rv_tags.add(new_tag)
+ continue
+
+ # Sort everything by zorder, to ensure that there are no zorder
+ # violations in the result.
+ rv_sl.sort(key=lambda a : a.zorder)
+
+ layer = new.layer_name
+ rv = renpy.display.layout.MultiBox(layout='fixed', focus=layer, **renpy.game.interface.layer_properties[layer])
+ rv.append_scene_list(rv_sl)
+
+ return rv
+
+ # Call merge_slide to actually do the merging.
+ rv = merge_slide(old_widget, new_widget)
+ rv.delay = delay
+
+ return rv
+