Sculpt Branch:
[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
19 # <pep8 compliant>
20
21 from _bpy import types as bpy_types
22
23 StructRNA = bpy_types.Struct.__bases__[0]
24 # StructRNA = bpy_types.Struct
25
26
27 class Context(StructRNA):
28     __slots__ = ()
29
30     def copy(self):
31         new_context = {}
32         generic_keys = StructRNA.__dict__.keys()
33         for item in dir(self):
34             if item not in generic_keys:
35                 new_context[item] = getattr(self, item)
36
37         return new_context
38
39
40 class Object(bpy_types.ID):
41     __slots__ = ()
42
43     @property
44     def children(self):
45         import bpy
46         return [child for child in bpy.data.objects if child.parent == self]
47
48
49 class _GenericBone:
50     '''
51     functions for bones, common between Armature/Pose/Edit bones.
52     internal subclassing use only.
53     '''
54     __slots__ = ()
55
56     def translate(self, vec):
57         self.head += vec
58         self.tail += vec
59
60     def parent_index(self, parent_test):
61         '''
62         The same as 'bone in other_bone.parent_recursive' but saved generating a list.
63         '''
64         # use the name so different types can be tested.
65         name = parent_test.name
66
67         parent = self.parent
68         i = 1
69         while parent:
70             if parent.name == name:
71                 return i
72             parent = parent.parent
73             i += 1
74
75         return 0
76
77     @property
78     def basename(self):
79         #return self.name.rsplit(".", 1)[0]
80         return self.name.split(".")[0]
81
82     @property
83     def parent_recursive(self):
84         parent_list = []
85         parent = self.parent
86
87         while parent:
88             if parent:
89                 parent_list.append(parent)
90
91             parent = parent.parent
92
93         return parent_list
94
95     @property
96     def length(self):
97         return (self.head - self.tail).length
98
99     @length.setter
100     def length(self, value):
101         """The distance from head to tail"""
102         self.tail = self.head + ((self.tail - self.head).normalize() * value)
103
104     @property
105     def children(self):
106         return [child for child in self._other_bones if child.parent == self]
107
108     @property
109     def children_recursive(self):
110         bones_children = []
111         for bone in self._other_bones:
112             index = bone.parent_index(self)
113             if index:
114                 bones_children.append((index, bone))
115
116         # sort by distance to parent
117         bones_children.sort(key=lambda bone_pair: bone_pair[0])
118         return [bone for index, bone in bones_children]
119
120     @property
121     def children_recursive_basename(self):
122         '''
123         Returns a chain of children with the same base name as this bone
124         Only direct chains are supported, forks caused by multiple children with matching basenames will.
125         '''
126         basename = self.basename
127         chain = []
128
129         child = self
130         while True:
131             children = child.children
132             children_basename = []
133
134             for child in children:
135                 if basename == child.basename:
136                     children_basename.append(child)
137
138             if len(children_basename) == 1:
139                 child = children_basename[0]
140                 chain.append(child)
141             else:
142                 if len(children_basename):
143                     print("multiple basenames found, this is probably not what you want!", bone.name, children_basename)
144
145                 break
146
147         return chain
148
149     @property
150     def _other_bones(self):
151         id_data = self.id_data
152         id_data_type = type(id_data)
153
154         if id_data_type == bpy_types.Object:
155             bones = id_data.pose.bones
156         elif id_data_type == bpy_types.Armature:
157             bones = id_data.edit_bones
158             if not bones: # not in editmode
159                 bones = id_data.bones
160
161         return bones
162
163
164 class PoseBone(StructRNA, _GenericBone):
165     __slots__ = ()
166
167
168 class Bone(StructRNA, _GenericBone):
169     __slots__ = ()
170
171
172 class EditBone(StructRNA, _GenericBone):
173     __slots__ = ()
174
175
176 def ord_ind(i1, i2):
177     if i1 < i2:
178         return i1, i2
179     return i2, i1
180
181
182 class Mesh(bpy_types.ID):
183     __slots__ = ()
184
185     def from_pydata(self, verts, edges, faces):
186         '''
187         Make a mesh from a list of verts/edges/faces
188         Until we have a nicer way to make geometry, use this.
189         '''
190         self.add_geometry(len(verts), len(edges), len(faces))
191
192         verts_flat = [f for v in verts for f in v]
193         self.verts.foreach_set("co", verts_flat)
194         del verts_flat
195
196         edges_flat = [i for e in edges for i in e]
197         self.edges.foreach_set("verts", edges_flat)
198         del edges_flat
199
200         def treat_face(f):
201             if len(f) == 3:
202                 return f[0], f[1], f[2], 0
203             elif f[3] == 0:
204                 return f[3], f[0], f[1], f[2]
205             return f
206
207         faces_flat = [v for f in faces for v in treat_face(f)]
208         self.faces.foreach_set("verts_raw", faces_flat)
209         del faces_flat
210
211     @property
212     def edge_keys(self):
213         return [edge_key for face in self.faces for edge_key in face.edge_keys]
214
215     @property
216     def edge_face_count_dict(self):
217         face_edge_keys = [face.edge_keys for face in self.faces]
218         face_edge_count = {}
219         for face_keys in face_edge_keys:
220             for key in face_keys:
221                 try:
222                     face_edge_count[key] += 1
223                 except:
224                     face_edge_count[key] = 1
225
226         return face_edge_count
227
228     @property
229     def edge_face_count(self):
230         edge_face_count_dict = self.edge_face_count_dict
231         return [edge_face_count_dict.get(ed.key, 0) for ed in mesh.edges]
232
233
234 class MeshEdge(StructRNA):
235     __slots__ = ()
236
237     @property
238     def key(self):
239         return ord_ind(*tuple(self.verts))
240
241
242 class MeshFace(StructRNA):
243     __slots__ = ()
244
245     @property
246     def edge_keys(self):
247         verts = tuple(self.verts)
248         if len(verts) == 3:
249             return ord_ind(verts[0], verts[1]), ord_ind(verts[1], verts[2]), ord_ind(verts[2], verts[0])
250
251         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])
252
253
254 import collections
255
256
257 class OrderedMeta(type):
258
259     def __init__(cls, name, bases, attributes):
260         super(OrderedMeta, cls).__init__(name, bases, attributes)
261         cls.order = list(attributes.keys())
262
263     def __prepare__(name, bases, **kwargs):
264         return collections.OrderedDict()
265
266
267 # Only defined so operators members can be used by accessing self.order
268 class Operator(StructRNA, metaclass=OrderedMeta):
269     __slots__ = ()
270
271
272 class Macro(StructRNA, metaclass=OrderedMeta):
273     # bpy_types is imported before ops is defined
274     # so we have to do a local import on each run
275     __slots__ = ()
276
277     @classmethod
278     def define(self, opname):
279         from _bpy import ops
280         return ops.macro_define(self, opname)
281
282
283 class Menu(StructRNA):
284     __slots__ = ()
285
286     def path_menu(self, searchpaths, operator):
287         layout = self.layout
288         # hard coded to set the operators 'path' to the filename.
289
290         import os
291         import bpy.utils
292
293         layout = self.layout
294
295         # collect paths
296         files = []
297         for path in searchpaths:
298             files.extend([(f, os.path.join(path, f)) for f in os.listdir(path)])
299
300         files.sort()
301
302         for f, path in files:
303
304             if f.startswith("."):
305                 continue
306
307             layout.operator(operator, text=bpy.utils.display_name(f)).path = path
308
309     def draw_preset(self, context):
310         '''Define these on the subclass
311          - preset_operator
312          - preset_subdir
313         '''
314         import bpy
315         self.path_menu(bpy.utils.preset_paths(self.preset_subdir), self.preset_operator)