Moving classes to separate listing broke panel order
[blender-staging.git] / release / scripts / startup / bl_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
21 import bpy
22 from bpy.types import Panel
23 from rna_prop_ui import PropertyPanel
24
25
26 class BoneButtonsPanel:
27     bl_space_type = 'PROPERTIES'
28     bl_region_type = 'WINDOW'
29     bl_context = "bone"
30
31     @classmethod
32     def poll(cls, context):
33         return (context.bone or context.edit_bone)
34
35
36 class BONE_PT_context_bone(BoneButtonsPanel, Panel):
37     bl_label = ""
38     bl_options = {'HIDE_HEADER'}
39
40     def draw(self, context):
41         layout = self.layout
42
43         bone = context.bone
44         if not bone:
45             bone = context.edit_bone
46
47         row = layout.row()
48         row.label(text="", icon='BONE_DATA')
49         row.prop(bone, "name", text="")
50
51
52 class BONE_PT_transform(BoneButtonsPanel, Panel):
53     bl_label = "Transform"
54
55     @classmethod
56     def poll(cls, context):
57         if context.edit_bone:
58             return True
59
60         ob = context.object
61         return ob and ob.mode == 'POSE' and context.bone
62
63     def draw(self, context):
64         layout = self.layout
65
66         ob = context.object
67         bone = context.bone
68
69         if bone and ob:
70             pchan = ob.pose.bones[bone.name]
71
72             row = layout.row()
73             col = row.column()
74             col.prop(pchan, "location")
75             col.active = not (bone.parent and bone.use_connect)
76
77             col = row.column()
78             if pchan.rotation_mode == 'QUATERNION':
79                 col.prop(pchan, "rotation_quaternion", text="Rotation")
80             elif pchan.rotation_mode == 'AXIS_ANGLE':
81                 #col.label(text="Rotation")
82                 #col.prop(pchan, "rotation_angle", text="Angle")
83                 #col.prop(pchan, "rotation_axis", text="Axis")
84                 col.prop(pchan, "rotation_axis_angle", text="Rotation")
85             else:
86                 col.prop(pchan, "rotation_euler", text="Rotation")
87
88             row.column().prop(pchan, "scale")
89
90             layout.prop(pchan, "rotation_mode")
91
92         elif context.edit_bone:
93             bone = context.edit_bone
94             row = layout.row()
95             row.column().prop(bone, "head")
96             row.column().prop(bone, "tail")
97
98             col = row.column()
99             sub = col.column(align=True)
100             sub.label(text="Roll:")
101             sub.prop(bone, "roll", text="")
102             sub.label()
103             sub.prop(bone, "lock")
104
105
106 class BONE_PT_transform_locks(BoneButtonsPanel, Panel):
107     bl_label = "Transform Locks"
108     bl_options = {'DEFAULT_CLOSED'}
109
110     @classmethod
111     def poll(cls, context):
112         ob = context.object
113         return ob and ob.mode == 'POSE' and context.bone
114
115     def draw(self, context):
116         layout = self.layout
117
118         ob = context.object
119         bone = context.bone
120         pchan = ob.pose.bones[bone.name]
121
122         split = layout.split(percentage=0.1)
123
124         col = split.column(align=True)
125         col.label(text="")
126         col.label(text="X:")
127         col.label(text="Y:")
128         col.label(text="Z:")
129
130         col = split.column()
131         col.active = not (bone.parent and bone.use_connect)
132         col.prop(pchan, "lock_location", text="Location")
133
134         col = split.column()
135         col.prop(pchan, "lock_rotation", text="Rotation")
136
137         col = split.column()
138         col.prop(pchan, "lock_scale", text="Scale")
139
140         if pchan.rotation_mode in {'QUATERNION', 'AXIS_ANGLE'}:
141             row = layout.row()
142             row.prop(pchan, "lock_rotations_4d", text="Lock Rotation")
143
144             sub = row.row()
145             sub.active = pchan.lock_rotations_4d
146             sub.prop(pchan, "lock_rotation_w", text="W")
147
148
149 class BONE_PT_curved(BoneButtonsPanel, Panel):
150     bl_label = "Bendy Bones"
151     #bl_options = {'DEFAULT_CLOSED'}
152
153     def draw(self, context):
154         ob = context.object
155         bone = context.bone
156         # arm = context.armature
157         pchan = None
158
159         if ob and bone:
160             pchan = ob.pose.bones[bone.name]
161             bbone = pchan
162         elif bone is None:
163             bone = context.edit_bone
164             bbone = bone
165         else:
166             bbone = bone
167
168         layout = self.layout
169         layout.prop(bone, "bbone_segments", text="Segments")
170
171         col = layout.column()
172         col.active = bone.bbone_segments > 1
173
174         row = col.row()
175         sub = row.column(align=True)
176         sub.label(text="Curve XY Offsets:")
177         sub.prop(bbone, "bbone_curveinx", text="In X")
178         sub.prop(bbone, "bbone_curveoutx", text="Out X")
179         sub.prop(bbone, "bbone_curveiny", text="In Y")
180         sub.prop(bbone, "bbone_curveouty", text="Out Y")
181
182         sub = row.column(align=True)
183         sub.label("Roll:")
184         sub.prop(bbone, "bbone_rollin", text="In")
185         sub.prop(bbone, "bbone_rollout", text="Out")
186         sub.prop(bone, "use_endroll_as_inroll")
187
188         row = col.row()
189         sub = row.column(align=True)
190         sub.label(text="Scale:")
191         sub.prop(bbone, "bbone_scalein", text="Scale In")
192         sub.prop(bbone, "bbone_scaleout", text="Scale Out")
193
194         sub = row.column(align=True)
195         sub.label("Easing:")
196         if pchan:
197             # XXX: have these also be an overlay?
198             sub.prop(bbone.bone, "bbone_in", text="Ease In")
199             sub.prop(bbone.bone, "bbone_out", text="Ease Out")
200         else:
201             sub.prop(bone, "bbone_in", text="Ease In")
202             sub.prop(bone, "bbone_out", text="Ease Out")
203
204         if pchan:
205             layout.separator()
206
207             col = layout.column()
208             col.prop(pchan, "use_bbone_custom_handles")
209
210             row = col.row()
211             row.active = pchan.use_bbone_custom_handles
212
213             sub = row.column(align=True)
214             sub.label(text="In:")
215             sub.prop_search(pchan, "bbone_custom_handle_start", ob.pose, "bones", text="")
216             sub.prop(pchan, "use_bbone_relative_start_handle", text="Relative")
217
218             sub = row.column(align=True)
219             sub.label(text="Out:")
220             sub.prop_search(pchan, "bbone_custom_handle_end", ob.pose, "bones", text="")
221             sub.prop(pchan, "use_bbone_relative_end_handle", text="Relative")
222
223
224 class BONE_PT_relations(BoneButtonsPanel, Panel):
225     bl_label = "Relations"
226
227     def draw(self, context):
228         layout = self.layout
229
230         ob = context.object
231         bone = context.bone
232         arm = context.armature
233         pchan = None
234
235         if ob and bone:
236             pchan = ob.pose.bones[bone.name]
237         elif bone is None:
238             bone = context.edit_bone
239
240         split = layout.split()
241
242         col = split.column()
243         col.label(text="Layers:")
244         col.prop(bone, "layers", text="")
245
246         col.separator()
247
248         if ob and pchan:
249             col.label(text="Bone Group:")
250             col.prop_search(pchan, "bone_group", ob.pose, "bone_groups", text="")
251             col.label(text="Object Children:")
252             col.prop(bone, "use_relative_parent")
253
254         col = split.column()
255         col.label(text="Parent:")
256         if context.bone:
257             col.prop(bone, "parent", text="")
258         else:
259             col.prop_search(bone, "parent", arm, "edit_bones", text="")
260
261         sub = col.column()
262         sub.active = (bone.parent is not None)
263         sub.prop(bone, "use_connect")
264         sub.prop(bone, "use_inherit_rotation")
265         sub.prop(bone, "use_inherit_scale")
266         sub = col.column()
267         sub.active = (not bone.parent or not bone.use_connect)
268         sub.prop(bone, "use_local_location")
269
270
271 class BONE_PT_display(BoneButtonsPanel, Panel):
272     bl_label = "Display"
273
274     @classmethod
275     def poll(cls, context):
276         return context.bone
277
278     def draw(self, context):
279         # note. this works ok in edit-mode but isn't
280         # all that useful so disabling for now.
281         layout = self.layout
282
283         ob = context.object
284         bone = context.bone
285         pchan = None
286
287         if ob and bone:
288             pchan = ob.pose.bones[bone.name]
289         elif bone is None:
290             bone = context.edit_bone
291
292         if bone:
293             split = layout.split()
294
295             col = split.column()
296             col.prop(bone, "hide", text="Hide")
297             sub = col.column()
298             sub.active = bool(pchan and pchan.custom_shape)
299             sub.prop(bone, "show_wire", text="Wireframe")
300
301             if pchan:
302                 col = split.column()
303
304                 col.label(text="Custom Shape:")
305                 col.prop(pchan, "custom_shape", text="")
306                 if pchan.custom_shape:
307                     col.prop(pchan, "use_custom_shape_bone_size", text="Bone Size")
308                     col.prop(pchan, "custom_shape_scale", text="Scale")
309                     col.prop_search(pchan, "custom_shape_transform", ob.pose, "bones", text="At")
310
311
312 class BONE_PT_inverse_kinematics(BoneButtonsPanel, Panel):
313     bl_label = "Inverse Kinematics"
314     bl_options = {'DEFAULT_CLOSED'}
315
316     @classmethod
317     def poll(cls, context):
318         ob = context.object
319         return ob and ob.mode == 'POSE' and context.bone
320
321     def draw(self, context):
322         layout = self.layout
323
324         ob = context.object
325         bone = context.bone
326         pchan = ob.pose.bones[bone.name]
327
328         row = layout.row()
329
330         active = pchan.is_in_ik_chain
331
332         split = layout.split(percentage=0.25)
333         split.prop(pchan, "lock_ik_x", text="X")
334         split.active = active
335         row = split.row()
336         row.prop(pchan, "ik_stiffness_x", text="Stiffness", slider=True)
337         row.active = pchan.lock_ik_x is False and active
338
339         split = layout.split(percentage=0.25)
340         sub = split.row()
341
342         sub.prop(pchan, "use_ik_limit_x", text="Limit")
343         sub.active = pchan.lock_ik_x is False and active
344         sub = split.row(align=True)
345         sub.prop(pchan, "ik_min_x", text="")
346         sub.prop(pchan, "ik_max_x", text="")
347         sub.active = pchan.lock_ik_x is False and pchan.use_ik_limit_x and active
348
349         split = layout.split(percentage=0.25)
350         split.prop(pchan, "lock_ik_y", text="Y")
351         split.active = active
352         row = split.row()
353         row.prop(pchan, "ik_stiffness_y", text="Stiffness", slider=True)
354         row.active = pchan.lock_ik_y is False and active
355
356         split = layout.split(percentage=0.25)
357         sub = split.row()
358
359         sub.prop(pchan, "use_ik_limit_y", text="Limit")
360         sub.active = pchan.lock_ik_y is False and active
361
362         sub = split.row(align=True)
363         sub.prop(pchan, "ik_min_y", text="")
364         sub.prop(pchan, "ik_max_y", text="")
365         sub.active = pchan.lock_ik_y is False and pchan.use_ik_limit_y and active
366
367         split = layout.split(percentage=0.25)
368         split.prop(pchan, "lock_ik_z", text="Z")
369         split.active = active
370         sub = split.row()
371         sub.prop(pchan, "ik_stiffness_z", text="Stiffness", slider=True)
372         sub.active = pchan.lock_ik_z is False and active
373
374         split = layout.split(percentage=0.25)
375         sub = split.row()
376
377         sub.prop(pchan, "use_ik_limit_z", text="Limit")
378         sub.active = pchan.lock_ik_z is False and active
379         sub = split.row(align=True)
380         sub.prop(pchan, "ik_min_z", text="")
381         sub.prop(pchan, "ik_max_z", text="")
382         sub.active = pchan.lock_ik_z is False and pchan.use_ik_limit_z and active
383
384         split = layout.split(percentage=0.25)
385         split.label(text="Stretch:")
386         sub = split.row()
387         sub.prop(pchan, "ik_stretch", text="", slider=True)
388         sub.active = active
389
390         if ob.pose.ik_solver == 'ITASC':
391             split = layout.split()
392             col = split.column()
393             col.prop(pchan, "use_ik_rotation_control", text="Control Rotation")
394             col.active = active
395             col = split.column()
396             col.prop(pchan, "ik_rotation_weight", text="Weight", slider=True)
397             col.active = active
398             # not supported yet
399             #row = layout.row()
400             #row.prop(pchan, "use_ik_linear_control", text="Joint Size")
401             #row.prop(pchan, "ik_linear_weight", text="Weight", slider=True)
402
403
404 class BONE_PT_deform(BoneButtonsPanel, Panel):
405     bl_label = "Deform"
406     bl_options = {'DEFAULT_CLOSED'}
407
408     def draw_header(self, context):
409         bone = context.bone
410
411         if not bone:
412             bone = context.edit_bone
413
414         self.layout.prop(bone, "use_deform", text="")
415
416     def draw(self, context):
417         layout = self.layout
418
419         bone = context.bone
420
421         if not bone:
422             bone = context.edit_bone
423
424         layout.active = bone.use_deform
425
426         row = layout.row()
427
428         col = row.column(align=True)
429         col.label(text="Envelope:")
430         col.prop(bone, "envelope_distance", text="Distance")
431         col.prop(bone, "envelope_weight", text="Weight")
432         col.prop(bone, "use_envelope_multiply", text="Multiply")
433
434         col = row.column(align=True)
435         col.label(text="Envelope Radius:")
436         col.prop(bone, "head_radius", text="Head")
437         col.prop(bone, "tail_radius", text="Tail")
438
439
440 class BONE_PT_custom_props(BoneButtonsPanel, PropertyPanel, Panel):
441     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'}
442     _property_type = bpy.types.Bone, bpy.types.EditBone, bpy.types.PoseBone
443
444     @property
445     def _context_path(self):
446         obj = bpy.context.object
447         if obj and obj.mode == 'POSE':
448             return "active_pose_bone"
449         else:
450             return "active_bone"
451
452
453 classes = (
454     BONE_PT_context_bone,
455     BONE_PT_transform,
456     BONE_PT_transform_locks,
457     BONE_PT_curved,
458     BONE_PT_relations,
459     BONE_PT_display,
460     BONE_PT_inverse_kinematics,
461     BONE_PT_deform,
462     BONE_PT_custom_props,
463 )
464
465 if __name__ == "__main__":  # only for live edit.
466     from bpy.utils import register_class
467     for cls in classes:
468         register_class(cls)