Module sverchok.utils.sv_transform_helper

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 bpy.props import (EnumProperty, BoolProperty)

from sverchok.data_structure import updateNode
from sverchok.settings import get_param

from math import pi

euler_order_items = [
    ('XYZ', 'XYZ', "", 0),
    ('XZY', 'XZY', "", 1),
    ('YXZ', 'YXZ', "", 2),
    ('YZX', 'YZX', "", 3),
    ('ZXY', 'ZXY', "", 4),
    ('ZYX', 'ZYX', "", 5)
]


class AngleUnits:
    RADIANS = "RAD"
    DEGREES = "DEG"
    UNITIES = "UNI"

    @classmethod
    def get_blender_enum(cls):
        return [
            (AngleUnits.RADIANS, "Radians", "Radians (0-2pi)", "", 0),
            (AngleUnits.DEGREES, "Degrees", "Degrees (0.0-360.0)", "", 1),
            (AngleUnits.UNITIES, "Unities", "Unities (0.0-1.0)", "", 2)
        ]


angle_remap_options = {
    # from               to
    (AngleUnits.RADIANS, AngleUnits.DEGREES): 180/pi,
    (AngleUnits.RADIANS, AngleUnits.UNITIES): 1/(2*pi),
    (AngleUnits.DEGREES, AngleUnits.RADIANS): pi/180,
    (AngleUnits.DEGREES, AngleUnits.UNITIES): 1/360,
    (AngleUnits.UNITIES, AngleUnits.RADIANS): 2*pi,
    (AngleUnits.UNITIES, AngleUnits.DEGREES): 360
}


class SvAngleHelper():

    def get_preferences(self):
        return get_param('auto_update_angle_values', False)


    def angle_conversion_factor(self, from_angle_units, to_angle_units):
        if from_angle_units == to_angle_units:
            return 1

        multiplier = angle_remap_options.get((from_angle_units, to_angle_units))

        if not multiplier:
            raise Error(f"node {self.name} failure to map angle units, {from_angle_units}->{to_angle_units}")

        return multiplier

    def radians_conversion_factor(self):
        return self.angle_conversion_factor(self.angle_units, AngleUnits.RADIANS)

    def degrees_conversion_factor(self):
        return self.angle_conversion_factor(self.angle_units, AngleUnits.DEGREES)

    def unities_conversion_factor(self):
        return self.angle_conversion_factor(self.angle_units, AngleUnits.UNITIES)

    def update_angle(self, context):
        ''' Wrapper to inhibit angle updates when units are changed '''
        if self.inhibit_updates:
            return

        updateNode(self, context)

    def update_angles(self, context, acf):
        ''' Override this in the derived class to update specific angle values'''
        # print("SvAngleHelper update_angles called")

    def update_angle_units(self, context):
        ''' Update all the angles to preserve their values in the new units '''
        if self.angle_units == self.last_angle_units:
            return

        auto_update_angle_values = self.get_preferences()

        if auto_update_angle_values:
            acf = self.angle_conversion_factor(self.last_angle_units, self.angle_units)

            self.inhibit_updates = True  # deactivate updates
            self.update_angles(context, acf)
            self.inhibit_updates = False  # reactivate updates

        self.last_angle_units = self.angle_units  # keep track of the last units
        updateNode(self, context)

    def angle_unit_conversion_factor(self, new_angle_units):
        return self.angle_conversion_factor(self.angle_units, new_angle_units)

    euler_order: EnumProperty(
        name="Euler Order", description="Order of the Euler rotations",
        default="XYZ", items=euler_order_items, update=updateNode)

    angle_units: EnumProperty(
        name="Angle Units", description="Angle units (Radians/Degrees/Unities)",
        default=AngleUnits.DEGREES, items=AngleUnits.get_blender_enum(),
        update=update_angle_units)

    last_angle_units: EnumProperty(
        name="Last Angle Units", description="Angle units (Radians/Degrees/Unities)",
        default=AngleUnits.DEGREES, items=AngleUnits.get_blender_enum())

    inhibit_updates: BoolProperty(
        name='Inhibit Update', description='Flag to inhibit update calls', default=False)

    def draw_angle_euler_buttons(self, context, layout):
        col = layout.column(align=True)
        col.prop(self, "euler_order", text="")

    def draw_angle_units_buttons(self, context, layout):
        box = layout.box()
        box.label(text="Angle Units")
        row = box.row(align=True)
        row.prop(self, "angle_units", expand=True)

Classes

class AngleUnits
Expand source code
class AngleUnits:
    RADIANS = "RAD"
    DEGREES = "DEG"
    UNITIES = "UNI"

    @classmethod
    def get_blender_enum(cls):
        return [
            (AngleUnits.RADIANS, "Radians", "Radians (0-2pi)", "", 0),
            (AngleUnits.DEGREES, "Degrees", "Degrees (0.0-360.0)", "", 1),
            (AngleUnits.UNITIES, "Unities", "Unities (0.0-1.0)", "", 2)
        ]

Class variables

var DEGREES
var RADIANS
var UNITIES

Static methods

def get_blender_enum()
Expand source code
@classmethod
def get_blender_enum(cls):
    return [
        (AngleUnits.RADIANS, "Radians", "Radians (0-2pi)", "", 0),
        (AngleUnits.DEGREES, "Degrees", "Degrees (0.0-360.0)", "", 1),
        (AngleUnits.UNITIES, "Unities", "Unities (0.0-1.0)", "", 2)
    ]
class SvAngleHelper
Expand source code
class SvAngleHelper():

    def get_preferences(self):
        return get_param('auto_update_angle_values', False)


    def angle_conversion_factor(self, from_angle_units, to_angle_units):
        if from_angle_units == to_angle_units:
            return 1

        multiplier = angle_remap_options.get((from_angle_units, to_angle_units))

        if not multiplier:
            raise Error(f"node {self.name} failure to map angle units, {from_angle_units}->{to_angle_units}")

        return multiplier

    def radians_conversion_factor(self):
        return self.angle_conversion_factor(self.angle_units, AngleUnits.RADIANS)

    def degrees_conversion_factor(self):
        return self.angle_conversion_factor(self.angle_units, AngleUnits.DEGREES)

    def unities_conversion_factor(self):
        return self.angle_conversion_factor(self.angle_units, AngleUnits.UNITIES)

    def update_angle(self, context):
        ''' Wrapper to inhibit angle updates when units are changed '''
        if self.inhibit_updates:
            return

        updateNode(self, context)

    def update_angles(self, context, acf):
        ''' Override this in the derived class to update specific angle values'''
        # print("SvAngleHelper update_angles called")

    def update_angle_units(self, context):
        ''' Update all the angles to preserve their values in the new units '''
        if self.angle_units == self.last_angle_units:
            return

        auto_update_angle_values = self.get_preferences()

        if auto_update_angle_values:
            acf = self.angle_conversion_factor(self.last_angle_units, self.angle_units)

            self.inhibit_updates = True  # deactivate updates
            self.update_angles(context, acf)
            self.inhibit_updates = False  # reactivate updates

        self.last_angle_units = self.angle_units  # keep track of the last units
        updateNode(self, context)

    def angle_unit_conversion_factor(self, new_angle_units):
        return self.angle_conversion_factor(self.angle_units, new_angle_units)

    euler_order: EnumProperty(
        name="Euler Order", description="Order of the Euler rotations",
        default="XYZ", items=euler_order_items, update=updateNode)

    angle_units: EnumProperty(
        name="Angle Units", description="Angle units (Radians/Degrees/Unities)",
        default=AngleUnits.DEGREES, items=AngleUnits.get_blender_enum(),
        update=update_angle_units)

    last_angle_units: EnumProperty(
        name="Last Angle Units", description="Angle units (Radians/Degrees/Unities)",
        default=AngleUnits.DEGREES, items=AngleUnits.get_blender_enum())

    inhibit_updates: BoolProperty(
        name='Inhibit Update', description='Flag to inhibit update calls', default=False)

    def draw_angle_euler_buttons(self, context, layout):
        col = layout.column(align=True)
        col.prop(self, "euler_order", text="")

    def draw_angle_units_buttons(self, context, layout):
        box = layout.box()
        box.label(text="Angle Units")
        row = box.row(align=True)
        row.prop(self, "angle_units", expand=True)

Subclasses

  • sverchok.nodes.curve.curve_circle.SvCircleCurveMk2Node
  • sverchok.nodes.generator.torus_mk2.SvTorusNodeMK2
  • sverchok.nodes.generators_extended.ellipse_mk3.SvEllipseNodeMK3
  • sverchok.nodes.generators_extended.ring_mk2.SvRingNodeMK2
  • sverchok.nodes.generators_extended.spiral_mk2.SvSpiralNodeMK2
  • sverchok.nodes.generators_extended.torus_knot_mk2.SvTorusKnotNodeMK2
  • sverchok.nodes.matrix.matrix_in_mk4.SvMatrixInNodeMK4
  • sverchok.nodes.matrix.matrix_out_mk2.SvMatrixOutNodeMK2
  • sverchok.nodes.quaternion.quaternion_in_mk2.SvQuaternionInNodeMK2
  • sverchok.nodes.quaternion.quaternion_out_mk2.SvQuaternionOutNodeMK2

Class variables

var angle_units : <_PropertyDeferred, , {'name': 'Angle Units', 'description': 'Angle units (Radians/Degrees/Unities)', 'default': 'DEG', 'items': [('RAD', 'Radians', 'Radians (0-2pi)', '', 0), ('DEG', 'Degrees', 'Degrees (0.0-360.0)', '', 1), ('UNI', 'Unities', 'Unities (0.0-1.0)', '', 2)], 'update': SvAngleHelper.update_angle_units() at 0x7f2f1b51ad30>, 'attr': 'angle_units'}>
var euler_order : <_PropertyDeferred, , {'name': 'Euler Order', 'description': 'Order of the Euler rotations', 'default': 'XYZ', 'items': [('XYZ', 'XYZ', '', 0), ('XZY', 'XZY', '', 1), ('YXZ', 'YXZ', '', 2), ('YZX', 'YZX', '', 3), ('ZXY', 'ZXY', '', 4), ('ZYX', 'ZYX', '', 5)], 'update': , 'attr': 'euler_order'}>
var inhibit_updates : <_PropertyDeferred, , {'name': 'Inhibit Update', 'description': 'Flag to inhibit update calls', 'default': False, 'attr': 'inhibit_updates'}>
var last_angle_units : <_PropertyDeferred, , {'name': 'Last Angle Units', 'description': 'Angle units (Radians/Degrees/Unities)', 'default': 'DEG', 'items': [('RAD', 'Radians', 'Radians (0-2pi)', '', 0), ('DEG', 'Degrees', 'Degrees (0.0-360.0)', '', 1), ('UNI', 'Unities', 'Unities (0.0-1.0)', '', 2)], 'attr': 'last_angle_units'}>

Methods

def angle_conversion_factor(self, from_angle_units, to_angle_units)
Expand source code
def angle_conversion_factor(self, from_angle_units, to_angle_units):
    if from_angle_units == to_angle_units:
        return 1

    multiplier = angle_remap_options.get((from_angle_units, to_angle_units))

    if not multiplier:
        raise Error(f"node {self.name} failure to map angle units, {from_angle_units}->{to_angle_units}")

    return multiplier
def angle_unit_conversion_factor(self, new_angle_units)
Expand source code
def angle_unit_conversion_factor(self, new_angle_units):
    return self.angle_conversion_factor(self.angle_units, new_angle_units)
def degrees_conversion_factor(self)
Expand source code
def degrees_conversion_factor(self):
    return self.angle_conversion_factor(self.angle_units, AngleUnits.DEGREES)
def draw_angle_euler_buttons(self, context, layout)
Expand source code
def draw_angle_euler_buttons(self, context, layout):
    col = layout.column(align=True)
    col.prop(self, "euler_order", text="")
def draw_angle_units_buttons(self, context, layout)
Expand source code
def draw_angle_units_buttons(self, context, layout):
    box = layout.box()
    box.label(text="Angle Units")
    row = box.row(align=True)
    row.prop(self, "angle_units", expand=True)
def get_preferences(self)
Expand source code
def get_preferences(self):
    return get_param('auto_update_angle_values', False)
def radians_conversion_factor(self)
Expand source code
def radians_conversion_factor(self):
    return self.angle_conversion_factor(self.angle_units, AngleUnits.RADIANS)
def unities_conversion_factor(self)
Expand source code
def unities_conversion_factor(self):
    return self.angle_conversion_factor(self.angle_units, AngleUnits.UNITIES)
def update_angle(self, context)

Wrapper to inhibit angle updates when units are changed

Expand source code
def update_angle(self, context):
    ''' Wrapper to inhibit angle updates when units are changed '''
    if self.inhibit_updates:
        return

    updateNode(self, context)
def update_angle_units(self, context)

Update all the angles to preserve their values in the new units

Expand source code
def update_angle_units(self, context):
    ''' Update all the angles to preserve their values in the new units '''
    if self.angle_units == self.last_angle_units:
        return

    auto_update_angle_values = self.get_preferences()

    if auto_update_angle_values:
        acf = self.angle_conversion_factor(self.last_angle_units, self.angle_units)

        self.inhibit_updates = True  # deactivate updates
        self.update_angles(context, acf)
        self.inhibit_updates = False  # reactivate updates

    self.last_angle_units = self.angle_units  # keep track of the last units
    updateNode(self, context)
def update_angles(self, context, acf)

Override this in the derived class to update specific angle values

Expand source code
def update_angles(self, context, acf):
    ''' Override this in the derived class to update specific angle values'''
    # print("SvAngleHelper update_angles called")