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.lerp(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.loops.add(sum((len(f) for f in faces)))
334 self.polygons.add(len(faces))
336 vertices_flat = [f for v in vertices for f in v]
337 self.vertices.foreach_set("co", vertices_flat)
340 edges_flat = [i for e in edges for i in e]
341 self.edges.foreach_set("vertices", edges_flat)
344 # this is different in bmesh
346 for i, p in enumerate(self.polygons):
349 p.loop_start = loop_index
350 p.loop_total = loop_len
352 loop_index += loop_len
356 return [edge_key for face in self.faces for edge_key in face.edge_keys]
359 class MeshEdge(StructRNA):
364 return ord_ind(*tuple(self.vertices))
367 class MeshFace(StructRNA):
372 """The midpoint of the face."""
373 face_verts = self.vertices[:]
374 mesh_verts = self.id_data.vertices
375 if len(face_verts) == 3:
376 return (mesh_verts[face_verts[0]].co + mesh_verts[face_verts[1]].co + mesh_verts[face_verts[2]].co) / 3.0
378 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
382 verts = self.vertices[:]
384 return ord_ind(verts[0], verts[1]), ord_ind(verts[1], verts[2]), ord_ind(verts[2], verts[0])
386 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])
389 class Text(bpy_types.ID):
393 """Return the text as a string."""
394 return "\n".join(line.body for line in self.lines)
396 def from_string(self, string):
397 """Replace text with this string."""
402 def users_logic(self):
403 """Logic bricks that use this text"""
405 return tuple(obj for obj in bpy.data.objects if self in [cont.text for cont in obj.game.controllers if cont.type == 'PYTHON'])
407 # values are module: [(cls, path, line), ...]
411 class Sound(bpy_types.ID):
416 """The aud.Factory object of the sound."""
418 return aud._sound_from_pointer(self.as_pointer())
422 def __new__(cls, name, bases, classdict, **args):
423 result = type.__new__(cls, name, bases, classdict)
424 if bases and bases[0] is not StructRNA:
425 from _weakref import ref as ref
426 module = result.__module__
428 # first part of packages only
430 module = module[:module.index(".")]
432 TypeMap.setdefault(module, []).append(ref(result))
437 def is_registered(cls):
438 return "bl_rna" in cls.__dict__
441 class OrderedDictMini(dict):
442 def __init__(self, *args):
444 dict.__init__(self, args)
446 def __setitem__(self, key, val):
447 dict.__setitem__(self, key, val)
448 if key not in self.order:
449 self.order.append(key)
451 def __delitem__(self, key):
452 dict.__delitem__(self, key)
453 self.order.remove(key)
456 class RNAMetaPropGroup(RNAMeta, StructMetaPropGroup):
460 class OrderedMeta(RNAMeta):
461 def __init__(cls, name, bases, attributes):
462 if attributes.__class__ is OrderedDictMini:
463 cls.order = attributes.order
465 def __prepare__(name, bases, **kwargs):
466 return OrderedDictMini() # collections.OrderedDict()
469 # Only defined so operators members can be used by accessing self.order
470 # with doc generation 'self.properties.bl_rna.properties' can fail
471 class Operator(StructRNA, metaclass=OrderedMeta):
474 def __getattribute__(self, attr):
475 properties = StructRNA.path_resolve(self, "properties")
476 bl_rna = getattr(properties, "bl_rna", None)
477 if bl_rna and attr in bl_rna.properties:
478 return getattr(properties, attr)
479 return super().__getattribute__(attr)
481 def __setattr__(self, attr, value):
482 properties = StructRNA.path_resolve(self, "properties")
483 bl_rna = getattr(properties, "bl_rna", None)
484 if bl_rna and attr in bl_rna.properties:
485 return setattr(properties, attr, value)
486 return super().__setattr__(attr, value)
488 def __delattr__(self, attr):
489 properties = StructRNA.path_resolve(self, "properties")
490 bl_rna = getattr(properties, "bl_rna", None)
491 if bl_rna and attr in bl_rna.properties:
492 return delattr(properties, attr)
493 return super().__delattr__(attr)
495 def as_keywords(self, ignore=()):
496 """ Return a copy of the properties as a dictionary.
498 ignore = ignore + ("rna_type",)
499 return {attr: getattr(self, attr) for attr in self.properties.rna_type.properties.keys() if attr not in ignore}
502 class Macro(StructRNA, metaclass=OrderedMeta):
503 # bpy_types is imported before ops is defined
504 # so we have to do a local import on each run
508 def define(self, opname):
510 return ops.macro_define(self, opname)
513 class PropertyGroup(StructRNA, metaclass=RNAMetaPropGroup):
517 class RenderEngine(StructRNA, metaclass=RNAMeta):
521 class KeyingSetInfo(StructRNA, metaclass=RNAMeta):
529 def _dyn_ui_initialize(cls):
530 draw_funcs = getattr(cls.draw, "_draw_funcs", None)
532 if draw_funcs is None:
534 def draw_ls(self, context):
535 # ensure menus always get default context
536 operator_context_default = self.layout.operator_context
538 for func in draw_ls._draw_funcs:
539 # so bad menu functions dont stop the entire menu from drawing.
544 traceback.print_exc()
546 self.layout.operator_context = operator_context_default
548 draw_funcs = draw_ls._draw_funcs = [cls.draw]
554 def append(cls, draw_func):
555 """Append a draw function to this menu, takes the same arguments as the menus draw function."""
556 draw_funcs = cls._dyn_ui_initialize()
557 draw_funcs.append(draw_func)
560 def prepend(cls, draw_func):
561 """Prepend a draw function to this menu, takes the same arguments as the menus draw function."""
562 draw_funcs = cls._dyn_ui_initialize()
563 draw_funcs.insert(0, draw_func)
566 def remove(cls, draw_func):
567 """Remove a draw function that has been added to this menu"""
568 draw_funcs = cls._dyn_ui_initialize()
570 draw_funcs.remove(draw_func)
575 class Panel(StructRNA, _GenericUI, metaclass=RNAMeta):
579 class Header(StructRNA, _GenericUI, metaclass=RNAMeta):
583 class Menu(StructRNA, _GenericUI, metaclass=RNAMeta):
586 def path_menu(self, searchpaths, operator, props_default={}):
588 # hard coded to set the operators 'filepath' to the filename.
596 layout.label("* Missing Paths *")
600 for directory in searchpaths:
601 files.extend([(f, os.path.join(directory, f)) for f in os.listdir(directory)])
605 for f, filepath in files:
607 if f.startswith("."):
610 preset_name = bpy.path.display_name(f)
611 props = layout.operator(operator, text=preset_name)
613 for attr, value in props_default.items():
614 setattr(props, attr, value)
616 props.filepath = filepath
617 if operator == "script.execute_preset":
618 props.menu_idname = self.bl_idname
620 def draw_preset(self, context):
621 """Define these on the subclass
626 self.path_menu(bpy.utils.preset_paths(self.preset_subdir), self.preset_operator)