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()) +
38 ["bl_rna", "rna_type", "copy"])
39 for attr in dir(self):
40 if not (attr.startswith("_") or attr in generic_attrs):
41 value = getattr(self, attr)
42 if type(value) != BuiltinMethodType:
43 new_context[attr] = value
48 class Library(bpy_types.ID):
53 """ID data blocks which use this library"""
56 # See: readblenentry.c, IDTYPE_FLAGS_ISLINKABLE,
57 # we could make this an attribute in rna.
58 attr_links = ("actions", "armatures", "brushes", "cameras",
59 "curves", "grease_pencil", "groups", "images",
60 "lamps", "lattices", "materials", "metaballs",
61 "meshes", "node_groups", "objects", "scenes",
62 "sounds", "speakers", "textures", "texts",
66 for attr in attr_links
67 for id_block in getattr(bpy.data, attr)
68 if id_block.library == self)
71 class Texture(bpy_types.ID):
75 def users_material(self):
76 """Materials that use this texture"""
78 return tuple(mat for mat in bpy.data.materials
79 if self in [slot.texture
80 for slot in mat.texture_slots
85 def users_object_modifier(self):
86 """Object modifiers that use this texture"""
88 return tuple(obj for obj in bpy.data.objects if
90 for mod in obj.modifiers
91 if mod.type == 'DISPLACE']
95 class Group(bpy_types.ID):
99 def users_dupli_group(self):
100 """The dupli group this group is used in"""
102 return tuple(obj for obj in bpy.data.objects
103 if self == obj.dupli_group)
106 class Object(bpy_types.ID):
111 """All the children of this object"""
113 return tuple(child for child in bpy.data.objects
114 if child.parent == self)
117 def users_group(self):
118 """The groups this object is in"""
120 return tuple(group for group in bpy.data.groups
121 if self in group.objects[:])
124 def users_scene(self):
125 """The scenes this object is in"""
127 return tuple(scene for scene in bpy.data.scenes
128 if self in scene.objects[:])
133 functions for bones, common between Armature/Pose/Edit bones.
134 internal subclassing use only.
138 def translate(self, vec):
139 """Utility function to add *vec* to the head and tail of this bone"""
143 def parent_index(self, parent_test):
145 The same as 'bone in other_bone.parent_recursive'
146 but saved generating a list.
148 # use the name so different types can be tested.
149 name = parent_test.name
154 if parent.name == name:
156 parent = parent.parent
163 """ Vector pointing down the x-axis of the bone.
165 from mathutils import Vector
166 return self.matrix.to_3x3() * Vector((1.0, 0.0, 0.0))
170 """ Vector pointing down the x-axis of the bone.
172 from mathutils import Vector
173 return self.matrix.to_3x3() * Vector((0.0, 1.0, 0.0))
177 """ Vector pointing down the x-axis of the bone.
179 from mathutils import Vector
180 return self.matrix.to_3x3() * Vector((0.0, 0.0, 1.0))
184 """The name of this bone before any '.' character"""
185 #return self.name.rsplit(".", 1)[0]
186 return self.name.split(".")[0]
189 def parent_recursive(self):
190 """A list of parents, starting with the immediate parent"""
196 parent_list.append(parent)
198 parent = parent.parent
204 """The midpoint between the head and the tail."""
205 return (self.head + self.tail) * 0.5
209 """ The distance from head to tail,
210 when set the head is moved to fit the length.
212 return self.vector.length
215 def length(self, value):
216 self.tail = self.head + ((self.tail - self.head).normalized() * value)
220 """ The direction this bone is pointing.
221 Utility function for (tail - head)
223 return (self.tail - self.head)
227 """A list of all the bones children."""
228 return [child for child in self._other_bones if child.parent == self]
231 def children_recursive(self):
232 """A list of all children from this bone."""
234 for bone in self._other_bones:
235 index = bone.parent_index(self)
237 bones_children.append((index, bone))
239 # sort by distance to parent
240 bones_children.sort(key=lambda bone_pair: bone_pair[0])
241 return [bone for index, bone in bones_children]
244 def children_recursive_basename(self):
246 Returns a chain of children with the same base name as this bone.
247 Only direct chains are supported, forks caused by multiple children
248 with matching base names will terminate the function
251 basename = self.basename
256 children = child.children
257 children_basename = []
259 for child in children:
260 if basename == child.basename:
261 children_basename.append(child)
263 if len(children_basename) == 1:
264 child = children_basename[0]
267 if len(children_basename):
268 print("multiple basenames found, "
269 "this is probably not what you want!",
270 self.name, children_basename)
277 def _other_bones(self):
278 id_data = self.id_data
279 id_data_type = type(id_data)
281 if id_data_type == bpy_types.Object:
282 bones = id_data.pose.bones
283 elif id_data_type == bpy_types.Armature:
284 bones = id_data.edit_bones
285 if not bones: # not in edit mode
286 bones = id_data.bones
291 class PoseBone(StructRNA, _GenericBone, metaclass=StructMetaPropGroup):
295 class Bone(StructRNA, _GenericBone, metaclass=StructMetaPropGroup):
299 class EditBone(StructRNA, _GenericBone, metaclass=StructMetaPropGroup):
302 def align_orientation(self, other):
304 Align this bone to another by moving its tail and settings its roll
305 the length of the other bone is not used.
307 vec = other.vector.normalized() * self.length
308 self.tail = self.head + vec
309 self.roll = other.roll
311 def transform(self, matrix, scale=True, roll=True):
313 Transform the the bones head, tail, roll and envelope
314 (when the matrix has a scale component).
316 :arg matrix: 3x3 or 4x4 transformation matrix.
317 :type matrix: :class:`mathutils.Matrix`
318 :arg scale: Scale the bone envelope by the matrix.
322 Correct the roll to point in the same relative
323 direction to the head and tail.
327 from mathutils import Vector
328 z_vec = self.matrix.to_3x3() * Vector((0.0, 0.0, 1.0))
329 self.tail = matrix * self.tail
330 self.head = matrix * self.head
333 scalar = matrix.median_scale
334 self.head_radius *= scalar
335 self.tail_radius *= scalar
338 self.align_roll(matrix * z_vec)
347 class Mesh(bpy_types.ID):
350 def from_pydata(self, vertices, edges, faces):
352 Make a mesh from a list of vertices/edges/faces
353 Until we have a nicer way to make geometry, use this.
357 float triplets each representing (X, Y, Z)
358 eg: [(0.0, 1.0, 0.5), ...].
360 :type vertices: iterable object
363 int pairs, each pair contains two indices to the
364 *vertices* argument. eg: [(1, 2), ...]
366 :type edges: iterable object
369 iterator of faces, each faces contains three or more indices to
370 the *vertices* argument. eg: [(5, 6, 8, 9), (1, 2, 3), ...]
372 :type faces: iterable object
374 self.vertices.add(len(vertices))
375 self.edges.add(len(edges))
376 self.loops.add(sum((len(f) for f in faces)))
377 self.polygons.add(len(faces))
379 vertices_flat = [f for v in vertices for f in v]
380 self.vertices.foreach_set("co", vertices_flat)
383 edges_flat = [i for e in edges for i in e]
384 self.edges.foreach_set("vertices", edges_flat)
387 # this is different in bmesh
389 for i, p in enumerate(self.polygons):
392 p.loop_start = loop_index
393 p.loop_total = loop_len
395 loop_index += loop_len
397 # if no edges - calculate them
398 if faces and (not edges):
399 self.update(calc_edges=True)
403 return [ed.key for ed in self.edges]
406 class MeshEdge(StructRNA):
411 return ord_ind(*tuple(self.vertices))
414 class MeshTessFace(StructRNA):
419 """The midpoint of the face."""
420 face_verts = self.vertices[:]
421 mesh_verts = self.id_data.vertices
422 if len(face_verts) == 3:
423 return (mesh_verts[face_verts[0]].co +
424 mesh_verts[face_verts[1]].co +
425 mesh_verts[face_verts[2]].co
428 return (mesh_verts[face_verts[0]].co +
429 mesh_verts[face_verts[1]].co +
430 mesh_verts[face_verts[2]].co +
431 mesh_verts[face_verts[3]].co
436 verts = self.vertices[:]
438 return (ord_ind(verts[0], verts[1]),
439 ord_ind(verts[1], verts[2]),
440 ord_ind(verts[2], verts[0]),
443 return (ord_ind(verts[0], verts[1]),
444 ord_ind(verts[1], verts[2]),
445 ord_ind(verts[2], verts[3]),
446 ord_ind(verts[3], verts[0]),
450 class MeshPolygon(StructRNA):
455 verts = self.vertices[:]
456 vlen = len(self.vertices)
457 return [ord_ind(verts[i], verts[(i + 1) % vlen]) for i in range(vlen)]
460 def loop_indices(self):
461 start = self.loop_start
462 end = start + self.loop_total
463 return range(start, end)
466 class Text(bpy_types.ID):
470 """Return the text as a string."""
471 return "\n".join(line.body for line in self.lines)
473 def from_string(self, string):
474 """Replace text with this string."""
479 def users_logic(self):
480 """Logic bricks that use this text"""
482 return tuple(obj for obj in bpy.data.objects
483 if self in [cont.text for cont in obj.game.controllers
484 if cont.type == 'PYTHON']
488 # values are module: [(cls, path, line), ...]
492 class Sound(bpy_types.ID):
497 """The aud.Factory object of the sound."""
499 return aud._sound_from_pointer(self.as_pointer())
503 def __new__(cls, name, bases, classdict, **args):
504 result = type.__new__(cls, name, bases, classdict)
505 if bases and bases[0] is not StructRNA:
506 from _weakref import ref as ref
507 module = result.__module__
509 # first part of packages only
511 module = module[:module.index(".")]
513 TypeMap.setdefault(module, []).append(ref(result))
518 def is_registered(cls):
519 return "bl_rna" in cls.__dict__
522 class OrderedDictMini(dict):
523 def __init__(self, *args):
525 dict.__init__(self, args)
527 def __setitem__(self, key, val):
528 dict.__setitem__(self, key, val)
529 if key not in self.order:
530 self.order.append(key)
532 def __delitem__(self, key):
533 dict.__delitem__(self, key)
534 self.order.remove(key)
537 class RNAMetaPropGroup(RNAMeta, StructMetaPropGroup):
541 class OrderedMeta(RNAMeta):
542 def __init__(cls, name, bases, attributes):
543 if attributes.__class__ is OrderedDictMini:
544 cls.order = attributes.order
546 def __prepare__(name, bases, **kwargs):
547 return OrderedDictMini() # collections.OrderedDict()
550 # Only defined so operators members can be used by accessing self.order
551 # with doc generation 'self.properties.bl_rna.properties' can fail
552 class Operator(StructRNA, metaclass=OrderedMeta):
555 def __getattribute__(self, attr):
556 properties = StructRNA.path_resolve(self, "properties")
557 bl_rna = getattr(properties, "bl_rna", None)
558 if bl_rna and attr in bl_rna.properties:
559 return getattr(properties, attr)
560 return super().__getattribute__(attr)
562 def __setattr__(self, attr, value):
563 properties = StructRNA.path_resolve(self, "properties")
564 bl_rna = getattr(properties, "bl_rna", None)
565 if bl_rna and attr in bl_rna.properties:
566 return setattr(properties, attr, value)
567 return super().__setattr__(attr, value)
569 def __delattr__(self, attr):
570 properties = StructRNA.path_resolve(self, "properties")
571 bl_rna = getattr(properties, "bl_rna", None)
572 if bl_rna and attr in bl_rna.properties:
573 return delattr(properties, attr)
574 return super().__delattr__(attr)
576 def as_keywords(self, ignore=()):
577 """Return a copy of the properties as a dictionary"""
578 ignore = ignore + ("rna_type",)
579 return {attr: getattr(self, attr)
580 for attr in self.properties.rna_type.properties.keys()
581 if attr not in ignore}
584 class Macro(StructRNA, metaclass=OrderedMeta):
585 # bpy_types is imported before ops is defined
586 # so we have to do a local import on each run
590 def define(self, opname):
592 return ops.macro_define(self, opname)
595 class PropertyGroup(StructRNA, metaclass=RNAMetaPropGroup):
599 class RenderEngine(StructRNA, metaclass=RNAMeta):
603 class KeyingSetInfo(StructRNA, metaclass=RNAMeta):
607 class AddonPreferences(StructRNA, metaclass=RNAMeta):
615 def _dyn_ui_initialize(cls):
616 draw_funcs = getattr(cls.draw, "_draw_funcs", None)
618 if draw_funcs is None:
620 def draw_ls(self, context):
621 # ensure menus always get default context
622 operator_context_default = self.layout.operator_context
624 for func in draw_ls._draw_funcs:
625 # so bad menu functions don't stop
626 # the entire menu from drawing
631 traceback.print_exc()
633 self.layout.operator_context = operator_context_default
635 draw_funcs = draw_ls._draw_funcs = [cls.draw]
641 def append(cls, draw_func):
643 Append a draw function to this menu,
644 takes the same arguments as the menus draw function
646 draw_funcs = cls._dyn_ui_initialize()
647 draw_funcs.append(draw_func)
650 def prepend(cls, draw_func):
652 Prepend a draw function to this menu, takes the same arguments as
653 the menus draw function
655 draw_funcs = cls._dyn_ui_initialize()
656 draw_funcs.insert(0, draw_func)
659 def remove(cls, draw_func):
660 """Remove a draw function that has been added to this menu"""
661 draw_funcs = cls._dyn_ui_initialize()
663 draw_funcs.remove(draw_func)
668 class Panel(StructRNA, _GenericUI, metaclass=RNAMeta):
672 class UIList(StructRNA, _GenericUI, metaclass=RNAMeta):
676 class Header(StructRNA, _GenericUI, metaclass=RNAMeta):
680 class Menu(StructRNA, _GenericUI, metaclass=RNAMeta):
683 def path_menu(self, searchpaths, operator,
684 props_default={}, filter_ext=None):
687 # hard coded to set the operators 'filepath' to the filename.
695 layout.label("* Missing Paths *")
699 for directory in searchpaths:
700 files.extend([(f, os.path.join(directory, f))
701 for f in os.listdir(directory)
702 if (not f.startswith("."))
703 if ((filter_ext is None) or
704 (filter_ext(os.path.splitext(f)[1])))
709 for f, filepath in files:
710 props = layout.operator(operator,
711 text=bpy.path.display_name(f),
714 for attr, value in props_default.items():
715 setattr(props, attr, value)
717 props.filepath = filepath
718 if operator == "script.execute_preset":
719 props.menu_idname = self.bl_idname
721 def draw_preset(self, context):
723 Define these on the subclass
728 self.path_menu(bpy.utils.preset_paths(self.preset_subdir),
729 self.preset_operator,
730 filter_ext=lambda ext: ext.lower() in {".py", ".xml"})
732 class Region(StructRNA):
735 def callback_add(self, cb, args, draw_mode):
737 Append a draw function to this region,
738 deprecated, instead use bpy.types.SpaceView3D.draw_handler_add
740 for area in self.id_data.areas:
741 for region in area.regions:
743 spacetype = type(area.spaces[0])
744 return spacetype.draw_handler_add(cb, args, self.type,
750 class NodeTree(bpy_types.ID, metaclass=RNAMetaPropGroup):
754 class NodeSocketTemplate():
757 # Default implementation:
758 # Create a single property using the socket template's 'value_property' attribute
759 # value_property should be created in the __init__ function
761 # If necessary this function can be overloaded in subclasses, e.g. to create multiple value properties
762 def define_node_properties(self, node_type, prefix):
763 if hasattr(self, "value_property"):
764 setattr(node_type, prefix+"value", self.value_property)
766 def init_socket(self, socket):
767 socket.type = self.type
768 if hasattr(self, "value_property"):
769 socket.value_property = self.value_property[1]['attr']
772 def gen_valid_identifier(seq):
775 # pull characters until we get a legal one for first in identifer
777 if ch == '_' or ch.isalpha():
780 # pull remaining characters and yield legal ones for identifier
782 if ch == '_' or ch.isalpha() or ch.isdigit():
785 def sanitize_identifier(name):
786 return ''.join(gen_valid_identifier(name))
788 def unique_identifier(name, identifier_list):
789 # First some basic sanitation, to make a usable identifier string from the name
790 base = sanitize_identifier(name)
791 # Now make a unique identifier by appending an unused index
794 while identifier in identifier_list:
796 identifier = base + str(index)
799 class RNAMetaNode(RNAMetaPropGroup):
800 def __new__(cls, name, bases, classdict, **args):
801 # Wrapper for node.init, to add sockets from templates
803 def create_sockets(self):
804 inputs = getattr(self, 'input_templates', None)
807 socket = self.inputs.new(type=temp.bl_socket_idname, name=temp.name, identifier=temp.identifier)
808 temp.init_socket(socket)
809 outputs = getattr(self, 'output_templates', None)
812 socket = self.outputs.new(type=temp.bl_socket_idname, name=temp.name, identifier=temp.identifier)
813 temp.init_socket(socket)
815 init_base = classdict.get('init', None)
817 def init_node(self, context):
819 init_base(self, context)
821 def init_node(self, context):
824 classdict['init'] = init_node
826 # Create the regular class
827 result = RNAMetaPropGroup.__new__(cls, name, bases, classdict)
829 # Add properties from socket templates
830 inputs = classdict.get('input_templates', None)
832 for i, temp in enumerate(inputs):
833 temp.identifier = unique_identifier(temp.name, [t.identifier for t in inputs[0:i]])
834 temp.define_node_properties(result, "input_"+temp.identifier+"_")
835 outputs = classdict.get('output_templates', None)
837 for i, temp in enumerate(outputs):
838 temp.identifier = unique_identifier(temp.name, [t.identifier for t in outputs[0:i]])
839 temp.define_node_properties(result, "output_"+temp.identifier+"_")
844 class Node(StructRNA, metaclass=RNAMetaNode):
848 def poll(cls, ntree):
852 class NodeSocket(StructRNA, metaclass=RNAMetaPropGroup):
857 """List of node links from or to this socket"""
858 return tuple(link for link in self.id_data.links
859 if (link.from_socket == self or
860 link.to_socket == self))
863 class NodeSocketInterface(StructRNA, metaclass=RNAMetaPropGroup):
867 # These are intermediate subclasses, need a bpy type too
868 class CompositorNode(Node):
872 def poll(cls, ntree):
873 return ntree.bl_idname == 'CompositorNodeTree'
878 class ShaderNode(Node):
882 def poll(cls, ntree):
883 return ntree.bl_idname == 'ShaderNodeTree'
886 class TextureNode(Node):
890 def poll(cls, ntree):
891 return ntree.bl_idname == 'TextureNodeTree'