982572b62a381fc346501015d729d55cbc02e97d
[blender-staging.git] / release / scripts / ui / properties_data_bone.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 # <pep8 compliant>
20 import bpy
21 from rna_prop_ui import PropertyPanel
22
23 narrowui = 180
24
25
26 class BoneButtonsPanel(bpy.types.Panel):
27     bl_space_type = 'PROPERTIES'
28     bl_region_type = 'WINDOW'
29     bl_context = "bone"
30
31     def poll(self, context):
32         return (context.bone or context.edit_bone)
33
34
35 class BONE_PT_context_bone(BoneButtonsPanel):
36     bl_label = ""
37     bl_show_header = False
38
39     def draw(self, context):
40         layout = self.layout
41
42         bone = context.bone
43         if not bone:
44             bone = context.edit_bone
45
46         row = layout.row()
47         row.label(text="", icon='BONE_DATA')
48         row.prop(bone, "name", text="")
49
50
51 class BONE_PT_custom_props(BoneButtonsPanel, PropertyPanel):
52
53     @property
54     def _context_path(self):
55         obj = bpy.context.object
56         if obj and obj.mode == 'POSE':
57             return "active_pose_bone"
58         else:
59             return "active_bone"
60
61
62 class BONE_PT_transform(BoneButtonsPanel):
63     bl_label = "Transform"
64
65     def draw(self, context):
66         layout = self.layout
67
68         ob = context.object
69         bone = context.bone
70         wide_ui = context.region.width > narrowui
71
72         if not bone:
73             bone = context.edit_bone
74             if wide_ui:
75                 row = layout.row()
76                 row.column().prop(bone, "head")
77                 row.column().prop(bone, "tail")
78
79                 col = row.column()
80                 sub = col.column(align=True)
81                 sub.label(text="Roll:")
82                 sub.prop(bone, "roll", text="")
83                 sub.label()
84                 sub.prop(bone, "locked")
85             else:
86                 col = layout.column()
87                 col.prop(bone, "head")
88                 col.prop(bone, "tail")
89                 col.prop(bone, "roll")
90                 col.prop(bone, "locked")
91
92         else:
93             pchan = ob.pose.bones[context.bone.name]
94
95             if wide_ui:
96                 row = layout.row()
97                 col = row.column()
98                 col.prop(pchan, "location")
99                 col.active = not (bone.parent and bone.connected)
100
101                 col = row.column()
102                 if pchan.rotation_mode == 'QUATERNION':
103                     col.prop(pchan, "rotation_quaternion", text="Rotation")
104                 elif pchan.rotation_mode == 'AXIS_ANGLE':
105                     #col.label(text="Rotation")
106                     #col.prop(pchan, "rotation_angle", text="Angle")
107                     #col.prop(pchan, "rotation_axis", text="Axis")
108                     col.prop(pchan, "rotation_axis_angle", text="Rotation")
109                 else:
110                     col.prop(pchan, "rotation_euler", text="Rotation")
111
112                 row.column().prop(pchan, "scale")
113
114                 layout.prop(pchan, "rotation_mode")
115             else:
116                 col = layout.column()
117                 sub = col.column()
118                 sub.active = not (bone.parent and bone.connected)
119                 sub.prop(pchan, "location")
120                 col.label(text="Rotation:")
121                 col.prop(pchan, "rotation_mode", text="")
122                 if pchan.rotation_mode == 'QUATERNION':
123                     col.prop(pchan, "rotation_quaternion", text="")
124                 elif pchan.rotation_mode == 'AXIS_ANGLE':
125                     col.prop(pchan, "rotation_axis_angle", text="")
126                 else:
127                     col.prop(pchan, "rotation_euler", text="")
128                 col.prop(pchan, "scale")
129
130
131 class BONE_PT_transform_locks(BoneButtonsPanel):
132     bl_label = "Transform Locks"
133     bl_default_closed = True
134
135     def poll(self, context):
136         return context.bone
137
138     def draw(self, context):
139         layout = self.layout
140
141         ob = context.object
142         bone = context.bone
143         pchan = ob.pose.bones[context.bone.name]
144
145         row = layout.row()
146         col = row.column()
147         col.prop(pchan, "lock_location")
148         col.active = not (bone.parent and bone.connected)
149
150         col = row.column()
151         if pchan.rotation_mode in ('QUATERNION', 'AXIS_ANGLE'):
152             col.prop(pchan, "lock_rotations_4d", text="Lock Rotation")
153             if pchan.lock_rotations_4d:
154                 col.prop(pchan, "lock_rotation_w", text="W")
155             col.prop(pchan, "lock_rotation", text="")
156         else:
157             col.prop(pchan, "lock_rotation", text="Rotation")
158
159         row.column().prop(pchan, "lock_scale")
160
161
162 class BONE_PT_relations(BoneButtonsPanel):
163     bl_label = "Relations"
164
165     def draw(self, context):
166         layout = self.layout
167
168         ob = context.object
169         bone = context.bone
170         arm = context.armature
171         wide_ui = context.region.width > narrowui
172
173         if not bone:
174             bone = context.edit_bone
175             pchan = None
176         else:
177             pchan = ob.pose.bones[context.bone.name]
178
179         split = layout.split()
180
181         col = split.column()
182         col.label(text="Layers:")
183         col.prop(bone, "layer", text="")
184
185         col.separator()
186
187         if ob and pchan:
188             col.label(text="Bone Group:")
189             col.prop_object(pchan, "bone_group", ob.pose, "bone_groups", text="")
190
191         if wide_ui:
192             col = split.column()
193         col.label(text="Parent:")
194         if context.bone:
195             col.prop(bone, "parent", text="")
196         else:
197             col.prop_object(bone, "parent", arm, "edit_bones", text="")
198
199         sub = col.column()
200         sub.active = (bone.parent is not None)
201         sub.prop(bone, "connected")
202         sub.prop(bone, "hinge", text="Inherit Rotation")
203         sub.prop(bone, "inherit_scale", text="Inherit Scale")
204         sub = col.column()
205         sub.active = (not bone.parent or not bone.connected)
206         sub.prop(bone, "local_location", text="Local Location")
207
208
209 class BONE_PT_display(BoneButtonsPanel):
210     bl_label = "Display"
211
212     def poll(self, context):
213         return context.bone
214
215     def draw(self, context):
216         layout = self.layout
217
218         ob = context.object
219         bone = context.bone
220         wide_ui = context.region.width > narrowui
221
222         if not bone:
223             bone = context.edit_bone
224             pchan = None
225         else:
226             pchan = ob.pose.bones[context.bone.name]
227
228         if ob and pchan:
229
230             split = layout.split()
231
232             col = split.column()
233             col.prop(bone, "draw_wire", text="Wireframe")
234             col.prop(bone, "hidden", text="Hide")
235
236             if wide_ui:
237                 col = split.column()
238
239             col.label(text="Custom Shape:")
240             col.prop(pchan, "custom_shape", text="")
241             if pchan.custom_shape:
242                 col.prop_object(pchan, "custom_shape_transform", ob.pose, "bones", text="At")
243
244
245 class BONE_PT_inverse_kinematics(BoneButtonsPanel):
246     bl_label = "Inverse Kinematics"
247     bl_default_closed = True
248
249     def poll(self, context):
250         return context.active_pose_bone
251
252     def draw(self, context):
253         layout = self.layout
254
255         ob = context.object
256         bone = context.bone
257         pchan = ob.pose.bones[bone.name]
258         wide_ui = context.region.width > narrowui
259
260         row = layout.row()
261         row.prop(ob.pose, "ik_solver")
262
263         split = layout.split(percentage=0.25)
264         split.prop(pchan, "ik_dof_x", text="X")
265         split.active = pchan.has_ik
266         row = split.row()
267         row.prop(pchan, "ik_stiffness_x", text="Stiffness", slider=True)
268         row.active = pchan.ik_dof_x and pchan.has_ik
269
270         if wide_ui:
271             split = layout.split(percentage=0.25)
272             sub = split.row()
273         else:
274             sub = layout.column(align=True)
275         sub.prop(pchan, "ik_limit_x", text="Limit")
276         sub.active = pchan.ik_dof_x and pchan.has_ik
277         if wide_ui:
278             sub = split.row(align=True)
279         sub.prop(pchan, "ik_min_x", text="")
280         sub.prop(pchan, "ik_max_x", text="")
281         sub.active = pchan.ik_dof_x and pchan.ik_limit_x and pchan.has_ik
282
283         split = layout.split(percentage=0.25)
284         split.prop(pchan, "ik_dof_y", text="Y")
285         split.active = pchan.has_ik and pchan.has_ik
286         row = split.row()
287         row.prop(pchan, "ik_stiffness_y", text="Stiffness", slider=True)
288         row.active = pchan.ik_dof_y and pchan.has_ik
289
290         if wide_ui:
291             split = layout.split(percentage=0.25)
292             sub = split.row()
293         else:
294             sub = layout.column(align=True)
295         sub.prop(pchan, "ik_limit_y", text="Limit")
296         sub.active = pchan.ik_dof_y and pchan.has_ik
297         if wide_ui:
298             sub = split.row(align=True)
299         sub.prop(pchan, "ik_min_y", text="")
300         sub.prop(pchan, "ik_max_y", text="")
301         sub.active = pchan.ik_dof_y and pchan.ik_limit_y and pchan.has_ik
302
303         split = layout.split(percentage=0.25)
304         split.prop(pchan, "ik_dof_z", text="Z")
305         split.active = pchan.has_ik and pchan.has_ik
306         sub = split.row()
307         sub.prop(pchan, "ik_stiffness_z", text="Stiffness", slider=True)
308         sub.active = pchan.ik_dof_z and pchan.has_ik
309
310         if wide_ui:
311             split = layout.split(percentage=0.25)
312             sub = split.row()
313         else:
314             sub = layout.column(align=True)
315         sub.prop(pchan, "ik_limit_z", text="Limit")
316         sub.active = pchan.ik_dof_z and pchan.has_ik
317         if wide_ui:
318             sub = split.row(align=True)
319         sub.prop(pchan, "ik_min_z", text="")
320         sub.prop(pchan, "ik_max_z", text="")
321         sub.active = pchan.ik_dof_z and pchan.ik_limit_z and pchan.has_ik
322         split = layout.split()
323         split.prop(pchan, "ik_stretch", text="Stretch", slider=True)
324         if wide_ui:
325             split.label()
326         split.active = pchan.has_ik
327
328         if ob.pose.ik_solver == 'ITASC':
329             split = layout.split()
330             col = split.column()
331             col.prop(pchan, "ik_rot_control", text="Control Rotation")
332             col.active = pchan.has_ik
333             if wide_ui:
334                 col = split.column()
335             col.prop(pchan, "ik_rot_weight", text="Weight", slider=True)
336             col.active = pchan.has_ik
337             # not supported yet
338             #row = layout.row()
339             #row.prop(pchan, "ik_lin_control", text="Joint Size")
340             #row.prop(pchan, "ik_lin_weight", text="Weight", slider=True)
341
342
343 class BONE_PT_deform(BoneButtonsPanel):
344     bl_label = "Deform"
345     bl_default_closed = True
346
347     def draw_header(self, context):
348         bone = context.bone
349
350         if not bone:
351             bone = context.edit_bone
352
353         self.layout.prop(bone, "deform", text="")
354
355     def draw(self, context):
356         layout = self.layout
357
358         bone = context.bone
359         wide_ui = context.region.width > narrowui
360
361         if not bone:
362             bone = context.edit_bone
363
364         layout.active = bone.deform
365
366         split = layout.split()
367
368         col = split.column()
369         col.label(text="Envelope:")
370
371         sub = col.column(align=True)
372         sub.prop(bone, "envelope_distance", text="Distance")
373         sub.prop(bone, "envelope_weight", text="Weight")
374         col.prop(bone, "multiply_vertexgroup_with_envelope", text="Multiply")
375
376         sub = col.column(align=True)
377         sub.label(text="Radius:")
378         sub.prop(bone, "head_radius", text="Head")
379         sub.prop(bone, "tail_radius", text="Tail")
380
381         if wide_ui:
382             col = split.column()
383         col.label(text="Curved Bones:")
384
385         sub = col.column(align=True)
386         sub.prop(bone, "bbone_segments", text="Segments")
387         sub.prop(bone, "bbone_in", text="Ease In")
388         sub.prop(bone, "bbone_out", text="Ease Out")
389
390         col.label(text="Offset:")
391         col.prop(bone, "cyclic_offset")
392
393 classes = [
394     BONE_PT_context_bone,
395     BONE_PT_transform,
396     BONE_PT_transform_locks,
397     BONE_PT_relations,
398     BONE_PT_display,
399     BONE_PT_inverse_kinematics,
400     BONE_PT_deform,
401
402     BONE_PT_custom_props]
403
404
405 def register():
406     register = bpy.types.register
407     for cls in classes:
408         register(cls)
409
410
411 def unregister():
412     unregister = bpy.types.unregister
413     for cls in classes:
414         unregister(cls)
415
416 if __name__ == "__main__":
417     register()