- use a generic bone class so all 3 bone types (Edit/Pose/Armature) - can have the...
[blender.git] / release / scripts / modules / bpy_types.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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18 from _bpy import types as bpy_types
19
20 StructRNA = bpy_types.Struct.__bases__[0]
21 # StructRNA = bpy_types.Struct
22
23
24 class Context(StructRNA):
25
26     def copy(self):
27         new_context = {}
28         generic_keys = StructRNA.__dict__.keys()
29         for item in dir(self):
30             if item not in generic_keys:
31                 new_context[item] = getattr(self, item)
32
33         return new_context
34
35
36 class Object(bpy_types.ID):
37
38     @property
39     def children(self):
40         import bpy
41         return [child for child in bpy.data.objects if child.parent == self]
42
43
44 class _GenericBone:
45     '''
46     functions for bones, common between Armature/Pose/Edit bones.
47     internal subclassing use only.
48     '''
49     def parent_index(self, parent_test):
50         '''
51         The same as 'bone in other_bone.parent_recursive' but saved generating a list.
52         '''
53         # use the name so different types can be tested.
54         name = parent_test.name
55         
56         parent = self.parent
57         i = 1
58         while parent:
59             if parent.name == name:
60                 return i
61             parent = parent.parent
62             i += 1
63         
64         return 0
65
66     @property
67     def parent_recursive(self):
68         parent_list = []
69         parent = self.parent
70         
71         while parent:
72             if parent:
73                 parent_list.append(parent)
74             
75             parent = parent.parent
76         
77         return parent_list
78
79     @property
80     def length(self):
81         return (self.head - self.tail).length
82
83     @property
84     def children(self):
85         return [child for child in self._other_bones if child.parent == self]
86
87     @property
88     def children_recursive(self):
89         bones_children = []
90         for bone in self._other_bones:
91             index = bone.parent_index(self)
92             if index:
93                 bones_children.append((index, bone))
94         
95         # sort by distance to parent
96         bones_children.sort(key=lambda bone_pair: bone_pair[0])
97         return [bone for index, bone in bones_children]
98
99     @property
100     def _other_bones(self):
101         id_data = self.id_data
102         id_data_type = type(id_data)
103         
104         if id_data_type == bpy_types.Object:
105             bones = id_data.pose.bones
106         elif id_data_type == bpy_types.Armature:
107             bones = id_data.edit_bones
108             if not bones: # not in editmode
109                 bones = id_data.bones
110         
111         return bones
112
113
114 class PoseBone(StructRNA, _GenericBone):
115     pass
116
117
118 class Bone(StructRNA, _GenericBone):
119     pass
120
121
122 class EditBone(StructRNA, _GenericBone):
123     pass
124
125
126 def ord_ind(i1,i2):
127     if i1<i2: return i1,i2
128     return i2,i1
129
130 class Mesh(bpy_types.ID):
131
132     @property
133     def edge_keys(self):
134         return [edge_key for face in self.faces for edge_key in face.edge_keys]
135
136     @property
137     def edge_face_count_dict(self):
138         face_edge_keys = [face.edge_keys for face in self.faces]
139         face_edge_count = {}
140         for face_keys in face_edge_keys:
141             for key in face_keys:
142                 try:
143                     face_edge_count[key] += 1
144                 except:
145                     face_edge_count[key] = 1
146
147         return face_edge_count
148
149     @property
150     def edge_face_count(self):
151         edge_face_count_dict = self.edge_face_count_dict
152         return [edge_face_count_dict.get(ed.key, 0) for ed in mesh.edges]
153
154
155 class MeshEdge(StructRNA):
156
157     @property
158     def key(self):
159         return ord_ind(*tuple(self.verts))
160
161
162 class MeshFace(StructRNA):
163
164     @property
165     def edge_keys(self):
166         verts = tuple(self.verts)
167         if len(verts)==3:
168             return ord_ind(verts[0], verts[1]),  ord_ind(verts[1], verts[2]),  ord_ind(verts[2], verts[0])
169
170         return ord_ind(verts[0], verts[1]),  ord_ind(verts[1], verts[2]),  ord_ind(verts[2], verts[3]),  ord_ind(verts[3], verts[0])
171
172
173 import collections
174 class OrderedMeta(type):
175     def __init__(cls, name, bases, attributes):
176         super(OrderedMeta, cls).__init__(name, bases, attributes)
177         cls.order = list(attributes.keys())
178     def __prepare__(name, bases, **kwargs):
179         return collections.OrderedDict()
180
181
182 # Only defined so operators members can be used by accessing self.order
183 class Operator(StructRNA, metaclass=OrderedMeta):
184     pass
185
186
187 class Menu(StructRNA):
188     
189     def path_menu(self, searchpaths, operator):
190         layout = self.layout
191         # hard coded to set the operators 'path' to the filename.
192         
193         import os
194
195         def path_to_name(f):
196             ''' Only capitalize all lowercase names, mixed case use them as is.
197             '''
198             f_base = os.path.splitext(f)[0]
199             
200             # string replacements
201             f_base = f_base.replace("_colon_", ":")
202             
203             f_base = f_base.replace("_", " ")
204             
205             if f_base.lower() == f_base:
206                 return ' '.join([w[0].upper() + w[1:] for w in f_base.split()])
207             else:
208                 return f_base
209
210         layout = self.layout
211
212         # collect paths
213         files = []
214         for path in searchpaths:
215             files.extend([(f, os.path.join(path, f)) for f in os.listdir(path)])
216
217         files.sort()
218
219         for f, path in files:
220
221             if f.startswith("."):
222                 continue
223
224             layout.operator(operator, text=path_to_name(f)).path = path
225     
226     def draw_preset(self, context):
227         '''Define these on the subclass
228          - preset_operator
229          - preset_subdir
230         '''
231         import bpy
232         self.path_menu(bpy.utils.preset_paths(self.preset_subdir), self.preset_operator)