Merge with trunk, revision 28528 - 28976.
[blender-staging.git] / release / scripts / keyingsets / keyingsets_utils.py
1 # This file defines a set of methods that are useful for various 
2 # Relative Keying Set (RKS) related operations, such as: callbacks
3 # for polling, iterator callbacks, and also generate callbacks. 
4 # All of these can be used in conjunction with the others. 
5
6 import bpy
7
8 ###########################
9 # General Utilities
10
11 # Append the specified property name on the the existing path
12 def path_add_property(path, prop):
13     if len(path):
14         return path + "." + prop;
15     else:
16         return prop;
17
18 ###########################
19 # Poll Callbacks
20
21 # selected objects
22 def RKS_POLL_selected_objects(ksi, context):
23     return context.active_object or len(context.selected_objects);
24     
25 # selected bones
26 def RKS_POLL_selected_bones(ksi, context):
27     # we must be in Pose Mode, and there must be some bones selected 
28     if (context.active_object) and (context.active_object.mode == 'POSE'):
29         if context.active_pose_bone or len(context.selected_pose_bones):
30             return True;
31     
32     # nothing selected 
33     return False;
34
35
36 # selected bones or objects
37 def RKS_POLL_selected_items(ksi, context):
38     return RKS_POLL_selected_bones(ksi, context) or RKS_POLL_selected_objects(ksi, context);
39
40 ###########################
41 # Iterator Callbacks
42
43 # all selected objects or pose bones, depending on which we've got
44 def RKS_ITER_selected_item(ksi, context, ks):
45     if (context.active_object) and (context.active_object.mode == 'POSE'):
46         for bone in context.selected_pose_bones:
47             ksi.generate(context, ks, bone)
48     else:
49         for ob in context.selected_objects:
50             ksi.generate(context, ks, ob)
51
52 ###########################
53 # Generate Callbacks
54
55 # 'Available' F-Curves
56 def RKS_GEN_available(ksi, context, ks, data):
57     # try to get the animation data associated with the closest 
58     # ID-block to the data (neither of which may exist/be easy to find)
59     id_block = data.id_data
60     adt = getattr(id_block, "animation_data", None)
61
62     # there must also be an active action...
63     if adt is None or adt.action is None:
64         return;
65         
66     # for each F-Curve, include an path to key it
67     # NOTE: we don't need to set the group settings here
68     for fcu in adt.action.fcurves:
69         ks.paths.add(id_block, fcu.data_path, index=fcu.array_index)
70     
71 # ------
72
73 # get ID block and based ID path for transform generators
74 def get_transform_generators_base_info(data):
75     # ID-block for the data 
76     id_block = data.id_data
77     
78     # get base path and grouping method/name
79     if isinstance(data, bpy.types.ID):
80         # no path in this case
81         path = ""
82         
83         # data on ID-blocks directly should get grouped by the KeyingSet
84         grouping = None
85     else:
86         # get the path to the ID-block
87         path = data.path_from_id()
88
89         # try to use the name of the data element to group the F-Curve
90         # else fallback on the KeyingSet name
91         grouping = getattr(data, "name", None)
92         
93     # return the ID-block and the path
94     return id_block, path, grouping
95
96 # Location 
97 def RKS_GEN_location(ksi, context, ks, data):
98     # get id-block and path info
99     id_block, base_path, grouping= get_transform_generators_base_info(data)
100     
101     # add the property name to the base path
102     path = path_add_property(base_path, "location")
103     
104     # add Keying Set entry for this...
105     if grouping:
106         ks.paths.add(id_block, path, grouping_method='NAMED', group_name=grouping)
107     else:
108         ks.paths.add(id_block, path)
109
110 # Rotation 
111 def RKS_GEN_rotation(ksi, context, ks, data):
112     # get id-block and path info
113     id_block, base_path, grouping= get_transform_generators_base_info(data)
114     
115     # add the property name to the base path
116     #   rotation mode affects the property used
117     if data.rotation_mode == 'QUATERNION':
118         path = path_add_property(base_path, "rotation_quaternion")
119     elif data.rotation_mode == 'AXISANGLE':
120         path = path_add_property(base_path, "rotation_axis_angle")
121     else:
122         path = path_add_property(base_path, "rotation_euler")
123     
124     # add Keying Set entry for this...
125     if grouping:
126         ks.paths.add(id_block, path, grouping_method='NAMED', group_name=grouping)
127     else:
128         ks.paths.add(id_block, path)
129
130 # Scaling 
131 def RKS_GEN_scaling(ksi, context, ks, data):
132     # get id-block and path info
133     id_block, base_path, grouping= get_transform_generators_base_info(data)
134     
135     # add the property name to the base path
136     path = path_add_property(base_path, "scale")
137     
138     # add Keying Set entry for this...
139     if grouping:
140         ks.paths.add(id_block, path, grouping_method='NAMED', group_name=grouping)
141     else:
142         ks.paths.add(id_block, path)
143
144 ###########################
145 # Un-needed stuff which is here to just shut up the warnings...
146
147 classes = []
148
149 def register():
150     register = bpy.types.register
151     for cls in classes:
152         register(cls)
153
154
155 def unregister():
156     unregister = bpy.types.unregister
157     for cls in classes:
158         unregister(cls)
159
160 if __name__ == "__main__":
161     register()
162
163 ###########################