Amaranth Addon
[blender-addons-contrib.git] / amaranth / animation / jump_frames.py
1 #  This program is free software; you can redistribute it and/or
2 #  modify it under the terms of the GNU General Public License
3 #  as published by the Free Software Foundation; either version 2
4 #  of the License, or (at your option) any later version.
5 #
6 #  This program is distributed in the hope that it will be useful,
7 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
8 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
9 #  GNU General Public License for more details.
10 #
11 #  You should have received a copy of the GNU General Public License
12 #  along with this program; if not, write to the Free Software Foundation,
13 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
14 """
15 Jump X Frames on Shift Up/Down
16
17 When you hit Shift Up/Down, you'll jump 10 frames forward/backwards.
18 Sometimes is nice to tweak that value.
19
20 In the User Preferences, Editing tab, you'll find a "Frames to Jump"
21 slider where you can adjust how many frames you'd like to move
22 forwards/backwards.
23
24 Make sure you save your user settings if you want to use this value from
25 now on.
26
27 Find it on the User Preferences, Editing.
28 """
29
30 import bpy
31
32 KEYMAPS = list()
33
34
35 # FUNCTION: Check if object has keyframes for a specific frame
36 def is_keyframe(ob, frame):
37     if ob is not None and ob.animation_data is not None and ob.animation_data.action is not None:
38         for fcu in ob.animation_data.action.fcurves:
39             if frame in (p.co.x for p in fcu.keyframe_points):
40                 return True
41     return False
42
43 # monkey path is_keyframe function
44 bpy.types.Object.is_keyframe = is_keyframe
45
46
47 # FEATURE: Jump to frame in-between next and previous keyframe
48 class AMTH_SCREEN_OT_keyframe_jump_inbetween(bpy.types.Operator):
49
50     """Jump to half in-between keyframes"""
51     bl_idname = "screen.amth_keyframe_jump_inbetween"
52     bl_label = "Jump to Keyframe In-between"
53     backwards = bpy.props.BoolProperty()
54
55     def execute(self, context):
56         back = self.backwards
57
58         scene = context.scene
59         ob = bpy.context.object
60         frame_start = scene.frame_start
61         frame_end = scene.frame_end
62
63         if not context.scene.get("amth_keyframes_jump"):
64             context.scene["amth_keyframes_jump"] = list()
65
66         keyframes_list = context.scene["amth_keyframes_jump"]
67
68         for f in range(frame_start, frame_end):
69             if ob.is_keyframe(f):
70                 keyframes_list = list(keyframes_list)
71                 keyframes_list.append(f)
72
73         if keyframes_list:
74             keyframes_list_half = []
75
76             for i, item in enumerate(keyframes_list):
77                 try:
78                     next_item = keyframes_list[i + 1]
79                     keyframes_list_half.append(int((item + next_item) / 2))
80                 except:
81                     pass
82
83             if len(keyframes_list_half) > 1:
84                 if back:
85                     v = (scene.frame_current == keyframes_list_half[::-1][-1],
86                          scene.frame_current < keyframes_list_half[::-1][-1])
87                     if any(v):
88                         self.report({"INFO"}, "No keyframes behind")
89                     else:
90                         for i in keyframes_list_half[::-1]:
91                             if scene.frame_current > i:
92                                 scene.frame_current = i
93                                 break
94                 else:
95                     v = (scene.frame_current == keyframes_list_half[-1],
96                          scene.frame_current > keyframes_list_half[-1])
97                     if any(v):
98                         self.report({"INFO"}, "No keyframes ahead")
99                     else:
100                         for i in keyframes_list_half:
101                             if scene.frame_current < i:
102                                 scene.frame_current = i
103                                 break
104             else:
105                 self.report({"INFO"}, "Object has only 1 keyframe")
106         else:
107             self.report({"INFO"}, "Object has no keyframes")
108
109         return {"FINISHED"}
110
111
112 # FEATURE: Jump forward/backward every N frames
113 class AMTH_SCREEN_OT_frame_jump(bpy.types.Operator):
114
115     """Jump a number of frames forward/backwards"""
116     bl_idname = "screen.amaranth_frame_jump"
117     bl_label = "Jump Frames"
118
119     forward = bpy.props.BoolProperty(default=True)
120
121     def execute(self, context):
122         scene = context.scene
123         preferences = context.user_preferences.addons["amaranth"].preferences
124
125         if preferences.use_framerate:
126             framedelta = scene.render.fps
127         else:
128             framedelta = preferences.frames_jump
129         if self.forward:
130             scene.frame_current = scene.frame_current + framedelta
131         else:
132             scene.frame_current = scene.frame_current - framedelta
133
134         return {"FINISHED"}
135
136
137 def ui_userpreferences_edit(self, context):
138     preferences = context.user_preferences.addons["amaranth"].preferences
139
140     col = self.layout.column()
141     split = col.split(percentage=0.21)
142     split.prop(preferences, "frames_jump",
143                text="Frames to Jump")
144
145
146 def label(self, context):
147
148     preferences = context.user_preferences.addons["amaranth"].preferences
149     layout = self.layout
150
151     if preferences.use_timeline_extra_info:
152         row = layout.row(align=True)
153
154         row.operator(AMTH_SCREEN_OT_keyframe_jump_inbetween.bl_idname,
155                      icon="PREV_KEYFRAME", text="").backwards = True
156         row.operator(AMTH_SCREEN_OT_keyframe_jump_inbetween.bl_idname,
157                      icon="NEXT_KEYFRAME", text="").backwards = False
158
159
160 def register():
161     bpy.utils.register_class(AMTH_SCREEN_OT_frame_jump)
162     bpy.utils.register_class(AMTH_SCREEN_OT_keyframe_jump_inbetween)
163     bpy.types.USERPREF_PT_edit.append(ui_userpreferences_edit)
164     bpy.types.USERPREF_PT_edit.append(label)
165
166     # register keyboard shortcuts
167     wm = bpy.context.window_manager
168     kc = wm.keyconfigs.addon
169
170     km = kc.keymaps.new(name="Frames")
171     kmi = km.keymap_items.new('screen.amth_keyframe_jump_inbetween', 'UP_ARROW', 'PRESS', shift=True, ctrl=True)
172     kmi.properties.backwards = False
173     KEYMAPS.append((km, kmi))
174
175     kmi = km.keymap_items.new('screen.amth_keyframe_jump_inbetween', 'DOWN_ARROW', 'PRESS', shift=True, ctrl=True)
176     kmi.properties.backwards = True
177     KEYMAPS.append((km, kmi))
178
179     kmi = km.keymap_items.new(
180         "screen.amaranth_frame_jump", "UP_ARROW", "PRESS", shift=True)
181     kmi.properties.forward = True
182     KEYMAPS.append((km, kmi))
183
184     kmi = km.keymap_items.new(
185         "screen.amaranth_frame_jump", "DOWN_ARROW", "PRESS", shift=True)
186     kmi.properties.forward = False
187     KEYMAPS.append((km, kmi))
188
189
190 def unregister():
191     bpy.utils.unregister_class(AMTH_SCREEN_OT_frame_jump)
192     bpy.utils.unregister_class(AMTH_SCREEN_OT_keyframe_jump_inbetween)
193     bpy.types.USERPREF_PT_edit.remove(ui_userpreferences_edit)
194     for km, kmi in KEYMAPS:
195         km.keymap_items.remove(kmi)
196     KEYMAPS.clear()