1 # ##### BEGIN GPL LICENSE BLOCK #####
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.
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.
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.
17 # ##### END GPL LICENSE BLOCK #####
21 from _bpy import types as bpy_types
24 StructRNA = bpy_types.Struct.__bases__[0]
25 StructMetaPropGroup = _bpy.StructMetaPropGroup
26 # StructRNA = bpy_types.Struct
28 bpy_types.BlendDataLibraries.load = _bpy._library_load
31 class Context(StructRNA):
35 from types import BuiltinMethodType
37 generic_attrs = list(StructRNA.__dict__.keys()) + ["bl_rna", "rna_type", "copy"]
38 for attr in dir(self):
39 if not (attr.startswith("_") or attr in generic_attrs):
40 value = getattr(self, attr)
41 if type(value) != BuiltinMethodType:
42 new_context[attr] = value
47 class Library(bpy_types.ID):
52 """ID datablocks which use this library"""
55 # See: readblenentry.c, IDTYPE_FLAGS_ISLINKABLE, we could make this an attribute in rna.
56 attr_links = "actions", "armatures", "brushes", "cameras", \
57 "curves", "grease_pencil", "groups", "images", \
58 "lamps", "lattices", "materials", "metaballs", \
59 "meshes", "node_groups", "objects", "scenes", \
60 "sounds", "speakers", "textures", "texts", "fonts", "worlds"
62 return tuple(id_block for attr in attr_links for id_block in getattr(bpy.data, attr) if id_block.library == self)
65 class Texture(bpy_types.ID):
69 def users_material(self):
70 """Materials that use this texture"""
72 return tuple(mat for mat in bpy.data.materials if self in [slot.texture for slot in mat.texture_slots if slot])
75 def users_object_modifier(self):
76 """Object modifiers that use this texture"""
78 return tuple(obj for obj in bpy.data.objects if self in [mod.texture for mod in obj.modifiers if mod.type == 'DISPLACE'])
81 class Group(bpy_types.ID):
85 def users_dupli_group(self):
86 """The dupli group this group is used in"""
88 return tuple(obj for obj in bpy.data.objects if self == obj.dupli_group)
91 class Object(bpy_types.ID):
96 """All the children of this object"""
98 return tuple(child for child in bpy.data.objects if child.parent == self)
101 def users_group(self):
102 """The groups this object is in"""
104 return tuple(group for group in bpy.data.groups if self in group.objects[:])
107 def users_scene(self):
108 """The scenes this object is in"""
110 return tuple(scene for scene in bpy.data.scenes if self in scene.objects[:])
115 functions for bones, common between Armature/Pose/Edit bones.
116 internal subclassing use only.
120 def translate(self, vec):
121 """Utility function to add *vec* to the head and tail of this bone."""
125 def parent_index(self, parent_test):
127 The same as 'bone in other_bone.parent_recursive' but saved generating a list.
129 # use the name so different types can be tested.
130 name = parent_test.name
135 if parent.name == name:
137 parent = parent.parent
144 """ Vector pointing down the x-axis of the bone.
146 from mathutils import Vector
147 return self.matrix.to_3x3() * Vector((1.0, 0.0, 0.0))
151 """ Vector pointing down the x-axis of the bone.
153 from mathutils import Vector
154 return self.matrix.to_3x3() * Vector((0.0, 1.0, 0.0))
158 """ Vector pointing down the x-axis of the bone.
160 from mathutils import Vector
161 return self.matrix.to_3x3() * Vector((0.0, 0.0, 1.0))
165 """The name of this bone before any '.' character"""
166 #return self.name.rsplit(".", 1)[0]
167 return self.name.split(".")[0]
170 def parent_recursive(self):
171 """A list of parents, starting with the immediate parent"""
177 parent_list.append(parent)
179 parent = parent.parent
185 """The midpoint between the head and the tail."""
186 return (self.head + self.tail) * 0.5
190 """The distance from head to tail, when set the head is moved to fit the length."""
191 return self.vector.length
194 def length(self, value):
195 self.tail = self.head + ((self.tail - self.head).normalized() * value)
199 """The direction this bone is pointing. Utility function for (tail - head)"""
200 return (self.tail - self.head)
204 """A list of all the bones children."""
205 return [child for child in self._other_bones if child.parent == self]
208 def children_recursive(self):
209 """a list of all children from this bone."""
211 for bone in self._other_bones:
212 index = bone.parent_index(self)
214 bones_children.append((index, bone))
216 # sort by distance to parent
217 bones_children.sort(key=lambda bone_pair: bone_pair[0])
218 return [bone for index, bone in bones_children]
221 def children_recursive_basename(self):
223 Returns a chain of children with the same base name as this bone
224 Only direct chains are supported, forks caused by multiple children with matching basenames will
225 terminate the function and not be returned.
227 basename = self.basename
232 children = child.children
233 children_basename = []
235 for child in children:
236 if basename == child.basename:
237 children_basename.append(child)
239 if len(children_basename) == 1:
240 child = children_basename[0]
243 if len(children_basename):
244 print("multiple basenames found, this is probably not what you want!", self.name, children_basename)
251 def _other_bones(self):
252 id_data = self.id_data
253 id_data_type = type(id_data)
255 if id_data_type == bpy_types.Object:
256 bones = id_data.pose.bones
257 elif id_data_type == bpy_types.Armature:
258 bones = id_data.edit_bones
259 if not bones: # not in editmode
260 bones = id_data.bones
265 class PoseBone(StructRNA, _GenericBone, metaclass=StructMetaPropGroup):
269 class Bone(StructRNA, _GenericBone, metaclass=StructMetaPropGroup):
273 class EditBone(StructRNA, _GenericBone, metaclass=StructMetaPropGroup):
276 def align_orientation(self, other):
278 Align this bone to another by moving its tail and settings its roll
279 the length of the other bone is not used.
281 vec = other.vector.normalize() * self.length
282 self.tail = self.head + vec
283 self.roll = other.roll
285 def transform(self, matrix, scale=True, roll=True):
287 Transform the the bones head, tail, roll and envalope (when the matrix has a scale component).
289 :arg matrix: 3x3 or 4x4 transformation matrix.
290 :type matrix: :class:`mathutils.Matrix`
291 :arg scale: Scale the bone envalope by the matrix.
293 :arg roll: Correct the roll to point in the same relative direction to the head and tail.
296 from mathutils import Vector
297 z_vec = self.matrix.to_3x3() * Vector((0.0, 0.0, 1.0))
298 self.tail = matrix * self.tail
299 self.head = matrix * self.head
302 scalar = matrix.median_scale
303 self.head_radius *= scalar
304 self.tail_radius *= scalar
307 self.align_roll(matrix * z_vec)
316 class Mesh(bpy_types.ID):
319 def from_pydata(self, vertices, edges, faces):
321 Make a mesh from a list of verts/edges/faces
322 Until we have a nicer way to make geometry, use this.
324 :arg vertices: float triplets each representing (X, Y, Z) eg: [(0.0, 1.0, 0.5), ...].
325 :type vertices: iterable object
326 :arg edges: int pairs, each pair contains two indices to the *vertices* argument. eg: [(1, 2), ...]
327 :type edges: iterable object
328 :arg faces: iterator of faces, each faces contains three or four indices to the *vertices* argument. eg: [(5, 6, 8, 9), (1, 2, 3), ...]
329 :type faces: iterable object
331 self.vertices.add(len(vertices))
332 self.edges.add(len(edges))
333 self.faces.add(len(faces))
335 vertices_flat = [f for v in vertices for f in v]
336 self.vertices.foreach_set("co", vertices_flat)
339 edges_flat = [i for e in edges for i in e]
340 self.edges.foreach_set("vertices", edges_flat)
346 return f[2], f[0], f[1], 0
348 return f[0], f[1], f[2], 0
349 elif f[2] == 0 or f[3] == 0:
350 return f[2], f[3], f[0], f[1]
353 faces_flat = [v for f in faces for v in treat_face(f)]
354 self.faces.foreach_set("vertices_raw", faces_flat)
359 return [edge_key for face in self.faces for edge_key in face.edge_keys]
362 class MeshEdge(StructRNA):
367 return ord_ind(*tuple(self.vertices))
370 class MeshFace(StructRNA):
375 """The midpoint of the face."""
376 face_verts = self.vertices[:]
377 mesh_verts = self.id_data.vertices
378 if len(face_verts) == 3:
379 return (mesh_verts[face_verts[0]].co + mesh_verts[face_verts[1]].co + mesh_verts[face_verts[2]].co) / 3.0
381 return (mesh_verts[face_verts[0]].co + mesh_verts[face_verts[1]].co + mesh_verts[face_verts[2]].co + mesh_verts[face_verts[3]].co) / 4.0
385 verts = self.vertices[:]
387 return ord_ind(verts[0], verts[1]), ord_ind(verts[1], verts[2]), ord_ind(verts[2], verts[0])
389 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])
392 class Text(bpy_types.ID):
396 """Return the text as a string."""
397 return "\n".join(line.body for line in self.lines)
399 def from_string(self, string):
400 """Replace text with this string."""
405 def users_logic(self):
406 """Logic bricks that use this text"""
408 return tuple(obj for obj in bpy.data.objects if self in [cont.text for cont in obj.game.controllers if cont.type == 'PYTHON'])
410 # values are module: [(cls, path, line), ...]
414 class Sound(bpy_types.ID):
419 """The aud.Factory object of the sound."""
421 return aud._sound_from_pointer(self.as_pointer())
425 def __new__(cls, name, bases, classdict, **args):
426 result = type.__new__(cls, name, bases, classdict)
427 if bases and bases[0] is not StructRNA:
428 from _weakref import ref as ref
429 module = result.__module__
431 # first part of packages only
433 module = module[:module.index(".")]
435 TypeMap.setdefault(module, []).append(ref(result))
440 def is_registered(cls):
441 return "bl_rna" in cls.__dict__
444 class OrderedDictMini(dict):
445 def __init__(self, *args):
447 dict.__init__(self, args)
449 def __setitem__(self, key, val):
450 dict.__setitem__(self, key, val)
451 if key not in self.order:
452 self.order.append(key)
454 def __delitem__(self, key):
455 dict.__delitem__(self, key)
456 self.order.remove(key)
459 class RNAMetaPropGroup(RNAMeta, StructMetaPropGroup):
463 class OrderedMeta(RNAMeta):
464 def __init__(cls, name, bases, attributes):
465 if attributes.__class__ is OrderedDictMini:
466 cls.order = attributes.order
468 def __prepare__(name, bases, **kwargs):
469 return OrderedDictMini() # collections.OrderedDict()
472 # Only defined so operators members can be used by accessing self.order
473 # with doc generation 'self.properties.bl_rna.properties' can fail
474 class Operator(StructRNA, metaclass=OrderedMeta):
477 def __getattribute__(self, attr):
478 properties = StructRNA.path_resolve(self, "properties")
479 bl_rna = getattr(properties, "bl_rna", None)
480 if bl_rna and attr in bl_rna.properties:
481 return getattr(properties, attr)
482 return super().__getattribute__(attr)
484 def __setattr__(self, attr, value):
485 properties = StructRNA.path_resolve(self, "properties")
486 bl_rna = getattr(properties, "bl_rna", None)
487 if bl_rna and attr in bl_rna.properties:
488 return setattr(properties, attr, value)
489 return super().__setattr__(attr, value)
491 def __delattr__(self, attr):
492 properties = StructRNA.path_resolve(self, "properties")
493 bl_rna = getattr(properties, "bl_rna", None)
494 if bl_rna and attr in bl_rna.properties:
495 return delattr(properties, attr)
496 return super().__delattr__(attr)
498 def as_keywords(self, ignore=()):
499 """ Return a copy of the properties as a dictionary.
501 ignore = ignore + ("rna_type",)
502 return {attr: getattr(self, attr) for attr in self.properties.rna_type.properties.keys() if attr not in ignore}
505 class Macro(StructRNA, metaclass=OrderedMeta):
506 # bpy_types is imported before ops is defined
507 # so we have to do a local import on each run
511 def define(self, opname):
513 return ops.macro_define(self, opname)
516 class PropertyGroup(StructRNA, metaclass=RNAMetaPropGroup):
520 class RenderEngine(StructRNA, metaclass=RNAMeta):
524 class KeyingSetInfo(StructRNA, metaclass=RNAMeta):
532 def _dyn_ui_initialize(cls):
533 draw_funcs = getattr(cls.draw, "_draw_funcs", None)
535 if draw_funcs is None:
537 def draw_ls(self, context):
538 # ensure menus always get default context
539 operator_context_default = self.layout.operator_context
541 for func in draw_ls._draw_funcs:
542 # so bad menu functions dont stop the entire menu from drawing.
547 traceback.print_exc()
549 self.layout.operator_context = operator_context_default
551 draw_funcs = draw_ls._draw_funcs = [cls.draw]
557 def append(cls, draw_func):
558 """Append a draw function to this menu, takes the same arguments as the menus draw function."""
559 draw_funcs = cls._dyn_ui_initialize()
560 draw_funcs.append(draw_func)
563 def prepend(cls, draw_func):
564 """Prepend a draw function to this menu, takes the same arguments as the menus draw function."""
565 draw_funcs = cls._dyn_ui_initialize()
566 draw_funcs.insert(0, draw_func)
569 def remove(cls, draw_func):
570 """Remove a draw function that has been added to this menu"""
571 draw_funcs = cls._dyn_ui_initialize()
573 draw_funcs.remove(draw_func)
578 class Panel(StructRNA, _GenericUI, metaclass=RNAMeta):
582 class Header(StructRNA, _GenericUI, metaclass=RNAMeta):
586 class Menu(StructRNA, _GenericUI, metaclass=RNAMeta):
589 def path_menu(self, searchpaths, operator, props_default={}):
591 # hard coded to set the operators 'filepath' to the filename.
599 layout.label("* Missing Paths *")
603 for directory in searchpaths:
604 files.extend([(f, os.path.join(directory, f)) for f in os.listdir(directory)])
608 for f, filepath in files:
610 if f.startswith("."):
613 preset_name = bpy.path.display_name(f)
614 props = layout.operator(operator, text=preset_name)
616 for attr, value in props_default.items():
617 setattr(props, attr, value)
619 props.filepath = filepath
620 if operator == "script.execute_preset":
621 props.menu_idname = self.bl_idname
623 def draw_preset(self, context):
624 """Define these on the subclass
629 self.path_menu(bpy.utils.preset_paths(self.preset_subdir), self.preset_operator)