Module sverchok.core.socket_conversions
Expand source code
# ##### BEGIN GPL LICENSE BLOCK #####
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License
#  as published by the Free Software Foundation; either version 2
#  of the License, or (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software Foundation,
#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
from copy import deepcopy
from enum import Enum
from sverchok.core.sv_custom_exceptions import ImplicitConversionProhibited
from sverchok.data_structure import get_data_nesting_level, is_ultimately
from sverchok.utils.field.vector import SvVectorField, SvMatrixVectorField, SvConstantVectorField
from sverchok.utils.field.scalar import SvScalarField, SvConstantScalarField
from sverchok.utils.curve import SvCurve
from sverchok.utils.surface import SvSurface
from sverchok.utils.solid_conversion import to_solid_recursive
from mathutils import Matrix, Quaternion
import numpy as np
from numpy import ndarray
def matrices_to_vfield(data):
    if isinstance(data, Matrix):
        data = deepcopy(data)
        return SvMatrixVectorField(data)
    if isinstance(data, (list, tuple)):
        return [matrices_to_vfield(item) for item in data]
    raise TypeError("Unexpected data type from Matrix socket: %s" % type(data))
def vertices_to_vfield(data):
    if isinstance(data, (tuple, list)) and len(data) == 3 and isinstance(data[0], (float, int)):
        data = deepcopy(data)
        return SvConstantVectorField(data)
    elif isinstance(data, (list, tuple)):
        return [vertices_to_vfield(item) for item in data]
    else:
        raise TypeError("Unexpected data type from Vertex socket: %s" % type(data))
def numbers_to_sfield(data):
    if isinstance(data, (int, float)):
        return SvConstantScalarField(data)
    elif isinstance(data, (list, tuple)):
        return [numbers_to_sfield(item) for item in data]
    else:
        raise TypeError("Unexpected data type from String socket: %s" % type(data))
def vectors_to_matrices(source_data):
    """This means we're going to get a flat list of the incoming
    locations and convert those into matrices proper."""
    location_matrices = []
    collect_matrix = location_matrices.append
    def get_all(data):
        for item in data:
            if isinstance(item, (tuple, list, ndarray)) and len(item) == 3 and isinstance(item[0], (float, int)):
                # generate location matrix from location
                x, y, z = item
                collect_matrix(Matrix([(1., .0, .0, x), (.0, 1., .0, y), (.0, .0, 1., z), (.0, .0, .0, 1.)]))
            else:
                get_all(item)
    get_all(source_data)
    return location_matrices
def matrices_to_vectors(source_data):
    locations = []
    collect_vector = locations.append
    def get_all(data):
        for sublist in data:
            collect_vector(sublist.to_translation()[:])
    get_all(source_data)
    return [locations]
def quaternions_to_matrices(source_data):
    matrices = []
    collect_matrix = matrices.append
    def get_all(data):
        for item in data:
            if isinstance(item, (tuple, list)) and len(item) == 4 and isinstance(item[0], (float, int)):
                mat = Quaternion(item).to_matrix().to_4x4()
                collect_matrix(mat)
            else:
                get_all(item)
    get_all(source_data)
    return matrices
def matrices_to_quaternions(source_data):
    quaternions = []
    collect_quaternion = quaternions.append
    def get_all(data):
        for mat in data:
            q = tuple(mat.to_quaternion())
            collect_quaternion(q)
    get_all(source_data)
    return [quaternions]
def string_to_vector(source_data):
    # it can be so that socket is string but data their are already vectors, performance-wise we check only first item
    if isinstance(source_data[0][0], (float, int)):
        return [[(v, v, v) for v in obj] for obj in source_data]
    return source_data
def string_to_color(source_data):
    # it can be so that socket is string but data their are already colors, performance-wise we check only first item
    if isinstance(source_data[0][0], (float, int)):
        return [[(v, v, v, 1) for v in obj] for obj in source_data]
    if len(source_data[0][0]) == 3:
        return vector_to_color(source_data)
    return source_data
def vector_to_color(source_data):
    if source_data and isinstance(source_data[0], np.ndarray):
        out = []
        for obj in source_data:
            out.append(np.concatenate((obj, np.ones((len(obj), 1))), axis=1))
        return out
    else:
        return [[(v[0], v[1], v[2], 1) for v in obj] for obj in source_data]
class NoImplicitConversionPolicy:
    """
    Base (empty) implicit conversion policy.
    This prohibits any implicit conversions.
    """
    @classmethod
    def convert(cls, socket, other, source_data):
        raise ImplicitConversionProhibited(socket)
class LenientImplicitConversionPolicy:
    """
    Lenient implicit conversion policy.
    Does not actually convert anything, but passes any
    type of data as-is.
    To be used for sockets that do not care about the
    nature of data they process (such as most List processing
    nodes).
    """
    @classmethod
    def convert(cls, socket, other, source_data):
        return source_data
class DefaultImplicitConversionPolicy(NoImplicitConversionPolicy):
    """
    Default implicit conversion policy.
    """
    default_conversions = {
        ('SvVerticesSocket', 'SvMatrixSocket'): vectors_to_matrices,
        ('SvVerticesSocket', 'SvColorSocket'): vector_to_color,
        ('SvMatrixSocket', 'SvVerticesSocket'): matrices_to_vectors,
        ('SvMatrixSocket', 'SvQuaternionSocket'): matrices_to_quaternions,
        ('SvQuaternionSocket', 'SvMatrixSocket'): quaternions_to_matrices,
        ('SvStringsSocket', 'SvVerticesSocket'): string_to_vector,
        ('SvStringsSocket', 'SvColorSocket'): string_to_color,
    }
    # socket types that are allowed to consume arbitrary data type.
    lenient_socket_types = {
        'SvStringsSocket',
        'SvObjectSocket',
        'SvColorSocket',
        'SvVerticesSocket',
    }
    @classmethod
    def convert(cls, to_sock, from_sock, source_data):
        # apply default conversion
        convert_pattern = (from_sock.bl_idname, to_sock.bl_idname)
        if conversion := cls.default_conversions.get(convert_pattern):
            return conversion(source_data)
        # conversion is not needed
        elif to_sock.bl_idname in cls.lenient_socket_types \
                or cls.is_expected_type_from_string_socket(to_sock, from_sock, source_data):
            return source_data
        # raise exception
        super().convert(to_sock, from_sock, source_data)
    expected_data_types = {
        'SvScalarFieldSocket': SvScalarField,
        'SvVectorFieldSocket': SvVectorField,
        'SvSurfaceSocket': SvSurface,
        'SvCurveSocket': SvCurve,
    }
    @classmethod
    def is_expected_type_from_string_socket(cls, to_sock, from_sock, source_data):
        if from_sock.bl_idname != 'SvStringsSocket':
            return False
        if expected_type := cls.expected_data_types.get(to_sock.bl_idname):
            return is_ultimately(source_data, expected_type)
def check_nesting_level(func):
    def the_check(source_data):
        level = get_data_nesting_level(source_data)
        if level > 2:
            raise TypeError("Too high data nesting level for Number -> Scalar Field conversion: %s" % level)
        return func(source_data)
    return the_check
class FieldImplicitConversionPolicy(DefaultImplicitConversionPolicy):
    default_conversions = {
        ('SvMatrixSocket', 'SvVectorFieldSocket'): matrices_to_vfield,
        ('SvVerticesSocket', 'SvVectorFieldSocket'): vertices_to_vfield,
        ('SvStringsSocket', 'SvScalarFieldSocket'): check_nesting_level(numbers_to_sfield),
    }
    @classmethod
    def convert(cls, to_sock, from_sock, source_data):
        # let policy to decide if deep copy of data is needed
        types_pattern = (from_sock.bl_idname, to_sock.bl_idname)
        if conversion := cls.default_conversions.get(types_pattern):
            return conversion(source_data)
        return super().convert(to_sock, from_sock, source_data)
class SolidImplicitConversionPolicy(NoImplicitConversionPolicy):
    @classmethod
    def convert(cls, socket, other, source_data):
        try:
            return to_solid_recursive(source_data)
        except TypeError as e:
            raise ImplicitConversionProhibited(
                socket, msg=f"Cannot perform implicit socket conversion for"
                            f" socket {socket.name}: {e}")
conversions = {
    'DefaultImplicitConversionPolicy': DefaultImplicitConversionPolicy,
    'FieldImplicitConversionPolicy': FieldImplicitConversionPolicy,
    'LenientImplicitConversionPolicy': LenientImplicitConversionPolicy,
    'SolidImplicitConversionPolicy': SolidImplicitConversionPolicy,
}
class ConversionPolicies(Enum):
    """It should keeps all policy classes"""
    DEFAULT = DefaultImplicitConversionPolicy
    FIELD = FieldImplicitConversionPolicy
    LENIENT = LenientImplicitConversionPolicy
    SOLID = SolidImplicitConversionPolicy
    @property
    def conversion(self):
        return self.value
    @property
    def conversion_name(self):
        return self.value.__name__Functions
- def check_nesting_level(func)
- 
Expand source codedef check_nesting_level(func): def the_check(source_data): level = get_data_nesting_level(source_data) if level > 2: raise TypeError("Too high data nesting level for Number -> Scalar Field conversion: %s" % level) return func(source_data) return the_check
- def matrices_to_quaternions(source_data)
- 
Expand source codedef matrices_to_quaternions(source_data): quaternions = [] collect_quaternion = quaternions.append def get_all(data): for mat in data: q = tuple(mat.to_quaternion()) collect_quaternion(q) get_all(source_data) return [quaternions]
- def matrices_to_vectors(source_data)
- 
Expand source codedef matrices_to_vectors(source_data): locations = [] collect_vector = locations.append def get_all(data): for sublist in data: collect_vector(sublist.to_translation()[:]) get_all(source_data) return [locations]
- def matrices_to_vfield(data)
- 
Expand source codedef matrices_to_vfield(data): if isinstance(data, Matrix): data = deepcopy(data) return SvMatrixVectorField(data) if isinstance(data, (list, tuple)): return [matrices_to_vfield(item) for item in data] raise TypeError("Unexpected data type from Matrix socket: %s" % type(data))
- def numbers_to_sfield(data)
- 
Expand source codedef numbers_to_sfield(data): if isinstance(data, (int, float)): return SvConstantScalarField(data) elif isinstance(data, (list, tuple)): return [numbers_to_sfield(item) for item in data] else: raise TypeError("Unexpected data type from String socket: %s" % type(data))
- def quaternions_to_matrices(source_data)
- 
Expand source codedef quaternions_to_matrices(source_data): matrices = [] collect_matrix = matrices.append def get_all(data): for item in data: if isinstance(item, (tuple, list)) and len(item) == 4 and isinstance(item[0], (float, int)): mat = Quaternion(item).to_matrix().to_4x4() collect_matrix(mat) else: get_all(item) get_all(source_data) return matrices
- def string_to_color(source_data)
- 
Expand source codedef string_to_color(source_data): # it can be so that socket is string but data their are already colors, performance-wise we check only first item if isinstance(source_data[0][0], (float, int)): return [[(v, v, v, 1) for v in obj] for obj in source_data] if len(source_data[0][0]) == 3: return vector_to_color(source_data) return source_data
- def string_to_vector(source_data)
- 
Expand source codedef string_to_vector(source_data): # it can be so that socket is string but data their are already vectors, performance-wise we check only first item if isinstance(source_data[0][0], (float, int)): return [[(v, v, v) for v in obj] for obj in source_data] return source_data
- def vector_to_color(source_data)
- 
Expand source codedef vector_to_color(source_data): if source_data and isinstance(source_data[0], np.ndarray): out = [] for obj in source_data: out.append(np.concatenate((obj, np.ones((len(obj), 1))), axis=1)) return out else: return [[(v[0], v[1], v[2], 1) for v in obj] for obj in source_data]
- def vectors_to_matrices(source_data)
- 
This means we're going to get a flat list of the incoming locations and convert those into matrices proper. Expand source codedef vectors_to_matrices(source_data): """This means we're going to get a flat list of the incoming locations and convert those into matrices proper.""" location_matrices = [] collect_matrix = location_matrices.append def get_all(data): for item in data: if isinstance(item, (tuple, list, ndarray)) and len(item) == 3 and isinstance(item[0], (float, int)): # generate location matrix from location x, y, z = item collect_matrix(Matrix([(1., .0, .0, x), (.0, 1., .0, y), (.0, .0, 1., z), (.0, .0, .0, 1.)])) else: get_all(item) get_all(source_data) return location_matrices
- def vertices_to_vfield(data)
- 
Expand source codedef vertices_to_vfield(data): if isinstance(data, (tuple, list)) and len(data) == 3 and isinstance(data[0], (float, int)): data = deepcopy(data) return SvConstantVectorField(data) elif isinstance(data, (list, tuple)): return [vertices_to_vfield(item) for item in data] else: raise TypeError("Unexpected data type from Vertex socket: %s" % type(data))
Classes
- class ConversionPolicies (value, names=None, *, module=None, qualname=None, type=None, start=1)
- 
It should keeps all policy classes Expand source codeclass ConversionPolicies(Enum): """It should keeps all policy classes""" DEFAULT = DefaultImplicitConversionPolicy FIELD = FieldImplicitConversionPolicy LENIENT = LenientImplicitConversionPolicy SOLID = SolidImplicitConversionPolicy @property def conversion(self): return self.value @property def conversion_name(self): return self.value.__name__Ancestors- enum.Enum
 Class variables- var DEFAULT
- var FIELD
- var LENIENT
- var SOLID
 Instance variables- var conversion
- 
Expand source code@property def conversion(self): return self.value
- var conversion_name
- 
Expand source code@property def conversion_name(self): return self.value.__name__
 
- class DefaultImplicitConversionPolicy
- 
Default implicit conversion policy. Expand source codeclass DefaultImplicitConversionPolicy(NoImplicitConversionPolicy): """ Default implicit conversion policy. """ default_conversions = { ('SvVerticesSocket', 'SvMatrixSocket'): vectors_to_matrices, ('SvVerticesSocket', 'SvColorSocket'): vector_to_color, ('SvMatrixSocket', 'SvVerticesSocket'): matrices_to_vectors, ('SvMatrixSocket', 'SvQuaternionSocket'): matrices_to_quaternions, ('SvQuaternionSocket', 'SvMatrixSocket'): quaternions_to_matrices, ('SvStringsSocket', 'SvVerticesSocket'): string_to_vector, ('SvStringsSocket', 'SvColorSocket'): string_to_color, } # socket types that are allowed to consume arbitrary data type. lenient_socket_types = { 'SvStringsSocket', 'SvObjectSocket', 'SvColorSocket', 'SvVerticesSocket', } @classmethod def convert(cls, to_sock, from_sock, source_data): # apply default conversion convert_pattern = (from_sock.bl_idname, to_sock.bl_idname) if conversion := cls.default_conversions.get(convert_pattern): return conversion(source_data) # conversion is not needed elif to_sock.bl_idname in cls.lenient_socket_types \ or cls.is_expected_type_from_string_socket(to_sock, from_sock, source_data): return source_data # raise exception super().convert(to_sock, from_sock, source_data) expected_data_types = { 'SvScalarFieldSocket': SvScalarField, 'SvVectorFieldSocket': SvVectorField, 'SvSurfaceSocket': SvSurface, 'SvCurveSocket': SvCurve, } @classmethod def is_expected_type_from_string_socket(cls, to_sock, from_sock, source_data): if from_sock.bl_idname != 'SvStringsSocket': return False if expected_type := cls.expected_data_types.get(to_sock.bl_idname): return is_ultimately(source_data, expected_type)AncestorsSubclassesClass variables- var default_conversions
- var expected_data_types
- var lenient_socket_types
 Static methods- def convert(to_sock, from_sock, source_data)
- 
Expand source code@classmethod def convert(cls, to_sock, from_sock, source_data): # apply default conversion convert_pattern = (from_sock.bl_idname, to_sock.bl_idname) if conversion := cls.default_conversions.get(convert_pattern): return conversion(source_data) # conversion is not needed elif to_sock.bl_idname in cls.lenient_socket_types \ or cls.is_expected_type_from_string_socket(to_sock, from_sock, source_data): return source_data # raise exception super().convert(to_sock, from_sock, source_data)
- def is_expected_type_from_string_socket(to_sock, from_sock, source_data)
- 
Expand source code@classmethod def is_expected_type_from_string_socket(cls, to_sock, from_sock, source_data): if from_sock.bl_idname != 'SvStringsSocket': return False if expected_type := cls.expected_data_types.get(to_sock.bl_idname): return is_ultimately(source_data, expected_type)
 
- class FieldImplicitConversionPolicy
- 
Default implicit conversion policy. Expand source codeclass FieldImplicitConversionPolicy(DefaultImplicitConversionPolicy): default_conversions = { ('SvMatrixSocket', 'SvVectorFieldSocket'): matrices_to_vfield, ('SvVerticesSocket', 'SvVectorFieldSocket'): vertices_to_vfield, ('SvStringsSocket', 'SvScalarFieldSocket'): check_nesting_level(numbers_to_sfield), } @classmethod def convert(cls, to_sock, from_sock, source_data): # let policy to decide if deep copy of data is needed types_pattern = (from_sock.bl_idname, to_sock.bl_idname) if conversion := cls.default_conversions.get(types_pattern): return conversion(source_data) return super().convert(to_sock, from_sock, source_data)AncestorsClass variables- var default_conversions
 Static methods- def convert(to_sock, from_sock, source_data)
- 
Expand source code@classmethod def convert(cls, to_sock, from_sock, source_data): # let policy to decide if deep copy of data is needed types_pattern = (from_sock.bl_idname, to_sock.bl_idname) if conversion := cls.default_conversions.get(types_pattern): return conversion(source_data) return super().convert(to_sock, from_sock, source_data)
 
- class LenientImplicitConversionPolicy
- 
Lenient implicit conversion policy. Does not actually convert anything, but passes any type of data as-is. To be used for sockets that do not care about the nature of data they process (such as most List processing nodes). Expand source codeclass LenientImplicitConversionPolicy: """ Lenient implicit conversion policy. Does not actually convert anything, but passes any type of data as-is. To be used for sockets that do not care about the nature of data they process (such as most List processing nodes). """ @classmethod def convert(cls, socket, other, source_data): return source_dataStatic methods- def convert(socket, other, source_data)
- 
Expand source code@classmethod def convert(cls, socket, other, source_data): return source_data
 
- class NoImplicitConversionPolicy
- 
Base (empty) implicit conversion policy. This prohibits any implicit conversions. Expand source codeclass NoImplicitConversionPolicy: """ Base (empty) implicit conversion policy. This prohibits any implicit conversions. """ @classmethod def convert(cls, socket, other, source_data): raise ImplicitConversionProhibited(socket)SubclassesStatic methods- def convert(socket, other, source_data)
- 
Expand source code@classmethod def convert(cls, socket, other, source_data): raise ImplicitConversionProhibited(socket)
 
- class SolidImplicitConversionPolicy
- 
Base (empty) implicit conversion policy. This prohibits any implicit conversions. Expand source codeclass SolidImplicitConversionPolicy(NoImplicitConversionPolicy): @classmethod def convert(cls, socket, other, source_data): try: return to_solid_recursive(source_data) except TypeError as e: raise ImplicitConversionProhibited( socket, msg=f"Cannot perform implicit socket conversion for" f" socket {socket.name}: {e}")AncestorsStatic methods- def convert(socket, other, source_data)
- 
Expand source code@classmethod def convert(cls, socket, other, source_data): try: return to_solid_recursive(source_data) except TypeError as e: raise ImplicitConversionProhibited( socket, msg=f"Cannot perform implicit socket conversion for" f" socket {socket.name}: {e}")