f42fd8e31072c154bd88b153d8401cd52e575262
[blender-staging.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 # <pep8-80 compliant>
20
21 from _bpy import types as bpy_types
22 import _bpy
23
24 StructRNA = bpy_types.Struct.__bases__[0]
25 StructMetaPropGroup = _bpy.StructMetaPropGroup
26 # StructRNA = bpy_types.Struct
27
28 bpy_types.BlendDataLibraries.load = _bpy._library_load
29
30
31 class Context(StructRNA):
32     __slots__ = ()
33
34     def copy(self):
35         from types import BuiltinMethodType
36         new_context = {}
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
44
45         return new_context
46
47
48 class Library(bpy_types.ID):
49     __slots__ = ()
50
51     @property
52     def users_id(self):
53         """ID data blocks which use this library"""
54         import bpy
55
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",
63                       "fonts", "worlds")
64
65         return tuple(id_block
66                      for attr in attr_links
67                      for id_block in getattr(bpy.data, attr)
68                      if id_block.library == self)
69
70
71 class Texture(bpy_types.ID):
72     __slots__ = ()
73
74     @property
75     def users_material(self):
76         """Materials that use this texture"""
77         import bpy
78         return tuple(mat for mat in bpy.data.materials
79                      if self in [slot.texture
80                                  for slot in mat.texture_slots
81                                  if slot]
82                      )
83
84     @property
85     def users_object_modifier(self):
86         """Object modifiers that use this texture"""
87         import bpy
88         return tuple(obj for obj in bpy.data.objects if
89                      self in [mod.texture
90                               for mod in obj.modifiers
91                               if mod.type == 'DISPLACE']
92                      )
93
94
95 class Group(bpy_types.ID):
96     __slots__ = ()
97
98     @property
99     def users_dupli_group(self):
100         """The dupli group this group is used in"""
101         import bpy
102         return tuple(obj for obj in bpy.data.objects
103                      if self == obj.dupli_group)
104
105
106 class Object(bpy_types.ID):
107     __slots__ = ()
108
109     @property
110     def children(self):
111         """All the children of this object"""
112         import bpy
113         return tuple(child for child in bpy.data.objects
114                      if child.parent == self)
115
116     @property
117     def users_group(self):
118         """The groups this object is in"""
119         import bpy
120         return tuple(group for group in bpy.data.groups
121                      if self in group.objects[:])
122
123     @property
124     def users_scene(self):
125         """The scenes this object is in"""
126         import bpy
127         return tuple(scene for scene in bpy.data.scenes
128                      if self in scene.objects[:])
129
130
131 class _GenericBone:
132     """
133     functions for bones, common between Armature/Pose/Edit bones.
134     internal subclassing use only.
135     """
136     __slots__ = ()
137
138     def translate(self, vec):
139         """Utility function to add *vec* to the head and tail of this bone"""
140         self.head += vec
141         self.tail += vec
142
143     def parent_index(self, parent_test):
144         """
145         The same as 'bone in other_bone.parent_recursive'
146         but saved generating a list.
147         """
148         # use the name so different types can be tested.
149         name = parent_test.name
150
151         parent = self.parent
152         i = 1
153         while parent:
154             if parent.name == name:
155                 return i
156             parent = parent.parent
157             i += 1
158
159         return 0
160
161     @property
162     def x_axis(self):
163         """ Vector pointing down the x-axis of the bone.
164         """
165         from mathutils import Vector
166         return self.matrix.to_3x3() * Vector((1.0, 0.0, 0.0))
167
168     @property
169     def y_axis(self):
170         """ Vector pointing down the x-axis of the bone.
171         """
172         from mathutils import Vector
173         return self.matrix.to_3x3() * Vector((0.0, 1.0, 0.0))
174
175     @property
176     def z_axis(self):
177         """ Vector pointing down the x-axis of the bone.
178         """
179         from mathutils import Vector
180         return self.matrix.to_3x3() * Vector((0.0, 0.0, 1.0))
181
182     @property
183     def basename(self):
184         """The name of this bone before any '.' character"""
185         #return self.name.rsplit(".", 1)[0]
186         return self.name.split(".")[0]
187
188     @property
189     def parent_recursive(self):
190         """A list of parents, starting with the immediate parent"""
191         parent_list = []
192         parent = self.parent
193
194         while parent:
195             if parent:
196                 parent_list.append(parent)
197
198             parent = parent.parent
199
200         return parent_list
201
202     @property
203     def center(self):
204         """The midpoint between the head and the tail."""
205         return (self.head + self.tail) * 0.5
206
207     @property
208     def length(self):
209         """ The distance from head to tail,
210             when set the head is moved to fit the length.
211         """
212         return self.vector.length
213
214     @length.setter
215     def length(self, value):
216         self.tail = self.head + ((self.tail - self.head).normalized() * value)
217
218     @property
219     def vector(self):
220         """ The direction this bone is pointing.
221             Utility function for (tail - head)
222         """
223         return (self.tail - self.head)
224
225     @property
226     def children(self):
227         """A list of all the bones children."""
228         return [child for child in self._other_bones if child.parent == self]
229
230     @property
231     def children_recursive(self):
232         """A list of all children from this bone."""
233         bones_children = []
234         for bone in self._other_bones:
235             index = bone.parent_index(self)
236             if index:
237                 bones_children.append((index, bone))
238
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]
242
243     @property
244     def children_recursive_basename(self):
245         """
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
249         and not be returned.
250         """
251         basename = self.basename
252         chain = []
253
254         child = self
255         while True:
256             children = child.children
257             children_basename = []
258
259             for child in children:
260                 if basename == child.basename:
261                     children_basename.append(child)
262
263             if len(children_basename) == 1:
264                 child = children_basename[0]
265                 chain.append(child)
266             else:
267                 if len(children_basename):
268                     print("multiple basenames found, "
269                           "this is probably not what you want!",
270                           self.name, children_basename)
271
272                 break
273
274         return chain
275
276     @property
277     def _other_bones(self):
278         id_data = self.id_data
279         id_data_type = type(id_data)
280
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
287
288         return bones
289
290
291 class PoseBone(StructRNA, _GenericBone, metaclass=StructMetaPropGroup):
292     __slots__ = ()
293
294
295 class Bone(StructRNA, _GenericBone, metaclass=StructMetaPropGroup):
296     __slots__ = ()
297
298
299 class EditBone(StructRNA, _GenericBone, metaclass=StructMetaPropGroup):
300     __slots__ = ()
301
302     def align_orientation(self, other):
303         """
304         Align this bone to another by moving its tail and settings its roll
305         the length of the other bone is not used.
306         """
307         vec = other.vector.normalized() * self.length
308         self.tail = self.head + vec
309         self.roll = other.roll
310
311     def transform(self, matrix, scale=True, roll=True):
312         """
313         Transform the the bones head, tail, roll and envelope
314         (when the matrix has a scale component).
315
316         :arg matrix: 3x3 or 4x4 transformation matrix.
317         :type matrix: :class:`mathutils.Matrix`
318         :arg scale: Scale the bone envelope by the matrix.
319         :type scale: bool
320         :arg roll:
321
322            Correct the roll to point in the same relative
323            direction to the head and tail.
324
325         :type roll: bool
326         """
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
331
332         if scale:
333             scalar = matrix.median_scale
334             self.head_radius *= scalar
335             self.tail_radius *= scalar
336
337         if roll:
338             self.align_roll(matrix * z_vec)
339
340
341 def ord_ind(i1, i2):
342     if i1 < i2:
343         return i1, i2
344     return i2, i1
345
346
347 class Mesh(bpy_types.ID):
348     __slots__ = ()
349
350     def from_pydata(self, vertices, edges, faces):
351         """
352         Make a mesh from a list of vertices/edges/faces
353         Until we have a nicer way to make geometry, use this.
354
355         :arg vertices:
356
357            float triplets each representing (X, Y, Z)
358            eg: [(0.0, 1.0, 0.5), ...].
359
360         :type vertices: iterable object
361         :arg edges:
362
363            int pairs, each pair contains two indices to the
364            *vertices* argument. eg: [(1, 2), ...]
365
366         :type edges: iterable object
367         :arg faces:
368
369            iterator of faces, each faces contains three or more indices to
370            the *vertices* argument. eg: [(5, 6, 8, 9), (1, 2, 3), ...]
371
372         :type faces: iterable object
373         """
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))
378
379         vertices_flat = [f for v in vertices for f in v]
380         self.vertices.foreach_set("co", vertices_flat)
381         del vertices_flat
382
383         edges_flat = [i for e in edges for i in e]
384         self.edges.foreach_set("vertices", edges_flat)
385         del edges_flat
386
387         # this is different in bmesh
388         loop_index = 0
389         for i, p in enumerate(self.polygons):
390             f = faces[i]
391             loop_len = len(f)
392             p.loop_start = loop_index
393             p.loop_total = loop_len
394             p.vertices = f
395             loop_index += loop_len
396
397         # if no edges - calculate them
398         if faces and (not edges):
399             self.update(calc_edges=True)
400
401     @property
402     def edge_keys(self):
403         return [ed.key for ed in self.edges]
404
405
406 class MeshEdge(StructRNA):
407     __slots__ = ()
408
409     @property
410     def key(self):
411         return ord_ind(*tuple(self.vertices))
412
413
414 class MeshTessFace(StructRNA):
415     __slots__ = ()
416
417     @property
418     def center(self):
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
426                     ) / 3.0
427         else:
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
432                     ) / 4.0
433
434     @property
435     def edge_keys(self):
436         verts = self.vertices[:]
437         if len(verts) == 3:
438             return (ord_ind(verts[0], verts[1]),
439                     ord_ind(verts[1], verts[2]),
440                     ord_ind(verts[2], verts[0]),
441                     )
442         else:
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]),
447                     )
448
449
450 class MeshPolygon(StructRNA):
451     __slots__ = ()
452
453     @property
454     def edge_keys(self):
455         verts = self.vertices[:]
456         vlen = len(self.vertices)
457         return [ord_ind(verts[i], verts[(i + 1) % vlen]) for i in range(vlen)]
458
459     @property
460     def loop_indices(self):
461         start = self.loop_start
462         end = start + self.loop_total
463         return range(start, end)
464
465
466 class Text(bpy_types.ID):
467     __slots__ = ()
468
469     def as_string(self):
470         """Return the text as a string."""
471         return "\n".join(line.body for line in self.lines)
472
473     def from_string(self, string):
474         """Replace text with this string."""
475         self.clear()
476         self.write(string)
477
478     @property
479     def users_logic(self):
480         """Logic bricks that use this text"""
481         import bpy
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']
485                      )
486
487
488 # values are module: [(cls, path, line), ...]
489 TypeMap = {}
490
491
492 class Sound(bpy_types.ID):
493     __slots__ = ()
494
495     @property
496     def factory(self):
497         """The aud.Factory object of the sound."""
498         import aud
499         return aud._sound_from_pointer(self.as_pointer())
500
501
502 class RNAMeta(type):
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__
508
509             # first part of packages only
510             if "." in module:
511                 module = module[:module.index(".")]
512
513             TypeMap.setdefault(module, []).append(ref(result))
514
515         return result
516
517     @property
518     def is_registered(cls):
519         return "bl_rna" in cls.__dict__
520
521
522 class OrderedDictMini(dict):
523     def __init__(self, *args):
524         self.order = []
525         dict.__init__(self, args)
526
527     def __setitem__(self, key, val):
528         dict.__setitem__(self, key, val)
529         if key not in self.order:
530             self.order.append(key)
531
532     def __delitem__(self, key):
533         dict.__delitem__(self, key)
534         self.order.remove(key)
535
536
537 class RNAMetaPropGroup(RNAMeta, StructMetaPropGroup):
538     pass
539
540
541 class OrderedMeta(RNAMeta):
542     def __init__(cls, name, bases, attributes):
543         if attributes.__class__ is OrderedDictMini:
544             cls.order = attributes.order
545
546     def __prepare__(name, bases, **kwargs):
547         return OrderedDictMini()  # collections.OrderedDict()
548
549
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):
553     __slots__ = ()
554
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)
561
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)
568
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)
575
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}
582
583
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
587     __slots__ = ()
588
589     @classmethod
590     def define(self, opname):
591         from _bpy import ops
592         return ops.macro_define(self, opname)
593
594
595 class PropertyGroup(StructRNA, metaclass=RNAMetaPropGroup):
596         __slots__ = ()
597
598
599 class RenderEngine(StructRNA, metaclass=RNAMeta):
600     __slots__ = ()
601
602
603 class KeyingSetInfo(StructRNA, metaclass=RNAMeta):
604     __slots__ = ()
605
606
607 class AddonPreferences(StructRNA, metaclass=RNAMeta):
608     __slots__ = ()
609
610
611 class _GenericUI:
612     __slots__ = ()
613
614     @classmethod
615     def _dyn_ui_initialize(cls):
616         draw_funcs = getattr(cls.draw, "_draw_funcs", None)
617
618         if draw_funcs is None:
619
620             def draw_ls(self, context):
621                 # ensure menus always get default context
622                 operator_context_default = self.layout.operator_context
623
624                 for func in draw_ls._draw_funcs:
625                     # so bad menu functions don't stop
626                     # the entire menu from drawing
627                     try:
628                         func(self, context)
629                     except:
630                         import traceback
631                         traceback.print_exc()
632
633                     self.layout.operator_context = operator_context_default
634
635             draw_funcs = draw_ls._draw_funcs = [cls.draw]
636             cls.draw = draw_ls
637
638         return draw_funcs
639
640     @classmethod
641     def append(cls, draw_func):
642         """
643         Append a draw function to this menu,
644         takes the same arguments as the menus draw function
645         """
646         draw_funcs = cls._dyn_ui_initialize()
647         draw_funcs.append(draw_func)
648
649     @classmethod
650     def prepend(cls, draw_func):
651         """
652         Prepend a draw function to this menu, takes the same arguments as
653         the menus draw function
654         """
655         draw_funcs = cls._dyn_ui_initialize()
656         draw_funcs.insert(0, draw_func)
657
658     @classmethod
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()
662         try:
663             draw_funcs.remove(draw_func)
664         except:
665             pass
666
667
668 class Panel(StructRNA, _GenericUI, metaclass=RNAMeta):
669     __slots__ = ()
670
671
672 class UIList(StructRNA, _GenericUI, metaclass=RNAMeta):
673     __slots__ = ()
674
675
676 class Header(StructRNA, _GenericUI, metaclass=RNAMeta):
677     __slots__ = ()
678
679
680 class Menu(StructRNA, _GenericUI, metaclass=RNAMeta):
681     __slots__ = ()
682
683     def path_menu(self, searchpaths, operator,
684                   props_default={}, filter_ext=None):
685
686         layout = self.layout
687         # hard coded to set the operators 'filepath' to the filename.
688
689         import os
690         import bpy.utils
691
692         layout = self.layout
693
694         if not searchpaths:
695             layout.label("* Missing Paths *")
696
697         # collect paths
698         files = []
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])))
705                           ])
706
707         files.sort()
708
709         for f, filepath in files:
710             props = layout.operator(operator,
711                                     text=bpy.path.display_name(f),
712                                     translate=False)
713
714             for attr, value in props_default.items():
715                 setattr(props, attr, value)
716
717             props.filepath = filepath
718             if operator == "script.execute_preset":
719                 props.menu_idname = self.bl_idname
720
721     def draw_preset(self, context):
722         """
723         Define these on the subclass
724         - preset_operator
725         - preset_subdir
726         """
727         import bpy
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"})
731
732 class Region(StructRNA):
733     __slots__ = ()
734
735     def callback_add(self, cb, args, draw_mode):
736         """
737         Append a draw function to this region,
738         deprecated, instead use bpy.types.SpaceView3D.draw_handler_add
739         """
740         for area in self.id_data.areas:
741             for region in area.regions:
742                 if region == self:
743                     spacetype = type(area.spaces[0])
744                     return spacetype.draw_handler_add(cb, args, self.type,
745                                                       draw_mode)
746
747         return None
748
749
750 class NodeTree(bpy_types.ID, metaclass=RNAMetaPropGroup):
751     __slots__ = ()
752
753
754 class NodeSocketTemplate():
755     type = 'UNDEFINED'
756
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
760     #
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)
765
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']
770
771
772 def gen_valid_identifier(seq):
773     # get an iterator
774     itr = iter(seq)
775     # pull characters until we get a legal one for first in identifer
776     for ch in itr:
777         if ch == '_' or ch.isalpha():
778             yield ch
779             break
780     # pull remaining characters and yield legal ones for identifier
781     for ch in itr:
782         if ch == '_' or ch.isalpha() or ch.isdigit():
783             yield ch
784
785 def sanitize_identifier(name):
786     return ''.join(gen_valid_identifier(name))
787
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
792     identifier = base
793     index = 0
794     while identifier in identifier_list:
795         index += 1
796         identifier = base + str(index)
797     return identifier
798
799 class RNAMetaNode(RNAMetaPropGroup):
800     def __new__(cls, name, bases, classdict, **args):
801         # Wrapper for node.init, to add sockets from templates
802
803         def create_sockets(self):
804             inputs = getattr(self, 'input_templates', None)
805             if inputs:
806                 for temp in inputs:
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)
810             if outputs:
811                 for temp in outputs:
812                     socket = self.outputs.new(type=temp.bl_socket_idname, name=temp.name, identifier=temp.identifier)
813                     temp.init_socket(socket)
814
815         init_base = classdict.get('init', None)
816         if init_base:
817             def init_node(self, context):
818                 create_sockets(self)
819                 init_base(self, context)
820         else:
821             def init_node(self, context):
822                 create_sockets(self)
823
824         classdict['init'] = init_node
825
826         # Create the regular class
827         result = RNAMetaPropGroup.__new__(cls, name, bases, classdict)
828
829         # Add properties from socket templates
830         inputs = classdict.get('input_templates', None)
831         if inputs:
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)
836         if outputs:
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+"_")
840
841         return result
842
843
844 class Node(StructRNA, metaclass=RNAMetaNode):
845     __slots__ = ()
846
847     @classmethod
848     def poll(cls, ntree):
849             return True
850
851
852 class NodeSocket(StructRNA, metaclass=RNAMetaPropGroup):
853     __slots__ = ()
854
855     @property
856     def links(self):
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))
861
862
863 class NodeSocketInterface(StructRNA, metaclass=RNAMetaPropGroup):
864     __slots__ = ()
865
866
867 # These are intermediate subclasses, need a bpy type too
868 class CompositorNode(Node):
869     __slots__ = ()
870
871     @classmethod
872     def poll(cls, ntree):
873             return ntree.bl_idname == 'CompositorNodeTree'
874
875     def update(self):
876         self.tag_need_exec()
877
878 class ShaderNode(Node):
879     __slots__ = ()
880
881     @classmethod
882     def poll(cls, ntree):
883             return ntree.bl_idname == 'ShaderNodeTree'
884
885
886 class TextureNode(Node):
887     __slots__ = ()
888
889     @classmethod
890     def poll(cls, ntree):
891             return ntree.bl_idname == 'TextureNodeTree'
892