54045cd7d6e3c01fe2f6f88768c9fbdb660a6fb6
[blender.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 from blf import gettext as _
25
26
27 class BoneButtonsPanel():
28     bl_space_type = 'PROPERTIES'
29     bl_region_type = 'WINDOW'
30     bl_context = "bone"
31
32     @classmethod
33     def poll(cls, context):
34         return (context.bone or context.edit_bone)
35
36
37 class BONE_PT_context_bone(BoneButtonsPanel, Panel):
38     bl_label = ""
39     bl_options = {'HIDE_HEADER'}
40
41     def draw(self, context):
42         layout = self.layout
43
44         bone = context.bone
45         if not bone:
46             bone = context.edit_bone
47
48         row = layout.row()
49         row.label(text="", icon='BONE_DATA')
50         row.prop(bone, "name", text="")
51
52
53 class BONE_PT_transform(BoneButtonsPanel, Panel):
54     bl_label = "Transform"
55
56     @classmethod
57     def poll(cls, context):
58         if context.edit_bone:
59             return True
60
61         ob = context.object
62         return ob and ob.mode == 'POSE' and context.bone
63
64     def draw(self, context):
65         layout = self.layout
66
67         ob = context.object
68         bone = context.bone
69
70         if bone and ob:
71             pchan = ob.pose.bones[bone.name]
72
73             row = layout.row()
74             col = row.column()
75             col.prop(pchan, "location")
76             col.active = not (bone.parent and bone.use_connect)
77
78             col = row.column()
79             if pchan.rotation_mode == 'QUATERNION':
80                 col.prop(pchan, "rotation_quaternion", text=_("Rotation"))
81             elif pchan.rotation_mode == 'AXIS_ANGLE':
82                 #col.label(text=_("Rotation"))
83                 #col.prop(pchan, "rotation_angle", text=_("Angle"))
84                 #col.prop(pchan, "rotation_axis", text=_("Axis"))
85                 col.prop(pchan, "rotation_axis_angle", text=_("Rotation"))
86             else:
87                 col.prop(pchan, "rotation_euler", text=_("Rotation"))
88
89             row.column().prop(pchan, "scale")
90
91             layout.prop(pchan, "rotation_mode")
92
93         elif context.edit_bone:
94             bone = context.edit_bone
95             row = layout.row()
96             row.column().prop(bone, "head")
97             row.column().prop(bone, "tail")
98
99             col = row.column()
100             sub = col.column(align=True)
101             sub.label(text=_("Roll:"))
102             sub.prop(bone, "roll", text="")
103             sub.label()
104             sub.prop(bone, "lock")
105
106
107 class BONE_PT_transform_locks(BoneButtonsPanel, Panel):
108     bl_label = "Transform Locks"
109     bl_options = {'DEFAULT_CLOSED'}
110
111     @classmethod
112     def poll(cls, context):
113         ob = context.object
114         return ob and ob.mode == 'POSE' and context.bone
115
116     def draw(self, context):
117         layout = self.layout
118
119         ob = context.object
120         bone = context.bone
121         pchan = ob.pose.bones[bone.name]
122
123         row = layout.row()
124         col = row.column()
125         col.prop(pchan, "lock_location")
126         col.active = not (bone.parent and bone.use_connect)
127
128         col = row.column()
129         if pchan.rotation_mode in {'QUATERNION', 'AXIS_ANGLE'}:
130             col.prop(pchan, "lock_rotations_4d", text=_("Lock Rotation"))
131             if pchan.lock_rotations_4d:
132                 col.prop(pchan, "lock_rotation_w", text="W")
133             col.prop(pchan, "lock_rotation", text="")
134         else:
135             col.prop(pchan, "lock_rotation", text=_("Rotation"))
136
137         row.column().prop(pchan, "lock_scale")
138
139
140 class BONE_PT_relations(BoneButtonsPanel, Panel):
141     bl_label = "Relations"
142
143     def draw(self, context):
144         layout = self.layout
145
146         ob = context.object
147         bone = context.bone
148         arm = context.armature
149         pchan = None
150
151         if ob and bone:
152             pchan = ob.pose.bones[bone.name]
153         elif bone is None:
154             bone = context.edit_bone
155
156         split = layout.split()
157
158         col = split.column()
159         col.label(text=_("Layers:"))
160         col.prop(bone, "layers", text="")
161
162         col.separator()
163
164         if ob and pchan:
165             col.label(text=_("Bone Group:"))
166             col.prop_search(pchan, "bone_group", ob.pose, "bone_groups", text="")
167
168         col = split.column()
169         col.label(text=_("Parent:"))
170         if context.bone:
171             col.prop(bone, "parent", text="")
172         else:
173             col.prop_search(bone, "parent", arm, "edit_bones", text="")
174
175         sub = col.column()
176         sub.active = (bone.parent is not None)
177         sub.prop(bone, "use_connect")
178         sub.prop(bone, "use_inherit_rotation", text=_("Inherit Rotation"))
179         sub.prop(bone, "use_inherit_scale", text=_("Inherit Scale"))
180         sub = col.column()
181         sub.active = (not bone.parent or not bone.use_connect)
182         sub.prop(bone, "use_local_location", text=_("Local Location"))
183
184
185 class BONE_PT_display(BoneButtonsPanel, Panel):
186     bl_label = "Display"
187
188     @classmethod
189     def poll(cls, context):
190         return context.bone
191
192     def draw(self, context):
193         # note. this works ok in editmode but isnt
194         # all that useful so disabling for now.
195         layout = self.layout
196
197         ob = context.object
198         bone = context.bone
199         pchan = None
200
201         if ob and bone:
202             pchan = ob.pose.bones[bone.name]
203         elif bone is None:
204             bone = context.edit_bone
205
206         if bone:
207             split = layout.split()
208
209             col = split.column()
210             col.prop(bone, "show_wire", text=_("Wireframe"))
211             col.prop(bone, "hide", text=_("Hide"))
212
213             if pchan:
214                 col = split.column()
215
216                 col.label(text=_("Custom Shape:"))
217                 col.prop(pchan, "custom_shape", text="")
218                 if pchan.custom_shape:
219                     col.prop_search(pchan, "custom_shape_transform", ob.pose, "bones", text=_("At"))
220
221
222 class BONE_PT_inverse_kinematics(BoneButtonsPanel, Panel):
223     bl_label = "Inverse Kinematics"
224     bl_options = {'DEFAULT_CLOSED'}
225
226     @classmethod
227     def poll(cls, context):
228         ob = context.object
229         return ob and ob.mode == 'POSE' and context.bone
230
231     def draw(self, context):
232         layout = self.layout
233
234         ob = context.object
235         bone = context.bone
236         pchan = ob.pose.bones[bone.name]
237
238         row = layout.row()
239         row.prop(ob.pose, "ik_solver")
240
241         split = layout.split(percentage=0.25)
242         split.prop(pchan, "lock_ik_x", icon='LOCKED' if pchan.lock_ik_x else 'UNLOCKED', text="X")
243         split.active = pchan.is_in_ik_chain
244         row = split.row()
245         row.prop(pchan, "ik_stiffness_x", text=_("Stiffness"), slider=True)
246         row.active = pchan.lock_ik_x == False and pchan.is_in_ik_chain
247
248         split = layout.split(percentage=0.25)
249         sub = split.row()
250
251         sub.prop(pchan, "use_ik_limit_x", text=_("Limit"))
252         sub.active = pchan.lock_ik_x == False and pchan.is_in_ik_chain
253         sub = split.row(align=True)
254         sub.prop(pchan, "ik_min_x", text="")
255         sub.prop(pchan, "ik_max_x", text="")
256         sub.active = pchan.lock_ik_x == False and pchan.use_ik_limit_x and pchan.is_in_ik_chain
257
258         split = layout.split(percentage=0.25)
259         split.prop(pchan, "lock_ik_y", icon='LOCKED' if pchan.lock_ik_y else 'UNLOCKED', text="Y")
260         split.active = pchan.is_in_ik_chain
261         row = split.row()
262         row.prop(pchan, "ik_stiffness_y", text=_("Stiffness"), slider=True)
263         row.active = pchan.lock_ik_y == False and pchan.is_in_ik_chain
264
265         split = layout.split(percentage=0.25)
266         sub = split.row()
267
268         sub.prop(pchan, "use_ik_limit_y", text=_("Limit"))
269         sub.active = pchan.lock_ik_y == False and pchan.is_in_ik_chain
270
271         sub = split.row(align=True)
272         sub.prop(pchan, "ik_min_y", text="")
273         sub.prop(pchan, "ik_max_y", text="")
274         sub.active = pchan.lock_ik_y == False and pchan.use_ik_limit_y and pchan.is_in_ik_chain
275
276         split = layout.split(percentage=0.25)
277         split.prop(pchan, "lock_ik_z", icon='LOCKED' if pchan.lock_ik_z else 'UNLOCKED', text="Z")
278         split.active = pchan.is_in_ik_chain
279         sub = split.row()
280         sub.prop(pchan, "ik_stiffness_z", text=_("Stiffness"), slider=True)
281         sub.active = pchan.lock_ik_z == False and pchan.is_in_ik_chain
282
283         split = layout.split(percentage=0.25)
284         sub = split.row()
285
286         sub.prop(pchan, "use_ik_limit_z", text=_("Limit"))
287         sub.active = pchan.lock_ik_z == False and pchan.is_in_ik_chain
288         sub = split.row(align=True)
289         sub.prop(pchan, "ik_min_z", text="")
290         sub.prop(pchan, "ik_max_z", text="")
291         sub.active = pchan.lock_ik_z == False and pchan.use_ik_limit_z and pchan.is_in_ik_chain
292
293         split = layout.split(percentage=0.25)
294         split.label(text=_("Stretch:"))
295         sub = split.row()
296         sub.prop(pchan, "ik_stretch", text="", slider=True)
297         sub.active = pchan.is_in_ik_chain
298
299         if ob.pose.ik_solver == 'ITASC':
300             split = layout.split()
301             col = split.column()
302             col.prop(pchan, "use_ik_rotation_control", text=_("Control Rotation"))
303             col.active = pchan.is_in_ik_chain
304             col = split.column()
305             col.prop(pchan, "ik_rotation_weight", text=_("Weight"), slider=True)
306             col.active = pchan.is_in_ik_chain
307             # not supported yet
308             #row = layout.row()
309             #row.prop(pchan, "use_ik_linear_control", text=_("Joint Size"))
310             #row.prop(pchan, "ik_linear_weight", text=_("Weight"), slider=True)
311
312
313 class BONE_PT_deform(BoneButtonsPanel, Panel):
314     bl_label = "Deform"
315     bl_options = {'DEFAULT_CLOSED'}
316
317     def draw_header(self, context):
318         bone = context.bone
319
320         if not bone:
321             bone = context.edit_bone
322
323         self.layout.prop(bone, "use_deform", text="")
324
325     def draw(self, context):
326         layout = self.layout
327
328         bone = context.bone
329
330         if not bone:
331             bone = context.edit_bone
332
333         layout.active = bone.use_deform
334
335         split = layout.split()
336
337         col = split.column()
338         col.label(text=_("Envelope:"))
339
340         sub = col.column(align=True)
341         sub.prop(bone, "envelope_distance", text=_("Distance"))
342         sub.prop(bone, "envelope_weight", text=_("Weight"))
343         col.prop(bone, "use_envelope_multiply", text=_("Multiply"))
344
345         sub = col.column(align=True)
346         sub.label(text=_("Radius:"))
347         sub.prop(bone, "head_radius", text=_("Head"))
348         sub.prop(bone, "tail_radius", text=_("Tail"))
349
350         col = split.column()
351         col.label(text=_("Curved Bones:"))
352
353         sub = col.column(align=True)
354         sub.prop(bone, "bbone_segments", text=_("Segments"))
355         sub.prop(bone, "bbone_in", text=_("Ease In"))
356         sub.prop(bone, "bbone_out", text=_("Ease Out"))
357
358         col.label(text=_("Offset:"))
359         col.prop(bone, "use_cyclic_offset")
360
361
362 class BONE_PT_custom_props(BoneButtonsPanel, PropertyPanel, Panel):
363     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'}
364     _property_type = bpy.types.Bone, bpy.types.EditBone, bpy.types.PoseBone
365
366     @property
367     def _context_path(self):
368         obj = bpy.context.object
369         if obj and obj.mode == 'POSE':
370             return "active_pose_bone"
371         else:
372             return "active_bone"
373
374 if __name__ == "__main__":  # only for live edit.
375     bpy.utils.register_module(__name__)